REST Swagger File Reference Format

The descriptor for a REST service is a JSON file that contains information about the API and its data structure. The K2 REST broker uses this descriptor to communicate with the REST service, whether that's a service you own or one available on the Internet. This JSON file may be referenced either as a local file or an HTTP URL.

There are different ways to get a Swagger file for using with the REST broker, listed here in order of recommendation:

  1. Use the Swashbuckle package to generate a Swagger descriptor file. See http://help.k2.com/KB001869 for details on how to do this. This is the recommended approach if you own the API and can modify the source code.
  2. Ask the person who owns the API to generate a Swagger file for you.
  3. Use RESTUnited to generate a Swagger file based on the payload of the service. See http://help.k2.com/KB001758 for details on how to do this. This is the recommended approach if you do NOT own the API. However, this approach typically requires that you to edit the Swagger descriptor to flatten entities.
  4. Generate a Swagger file manually. This is the least desirable approach as it requires extensive knowledge of the Swagger schema for the REST broker, JSON formatting, and knowledge of the API. However, this approach remains feasible if it is your only option. It is best to start with known Swagger descriptor files. See http://help.k2.com/kb001786 for more information.

The REST broker 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

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.

Paths in the swagger file must be unencoded. Encoded paths will be re-encoded and this will break the URL.

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:

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):

File Parameters

Swagger only supports describing file uploads using a multi-part form POST. For detailed information, see the Swagger documentation on parameters. For information about getting files from a service, see REST Swagger File Reference Format

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:

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:

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:

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:

Handling Files in the Response

If the service you are calling returns a file (such as a PDF or Word document), you cannot directly map the returned file to a SmartObject property of type “File”. Instead, any unsupported or unrecognized type is mapped to a SmartObject property of type “Object” (Memo behind the scene), and then you can use the “Get File from Content” inline function to decode the file contents.

The example below shows you how to handle Base64-endcoded strings. You can specify a file type in your Swagger file for the entity, and the file property is automatically mapped to a Service Object property of type Object (Memo) when you register the service instance. At this point, you can use the Get File From Content inline function to decode the Base64-encoded string to a file. This allows you to, for example, attach it to a workflow email notification.

Example

This example uses several SmartObject events to retrieve the property containing the Base64-encoded string, followed by a Mail event that uses the inline function to decode the file and add it to the email as an attachment. All events are placed in the same activity since the data returned from the SmartObject calls are stored in activity fields. The string representing the Base64-encoded file resides inside three nested complex types, which is why there are three deserialize calls which use the Deserialize methods generated for the complex types returned from the service call.

The complex types from the Swagger file are described in the following Definitions snippet from the descriptor. The entity referenced in the response is Return. From that point, references are made to the nested types. In the definition for Out it references FirstLevel, which then references the model for SecondLevel which contains the content node.

"definitions": {
"Return": {
"description": "Model for Return",
"properties": {
"code": {
"type": "string",

},
"ok": {
"$ref": "#/definitions/Ok",

},
"out": {
"$ref": "#/definitions/Out",

},
"status": {
"type": "string",

}
},

},
"SecondLevel": {
"description": "Model for SecondLevel",
"required": ["content"],
"properties": {
"content": {
"type": "Data",

},
"contentType": {
"type": "string",

},
"fileName": {
"type": "string"
}
}
},
"FirstLevel": {
"description": "Model for FirstLevel",
"required": ["SecondLevel"],
"properties": {
"SecondLevel": {
"$ref": "#/definitions/SecondLevel",

}
}
},
"Out": {
"description": "Model for Out",
"required": ["FirstLevel"],
"properties": {
"FirstLevel": {
"$ref": "#/definitions/FirstLevel",

}
}
}

The final SmartObject call, in the Deserialize Third Level event, takes as an input the serialized string from the Second Level.

The return mapping populates the Content field which contains the Base64-encoded file string. .

You can use the Auto Map > Map and Create option on the Return Mappings page to create the activity fields based on the returned properties. However, it is a best practice, especially when dealing with files, to only create activity fields for the types that you need. This keeps the data stored in the workflow to a minimum. In this case, we’re only interested in the Out property.

You can use the Data event to set these values to an empty string at the end of the activity so that your workflow does not store the potentially large string values. You can also clear the Keep Audit flag on these values so that K2 does not store value changes.

At each level, we’re deserializing into activity fields, resulting in the final string representing the file, which is stored in the Content field.

In the Mail event, use the Content field to pass in the Base64-encoded string to the Get File From Content inline function. In this example, we’ve named the file Invoice.pdf, but you could pass in a dynamic value instead.

The invoice is attached to the email and can be previewed in Outlook (blurred here).

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"

}

}

}

K2 Cloud User Guide