Abstract class or interface in RAML modeling - rest

Is it possible to model abstract class or interface using RAML? If not, how can we impose constraints in supertypes that the subtypes must define?

You can model super types and inheritance in types like so:
types:
ResponseNoId:
properties:
something1:
something2?:
ResponseId:
type: ResponseNoId
properties:
id:
Response:
ResponseNoId|ResponseId
/test:
get:
responses:
200:
body:
application/json:
type: ResponseId
In this example ResponseId inherits something1 and something2 from ResponseNoId but adds a new property called id.
Also Response allows you to use either in your resource.
In you resources you can now define type: Response and it only allow one of the child types.

Related

Openapi discriminator without propertyname

I need inheritance only with one field in super class, so propertyName is no need. Is anyway to ignore propertyName?
Base:
type: object
discriminator:
propertyName: useless
required:
- id
properties:
id:
type: integer
format: int64
example: 1
Response like
"useless":"subtype", "id":1
And throws error like
Unrecognized field "useless"
I need inheritance only with id, without field propertyName, is anyway to do it?
Or make propertyName as integer id?

OpenAPI discriminator oneOf default

Given the following OpenAPI YAML, do we always need to define petType while creating Pet?
Can I by default create a pet of type Dog without specifying petType? Is there some way to do this with OpenAPI?
Pet:
oneOf:
- $ref: '#/components/schemas/Cat'
- $ref: '#/components/schemas/Dog'
- $ref: '#/components/schemas/Lizard'
discriminator:
propertyName: petType
do we always need to define petType while creating Pet
Yes. As explained in the Discriminator Object section of the OpenAPI Specification (emphasis mine):
a discriminator MAY act as a "hint" to ... selection of the matching schema ... We can then describe exactly which field tells us which schema to use:
...
The expectation now is that a property with name petType MUST be present in ... payload, and the value will correspond to the name of a schema defined in the OAS document.
I had a similar problem myself, and I was able to work out the following solution:
Pet:
x-field-extra-annotation: '#com.fasterxml.jackson.annotation.JsonTypeInfo(
use = com.fasterxml.jackson.annotation.JsonTypeInfo.Id.NAME,
include = com.fasterxml.jackson.annotation.JsonTypeInfo.As.PROPERTY,
property = "petType", visible = true,
defaultImpl = Dog.class)'
oneOf:
- $ref: '#/components/schemas/Cat'
- $ref: '#/components/schemas/Dog'
- $ref: '#/components/schemas/Lizard'
discriminator:
propertyName: petType
I used 6.0.1 version of the OpenAPI generator.
Just in case, here is the link to the documentation where I found many other helpful solutions:
https://openapi-generator.tech/docs/generators/spring

Is there a Deconstruct Mongo Response to DTO short cut?

If I have a table in a mongoDB with five properties and I only want to return four of them and none of the mongo added info such as v1 I can map the reposne to a dto like so,
const product = await this.productModel.findById(productId).exec()
return { id: product.id, title: product.title }
Is there a deconstruct shortcut for the return, to extract every field from an interface (Product) from the product response, to save typing each property out ? If for example im retunring 127 properties from a table of entires with 140.
interface Product {
id: string
title: string
...
}
Unfortunately no, typescript interfaces do not really exist when your program compiles
Interface is a structure that defines the contract in your application. It defines the syntax for classes to follow. Classes that are derived from an interface must follow the structure provided by their interface.
The TypeScript compiler does not convert interface to JavaScript. It
uses interface for type checking. This is also known as "duck typing"
or "structural subtyping".
So, you can't really read interface fields and then write some logic (you can maybe achieve this through reflection but it's a bad practice)
An alternative is to explicitly define what fields are to include/or exclude from your object
Suppose that I have an object with this interface:
interface Foo {
field1: string;
field2: string;
field3: string;
.....
field140: string;
}
What you can do here is to define what properties you want to exclude (you take the exclude approach here since you are returning 127 fields of 140)
// This isn't an implementation with mongoose (if you are using it),
// it's just to give you the idea
const FIELDS_TO_EXCLUDE = ["field128", "field129", "field130", ..., "field140"];
productModel.toDTO(){
const documentData = this;
FIELDS_TO_EXCLUDE.forEach(x => delete documentData[x]);
return documentData;
}
In this way, when you will execute the toDTO function your manipulate itself excluding (or including) the fields you want

RAML support for a resource PATCH (RFC 7396)

Problem
I'm trying to describe an API which is supposed to have resources PATCH methods work like described in RFC 7396.
Basically a POST method have a set of properties in it's body, some required some not.
PATCH (by some standards) has no fields required - requests body contains only the fields you want to update, additionally the API allows a client to "clean" a field by sending a null.
Example:
#%RAML 1.0
title: test
mediaType: [application/json]
types:
Create:
type: object
properties:
description: string
Retrieve:
type: Create
properties:
id: string
Update:
type: object
properties:
description?: nil | string
/resource:
get:
responses:
200:
body:
type: Retrieve[]
post:
body:
type: Create
/{id}:
uriParameters:
id: string
get:
responses:
200:
body:
type: Retrieve
patch:
body:
type: Update
responses:
200:
body:
type: Retrieve
in the example I have a Update type specified just for that task, because... Create has a required description, Retrieve inherits from Create as it should return all it's fields and the additional id, Update can't inherit from Create as RAML does not allow to override a required field with one that is not required.
Questions:
Is there a better way to describe that API?
If not is nil | string the proper way to go when writing that Update type "from scratch"?
I don't think there's a better way. Only thing is, if you have not required properties in both the create and the update, then you could create a base type.
Yes, nil | [type] is the way to go. You need to be able to "clean" the property by sending null.

GraphQL: best way to manage mutations with interfaces?

I'am new to GraphQL but I really like it. Now that I'am playing with interfaces and unions, I'am facing a problem with mutations.
Suppose that I have this schema :
interface FoodType {
id: String
type: String
}
type Pizza implements FoodType {
id: String
type: String
pizzaType: String
toppings: [String]
size: String
}
type Salad implements FoodType {
id: String
type: String
vegetarian: Boolean
dressing: Boolean
}
type BasicFood implements FoodType {
id: String
type: String
}
Now, I'd like to create new food items, so I started doing something like this :
type Mutation {
addPizza(input:Pizza):FoodType
addSalad(input:Salad):FoodType
addBasic(input:BasicFood):FoodType
}
This did not work for 2 reasons :
If I want to pass an object as parameter, this one must be an "input" type. But "Pizza", "Salad" and "BasicFood" are just "type".
An input type cannot implement an interface.
So, my question is : How do you work with mutations in this context of interface without having to duplicate types too much? I'd like to avoid having a Pizza type for queries and an InputPizza type for mutations.
Thank you for your help.
Input and Output types are fundamentally different things, just because you may have ones that happen to represent a similar object (eg Pizza and PizzaInput), it's a false equivalence.
Let's say in your schema Pizza has a mandatory id field (your IDs aren't mandatory, but probably should be). It probably wouldn't make sense for PizzaInput to have an ID field -- it would be generated by the server. The point i'm making is that in anything but the simplest systems, there's going to be processing to turn user input into a fully-fledged object to return.
You just have to bite the bullet and do what feels like duplicated work, you'll see the benefits in the long run.