Skip to content

fix(repository-json-schema): added type 'object' to model json schema#5749

Merged
jannyHou merged 1 commit into
loopbackio:masterfrom
ShaunSpinelli:fix/models-schema
Jul 2, 2020
Merged

fix(repository-json-schema): added type 'object' to model json schema#5749
jannyHou merged 1 commit into
loopbackio:masterfrom
ShaunSpinelli:fix/models-schema

Conversation

@ShaunSpinelli

Copy link
Copy Markdown

#3804

Added type 'object' to generated model schema to allow ajv validation to work correctly.

Fixes #3804

Checklist

👉 Read and sign the CLA (Contributor License Agreement) 👈

  • npm test passes on your machine
  • New tests added or existing tests modified to cover all changes
  • Code conforms with the style guide
  • API Documentation in code was updated
  • Documentation in /docs/site was updated
  • Affected artifact templates in packages/cli were updated
  • Affected example projects in examples/* were updated

👉 Check out how to submit a PR 👈

@bajtos bajtos left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @ShaunSpinelli for the pull request. Please add a higher-level test to reproduce the problem you are fixing, the one you described in #3804.

We were discussing type: 'object' change in the past in #3828. During the review, I identified few more test cases to write to ensure all works as expected. I'll re-post them in comments below in a moment.

@bajtos

bajtos commented Jun 16, 2020

Copy link
Copy Markdown
Member

From #3828 (review)

  • Loopback generated JSON Schema omits this declaration. build-schema.ts:353.
  • Omitting this declaration causes ajv validation to pass when a required element (which is a reference to a different object) is set to null.
  • Adding the declaration: "type: 'object'" to generated schemas enables ajv validation to correctly fail when a required referenced property is present but the value set to null.

From #3828 (comment)

I was thinking about adding a new test to packages/rest/src/__tests__/acceptance/validation/validation.acceptance.ts. Apparently, we already have a test to verify that request bodies containing only null are rejected:

https://github.com/strongloop/loopback-next/blob/512d37d4f8165ab02c522f071eb1862bf57125b8/packages/rest/src/__tests__/acceptance/validation/validation.acceptance.ts#L95-L101

If I understand your scenario correctly, then you want to reject requests when a required property is set to null. I think the simplest option is to define such property in the existing Product model and then add a new test to verify what happens when it's set to null. Just make sure to make the new property optional so that existing tests pass.

Alternatively, you can create a new context() block where you define a model & a controller that are specific to your scenario only. This is better in the long run, because we don't want the existing Product controller to grow super large to cover all different property types & definitions.

From #3828 (comment)

I think the change in behavior may be caused by #3895, where we started to add additionalProperties: false to schema emitted for models in strict mode (which is the default).

When I change the definition of Product model in the acceptance test and disable strict mode, then your new test fails as expected. (Unfortunately, there is one more test that fails too.)

Obviously, we don't want to change all existing tests to start testing the scenario with strict: false and no longer test the default setup with strict: true. To support non-strict mode, we need to create a new group of tests in validation.acceptance.ts that will use a new model with strict: false.

Let me ask you a question: how are your models configured? Do you use the default "strict" model behavior, or are you disabling it via model settings? If you are using strict mode, then I think you should be find a this pull request is not needed for your app.

@bajtos

bajtos commented Jun 16, 2020

Copy link
Copy Markdown
Member

IMO, we need to add tests to cover the following scenarios:

I feel it's crucial to verify the outcome of AJV validation before the proposed changes and after the change to ensure we are getting the desired behavior. I consider the changes made in the existing tests as "unit-level" - we know what schema is emitted, but don't verify that it gives the desired behavior during validation.

@ShaunSpinelli

Copy link
Copy Markdown
Author

Hi @bajtos, Thanks for the feedback. I have updated the tests for

And in addition added tests for when the body of the request is a string, which was the initial reason I discovered the issue.

Let me know if these tests are what you were expecting and if there is any other work that required. Cheers

@bajtos bajtos left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank your @ShaunSpinelli for adding new tests, the change looks good to me know.

@jannyHou you are one of the code owners for OpenAPI-related code, could you please review the proposed changes took and take care of landing the pull request?

async create(
@requestBody({required: true}) data: ProductNotStrict,
): Promise<ProductNotStrict> {
return new ProductNotStrict({id: 1, name: 'pencil'});

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please add a code comment to explain why you are using hard-coded model properties instead of passing the data argument? I assume this is needed to bypass validation at juggler level and ensure we reject the request at REST API layer.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ShaunSpinelli same question as @bajtos mentioned, any reason why not use:

return new Product(data);

?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @bajtos and @jannyHou. Thanks for the feedback, that was a mistake and it has been corrected to:

return new ProductNotStrict(data);

@jannyHou jannyHou left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @ShaunSpinelli the changes LGTM. I left a comment, same as @bajtos mentioned, just in case there is anything we didn't catch.

Other than that the PR is good to land 👍 .

async create(
@requestBody({required: true}) data: ProductNotStrict,
): Promise<ProductNotStrict> {
return new ProductNotStrict({id: 1, name: 'pencil'});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ShaunSpinelli same question as @bajtos mentioned, any reason why not use:

return new Product(data);

?

@jannyHou jannyHou left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the update @ShaunSpinelli , the PR looks good now 👍 I am going to land it.
Again we appreciate your contribution 🎉 .

@jannyHou jannyHou merged commit 5c5f9ef into loopbackio:master Jul 2, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Validation passes when an required complex property value is set to an empty string.

4 participants