Use an !include in a union
I want to create a required, nullable property with a user-defined type in a separate file. How can I accomplish this?
Response.raml
#%RAML 1.0 DataType
type: object
properties:
error: nil | !include Error.raml
Error.raml
#%RAML 1.0 DataType
type: object
properties:
code: string
message: string
!includes cannot participate in a union so your example is invalid.
There are several ways to achieve this depending on the structure of your RAML definition(s), one of them is to use a RAML library, e.g.:
Library.raml:
#%RAML 1.0 Library
types:
Error: !include Error.raml
Response:
type: object
properties:
error: Error?
Error.raml:
#%RAML 1.0 DataType
type: object
properties:
code: string
message: string
PS: Note that error: Error? is equivalent to error: Error | nil. error: <anyTypeHere> also implies that the error property MUST be present regardless of its value. If what you intend is to make the error property optional, then you'll want to write error?: Error instead.
I also miss the possibility to do an include in an Union, especially when building datatypes where a value must be either another datatype or nil.
Either this could be managed by accepting nil | !include Type.raml or a nullable property added to the field, similar to the requiredproperty.
I don't see much of a limitation here since any type can be first defined (in that case by !includeing) it and later re-used in an union. Also, nullable can be expressed either using the | nil syntax of the ? (trailing question mark) syntax.
May I ask how would you do this so currency can be null or the type Currency?
#%RAML 1.0 DataType
type: object
additionalProperties: false
properties:
id: integer
entityId: integer
entityName: string
managingEntityId: integer?
currencyCode: string
currency: any # !include Currency.raml | nil
You can either turn your fragment into a RAML Library:
#%RAML 1.0 Library
types:
Currency:
MyType:
type: object
additionalProperties: false
properties:
id: integer
entityId: integer
entityName:
managingEntityId: integer?
currencyCode:
currency: Currency? # equivalent to: Currency | nil
or use an external library in which Currency type would be defined:
#%RAML 1.0 DataType
uses:
lib: myLibrary.raml
type: object
additionalProperties: false
properties:
id: integer
entityId: integer
entityName: string
managingEntityId: integer?
currencyCode: string
currency: lib.Currency? # equivalent to: lib.Currency | nil
After your suggestions, I played a bit with my RAML definitions and used both suggestions. Here are my thoughts:
- Declaring the type as a library (your first example) has two disadvantages:
- If we choose to include all types in the library file, the file might become very long (Salesforce objects for example) and would break the factorisation of the definitions.
- or we only include one type per library file, which would defeat the purpose of having both libraries and datatypes. I see libraries as a way to put together multiples types and make them easily importable. I use this to bring all files in the same folder and import them together.
- including a library in the DataType with
usescan create cyclic dependencies if the datatypes is included in the library. For example (see code below), if I have a datatypeAddress.raml, another datatypeCustomer.ramlwith a field of typeAddress, and both types are used in thelibrary.ramlfile, importing the library will create a cyclic dependency asCustomer.ramlwill be included in itself when usinglibrary.raml.
Currently, the only solution I found is to use a type any for my field instead of including the type/using a library, especially If I do not want to create multiple libraries included in each other, defining types directly in the library, or defining the type inline (defining the Address type in the address field of Customer.raml).
The solutions I can imagine are:
- The possibility to nullify a type via an additional property
nullable(similar to required). This would allow to explicitly declare a type nullable, including a type included. - The possibility to have a
typesfield in a datatype definition or allowusesto import other datatypes in a datatype declaration. This would not only cover nullability, but also union of includes as the included type is imported before the type definition. - The possibility to have an union on the same line as an include :
!include Address.raml | nil, but this might become messy, especially if multiple !include are brought together.
Here are the example types to illustrate the cyclic dependencies:
Address.raml
#%RAML 1.0 DataType
type: object
properties:
street: string
Customer.raml
#%RAML 1.0 DataType
uses:
library: library.raml
type: object
properties:
name: string
address: library.Address | nil
library.raml
#%RAML 1.0 Library
types:
Address: !include Address.raml
Customer: !include Customer.raml