Table of Contents

Customisations

A powerful feature of the On Key REST API, is the ability for consumers of the API to define their own customisations for most of the existing On Key API endpoints (known as Resource Actions). Resource Action customisations allows consumers of the API to be very specific about the information they send to or receive from the On Key server.

For example, if you are only interested in a small subset of information returned by a Resource Action, you can create a customisation to return a smaller subset compared to the default system provided implementation of the same Resource Action.

The same ability to limit, filter, sort and parameterise data using the OKQL is also supported by Resource Action customisations. Instead of having to provide these values as query string parameters with every call, they are stored as part of the Resource Action customisation. Consumers can call these customisations by simply appending ?cid={customisationId} to the existing Resource Action endpoint. This allows customisations to be created and managed centrally by a group of domain experts but still allow all users to execute the endpoints without having to remember what OKQL fragments to add as part of their API calls.

To understand the scope of what Resource Actions and properties are available for customisation, it is important to first understand the metadata endpoints that are available to inspect the existing On Key Resource Actions.

Metadata Endpoints

The On Key REST API provides a rich set of metadata endpoints as part of the System Documents API service grouping. The metadata exposed by these endpoints include information on all the Resources and their respective Resource Actions.

Resource Metadata

To find out what Resource Actions are available for a Resource, inspect the metadata for the Resource as illustrated in the following request for a WorkOrder Resource.

curl -X GET https://{server}/api/tenants/{client}/{connection}/docs/modules/wm/resources/workorder \
-H 'Authorization: Bearer {accessToken}'

On Key responds with a list of Resource Actions that are available for the WorkOrder Resource.

{
  "name": "WorkOrder",
  "self": {
    "href": "api/tenants/{client}/{connection}/DOCS/Modules/WM/Resources/WorkOrder",
    "method": "GET",
    "module": "WM",
    "resource": "WorkOrder"
  },
  "primaryActions": {
    "fetch": {
      "doc": "api/tenants/{client}/{connection}/DOCS/Modules/WM/Resources/WorkOrder/Actions/GetWorkOrder",
      "href": "api/tenants/{client}/{connection}/Modules/WM/WorkOrders/{WorkOrder->Id}",
      "method": "GET",
      "module": "WM",
      "resource": "WorkOrder",
      "resourceAction": "GetWorkOrder"
    },
    "batchFetch": {
      "doc": "api/tenants/{client}/{connection}/DOCS/Modules/WM/Resources/WorkOrder/Actions/BatchGetWorkOrder",
      "href": "api/tenants/{client}/{connection}/Modules/WM/WorkOrders/Batch/{ids:IdList}",
      "method": "GET",
      "module": "WM",
      "resource": "WorkOrder",
      "resourceAction": "BatchGetWorkOrder"
    },
    "query": {
      "doc": "api/tenants/{client}/{connection}/DOCS/Modules/WM/Resources/WorkOrder/Actions/GetWorkOrderCollection",
      "href": "api/tenants/{client}/{connection}/Modules/WM/WorkOrders/",
      "method": "GET",
      "module": "WM",
      "resource": "WorkOrder",
      "resourceAction": "GetWorkOrderCollection"
    },
    "create": {
      "doc": "api/tenants/{client}/{connection}/DOCS/Modules/WM/Resources/WorkOrder/Actions/CreateWorkOrder",
      "href": "api/tenants/{client}/{connection}/Modules/WM/WorkOrders/",
      "method": "POST",
      "module": "WM",
      "resource": "WorkOrder",
      "resourceAction": "CreateWorkOrder"
    },
    "batchCreate": {
      "doc": "api/tenants/{client}/{connection}/DOCS/Modules/WM/Resources/WorkOrder/Actions/BatchCreateWorkOrder",
      "href": "api/tenants/{client}/{connection}/Modules/WM/WorkOrders/Batch",
      "method": "POST",
      "module": "WM",
      "resource": "WorkOrder",
      "resourceAction": "BatchCreateWorkOrder"
    },
    "update": {
      "doc": "api/tenants/{client}/{connection}/DOCS/Modules/WM/Resources/WorkOrder/Actions/UpdateWorkOrder",
      "href": "api/tenants/{client}/{connection}/Modules/WM/WorkOrders/{WorkOrder->Id}",
      "method": "PATCH",
      "module": "WM",
      "resource": "WorkOrder",
      "resourceAction": "UpdateWorkOrder"
    },
    "batchUpdate": {
      "doc": "api/tenants/{client}/{connection}/DOCS/Modules/WM/Resources/WorkOrder/Actions/BatchUpdateWorkOrder",
      "href": "api/tenants/{client}/{connection}/Modules/WM/WorkOrders/Batch",
      "method": "PATCH",
      "module": "WM",
      "resource": "WorkOrder",
      "resourceAction": "BatchUpdateWorkOrder"
    }
  },
  "links": [
    ...
  ]
}

