mirror of https://github.com/ojizero/portal
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.
139 lines
3.9 KiB
139 lines
3.9 KiB
import { Client, Response, RequestConfig } from './client' |
|
|
|
import defaults from 'lodash.defaultsdeep' |
|
import { |
|
parse as parseQuery, |
|
stringify as stringifyQuery, |
|
} from 'querystring' |
|
|
|
import { ValdiationSpec, ensureValidData } from './validation'; |
|
import { OutgoingHttpHeaders } from 'http'; |
|
|
|
const applicationJson = 'application/json' |
|
|
|
export interface MethodSpec { |
|
path: string, |
|
method?: string, |
|
params?: ValdiationSpec, |
|
body?: ValdiationSpec, |
|
queryString?: ValdiationSpec, |
|
contentType?: string, |
|
accept?: string, |
|
headers?: OutgoingHttpHeaders, |
|
} |
|
|
|
export type RouteFunction = (...args: any[]) => Promise<Response> |
|
export type MethodFactory = (spec: MethodSpec) => RouteFunction |
|
|
|
export function methodGenerator (client: Client): MethodFactory { |
|
return function methodFactory (spec: MethodSpec): RouteFunction { |
|
const { |
|
path, |
|
method: _method = 'GET', |
|
params = undefined, |
|
body = undefined, |
|
queryString = undefined, |
|
contentType = applicationJson, |
|
accept = applicationJson, |
|
headers = {}, |
|
} = spec |
|
|
|
const method = _method.toUpperCase() |
|
|
|
const defaultOptions: RequestConfig = { |
|
headers: { |
|
'Accept': accept, |
|
'Content-Type': contentType, |
|
...headers, |
|
} |
|
} |
|
|
|
return async function (...args: any[]): Promise<Response> { |
|
let length = args.length |
|
|
|
let query |
|
let payload |
|
let options |
|
|
|
if (typeof args[length - 1] === 'object') { |
|
// If the last argument it an |
|
// object use it as options |
|
options = args[length - 1] |
|
args = args.slice(0, length - 1) |
|
length -= 1 |
|
} |
|
|
|
if (typeof args[length - 1] === 'object') { |
|
// If the second to last argument it an |
|
// object use it as a payload |
|
payload = args[length - 1] |
|
args = args.slice(0, length - 1) |
|
length -= 1 |
|
} else if ((!!body) && typeof options !== 'undefined') { |
|
// If paylaod is expected and options is defined use |
|
// the last argument as payload instead of options |
|
payload = options |
|
options = undefined |
|
} |
|
|
|
if (typeof args[length - 1] === 'object') { |
|
// If the third to last argument it an |
|
// object use it as a query string |
|
query = args[length - 1] |
|
args = args.slice(0, length - 1) |
|
length -= 1 |
|
} else if ((!!queryString) && (!!body)) { |
|
// Both query string and body are expected |
|
if (method === 'GET') { |
|
// For GET requests give precedence to query string |
|
query = payload |
|
payload = undefined |
|
} else { |
|
// For everything else give precedence to payload |
|
query = undefined |
|
} |
|
} else if ((!!queryString) && (!body)) { |
|
// Query string is expected but body isn't |
|
query = payload |
|
payload = undefined |
|
} |
|
|
|
ensureValidData(params, args, 'Parameters') |
|
ensureValidData(body, payload, 'Payload') |
|
ensureValidData(queryString, query, 'Query string') |
|
|
|
// Regexp here is global we wanna |
|
// match all avaialbel parameters |
|
let paramsCount: number = (path.match(/:[^\/:]+/g) || []).length |
|
|
|
let fullPath: string = args.reduce((acc, arg) => { |
|
paramsCount -= 1 |
|
|
|
// Regexp here isn't global we wanna |
|
// match the first parameter only |
|
return acc.replace(/:[^\/:&?]+/, arg) |
|
}, path) |
|
|
|
if (paramsCount !== 0) throw new Error('Number of provided parameters does not macth request path arguments') |
|
|
|
if (query) { |
|
let attachedQuery |
|
[ fullPath, attachedQuery ] = fullPath.split('?', 2) |
|
|
|
query = { |
|
...parseQuery(attachedQuery), |
|
...query, |
|
} |
|
|
|
query = stringifyQuery(query) |
|
fullPath = `${fullPath}?${query}` |
|
} |
|
|
|
options = defaults({}, defaultOptions, options) |
|
|
|
return client.request(method, fullPath, payload, options) |
|
} |
|
} |
|
} |
|
|
|
export default methodGenerator
|
|
|