I have models in my application which have properties of various enum types, or arrays of enums. Is there a way in lb4 to generate OpenAPI enum types from those?
For instance:
enum Direction {
North = "north",
South = "south",
}
@model()
class Foo extends Model {
...
@property.array(Direction, {required: true})
directions: Direction[];
}
I would have hoped to be able to provide additional metadata to @property, along the lines of:
@property.array(Direction, {required: true, itemType: { enum: ["north", "south"]}})
For now, I've only had success describing them as numbers or strings. I've attempted to provide a Type<> object around these, but with no luck. If i'm understanding this comment correctly, it appears this may not be possible, but I would like to confirm: https://github.com/strongloop/loopback-next/blob/eeab36c352301d68a5f277907a072427d71a02b3/packages/repository-json-schema/src/build-schema.ts#L146-L147
If that's the case, are there plans to support types like this? In my specific case, I don't have need for any interpreted ORM behavior, as I'm not using the lb4 ORM in this scenario, simply describing API types for my application.
Only primitives are generated for enums.
Enum types can be generated, given openapi metadata descriptions for possible values.
Please note TypeScript enum is only visible to the complier and we cannot reflect it at runtime. To mark it as an enum for openapi spec, you probably have to customize the itemType.
@raymondfeng Is this related to enum being a const? This twitter post makes a distinction but does that have no bearing on runtime in this context?
Not really. No matter how TypeScript transpiles the source code, there is no metadata visible at runtime. We wish tsc supports plugins to emit additional typings, such as string[].
Ah after further reading: Point 5, if only we had the appropriate design pattern to hand. 馃敭 Sounds like a lot more code to me! :)
Recently, we landed https://github.com/strongloop/loopback-next/pull/2685 which allows model properties to specify additional JSON Schema constraints. Would that work for you? I am thinking about something along the following lines (don't have to verify whether it works as intended):
@model()
class Foo extends Model {
...
@property.array(Direction, {
required: true,
jsonSchema: {
enum: ['north', 'south'],
}
})
directions: Direction[];
}
For longer term, I think we should add first-class support for enum types, both at ORM and API layer.
See also the discussion in https://github.com/strongloop/loopback-next/issues/1624
@bajtos yes! That exactly what I was intuitively attempting at first hoping something like that could work. Agreed that #1624 heads in the right direction (for me at least), as specifying the shape of my data at the model level makes more sense than at the controller level. (I can always break out view models specifically for interaction with API clients if the structures need to be different than ORM-bound models)
@bajtos, do you think it's a duplicate of #1624 or should be labelled as feature?
In my experience, supporting ENUMS requires changes in several parts of the framework. We used to have an issue tracking this feature for LoopBack 3.x (see https://github.com/strongloop/loopback/issues/1321), I created a new Epic issue specific to LB4: https://github.com/strongloop/loopback-next/issues/3033
This particular GH issue seems to be centered around validation of ENUM model properties at OpenAPI level, which was already done for request bodies in https://github.com/strongloop/loopback-next/pull/2685 and eventually will be done for other parameters in #1624
Let's close this issue as a duplicate.
I'm currently working on validating with enums, and can confirm the following behavior:
The @property decorator within the model doesn't validate enums. My enum AlertType here is just a basic string enum of arbitrary strings.
@property({
type: 'string',
required: true,
jsonSchema: {
enum: Object.values(AlertType),
},
})
type: AlertType
Using the @property decorator, Loopback does not validate that the data I pass for this property to create an instance of this model and I can send it any junk string and it accepts it.
BUT, if I move this out of the decorator, and into the actual openAPI Spec:
export const CreateSchema = {
type: 'object',
properties: {
type: {
type: 'string',
enum: Object.values(AlertType), // <-- here
},
[ ... ]
Loopback will now validate the property to only match the values I have within the enum AlertType.
Took me a little while to figure this out, perhaps my googlefu isn't as strong as I thought but, in case anyone searching for a similar use-case stumbles upon this, this is how I got custom types to validate via Loopback API.
Just for completion's sake, the CreateSchema above, is part of the @requestBody object:
export const CreateRequestBody = {
[ ... ]
content: {
'application/json': {
schema: CreateSchema,
},
},
[ ... ]
}
where:
@requestBody(CreateRequestBody)
data: [ ... ]
Maybe this will be useful to someone?
Most helpful comment
I'm currently working on validating with enums, and can confirm the following behavior:
The
@propertydecorator within the model doesn't validate enums. My enumAlertTypehere is just a basic string enum of arbitrary strings.Using the
@propertydecorator, Loopback does not validate that the data I pass for this property to create an instance of this model and I can send it any junk string and it accepts it.BUT, if I move this out of the decorator, and into the actual openAPI Spec:
Loopback will now validate the property to only match the values I have within the enum
AlertType.Took me a little while to figure this out, perhaps my googlefu isn't as strong as I thought but, in case anyone searching for a similar use-case stumbles upon this, this is how I got custom types to validate via Loopback API.
Just for completion's sake, the
CreateSchemaabove, is part of the@requestBodyobject:where:
Maybe this will be useful to someone?