Every Resource Action includes a doc link that points to the metadata endpoint for retrieving more information about a specific Work Order Resource Action.

Resource Action Metadata

To inspect the metadata for a specific Resource Action, use the doc link to send a GET request as illustrated in the following GetWorkOrderCollection Resource Action request.

curl -X GET https://{server}/api/tenants/{client}/{connection}/docs/modules/wm/resources/workorder/actions/getworkordercollection \
-H 'Authorization: Bearer {accessToken}'

On Key responds with all the metadata information for the GetWorkOrderCollection Resource Action.

{
  "type": "Query",
  "name": "GetWorkOrderCollection",
  "endpoint": {
    "doc": "api/tenants/{client}/{connection}/DOCS/Modules/WM/Resources/WorkOrder/Actions/GetWorkOrderCollection",
    "href": "api/tenants/{client}/{connection}/Modules/WM/WorkOrders/",
    "method": "GET",
    "module": "WM",
    "resource": "WorkOrder",
    "resourceAction": "GetWorkOrderCollection",
    "type": "primaryAction"
  },
  "allowAnonymous": false,
  "resource": {
    "href": "api/tenants/{client}/{connection}/DOCS/Modules/WM/Resources/WorkOrder",
    "method": "GET",
    "module": "WM",
    "resource": "WorkOrder"
  },
  "self": {
    "href": "api/tenants/{client}/{connection}/DOCS/Modules/WM/Resources/WorkOrder/Actions/GetWorkOrderCollection",
    "method": "GET",
    "type": "documentation"
  },
  "uriFeatures": {
    "canSubSelect": true,
    "canFilter": true,
    "canOrder": true,
    "canPage": true,
    "canRunAsync": false,
    "canSchedule": false,
    "canScheduleRecurring": false
  },
  "response": {
    "contentType": "application/vnd.onkey.entitycollectionpage+json",
    "features": {
      "canSaveCustomInstance": true,
      "canExpand": true,
      "canOrder": true,
      "queryStringParameters": null
    },
    "entity": {
      "name": "WorkOrder",
      "metadataTree": {
        "href": "api/tenants/{client}/{connection}/DOCS/Modules/WM/Metadata/Entities/WorkOrder/Tree",
        "method": "GET",
        "rel": "Expand",
        "type": "navigation"
      }
    },
    "properties": [
      {
        "name": "id",
        "path": "WorkOrder->Id",
        "dataType": "integer",
        "format": "int64",
        "validations": []
      },
      {
        "name": "code",
        "path": "WorkOrder->Code",
        "dataType": "string",
        "validations": [
          {
            "type": "null",
            "origin": "domain",
            "value": false
          },
          {
            "type": "minLength",
            "origin": "domain",
            "value": 1
          },
          {
            "type": "maxLength",
            "origin": "domain",
            "value": 50
          }
        ]
      },
      {
        "name": "description",
        "path": "WorkOrder->Description",
        "dataType": "string",
        "translatable": true,
        "validations": [
          {
            "type": "null",
            "origin": "domain",
            "value": false
          },
          {
            "type": "minLength",
            "origin": "domain",
            "value": 1
          },
          {
            "type": "maxLength",
            "origin": "domain",
            "value": 100
          }
        ]
      },
      ...
    ]
  }
} 

