loopbackio / loopback-next

LoopBack makes it easy to build modern API applications that require complex integrations.
https://loopback.io
Other
4.95k stars 1.07k forks source link

Loopback discriminator bug #10656

Open pnoebauer opened 3 weeks ago

pnoebauer commented 3 weeks ago

Describe the bug

The bug in the LoopBack framework causes discriminators in OpenAPI schemas to not function correctly. This issue arises because loopback-next/packages/rest/src/validation/request-body.validator.ts#L87 invokes the openapi-schema-to-json-schema library function without passing the necessary options argument. By default, this function removes OpenAPI properties such as discriminator unless {keepNotSupported: ['discriminator']} is passed in as second argument, which loopback currently does not. Consequently discriminators do not work in loopback.

Detailed Explanation When a request is made with a payload that should be validated against a model using discriminators, LoopBack fails to apply the discriminator. Instead of validating the payload against the specific model indicated by the discriminator, it validates against all possible models, resulting in incorrect validation errors (properties show as invalid because loopback incorrectly validates against other schemas it should not validate against). In short, the behaviour is the same as when not setting discriminators at all.

Bug Illustration: The bug is located in loopback-next/packages/rest/src/validation/request-body.validator.ts#L87. Added comments and TODO notes in the pr to highlight the issue. Demonstrated the bug in the /examples/validation-app by adding a controller, models, and tests. Code Changes: In loopback-next/packages/rest/src/validation/request-body.validator.ts:

function convertToJsonSchema(openapiSchema: SchemaObject) {
  /*
    loopback does not pass the options arg to function toJsonSchema(schema, options?),
    which is part of the openapi-schema-to-json-schema library;
    by default toJsonSchema() removes discriminators, which is why they do not work
    in loopback
  */
  // TODO: fix below bug; toJsonSchema requires the options object to enable discriminators
  const jsonSchema = toJsonSchema(openapiSchema);
  // NOTE: replacing above line with below fixes discriminators not working in loopback and all tests pass
  // const jsonSchema = toJsonSchema(openapiSchema, {
  //   keepNotSupported: ['discriminator'],
  // });
}

Example Application: Created an example application under /examples/validation-app to reproduce the issue. Added a controller and models to showcase the problem. Included tests that demonstrate the expected error messages if discriminators worked correctly.

Logs

No response

Additional information

Both loopback and ajv support open-api discriminators. Ajv by default does not support discriminators but ajv allows enabling them via its options. This can be achieved in loopback by:

    this.bind(RestBindings.REQUEST_BODY_PARSER_OPTIONS).to({
      validation: {
        discriminator: true,
      },
    }); 

However, loopback-rest is dependent on another library openapi-schema-to-json-schema, and currently loopback does not pass any options object argument when invoking a function from that library. Consequently discriminators are getting deleted and do not work in loopback.

Loopback can simply remedy this by changing const jsonSchema = toJsonSchema(openapiSchema);

to const jsonSchema = toJsonSchema(openapiSchema, {keepNotSupported: ['discriminator']});

But currently no options are getting forwarded to that library.

Reproduction

https://github.com/pnoebauer/loopback-next/tree/discriminator-validation-issue ; https://github.com/loopbackio/loopback-next/pull/10655

pnoebauer commented 1 week ago

created pr to fix this bug https://github.com/loopbackio/loopback-next/pull/10655