Skip to content
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

OpenAPI decorator does not properly generate schemas of type Partial #1179

Closed
5 tasks
shimks opened this issue Mar 23, 2018 · 9 comments
Closed
5 tasks

OpenAPI decorator does not properly generate schemas of type Partial #1179

shimks opened this issue Mar 23, 2018 · 9 comments
Labels
feature Juggler OpenAPI REST Issues related to @loopback/rest package and REST transport in general Schema

Comments

@shimks
Copy link
Contributor

shimks commented Mar 23, 2018

Description / Steps to reproduce / Feature proposal

From todo.controllers.ts in example-getting-started:

    async createTodo(@requestBody() todo: Todo) {
    // TODO(bajtos) This should be handled by the framework
    // See https://github.com/strongloop/loopback-next/issues/118
      if (!todo.title) {
        return Promise.reject(new HttpErrors.BadRequest('title is required'));
      }
      return await this.todoRepo.create(todo);
    }

It should be noted that the spec generated by @requestBody is not correct since the argumnet that the operation takes in does not need an id property. One may think that Partial<Todo> may be the next best thing, but it's not possible at the moment to specify which properties are optional and which aren't.

We should find a way to generate the schema for a model when used as a parameter so that id property is not included in the generated schema

Proposed behavior:

@post('/')
makeStuff(@requestBody(Note) note: Partial<Note>) {}
  • Whenever requestBody is used on a model that extends Entity, always generate (or modify) its spec without the property that's been assigned as id.
  • pros:
    • simple ux
  • cons:
    • Not very flexible
    • requestBody becomes very overloaded

Acceptance Criteria:

@post('/exclude')
postWithout(@requestBody(Note, {excludeProperties: ['id']}) note: Partial<Note>) {}
@post('/include')
PostWith(@requestbody(Note, {includeProperties: ['title', 'description']) note: Partial<Note>) {}
  • explore more options from TypeScript (like Partial) to see if they can infer or provide us with additional data/metadata

  • allow requestBody (or other new decorator to create the appropriate metadata) to take in the model type and schema generation options to produce a custom schema

    • specifying excludeProperties should generate an OpenAPI Schema that lacks the given keys and their values when converting from the JSON Schema generated in rest
    • specifying includeProperties should generate an OpenAPI Schema that contains only the given keys and their values when converted from the JSON Schema generated in rest
  • Backwards compatibility must be preserved (@requestBody(spec))

  • related to fix(repository): fix broken code in readme #1121

See Reporting Issues for more tips on writing good issues

@shimks shimks added bug needs discussion Juggler OpenAPI Schema REST Issues related to @loopback/rest package and REST transport in general labels Mar 23, 2018
@dhmlau dhmlau added the DP3 label Apr 20, 2018
@bajtos
Copy link
Member

bajtos commented Apr 24, 2018

I don't think it's a good idea to interpret Partial<Note> as a data-type omitting the primary key (id property) because not all applications are generating ids on the server/in the database. In some cases, the clients are responsible to generate a unique id (typically a GUID/UUID). This is important for example for offline-first implementation.

I think we need a way how to tweak the auto-magic algorithm that's currently in use to build JSON Schema/OpenAPI Schema description of requestBody payload from our model definition. The we should give the application developer control of requestBody schema generated for each controller method (API endpoint). Please let's not make this even more magical than it already is!

A possible API:

@post('/')
makeStuff(@requestBody(Note, {propertyBlacklist: ['id']}) note: Partial<Note>) {}

(propertyBlacklist allows us to also implement propertyWhitelist. Alternative naming scheme: exceptProperties & onlyProperties).

@bajtos bajtos removed their assignment Apr 24, 2018
@virkt25
Copy link
Contributor

virkt25 commented Apr 24, 2018

+1 to having a way to tweak the auto-magic algorithm but I also think we should have a default "magic" fallback that we default to and then a way to tweak it if needed. I have another alternative name for propertyBlacklist & friends ... includeProperties / excludeProperties where we can even abbreviate Properties with Props

@dhmlau dhmlau added feature and removed bug labels Apr 24, 2018
@jannyHou
Copy link
Contributor

jannyHou commented Apr 24, 2018

+1 for the discussion above.
And I would prefer to have the property inclusion/exclusion functions as Model static methods instead of processing them in the decorator function. So we don't need to duplicate the implementation every time a new artifact needs it.


We can also implement some schema combination methods in package @loopback/repository-json-schema to support allof, oneof, anyof, not: https://spacetelescope.github.io/understanding-json-schema/reference/combining.html. Especially oneof, which would be useful in polymorphic relations. While the proposal is off topic in this issue.

@bajtos
Copy link
Member

bajtos commented Apr 26, 2018

BTW, this is a problem we faced in LoopBack 3.x too, see strongloop/loopback#2924

Another example of a property that must be omitted when creating a new model is _rev property used by Cloudant and CouchDB.

The solution in LB 3.x touched several layers:

Maybe we can get some inspiration from this existing solution and perhaps leverage certain parts?

@raymondfeng
Copy link
Contributor

+1 for the discussion above.
And I would prefer to have the property inclusion/exclusion functions as Model static methods instead of processing them in the decorator function. So we don't need to duplicate the implementation every time a new artifact needs it.

The overlapping between parameter value and model instance validation is interesting:

For example, CustomerController.create(customer) probably hints two types of validations here:

  1. Make sure customer parameter is valid. One use case is to disallow customer.id to be passed in during creation.
  2. Make sure customer is a valid instance of Customer model, such as email is required.

I start to lean toward that a parameter can impose additional constraints beyond the model type checking as such validations only make sense in the method invocation context. For the model itself, it's hard to describe when id should be supplied or not.

@bajtos
Copy link
Member

bajtos commented Apr 27, 2018

I start to lean toward that a parameter can impose additional constraints beyond the model type checking as such validations only make sense in the method invocation context. For the model itself, it's hard to describe when id should be supplied or not.

This makes a lot of sense to me too. I think it's also in line with includeProperties / excludeProperties we discussed earlier.

@shimks
Copy link
Contributor Author

shimks commented Apr 27, 2018

I think this issue is ready for estimation unless anyone disagrees with the given acceptance criteria
I'm thinking now that the issue is more closely tied to the result from the spike #1057 and that whatever implementation is used achieve the UX we've settled on will need to leverage it.
In short, the acceptance criteria cannot be decided as of yet because it's blocked by #1057.

@bajtos
Copy link
Member

bajtos commented Sep 20, 2018

(cross-posted @raymondfeng's #1722 (comment))

I'm proposing to introduce an @schema decorator or extend existing decorators so that a schema can be something like:

{
  schema: {
    'x-ts-type': Order,
    'x-ts-type-options': {
      partial: true,
      includes: ['p1', 'p2'],
      excludes: []
    }
  }

@bajtos
Copy link
Member

bajtos commented Jun 27, 2019

Closing in favor of #2652 Emit schema with all model properties optional and #2653 Emit schema excluding certain model properties.

@bajtos bajtos closed this as completed Jun 27, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature Juggler OpenAPI REST Issues related to @loopback/rest package and REST transport in general Schema
Projects
None yet
Development

No branches or pull requests

8 participants