express-openapi-validator icon indicating copy to clipboard operation
express-openapi-validator copied to clipboard

Fix #699 serdes missed on items in a collection.

Open robertjustjones opened this issue 4 years ago • 19 comments

In the schema preprocessor recursion, items was left out of the handling for allOf, oneOf, anyOf, properties.

In my addition, I have commented out a check for schema.type == 'array'. Please advice on whether it's prudent to add or should be dropped out.

The test 699.spec.ts fails without this fix and passes with it. Alternatively, serdes.spec.ts could incorporate that change.

One note, I use Date rather than DateTime because json serialization is doing date.toString, so the serdes code isn't really being tested for date-time. Perhaps changing the test to use any other date format that toISOString would help.

#699

robertjustjones avatar Feb 12 '22 17:02 robertjustjones

Rebased in renewed hope of getting merged.

robertjustjones avatar Apr 05 '22 19:04 robertjustjones

Coming here after a bunch of error like this : .response.items[0].createdAt should be string with this kind of declarations:

                  "items": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "required": ["createdAt"],
                      "properties": {
                        "createdAt": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }

I was not getting this errors before, so that strange...

I tried your MR and i confirm your fix work well! Adding schema.type == 'array' looks good to me, but you should use a strict equality operator for code consistence.

Hope it will be merge soon!

Fabiencdp avatar May 18 '22 15:05 Fabiencdp

@Fabiencdp this was more work that I anticipated because the serdes test has been changed. What's odd is that when I run 699.spec with the "array" lines commented out, I would expect the test to fail but it's passing. I think something solved the array issue more subtly, perhaps bumping AJV. Could that be? There's no sense adding the code (202-204 in schema.preprocessor) if it's not needed. I would feel more comfortable getting tests for "array" in serdes.spec, but without them failing it's hard to see if I have them correct.

I've left my commit unsquashed it you'd like to look, not intending for them to be merged as is. Let me know about the above and I can resubmit.

Also, very happy to see the dicer issue addressed.

robertjustjones avatar Jun 16 '22 15:06 robertjustjones

@robertjustjones, i'll check as soon as i can, I had some good test cases on a living application

Fabiencdp avatar Jun 16 '22 20:06 Fabiencdp

I just tried with the last master version (4.14.0-beta.2), builded locally, imported to the live project, still have the serdes errors about dates. Then i added your fix (202-204 in schema.preprocessor), build it again, and it work well!

I didn't worked on the test for the moment, i'll try in the weekend.

Fabiencdp avatar Jun 17 '22 08:06 Fabiencdp

Hi @robertjustjones, can you tell me wich test you expect to fail ?

EDIT: get it, should be the "should control GOOD id format and get a response in expected format" wich check for date format as string, so when commenting the 202-204, it should fail, am i right ? Good catch

Fabiencdp avatar Jun 24 '22 08:06 Fabiencdp

I did some test using a reduced an anonymized part of our project schema, get it to work correctly. here is a gist: https://github.com/robertjustjones/express-openapi-validator/commit/9631dfd99edfeaae1c58cb402c80d3d6c367ce7b#diff-79c6ff5eb8a158b26e0c693cf08386a520d8bd27866f170ca808134d17317d5fR212 (it's an ugly WIP, please don't pay attention to linter stuff)

Fabiencdp avatar Jun 24 '22 10:06 Fabiencdp

@robertjustjones, I digged about the test who should not pass without your fix, I found that it pass only when the array use a $ref. Instead, if you define the schema directly, the test fail as expected. This may be the reason why this bug has not yet been discovered.

To be clear, without the fix, the initial problem is: Date object is not converted to a date-time string, and validating against a schema property defined as date-time fail with this kind of error: "path/0/prop must be string"

For example, let say this part of schema:

       history:
          type: array
          items:
            type: object
            properties:
              updateDate:
                type: string
                format: date-time

with this response object :

let json = {
  history: [
    {
      updateDate: new Date()
    },
  ],
};

Will return {message: "/response/history/0/updateDate must be string", code: 500} wich is not good. Date is a valid date.

But, if you define the schema with a $ref, like this:

        history:
          type: array
          items:
            $ref: "#/components/schemas/HistoryItem"

The validator don't throw and response is valid: { history: [ { updateDate: '2022-08-23T21:46:36.588Z' } ] } It look's like serdes serialization is only used with $ref

So... What should we do ?

Did we must find why it happen ? It could be a problem with the way that reference are parsed ?

Or did we should accept your fix ?

I'll dig a little more while waiting your opinion.

EDIT: I think your fix is right, without it array items don't pass throught schemaVisitor method, so serdes are not applied to items.

Fabiencdp avatar Aug 23 '22 21:08 Fabiencdp

@Fabiencdp does the latest commit with the modified 699.spec cover the case you mention with historyWithoutRef?

robertjustjones avatar Aug 24 '22 15:08 robertjustjones

Nop, i didn't push my last test at the moment

Fabiencdp avatar Aug 24 '22 16:08 Fabiencdp

Sorry, I misunderstood your last comment, I'll check that today

Fabiencdp avatar Aug 25 '22 06:08 Fabiencdp

@robertjustjones, no, it does not cover the case, the schema must be :

        historyWithoutRef:
          type: array
          items:
            type: object
            properties:
              modificationDate:
                type: string
                format: date

Then, comment your fix (schema.preprocessor.ts l202-204)

The run the test, you should get a 500 with body {message: "/response/historyWithoutRef/0/modificationDate must be string", code: 500}

If you re-active your fix, the test pass with success.

Fabiencdp avatar Aug 25 '22 07:08 Fabiencdp

@Fabiencdp you were exactly right! The tests looks to be passing and failing (without the code fix) as expected now.