Besides some general information about the GetWorkOrderCollection Resource Action, some values are important for customisation purposes:

Value Description
response.canSaveCustomInstance Can a customisation be created for the endpoint
response.canExpand Can the customisation expand to include additional information
response.canOrder Can the customisation include an orderBy clause
properties List of properties that are available to include in a customisation

Notice that for every property, additional information such as the validation rules, data type and more are included to give full context for all the data that is allowed for the Resource Action.

For Resource Actions that are executed using a POST or PUT request, the information is stored within a request property as illustrated below for the CreateWorkOrder Resource Action:

{
  "type": "Create",
  "name": "CreateWorkOrder",
  "endpoint": {
    "doc": "api/tenants/{client}/{connection}/DOCS/Modules/WM/Resources/WorkOrder/Actions/CreateWorkOrder",
    "href": "api/tenants/{client}/{connection}/Modules/WM/WorkOrders/",
    "method": "POST",
    "module": "WM",
    "resource": "WorkOrder",
    "resourceAction": "CreateWorkOrder",
    "type": "primaryAction"
  },
  "allowAnonymous": false,
  "resource": {
    "href": "api/tenants/{client}/{connection}/DOCS/Modules/WM/Resources/WorkOrder",
    "method": "GET",
    "module": "WM",
    "resource": "WorkOrder"
  },
  "self": {
    "href": "api/tenants/{client}/{connection}/DOCS/Modules/WM/Resources/WorkOrder/Actions/CreateWorkOrder",
    "method": "GET",
    "type": "documentation"
  },
  "uriFeatures": {
    "canSubSelect": false,
    "canFilter": false,
    "canOrder": false,
    "canPage": false,
    "canRunAsync": true,
    "canSchedule": true,
    "canScheduleRecurring": false
  },
  "request": {
    "contentType": "application/vnd.onkey.entity+json",
    "features": {
      "canSaveCustomInstance": false,
      "canExpand": false,
      "canOrder": false,
      "queryStringParameters": null
    },
    "entity": {
      "name": "WorkOrder",
      "metadataTree": {
        "href": "api/tenants/{client}/{connection}/DOCS/Modules/WM/Metadata/Entities/WorkOrder/Tree",
        "method": "GET",
        "rel": "Expand",
        "type": "navigation"
      }
    },
    "properties": [
      {
        "name": "assetId",
        "path": "WorkOrder->Asset_Id",
        "dataType": "integer",
        "format": "int64",
        "originalSourcePath": "Asset->Id",
        "referenceGroup": "asset",
        "validations": [
          {
            "type": "minValue",
            "exclusive": true,
            "origin": "domain",
            "value": 1
          }
        ]
      },
      {
        "name": "code",
        "path": "WorkOrder->Code",
        "dataType": "string",
        "systemGenerated": true,
        "validations": [
          {
            "type": "null",
            "origin": "domain",
            "value": false
          },
          {
            "type": "minLength",
            "origin": "domain",
            "value": 1
          },
          {
            "type": "maxLength",
            "origin": "domain",
            "value": 50
          }
        ]
      }, ...
    ]
  }
}

Example

To illustrate the resource customisation feature, consider a requirement to create a Work TODO dashboard. The TODO dashboard must contain swim-lanes/groupings of Work Orders grouped by Work Order status. The Work Orders must be ordered by when the work needs to be completed and only display a small subset of Work Order information on a Work Order card visualisation.

Create Customisation

After analysing and inspecting the metadata endpoints for the Work Order Resource, we identify the GetWorkOrderCollection Resource Action as the endpoint to customise as it returns a collection of Work Orders. To cater for the statuses/groupings, we add a filter to the customisation so that all Work Orders returned for a request are of the same status. To allow re-using the same customisation for different statuses, we parameterise the filter so that we do not hard-code the customisation to use a specific status only. We also sort the Work Orders based on when they need to be completed.

The following POST request illustrates the complete request to create the corresponding GetWorkOrderCollection resource action customisation.

