-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(rest): add support for form request body #1838
Conversation
Correctly supporting Let's make sure we have a good understanding of how different values are treated by our body parser and that we are ok with any limitations discovered. For example, let's consider the following schema - I am typing it from top of my head, I hope I won't make any mistakes. const schema ={
type: 'object',
properties: {
id: {type: 'number'},
created: {type: 'string', format: 'date-time'},
tagIds: {type: 'array', items: {type: 'number'}}
}
};
const spec = givenOperationWithRequestBody({
description: 'data',
content: {
'application/x-www-form-urlencoded': {schema}
},
}); Things to consider:
Please add more tests to demonstrate how our REST layer is going to treat these scenarios. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PTAL at my comment above, plus there is a missing test to add.
packages/rest/src/parser.ts
Outdated
) { | ||
return await parseFormBody(request).catch((err: HttpError) => { | ||
debug('Cannot parse request body %j', err); | ||
err.statusCode = 400; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to code coverage report, this branch is not covered. Please add a test to verify how malformed form bodies are handled, we already have a similar test for JSON bodies.
FWIW, I wrote pretty extensive (possibly too extensive) test suite for strong-remotin 3.0, I think many of the test cases are relevant for loopback-next too and we should incorporate them into our test suite too. |
If you allow me to submit some input here. Now, what do I expect from a framework like loopback, is to allow me to transform my data into the type I annotated. This kind of discussion may belong somewhere else ; I would be happy to discuss this matter with you. |
@JesusTheHun thank you for chiming in, this is exactly the kind of feedback we need from our users 👍
Makes sense.
That would be my expectation to! Few discussion points:
Agreed, we were thinking about this sort of extensibility ourselves too. In my opinion, custom data transformers should be left out of the initial implementation and explored as the next step after the basic support for form-data requests is done. |
Feature v1 : @formData()
myMethod(formData: object)
// or
@formData({
properties: ['date', 'username']
// a shorthand for
properties: [
{fieldName: 'date', name: 'date'},
{fieldName: 'username', name: 'username'}
]
})
myMethod(date: string, username: string) Feature v2 : // The key 'user' is registred in the application, and binded to the data transformer
// that returns an instance of MyUserModel.
@formData({
properties: [
'foo', // shorthand still works
{fieldName: 'date', type: 'date'},
{fieldName: 'username', name: 'user', type: 'user'}]
})
myMethod(date: Date, user: MyUserModel) Does that make sense ? |
3cb4df3
to
b7b9686
Compare
@bajtos PTAL |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The implementation looks reasonable now.
Please add tests to cover the error paths per my comments below.
packages/rest/src/parser.ts
Outdated
) { | ||
const body = await parseFormBody(request).catch((err: HttpError) => { | ||
debug('Cannot parse request body %j', err); | ||
err.statusCode = 400; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This branch is still not covered by any test, see https://coveralls.io/builds/19498795/source?filename=packages%2Frest%2Fsrc%2Fparser.ts
Please add a new test. AFAICT, body/form
returns error only when the request body is larger than the configured limit (1MB by default) or when an unsupported content-encoding is used. I guess our best option is to explicitly set Content-Length
header of the outgoing request to a value larger than 1MB.
Relevant source code:
packages/rest/src/parser.ts
Outdated
coercionRequired: true, | ||
}; | ||
} | ||
|
||
if (contentType && !/json/.test(contentType)) { | ||
throw new HttpErrors.UnsupportedMediaType( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This branch is not covered by any test, see https://coveralls.io/builds/19498795/source?filename=packages%2Frest%2Fsrc%2Fparser.ts
Please add a new test, for example sending a text/plain
payload (request body).
b7b9686
to
55cfec0
Compare
@bajtos More tests are added to cover the error cases. There are a few things that we need to fix as follow-ups:
|
bb38d66
to
6d95802
Compare
@raymondfeng Thank you for addressing my comments. I really dislike pull requests combining multiple changes that could have been done in standalone pull requests. Such large pull requests are difficult to review and take long to land. It would be much better if this PR stayed focused on supporting
Please note that we are parsing the request body only if the operation is accepting a parameter parsed from the request body, therefore we cannot simply mount Also at the time we implemented minimal body parsing, we were not using Express yet (IIRC). I believe that both
+1 That's out of scope of this pull request IMO.
Agreed. Again, this is best left for a follow-up pull request. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Besides the comments below, I'd like to see tests verifying what happens when the content is text or html and the method expects an object. This can happen for example when a client sends text or html payload to CRUD api like POST /products
(create a new product).
It makes me wonder how OpenAPI spec is dealing with this issue. AFAICT, Request Body Object typically enumerates content types supported by the endpoint.
IMO, LB4 should leverage the information provided in OpenAPI spec and reject requests with content-type not allowed by the spec early on. There is no need to parse the request body (e.g. text or html) when the endpoint is not prepared to accept it.
packages/rest/src/parser.ts
Outdated
/** | ||
* Express body parser function type | ||
*/ | ||
type BodyParser = ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The file packages/rest/src/parser.ts
is becoming too large to my taste. Since you are already moving code around in this pull request, please move all body-parsing code into a new file, e.g. packages/rest/src/body-parser.ts
.
packages/rest/src/parser.ts
Outdated
* @param request Http request | ||
*/ | ||
function parse(handle: BodyParser, request: Request): Promise<void> { | ||
// A hack to fool TypeScript as we don't need `response` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// A hack to fool TypeScript as we don't need
response
👎
Let's keep using body
module as it does not require us to do such hacks. body
also provides Promise API out of the box.
packages/rest/src/parser.ts
Outdated
reject(err); | ||
return; | ||
} | ||
resolve(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In case we decide to keep this code around: let's resolve the promise with request.body
so that consumers of parse
don't need to understand Express-specific concepts like storing body
on the request
object.
packages/rest/src/parser.ts
Outdated
body = await parseUrlencodedBody(request, options); | ||
if (body) return body; | ||
body = await parseTextBody(request, options); | ||
if (body) return body; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks rather non-optimal to me. Why are we invoking so many body parsers for each request, when the Content-Type
header should contain enough information to decide which parser to invoke?
packages/rest/src/parser.ts
Outdated
options, | ||
); | ||
if (typeis(request, jsonOptions.type)) { | ||
await parse(json(jsonOptions), request); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIUC, this is creating a new json
parser function/middleware for each incoming request. That looks like a preliminary performance pessimization to me - not only we are creating extra closures, but we also prevent the parser from applying memoization/caching and other techniques to improve performance. (This is probably not relevant to JSON, but it may be important for other parsers.)
It would be great if we could reuse parser functions (middleware handlers created by body-parser
factories) across the requests.
packages/rest/src/types.ts
Outdated
/** | ||
* Options for request body parsing | ||
*/ | ||
export type RequestBodyParserOptions = OptionsJson & |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it a good idea to mix all content-type-specific options at the top level?
What if a user wants to configure different limits for JSON and Text payloads? Is it a valid requirement to support?
.catch(err => { | ||
// The server side can close the socket before the client | ||
// side can send out all data | ||
if (err && err.code !== 'EPIPE') throw err; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please extract this code into a shared helper function.
Example usage:
.catch(rethrowUnlessPipeError)
}); | ||
}); | ||
|
||
it('rejects over-limit request form body', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Uh, I did not anticipate how complex this test will become when I asked to add it. Considering that we didn't have this kind of tests before this pull request (in the current master
), I am ok to leave them out of this PR.
In case you prefer to keep these two new tests, then please:
- make it more clear that
EPIPE
is one of the expected outcomes of the test - add a check to verify that the controller method/route handler was not invoked
6d95802
to
ceff03f
Compare
@bajtos I move the switch of |
7f51f2d
to
ba7ffcc
Compare
@bajtos PTAL |
ba7ffcc
to
3d9a005
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code changes looks much better now! I have few minor comments, PTAL.
Besides the code changes, these two new features (form-urlencoded body, customizable parser config) really need documentation. Please add the docs as part of this pull request.
- As a developer building LB4 app accepting large data (>1MB), I want to learn how to increase the maximum request body size accepted by the framework.
- As a developer building a SPA using HTML forms to submit data to my API server, I want to understand how can I write a controller method accepting data in
application/x-www-form-urlencoded
format.
packages/rest/src/parser.ts
Outdated
throw err; | ||
}, | ||
); | ||
// form parser returns an object with prototype |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment looks misplaced to me. IIUC, it's referring to Object.assign
call below, could you please move it one line down?
return {
// form parser returns an object with prototype
value: Object.assign({}, body),
coercionRequired: true,
};
packages/rest/src/parser.ts
Outdated
contentType && | ||
contentType.startsWith('application/x-www-form-urlencoded') | ||
) { | ||
const body = await parseFormBody(request, options).catch( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find it rather weird that you are mixin await
and .catch()
style. Can you rewrite this using try/catch please?
try {
const body = await parseFormBody(request, options)
return { /* ... */ };
} catch (err) {
// handle the error, use a helper function shared by JSON & form parsers
throw normalizeBodyParserError(err);
}
packages/rest/src/parser.ts
Outdated
err.statusCode = 400; | ||
throw err; | ||
}); | ||
const jsonBody = await parseJsonBody(request, options).catch( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto, please use try/catch
instead of .catch()
.
statusCode: 413, | ||
}, | ||
}) | ||
.catch(catchEpipeError) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When I read this line, I have no idea what is the intent. What happens when EPIPE
error is caught? What happens when a different error was thrown?
Also I believe E
in EPIPE
is an abbreviation for Error
(EPIPE
= error pipe), thus EpipeError
feels rather redundant to me ("error pipe error").
I am proposing to rename catchEpipeError
to ignorePipeError
.
Besides the comments posted above (I hope they were persisted by GitHub despite the current outage), I'd like to discuss the following point I raised above:
IIRC, our If we are accepting In the documentation for the new features, please include a short section describing the problem of coercion from urlencoded format that supports string type only into JavaScript types like |
d44f033
to
0764840
Compare
@bajtos I believe that I have addressed your comments and added more information to the docs. PTAL. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are making great progress here!
Please add a test to show how request bodies with complex properties are handled when the body uses URL encoding (not JSON). For example, a Building
model has an location
property that's a GeoPoint with two number properties: lat
and lng
.
An example payload in JSON:
{
"name": "IBM HQ",
"location": {
"lat": 40.741895,
"long": -73.989308,
}
}
How are we going to treat the following url-encoded payload? Will we also coerce lat/lng from strings to numbers?
name=IBM%20HQ&location[lat]=0.741895&location[lng]=-73.989308
To be clear, I am not asking you to implement support for nested objects as part of this pull request, just write a test documenting what happens when the request body contains nested properties.
It would be great to have a test for nested arrays too, e.g. a Product
can have a list of tagIds
- which of the following alternatives work?
name=Pen&tagsId[0]=10&tagIds[1]=20
name=Pen&tagsId[]=10&tagIds[]=20
name=Pen&tagsId=10&tagIds=20
I ran our Todo example application to verify the OpenAPI spec rendered for LB4 applications and the description of request bodies does not look correct to me.
openapi: 3.0.0
# [skipped info object]
paths:
/todos:
post:
# [skipped tags, responses, x-controller/operation-name]
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Todo'
The request body content Notice that requestBody > content
describes only application/json
type and does not allow urlencoded.
As I mentioned earlier (#1838 (comment)), I think we should emit entries for all supported content types by default. I am open to alternative solutions - let's dicuss!
IIRC, our
@requestBody
decorator describes the OpenAPI spec operation as acceptingapplication/json
only by default, see here:If we are accepting
application/x-www-form-urlencoded
now too, then the decorator should be changed to correctly announce that fact in the OpenAPI spec we are emitting.
I think we should also look into ways how to allow app developers to control which content-types they are willing to accept, but that's out of scope of this pull request - let's open a new issue to discuss the options.
docs/site/Parsing-requests.md
Outdated
Please note that `urlencoded` media type does not support data typing. For | ||
example, `key=3` is parsed as `{key: '3'}`. The raw result is then coerced by | ||
AJV based on the matching content schema. See the rules at | ||
https://github.com/epoberezkin/ajv/blob/master/COERCION.md. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's change this naked URL into a nice hyperlink please. For example:
The coercion rules are described in [AJV type coercion rules](https://github.com/epoberezkin/ajv/blob/master/COERCION.md)
docs/site/Parsing-requests.md
Outdated
``` | ||
|
||
By default, the `limit` is `1MB`. Any request with a body length exceeding the | ||
limit will be rejected with http status code 413 (request entity too large). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where can readers find a list of all available options? Can we describe them here? Each item should describe: option name, value type (string, number, boolean, etc.), default value (if any), description.
At minimum, please add a hyperlink pointing users to an existing documentation living elsewhere (e.g. apidocs for RequestBodyParserOptions
or the docs for the body
module).
@@ -46,11 +52,11 @@ export function validateRequestBody( | |||
throw err; | |||
} | |||
|
|||
const schema = getRequestBodySchema(requestBodySpec); | |||
const schema = options.schema || getRequestBodySchema(requestBodySpec); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the purpose of keeping the old flavor where the schema is extracted from requestBodySpec
without taking into account the content type? Can we remove this flavor it please and always require callers of validateRequestBody
to provide the schema
object.
Alternatively, and I think may be a better design, modify validateRequestBody
to accept full RequestBody
interface with all metadata (instead of accepting the body payload only).
export function validateRequestBody(
body: RequestBody,
spec: RequestBodyObject | undefined,
globalSchemas?: SchemasObject,
) {
// ...
const schema = body.schema;
// ...
}
packages/rest/src/parser.ts
Outdated
@@ -129,9 +208,12 @@ function buildOperationArguments( | |||
} | |||
|
|||
debug('Validating request body - value %j', body); | |||
validateRequestBody(body, operationSpec.requestBody, globalSchemas); | |||
validateRequestBody(body.value, operationSpec.requestBody, globalSchemas, { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I proposed elsewhere, let's change validateRequestBody
to accept the full RequestBody
object, it will greatly simplify this line.
validateRequestBody(body, operationSpec.requestBody, globalSchemas);
The function buildOperationArguments
is already complex enough and doing a lot, let's try to push additional functionality down to helper functions like validateRequestBody
.
}); | ||
}); | ||
|
||
let bodyParamControllerInvoked = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please reset this value back to false
in a beforeEach
hook, otherwise the first test touching this variable can trigger failure of other tests sharing it.
// The server side can close the socket before the client | ||
// side can send out all data. For example, `response.end()` | ||
// is called before all request data has been processed due | ||
// to size limit |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's add a link to the relevant Node.js issue please.
function givenBodyParamController() { | ||
bodyParamControllerInvoked = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find this rather hacky, let's move the reset to a before-each hook please.
Have you considered using Sinon stubs instead? https://sinonjs.org/releases/v7.0.0/stubs/
928658b
to
eb3af8f
Compare
We're going to have long list of media types supported with extensions. Defaulting to only |
@bajtos I have addressed your comments. PTAL. |
ecb2edb
to
acfb1ac
Compare
``` | ||
|
||
The encoded value | ||
`'name=IBM%20HQ&location[lat]=0.741895&location[lng]=-73.989308&tags[0]=IT&tags[1]=NY'` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great example 👍
packages/rest/src/parser.ts
Outdated
@@ -129,9 +223,11 @@ function buildOperationArguments( | |||
} | |||
|
|||
debug('Validating request body - value %j', body); | |||
validateRequestBody(body, operationSpec.requestBody, globalSchemas); | |||
validateRequestBody(body, operationSpec.requestBody, globalSchemas, { | |||
coerceTypes: body.coercionRequired, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we passing coerceTypes
in options, when validateRequestBody
can infer the value from body.coercionRequired
?
packages/rest/src/types.ts
Outdated
export type RequestBodyParserOptions = { | ||
/** | ||
* The limit of request body size. By default it is 1MB (1024 * 1024). If a | ||
* stream contains more then 1MB, it returns an error. This prevents someone |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo: more then
. should be more than
.
@@ -81,7 +81,8 @@ export function requestBody(requestBodySpec?: Partial<RequestBodyObject>) { | |||
return function(target: Object, member: string, index: number) { | |||
debug('@requestBody() on %s.%s', target.constructor.name, member); | |||
debug(' parameter index: %s', index); | |||
debug(' options: %s', inspect(requestBodySpec, {depth: null})); | |||
if (debug.enabled) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a comment please to tell our code coverage tool to ignore the fact that this block is intentionally skipped by tests and thus should be not be counted towards code coverage?
@@ -95,7 +96,8 @@ export function requestBody(requestBodySpec?: Partial<RequestBodyObject>) { | |||
|
|||
const paramType = paramTypes[index]; | |||
const schema = resolveSchema(paramType); | |||
debug(' inferred schema: %s', inspect(schema, {depth: null})); | |||
if (debug.enabled) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a comment please to tell our code coverage tool to ignore the fact that this block is intentionally skipped by tests and thus should be not be counted towards code coverage?
@@ -109,7 +111,8 @@ export function requestBody(requestBodySpec?: Partial<RequestBodyObject>) { | |||
requestBodySpec[REQUEST_BODY_INDEX] = index; | |||
} | |||
|
|||
debug(' final spec: ', inspect(requestBodySpec, {depth: null})); | |||
if (debug.enabled) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a comment please to tell our code coverage tool to ignore the fact that this block is intentionally skipped by tests and thus should be not be counted towards code coverage?
const schema = getRequestBodySchema(requestBodySpec); | ||
debug('Request body schema: %j', util.inspect(schema, {depth: null})); | ||
const schema = body.schema; | ||
if (debug.enabled) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a comment please to tell our code coverage tool to ignore the fact that this block is intentionally skipped by tests and thus should be not be counted towards code coverage?
'Converted OpenAPI schema to JSON schema: %s', | ||
util.inspect(jsonSchema, {depth: null}), | ||
); | ||
if (debug.enabled) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a comment please to tell our code coverage tool to ignore the fact that this block is intentionally skipped by tests and thus should be not be counted towards code coverage?
@@ -111,7 +105,9 @@ function validateValueAgainstSchema( | |||
|
|||
const validationErrors = validate.errors; | |||
|
|||
debug('Invalid request body: %s', util.inspect(validationErrors)); | |||
if (debug.enabled) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto, let's disable code coverage for this block please
I am not sure I fully understand what you are proposing. Here is my opinion:
Does this match your vision too? If yes, then I would like this pull request to include the following tests (some of them may already exist, IDK):
If no, then let's discuss more. |
acfb1ac
to
1d43789
Compare
@bajtos We are on the same page.
We have a test to cover that. See https://github.com/strongloop/loopback-next/blob/acfb1ac77a1a88e4dfcbee6e8e75cf0cb7042768/packages/rest/test/integration/http-handler.integration.ts#L271
PTAL. |
0c0d7d3
to
a478e56
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@raymondfeng Mostly LGTM 👍 a few nitpicks and one question regarding the referenced schema, see comment , please note AJV
supports resolving a reference.
@@ -194,6 +194,45 @@ describe('RestServer.getApiSpec()', () => { | |||
}); | |||
}); | |||
|
|||
it('emits all media types for request body', () => { | |||
const opSepc = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: we can leverage OperationSpecBuild
to build the spec.
@@ -58,6 +59,223 @@ describe('operationArgsParser', () => { | |||
expect(args).to.eql([{key: 'value'}]); | |||
}); | |||
|
|||
it('parses body parameter for urlencoded', async () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this PR support a referenced schema?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we do.
@@ -128,6 +128,7 @@ function validateValueAgainstSchema( | |||
function createValidator( | |||
schema: SchemaObject, | |||
globalSchemas?: SchemasObject, | |||
options?: AJV.Options, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you created a type RequestBodyValidationOptions
to be consistent.
@@ -92,15 +88,16 @@ const compiledSchemaCache = new WeakMap(); | |||
function validateValueAgainstSchema( | |||
// tslint:disable-next-line:no-any | |||
body: any, | |||
schema: SchemaObject, | |||
schema: SchemaObject | ReferenceObject, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, any reason remove the ReferenceObject
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's adding ReferenceObject
back.
packages/rest/src/parser.ts
Outdated
} | ||
} | ||
|
||
throw new HttpErrors.UnsupportedMediaType( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a file to organize the HttpErrors we defined: https://github.com/strongloop/loopback-next/blob/a478e567daaf732098ca2c1b9994a479146208fe/packages/rest/src/rest-http-error.ts
Defining errors there makes error messages re-usable, e.g. assert the error and message in test.
a478e56
to
ba40a29
Compare
@jannyHou Good catches. I fixed them. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks mostly good. Please consider addressing the remaining comments above and below too (e.g. disabling code coverage for if (debug.enabled)
).
@@ -194,6 +194,44 @@ describe('RestServer.getApiSpec()', () => { | |||
}); | |||
}); | |||
|
|||
it('emits all media types for request body', () => { | |||
const opSepc = anOperationSpec() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo: opSepc
, should be opSpec
. Since this is the spec we are expecting to be created, and not the spec used to define the API, please rename the variable to expectedOpSpec
or expectedSpec
to make that distinction clear.
See #1797 - allow applications to configure body parser options - match content type to request body spec - use RequestBody for validation - extract unsupported media type error
ba40a29
to
61834d8
Compare
Congratulations gentlemen ! I'll get my hands on it as soon as the doc is online and give you a feedback |
See #1797
Checklist
npm test
passes on your machinepackages/cli
were updatedexamples/*
were updated