I squashed the commits. Let's gets this one merged. Thanks, so much!

robertjustjones avatar Aug 25 '22 13:08 robertjustjones

@cdimascio we also need your review

Fabiencdp avatar Aug 25 '22 14:08 Fabiencdp

@cdimascio when do you think you will have time to take a look at this PR?

srgg avatar Aug 31 '22 12:08 srgg

@robertjustjones I've faced with another allOf corner case and curious: if your changes also fixing that:

    post:
      requestBody:
        required: true
        content:
          application/json:
            schema:
              additionalProperties: false
              oneOf:
                - type: array
                  items:
                    $ref: '#/components/schemas/Variable'
                - $ref: '#/components/schemas/Variable'

  schemas:
    Variable:
      description: ""
      type: object
      required:
        - name
        - value
      additionalProperties: false
      properties:
        name:
          type: string
        value:
          type: string
          nullable: true

Check1, body:

{
  "name": "new variable",
  "value": "just new"
}

Fails and this is not expected:

{
  "code": 400,
  "message": "request/body must NOT have additional properties, request/body must NOT have additional properties",
  "errors": [
    {
      "path": "/body/name",
      "message": "must NOT have additional properties",
      "errorCode": "additionalProperties.openapi.validation"
    },
    {
      "path": "/body/value",
      "message": "must NOT have additional properties",
      "errorCode": "additionalProperties.openapi.validation"
    }
  ]
}

Check2, body:

[
  {
    "name": "new variable",
    "value": "just new"
  },
  {
    "name": "new variable",
    "value": "just new"
  }
]

Pass request validation as expected

srgg avatar Sep 01 '22 02:09 srgg

@srgg

I'm not sure if "additionalProperties" is valid before "oneOf", "additionalProperties" should be inside a schema of type object, so only in components/schemas/Variable

You should try to change this part:

    post:
      requestBody:
        required: true
        content:
          application/json:
            schema:
              oneOf:
                - type: array
                  items:
                    $ref: '#/components/schemas/Variable'
                - $ref: '#/components/schemas/Variable'

Fabiencdp avatar Sep 01 '22 09:09 Fabiencdp

@Fabiencdp It seems that you are tight, thank you. So it is another issue, I have turned schema validation and did not get any error about that invalid additionalProperties declaration.

UPD: One more proof that case I mistakenly broad up is not relevant for express-openapi-validator, it seems that allOf with additionalProperties a major issue at JSON schema level: https://stackoverflow.com/questions/22689900/json-schema-allof-with-additionalproperties

srgg avatar Sep 01 '22 11:09 srgg

@Fabiencdp @cdimascio looks like this PR would also close out #666. Identical code fix with similar test.

robertjustjones avatar Sep 02 '22 12:09 robertjustjones

@cdimascio Allow me to revive you about this MR, if we can close this quickly :) Thanks you

Fabiencdp avatar Oct 03 '22 07:10 Fabiencdp

@robertjustjones, I digged about the test who should not pass without your fix, I found that it pass only when the array use a $ref. Instead, if you define the schema directly, the test fail as expected. This may be the reason why this bug has not yet been discovered.

To be clear, without the fix, the initial problem is: Date object is not converted to a date-time string, and validating against a schema property defined as date-time fail with this kind of error: "path/0/prop must be string"

For example, let say this part of schema:

       history:
          type: array
          items:
            type: object
            properties:
              updateDate:
                type: string
                format: date-time

with this response object :

let json = {
  history: [
    {
      updateDate: new Date()
    },
  ],
};

Will return {message: "/response/history/0/updateDate must be string", code: 500} wich is not good. Date is a valid date.

But, if you define the schema with a $ref, like this:

        history:
          type: array
          items:
            $ref: "#/components/schemas/HistoryItem"

The validator don't throw and response is valid: { history: [ { updateDate: '2022-08-23T21:46:36.588Z' } ] } It look's like serdes serialization is only used with $ref

So... What should we do ?

Did we must find why it happen ? It could be a problem with the way that reference are parsed ?

Or did we should accept your fix ?

I'll dig a little more while waiting your opinion.

EDIT: I think your fix is right, without it array items don't pass throught schemaVisitor method, so serdes are not applied to items.

Just spent several hours on this and found your comment which solved. Looking forward to having this one merged! Thank you for finding.

sgoodluck avatar Oct 13 '22 19:10 sgoodluck

@cdimascio many of us have spent a fair amount of time chasing this solved issue after the clear fix was known. Please do stamp this one approved and release it.

robertjustjones avatar Oct 14 '22 14:10 robertjustjones

Hey @cdimascio would some coffee help get this merged?

robertjustjones avatar Oct 31 '22 21:10 robertjustjones

The owner has no github activity since july 2022... hope he is well. A backup reviewer would help in this case... I can try to send him an email, or should we fork this and declare this repository as dead?

Fabiencdp avatar Nov 09 '22 11:11 Fabiencdp

@Fabiencdp You can check the owner's activity for the previous year, it seems to me that the owner appears once in a while (in 6-7 months). Not a good schedule as for production usage, but I have no chance to support this repo :(. Therefore, the question is there a chance that somebody can fork it and re-bake community around a new repo

srgg avatar Nov 09 '22 11:11 srgg

He's still recently active on LinkedIn, presumably just focused in other areas like we are. Adding reviewers and/or co-owners would seem to be the way to go to share the load.

robertjustjones avatar Nov 09 '22 14:11 robertjustjones

Thanks @cdimascio !

robertjustjones avatar Nov 21 '22 17:11 robertjustjones

Screen Shot 2022-11-22 at 2 24 13 PM

robertjustjones avatar Nov 22 '22 20:11 robertjustjones

:) thank you

cdimascio avatar Nov 28 '22 03:11 cdimascio