You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

method.ts 3.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. import { Client, Response, RequestConfig } from './client'
  2. import defaults from 'lodash.defaultsdeep'
  3. import {
  4. parse as parseQuery,
  5. stringify as stringifyQuery,
  6. } from 'querystring'
  7. import { ValdiationSpec, ensureValidData } from './validation';
  8. import { OutgoingHttpHeaders } from 'http';
  9. const applicationJson = 'application/json'
  10. export interface MethodSpec {
  11. path: string,
  12. method?: string,
  13. params?: ValdiationSpec,
  14. body?: ValdiationSpec,
  15. queryString?: ValdiationSpec,
  16. contentType?: string,
  17. accept?: string,
  18. // strict?: boolean,
  19. headers?: OutgoingHttpHeaders,
  20. }
  21. export type RouteFunction = (...args: any[]) => Promise<Response>
  22. export type MethodFactory = (spec: MethodSpec) => RouteFunction
  23. export function methodGenerator (client: Client): MethodFactory {
  24. return function methodFactory (spec: MethodSpec): RouteFunction {
  25. const {
  26. path,
  27. method: _method = 'GET',
  28. params = undefined,
  29. body = undefined,
  30. queryString = undefined,
  31. contentType = applicationJson,
  32. accept = applicationJson,
  33. headers = {},
  34. } = spec
  35. const method = _method.toUpperCase()
  36. const defaultOptions: RequestConfig = {
  37. headers: {
  38. 'Accept': accept,
  39. 'Content-Type': contentType,
  40. ...headers,
  41. }
  42. }
  43. return async function (...args: any[]): Promise<Response> {
  44. let length = args.length
  45. let query
  46. let payload
  47. let options
  48. if (typeof args[length - 1] === 'object') {
  49. options = args[length - 1]
  50. args = args.slice(0, length - 1)
  51. length -= 1
  52. }
  53. if (typeof args[length - 1] === 'object') {
  54. payload = args[length - 1]
  55. args = args.slice(0, length - 1)
  56. length -= 1
  57. } else if (
  58. // TODO: ??? does this make sense ?
  59. (typeof body !== 'undefined' || method === 'POST' || method === 'PUT')
  60. && typeof options !== 'undefined'
  61. ) {
  62. const optionsHasPayload = 'payload' in options
  63. payload = optionsHasPayload ? options.payload : options
  64. options = optionsHasPayload ? options : undefined
  65. }
  66. if (typeof args[length - 1] === 'object') {
  67. query = args[length - 1]
  68. args = args.slice(0, length - 1)
  69. length -= 1
  70. } else if (typeof queryString !== 'undefined' && typeof options !== 'undefined') {
  71. const optionsHasQueryString = 'queryString' in options
  72. query = optionsHasQueryString ? options.queryString : options
  73. options = optionsHasQueryString ? options : undefined
  74. } else if (typeof queryString !== 'undefined' && typeof payload !== 'undefined') {
  75. query = payload
  76. payload = undefined
  77. }
  78. ensureValidData(params, args, 'Parameters')
  79. ensureValidData(body, payload, 'Payload')
  80. ensureValidData(queryString, query, 'Query string')
  81. // Regexp here isn global we wanna
  82. // match all avaialbel parameters
  83. let paramsCount = (path.match(/:[^\/:]+/g) || []).length
  84. let fullPath: string = args.reduce((acc, arg) => {
  85. paramsCount -= 1
  86. // Regexp here isn't global we wanna
  87. // match the first parameter only
  88. return acc.replace(/:[^\/:]+/, arg)
  89. }, path)
  90. if (paramsCount !== 0) throw new Error('Number of provided parameters does not macth request path arguments')
  91. if (query) {
  92. let attachedQuery
  93. [ fullPath, attachedQuery ] = fullPath.split('?', 2)
  94. query = {
  95. ...query,
  96. ...parseQuery(attachedQuery),
  97. }
  98. query = stringifyQuery(query)
  99. fullPath = `${fullPath}?${query}`
  100. }
  101. options = defaults({}, defaultOptions, options)
  102. return client.request(method, fullPath, payload, options)
  103. }
  104. }
  105. }
  106. export default methodGenerator