curl -X POST https://{server}/api/tenants/{client}/{connection}/docs/modules/wm/resources/workorder/actions/getworkordercollection/customisations \
-H 'Content-Type: application/vnd.onkey.customresourceactioncreate+json' \ 
-H 'Authorization: Bearer {accessToken}' \
-d '{
"name": "WorkToDoBasedOnStatusSummaryList",
"description": "A list of TODO Work Orders with their summary information",
"isTemp": false,
"restrictionLevel": "Public",
"response": {
    "properties": [
        { 
          "path": "WorkOrder->Code" 
        },
        { 
          "path": "WorkOrder->Description" 
        },
        { 
          "path": "WorkOrder->Asset_Code" 
        },
        { 
          "path": "WorkOrder->Asset_Description" 
        },
        { 
          "path": "WorkOrder->CompleteBy" 
        },
        { 
          "path": "WorkOrder->WorkRequired" 
        },
        { 
          "path": "WorkOrder->Site_Code" 
        },
        { 
          "path": "WorkOrder->Site_Description" 
        },
        { 
          "path": "WorkOrder->Requester" 
        }
        ],
    "orderBy": "WorkOrder->RequiredBy",
    "criteria": "WorkOrder->Status_Code = @Status"
  }
}'

Some of the values included with the request are:

Value Description
name An unique name for the customisation
description A description for the customisation
isTemp If true, the customisation will be created temporarily and cleaned up on a schedule. Set to false to ensure that the customisation gets persisted indefinitely to the database.
restrictionLevel Access level for the customisation:
  • Public - anybody can call and manage the customisation
  • PublicReadOnly - anybody can call the customisation, but only the creator can manage it
  • Private - only the creator can call and manage the customisation
response definition of the response customisation
response.properties list of properties to return
response.orderBy order to return data in
response.criteria criteria used to filter the data

Once created, On Key will respond with a 201 Created status code and include the unique customisationId for the customisation using the OnKey-Resource-Id header.

HTTP/1.1 201 Created
OnKey-Resource-Id: 1631206385100001
OnKey-Resource-Location: api/tenants/{client}/{connection}/docs/modules/{module:Module}/resources/{resourceName}/actions/{resourceActionName}/customisations/{customisationId:long}

To view the customisation created, issue a GET request using the customisationId:

curl -X GET https://{server}/api/tenants/{client}/{connection}/modules/wm/resources/workorder/actions/getworkordercollection/customisations/1631206385100001 \
-H 'Authorization: Bearer {accessToken}'

The response should contain a json body with the content of the response property send through with the create customisation POST request:

{
    "id": 1631206385100001,
    "version": 1,
    "resource": "WorkOrder",
    "resourceAction": "GetWorkOrderCollection",
    "name": "WorkToDoBasedOnStatusSummaryList",
    "description": "A list of TODO Work Orders summary information",
    "isTemp": false,
    "response": {
        "properties": [
            {
                "path": "WorkOrder->Code"
            },
            {
                "path": "WorkOrder->Description"
            },
            {
                "path": "WorkOrder->Asset_Code"
            },
            {
                "path": "WorkOrder->Asset_Description"
            },
            {
                "path": "WorkOrder->CompleteBy"
            },
            {
                "path": "WorkOrder->WorkRequired"
            },
            {
                "path": "WorkOrder->Site_Code"
            },
            {
                "path": "WorkOrder->Site_Description"
            },
            {
                "path": "WorkOrder->Requester"
            }
        ],
        "criteria": "WorkOrder->Status_Code = @Status",
        "orderBy": "WorkOrder->RequiredBy"
    },
    "links": [ ...
    ],
    "restrictionLevel": "Public"
}

Execute Customisation

To execute the resource customisation, we append the ?cid={customisationId} to the normal GetWorkOrderCollection endpoint. If we specify a valid customisationId On Key will, instead of executing the system defined GetWorkOrderCollection resource action, load and execute the customisation instead.

curl -X GET https://{server}/api/tenants/{client}/{connection}/modules/wm/workorders/?cid=1631206385100001 \
-H 'Authorization: Bearer {accessToken}'

When we send the request, On Key responds with an error indicating that the specific customisation cannot be executed as we did not specify a value for the @Status parameter.

