-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Extract function for parsing operation arguments (#295)
- Loading branch information
1 parent
7be3de2
commit 3e25c78
Showing
7 changed files
with
242 additions
and
84 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
// Copyright IBM Corp. 2017. All Rights Reserved. | ||
// Node module: @loopback/core | ||
// This file is licensed under the MIT License. | ||
// License text available at https://opensource.org/licenses/MIT | ||
|
||
import {ServerRequest as Request} from 'http'; | ||
import { | ||
PathParameterValues, | ||
OperationArgs, | ||
ParsedRequest, | ||
HttpError, | ||
createHttpError, | ||
} from './router/SwaggerRouter'; | ||
import {OperationObject, ParameterObject} from '@loopback/openapi-spec'; | ||
import {promisify} from './promisify'; | ||
|
||
type jsonBodyFn = (req: Request, cb: (err?: Error, body?: {}) => void) => void; | ||
const jsonBody: jsonBodyFn = require('body/json'); | ||
|
||
// tslint:disable:no-any | ||
type MaybeBody = any | undefined; | ||
// tslint:enable:no-any | ||
|
||
const parseJsonBody: (req: Request) => Promise<MaybeBody> = promisify(jsonBody); | ||
|
||
export async function parseOperationArgs(request: ParsedRequest, operationSpec: OperationObject, pathParams: PathParameterValues): Promise<OperationArgs> { | ||
const args: OperationArgs = []; | ||
const body = await loadRequestBodyIfNeeded(operationSpec, request); | ||
return buildOperationArguments(operationSpec, request, pathParams, body); | ||
} | ||
|
||
function loadRequestBodyIfNeeded(operationSpec: OperationObject, request: Request): Promise<MaybeBody> { | ||
if (!hasArgumentsFromBody(operationSpec)) | ||
return Promise.resolve(); | ||
|
||
const contentType = request.headers['content-type']; | ||
if (contentType && !/json/.test(contentType)) { | ||
const err = createHttpError(415, `Content-type ${contentType} is not supported.`); | ||
return Promise.reject(err); | ||
} | ||
|
||
return parseJsonBody(request).catch((err: HttpError) => { | ||
err.statusCode = 400; | ||
return Promise.reject(err); | ||
}); | ||
} | ||
|
||
function hasArgumentsFromBody(operationSpec: OperationObject): boolean { | ||
if (!operationSpec.parameters || !operationSpec.parameters.length) | ||
return false; | ||
|
||
for (const paramSpec of operationSpec.parameters) { | ||
if ('$ref' in paramSpec) continue; | ||
const source = (paramSpec as ParameterObject).in; | ||
if (source === 'formData' || source === 'body') | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
function buildOperationArguments(operationSpec: OperationObject, request: ParsedRequest, | ||
pathParams: PathParameterValues, body?: MaybeBody): OperationArgs { | ||
const args: OperationArgs = []; | ||
|
||
for (const paramSpec of operationSpec.parameters || []) { | ||
if ('$ref' in paramSpec) { | ||
// TODO(bajtos) implement $ref parameters | ||
throw new Error('$ref parameters are not supported yet.'); | ||
} | ||
const spec = paramSpec as ParameterObject; | ||
switch (spec.in) { | ||
case 'query': | ||
args.push(request.query[spec.name]); | ||
break; | ||
case 'path': | ||
args.push(pathParams[spec.name]); | ||
break; | ||
case 'header': | ||
args.push(request.headers[spec.name.toLowerCase()]); | ||
break; | ||
case 'formData': | ||
args.push(body ? body[spec.name] : undefined); | ||
break; | ||
case 'body': | ||
args.push(body); | ||
break; | ||
default: | ||
throw createHttpError(501, 'Parameters with "in: ' + spec.in + '" are not supported yet.'); | ||
} | ||
} | ||
return args; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
// Copyright IBM Corp. 2017. All Rights Reserved. | ||
// Node module: @loopback/core | ||
// This file is licensed under the MIT License. | ||
// License text available at https://opensource.org/licenses/MIT | ||
|
||
// TODO(bajtos) Move this file to a standalone module, or find an existing | ||
// npm module that we could use instead. Just make sure the existing | ||
// module is using native utils.promisify() when available. | ||
|
||
// tslint:disable:no-any | ||
|
||
import * as util from 'util'; | ||
|
||
const nativePromisify = (util as any).promisify; | ||
|
||
export function promisify<T>(func: (callback: (err: any, result: T) => void) => void): () => Promise<T>; | ||
export function promisify<T, A1>(func: (arg1: A1, callback: (err: any, result: T) => void) => void): (arg1: A1) => Promise<T>; | ||
export function promisify<T, A1, A2>(func: (arg1: A1, arg2: A2, callback: (err: any, result: T) => void) => void): (arg1: A1, arg2: A2) => Promise<T>; | ||
|
||
export function promisify<T>(func: (...args: any[]) => void): (...args: any[]) => Promise<T> { | ||
if (nativePromisify) | ||
return nativePromisify(func); | ||
|
||
// The simplest implementation of Promisify | ||
return (...args) => { | ||
return new Promise((resolve, reject) => { | ||
try { | ||
func(...args, (err?: any, result?: any) => { | ||
if (err) reject(err); | ||
else resolve(result); | ||
}); | ||
} catch (err) { | ||
reject(err); | ||
} | ||
}); | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.