REST Swagger File Reference Format
The descriptor for a REST service is a JSON string that holds the information necessary for the K2 REST Broker to generate an assembly that allows the K2 Server to communicate with the REST service. This JSON string may be referenced either as a local file or an HTTP URI.
A custom schema was created due to a lack of tooling and traction for other more prevalent standards for REST service descriptors. In the creation of a custom descriptor, every attempt was made to use pre-existing standards where applicable. The REST Broker currently only supports portions of the Swagger v2 specification. For details about which elements are supported, see the Swagger Support Matrix.
Anatomy of a Swagger file
Field | Description |
---|---|
host | Name of the server hosting the REST service. (e.g. www.example.com) |
basePath | The root path to the API operations (e.g. /api/v2) |
schemes | The REST Broker currently only supports http and https |
paths | This is explained in more detail in the section below |
definitions | Defines the object entities used by the service. Each entity defined here will be represented as a SmartObject in K2 blackpearl |
Paths
The paths portion of a Swagger file describes the URL paths, relative to the host and basePath previously defined, that the service provides. The path string may include path template tokens, which will be replaced with parameter values as described below.
Example paths:
"/pets" : {
"get": { ... },
"post": { ... },
},
"/order/{id}" : {
"get": { ... },
}
Paths MUST begin with a slash (/).
Each path contains one or more operation nodes. An operation represents an HTTP verb or action. Swagger 2.0 supports seven operations; the current version of the REST Broker supports the following five:
"get": { ... },
"post": { ... },
"put": { ... },
"delete": { ... },
"patch": { ... },
Each path can define only one of each operation type. For example, a path cannot define two get operations. If you are describing a service that exposes two GET calls for the same path try appending an extraneous query parameter to make Swagger see it as a different path:
"/pets" : {
"get": { ... },
},
"/pets?brokerWorkaround=ignoreMe" : {
"get": { ... },
},
OperationId (method name)
By convention, the operationId property is used to provide a friendly name for the operation. The REST Broker uses this value for the Method name. If no operationId is provided in the descriptor, the broker will attempt to construct a method name from the path and operation type (e.g. get, post, delete)
"paths": {
"/customers": {
"get": {
"operationId": "GetAllCustomers",
...,
},
}
}
Parameters
In Swagger, the body of the HTTP request and headers as well as form, query and URL parameters are all described as parameters of the operation:
{
"in": "path",
"name": "id",
"required": true,
"type": "integer"
},
{
"in": "body",
"name": "body",
"required": true,
"schema": { "$ref": "#/definitions/Pet" }
}
The type of parameter is noted by the in property, which can be any of the following:
- path
- query
- body
- file
- header
The current version of the REST Broker supports all of these EXCEPT header.
The first example above defines a parameter that is passed as part of the path itself. A path template token with the same name is expected to be in the path definition:
"paths": {
"/order/{id}" : {
"get": {
"parameters": [
{
"in": "path",
"name": "id",
"required": true,
"type": "integer"
}
]
}
}
}
The same technique can be used for query parameters. Body parameters define the contents of the main body of the HTTP request. File parameters are described below.
A few rules about parameters (for more, see the Swagger documentation on parameters):
- Parameters in path MUST be required.
- Parameters in body MUST provide a value for schema. (Other parameters MUST define a type.)
- The "header" value for in is not currently supported by the REST Broker.
File Parameters
Swagger only supports describing file uploads using a multi-part form POST. For detailed information, see the Swagger documentation on parameters. The following example outlines the basics for defining a file upload parameter:
"get": {
"consumes": [
"application/x-www-form-urlencoded"
],
"parameters": [
{
"in": "formData",
"name": "file",
"required": true,
"type": "file"
}
]
}
The rules for file parameters are as follows:
- MUST be in formData
- consumes MUST be one of the following values:
- application/x-www-form-urlencoded
- multipart/form-data
Responses
Each Swagger operation MUST include at least one response definition, and should include the response for a successful call. Each response consists of the relevant HTTP Status Code - or the keyword default - and the defining Response Object:
"post": {
...,
"responses": {
"201": {
"description": "Customer created (ID returned)",
"schema": { "type": "integer" }
},
"400": {
"description": "Invalid Customer data"
},
"default": {
"description": "Unexpected error (detail included)",
"schema": { "type": "string" }
}
}
}
The REST Broker determines what it considers to be the "success" response - the response it expects to receive from the operation - using the following logic:
- The lowest 2xx HTTP Status Code present.1.
- The default response, if present.2.
- The first response in the list.3.
- If no responses are found, the broker will not expect a response value from the operation.
Any other response that occurs is handled as an error.
Definitions
All complex entities used or returned by the API should be defined in the Swagger definitions section. The REST Broker creates a SmartObject for each entity described.
Swagger DOES NOT support the following:
- Defining complex objects elsewhere in the descriptor (e.g. in a Response object). Such objects must be defined here and then referenced using $ref.
- Inline definitions: Meaning that if an entity contains a property of another entity type you must declare each entity separately and then reference them for nesting purposes.
- It may be necessary to define an object to hold the response that is actually not part of the response model. See When to Use a Root Object for more information.
Example:
"definitions" : {
"Customer": {
properties: {
"ID": { "type": "integer" },
"Active": { "type": "boolean" },
...
}
},
"Order": {
properties: {
"ID": { "type": "number" },
"Customer": {
"$ref": "#/definitions/Customer"
},
"Date": {
"type": "string",
"format": "date-time"
},
...
},
},
}
Each entity is defined by its name (i.e. "Customer" highlighted above) followed by a JSON Schema object.
Swagger strays from the JSON Schema specification for the following properties:
- allOf - used to implement a simple inheritance. NOT CURRENTLY SUPPORTED by the REST Broker.
- additionalProperties - defines a map, or dictionary, of dynamic properties. NOT CURRENTLY SUPPORTED by the REST Broker.
For more detailed information, see the Swagger documentation for the Schema Object.
K2 Swagger Extensions
In order to integrate smoothly with K2 technologies the REST Broker recognizes the following extensions to the Swagger specification:
Extension | Swagger Element | Description |
---|---|---|
x-k2-displayName | Schema | Defines a friendly name to display to the end-user |
Example:
"userTypeCustomer": {
"properties": {
"Name": {
"schema": { "type": "string" }
},
...
},
"x-k2-displayName": "User (Customer)"
}
Extension | Swagger Element | Description |
---|---|---|
x-k2-entityName | Operation | Specifies which entity (i.e. SmartObject) the operation (i.e. method) should be attached to |
Example:
"paths": {
"/orders?for={id}": {
"get": {
"operationId": "CustomerOrders",
...,
"x-k2-entityName": "Customer"
},
}
},
"definitions": {
"Customer": {
...
}
}
Resolving Validation or Namespace Errors
If you see validation or namespace errors when trying to register your instance, it may mean that the Swagger file defintions section contains nested objects. The REST broker does not work with nested complex entities of the object type. For example, the category of the Pet object references the definition of category:
"Pet" : {
"type" : "object",
"required" : ["name", "photoUrls"],
"properties" : {
"id" : {
"type" : "integer",
"format" : "int64"
},
"category" : {
"$ref" : "#/definitions/Category"
},
"name" : {
"type" : "string",
},
The category entity is defined separately (as a sibling of Pet) as its own object. Simple types are properties of an object, but object types must be defined separately and referenced.
"Category" : {
"type" : "object",
"properties" : {
"id" : {
"type" : "integer",
"format" : "int64"
Dynamic Bindings
The REST broker supports static constructs, which means that if the objects returned by the service vary from call to call, the REST broker cannot work with those objects.
Dynamic API URI bindings: Some services implement load balancing by requiring a call to establish a session and, as a result of that call, you get a base URI to be used for all subsequent calls. Salesforce and Eloqua are examples of these services. The Swagger spec partially supports this construct at the time of publication but it isn’t defined enough for the REST broker to work with.
Dynamic Property Name bindings: Some services implement dynamic property names, where calls to the same endpoint result in the same payload structure but the names of the properties change with every call. The Swagger spec partially supports this construct at the time of publication but it isn’t defined enough for the REST broker to work with.
When to Use a Root Object
Sometimes it is necessary to create a root object to hold the contents of the returned objects.
For example, the following structure is returned by a service:
{
"result": [{
"sys_name": "SLA Repair Log Entry0004",
"sys_class_name": "sys_db_object"
},
{
"sys_name": "Dscy Priv Command Affinity",
"sys_class_name": "sys_db_object"
},
{
"sys_name": "Sys Plugins",
"sys_class_name": "sys_db_object"
},
{
"sys_name": "Alm Fixed Assets",
"sys_class_name": "sys_db_object"
},
{
"sys_name": "CMDB CI Server",
"sys_class_name": "sys_db_object"
}]
}
The result object is not an array but it contains an array of strings. Creating a root object is necessary for the service to serialize and deserialize properly. To do this, first create an entity called rootObject that includes a reference to your result object and is an array object, as follows:
"rootObject": {
"type": "object",
"properties": {
"result": {
"type": "array",
"items": {
"$ref": "#/definitions/result"
}
}
}
},
Use a reference to this rootObject in your method response, as follows:
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/rootObject"
}
}
}
Finally, for your result object, specify the properties of type string that are parsed from the array:
"result": {
"type":"object",
"description": "Model for Result",
"properties": {
"sys_class_name": {
"type": "string"
},
"sys_name": {
"type": "string"
}
}
}