{
  "messages": [
    {
      "code": "Core_UnexpectedError_Message",
      "message": "An unexpected error occurred: Core_Grammars_UnknownParameterReference: Could not find a parameter reference '@Status'. Check that parameter reference is added to $param list",
      "severity": "error"
    }
  ]
}

We need to add a $param fragment with a valid status code for the @Status parameter.

curl -X GET https://{server}/api/tenants/{client}/{connection}/modules/wm/workorders/?cid=1631206385100001&$param=@Status:'AP' \
-H 'Authorization: Bearer {accessToken}'

Once added, On Key responds with a collection of sorted Work Orders for the specific status.

{
  "count": 150,
  "self": {
    "href": "api/tenants/{client}/{connection}/Modules/WM/WorkOrders/?cid=1631206385100001&$param=@Code%3A%27AP%27&$top=2001&$skip=0",
    "method": "GET",
    "type": "navigation"
  },
  "items": [
    {
      "class": "DomainDynamicRecord",
      "properties": {
        "code": "S00156",
        "description": "Task(s) for the following Intervals: Week;  ",
        "assetCode": "BOIL002",
        "assetDescription": "Coal Boiler",
        "completeBy": "2005-01-09T22:00:00.0000000Z",
        "workRequired": "Task(s) for the following Intervals: Week;  ",
        "siteCode": "A31",
        "siteDescription": "ABC Mining Port Alfred",
        "requester": "Scheduler",
        "requiredBy": "2005-01-09T22:00:00.0000000Z"
      },
      "links": []
    },
    {
      "class": "DomainDynamicRecord",
      "properties": {
        "code": "S00172",
        "description": "Task(s) for the following Intervals: Week;  ",
        "assetCode": "BOIL002",
        "assetDescription": "Coal Boiler",
        "completeBy": "2005-01-09T22:00:00.0000000Z",
        "workRequired": "Task(s) for the following Intervals: Week;  ",
        "siteCode": "A31",
        "siteDescription": "ABC Mining Port Alfred",
        "requester": "Scheduler",
        "requiredBy": "2005-01-09T22:00:00.0000000Z"
      },
      "links": []
    }, ...
  ]
}

To populate the additional swim-lanes/groupings, we can issue the same request using different @Status parameter values.

Tip

It is also possible to issue a single request to get all the work orders for the different statuses associated with the swim-lanes/groupings. Include the status as part of the properties being returned so that you can group on it. Change the filter to, instead of using a @Status parameter, use an IN operator with an array of possible status values, i.e. "criteria": "WorkOrder->Status_Code IN ('AP','CL','AF')"

Change Customisation Execution

Even though we can project, filter and order the data as part of defining a Resource Customisation, we can still use OKQL to further filter, project and page through the data returned by a customisation.

Important

The scope of what data you can further project or filter on is limited to the data used by/defined within your customisation. You cannot use data that is not included in your customisation definition.

To illustrate, say we want to limit the TODO dashboard to only show the TODO work for a specific Site. As SiteCode is included in our customisation definition, we can execute the customisation with an additional $filter as illustrated below:

curl -X GET https://{server}/api/tenants/{client}/{connection}/modules/wm/workorders/?cid=1631206385100001&$param=@Status:'AP'&$filter=siteCode='A31' \
-H 'Authorization: Bearer {accessToken}'

When executed, the original WorkOrder->Status_Code = @Status filter of the customisation will be combined with the new siteCode='A31' filter to create a $filter=WorkOrder->Status_Code = @Status and siteCode='A31' filter expression. This will return the same information but only for the subset of A31 sites.

If, for some reason, you want to override the default behaviour and exclude the original customisation filter, include an additional ?mergeFilter=false query string parameter. This will create a $filter=siteCode='A31' filter expression that will ignore the status criteria when executed as illustrated below:

curl -X GET https://{server}/api/tenants/{client}/{connection}/modules/wm/workorders/?cid=1631206385100001&$param=@Status:'AP'&$filter=siteCode='A31'&mergeFilter=false \
-H 'Authorization: Bearer {accessToken}'