Chapter 1—Web Services

The Web Services component provides support for RESTful web services for client and server. The component instantiates a new Web Worker Object with properties and methods based on the type of web service you are using. You must create the client interface (remote forms or window classes etc) to the Web Service Object.

In addition, the Web Server plug-in allows the Omnis App Server to expose your Omnis code as a RESTful Web Service. This is described under the Creating your own Web Services section.

In addition, there is a JSON external component, called OJSON, that allows JSON based objects returned from RESTful resources to be manipulated: this is described in the OJSON chapter.

From Studio 8.1 onwards, you no longer need to serialize Omnis with a separate Web Services plug-in serial number in order to develop a Web Services client or to consume a Web Service. If you are creating your own Web Services, from your Omnis code, your server will still require an Omnis App Server deployment license when you are ready to deploy your app.

Note that the old WSDL-based Web Services component available in versions prior to Studio 8.x is no longer supported in Omnis and has been removed.

What is REST?

REST is the predominant architectural style being used today to consume and publish Web Services, and is seen as an alternative to other distributed-computing specifications such as SOAP. A RESTful Web Service is identified by a URI, and a client interacts with the resource via HTTP requests and responses using a fixed set of HTTP methods.

A RESTful Web Service follows the following rules to provide access to the resources represented by the server:

  1. The resource must be identified by a URI, which is a string of characters similar to a web address, that points to the resource.

  2. A client interacts with the resource via HTTP requests and responses using a fixed set of HTTP methods.

  3. One or more representations of a resource can be returned and are identified by media types.

  4. The content of a resource can link to further resources.

There are two sides to consider for RESTful Web Services:

  1. a Client, which would be an Omnis library containing methods to consume a RESTful Web Service,

  2. and the Server, where you can implement a RESTful Web Service by exposing the business logic (remote task methods) in your Omnis library to be consumed by clients.

Example Library

There is an example library that demonstrates the capability of Omnis to consume a RESTful web service – the library is available with a Tech Note: TNWS0002 which is available on the Omnis website.

The example library presents basic weather forecast information by consuming a web service provided by openweathermap.org. The example is based on the free API service which you can use for the example.

API Key

To use the web service consumed in the example library, you must obtain a trial API key from openweathermap.org (which must not be shared with other people): note that the API key we used to create and run the online demo has been removed from the example library and you will need to obtain your own API key.

When you open the example Omnis library, you will be prompted to enter an API key. This value is stored in the remote task in the tAPIKey task variable, and you should not be asked to enter it again.

If you reuse any portion of the example app for your own development and deployment, or create your own application using the weather data from openweathermap.org, please remember to obtain a paid-for API key for your own or your clients use.

Testing the Example Library

To test the web service and display the weather forecast, open the example library, right click on the jsWeather remote form and select 'Test Form' from the context menu. The form should open in your desktop web browser and show the current weather for Saxmundham (the home town for the Omnis development team in the UK) – the same information can be accessed in a table format by selecting the Table hyperlink. In the main remote form there is a pictorial summary of today’s weather with the maximum & minimum expected temperatures, along with the forecast for the next four days. You can find the forecasts for other locations by entering either the city name or zip/post code.

The World tab gives a summary forecast for 20 selected cities in the world. Since the example library uses the free version of the API, all data is cached within the example library to prevent unnecessary calls back to the server.

If you publish the form to a webserver, when the form opens, it will try to identify your location using the IP address returned from the remote task. If this fails, it will revert to ‘Saxmundham’ as the default location.

Queueing RESTful requests & Licensing

RESTful requests to the Omnis Server consume a web user license for the duration of the request. In versions prior to Studio 8.1, if all licensed connections were in use when a new RESTful request came into the server, the client received an error. In Studio 8.1 onwards, RESTful requests are now queued internally until they succeed. Note that requests will never be re-queued in a single threaded server (a server where Start server has not been called) since everything executes sequentially.

In addition, there is a new sys function, sys(234), which returns a row of information containing statistics about RESTful requests to the Omnis server. The row has three columns: column 1 is the count of successful calls; column 2 is count of calls resulting in an error; and column 3 is the count of calls internally re-queued because there was not a free user.

Creating a Web Services Client

There are a few key requirements for creating a Web Services Client which are:

  1. An HTTP client that allows resources to be submitted and received using various HTTP methods.

  2. An HTTP client that allows HTTP headers to be specified for requests, and analysed for responses.

  3. A means to manipulate the important media types for RESTful resources: XML or JSON.

  4. Support for HTTPS, if required, which in a business environment is usually essential.

  5. Support for HTTP basic and digest authentication.

The Web Services Client is implemented as an External Component Object. The External Objects group in Omnis Studio includes a group called OW3 Worker Objects: this contains a HTTPClientWorker object which is an HTTP Worker Object (along with FTP, SMTP and IMAP objects). NOTE to existing users: in versions prior to Studio 8.1 you needed to install and configure Java in order to use the HTTPClientWorker object in the Web Worker Objects group, but the new HTTP worker object in the OW3 Worker Objects group does not require Java.

The HTTP worker object functions in a similar manner to the DAM worker objects, although there is a simplification in the way they handle re-use of the object when a request is currently in progress: see the notes about $init.

To use the HTTP worker object, you need to create an Object Class which is a subclass of HTTPClientWorker. Having created a new Object class, set its $superclass property to the name of the HTTPClientWorker object by clicking on the dropdown list and selecting the HTTPClientWorker object in the OW3 Worker Objects group in the Select Object dialog.

In the object class, the methods $completed and $cancelled are inherited from the superclass (the HTTP worker object) which the client worker calls with either the results of a request, or to say the request was cancelled.

You then need to create an Object instance variable in your JavaScript remote form (or window class) based on the new Object class to instantiate the object and allow you to interact with the web service by running its methods.

Properties

The HTTPClientWorker object has the following properties:

$state

A kWorkerState... constant that indicates the current state of the worker object.

$errorcode

Error code associated with the last action (zero means no error).

$errortext

Error text associated with the last action (empty means no error).

$threadcount

The number of active background threads for all instances of this type of worker object.

$proxyserver

The URI of the proxy server to use for all requests from this object, e.g. http://www.myproxy.com:8080. Must be set before executing $init for this object.

$timeout

The timeout in seconds for requests from this object. Zero means default to the standard timeout for the HTTP client. Must be set before executing $init for this object.

$shareconnections

(Only applies to the old HTTPClientWorker object in the Web Worker Objects group) If true (default) the object shares connections to HTTP servers with other HTTPClientWorker objects rather than managing its own set of connections (consider authentication when setting this - different objects may need different authentication credentials, in which case you should not share connections). Must be set before executing $init for this object.

Methods

The HTTPClientWorker object has the following methods:

$init()

$init(cURI,iMethod,lHeaders,vContent[,iAuthType,cUserName,cPassword,cRealm]) Initializes the worker object so that it is ready to perform the specified HTTP request. Returns true if the worker was successfully initialized. The parameters are:

Parameter Description
cURI The URI of the resource, optionally including the URI scheme (http or https), e.g. http://www.myserver.com/myresource. If you omit the URI scheme, e.g. www.myserver.com/myresource, the URI scheme defaults to http
iMethod A kOW3httpMethod... constant that identifies the HTTP method to perform: kOW3httpMethodDelete, kOW3httpMethodGet, kOW3httpMethodHead, kOW3httpMethodOptions, kOW3httpMethodPatch, kOW3httpMethodPost, kOW3httpMethodPut, kOW3httpMethodTrace
lHeaders A two column list where each row is an HTTP header to add to the HTTP request. Column 1 is the HTTP header name, e.g. 'content-type' and column 2 is the HTTP header value, e.g. 'application/json'
vContent A binary or character variable containing the content to send with the HTTP request. If you supply a character variable, the worker converts it to UTF-8 to send in the request. Content can only be sent with Patch, Post and Put methods
iAuthType A kOW3httpAuthType... constant that specifies the type of authentication required for this request. If you omit this and the remaining parameters, there is no authentication (and this parameter defaults to kOW3httpAuthTypeNone). Supported values are kOW3httpAuthTypeNone, kOW3httpAuthTypeBasic and kOW3httpAuthTypeDigest
cUserName The user name to use with authentication types kOW3httpAuthTypeBasic and kOW3httpAuthTypeDigest
cPassword The password to use with authentication types kOW3httpAuthTypeBasic and kOW3httpAuthTypeDigest
cRealm The realm to use with authentication type kOW3httpAuthTypeDigest

NOTE: If you call $init when a request is already running on a background thread, the object will cancel the running request, and wait for the request to abort before continuing with $init.

$run()

Runs the worker on the main thread. Returns true if the worker executed successfully. The callback $completed will be called with the results of the request.

The following method is from the example library – the method initializes the Web Services object, having already setup the main parameters for the $init() method, and calls the web service.

# iURI (Char) initialized as "http://api.openweathermap.org/data/2.5/weather"
# The full URI sent has the API key, location appended as well as an optional parameter to return the data in metric format
# iHTTPMethod (Int) set to kOW3httpMethodGet
# iHeadersList (List)
# iContentChar (Char)
Do iHeadersList.$define(iHeaderNameiHeaderValue)
Do iHeadersList.$add("content-type""application/json")
# call the web service
Do iRestfulObj.$init(iURIiHTTPMethodiHeadersListiContentChar)
Do iRestfulObj.$run() Returns lStatus

See $completed for handling the response from the web service.

Note: $run should not be used if you are hosting the RESTful service you are calling in the same instance of Omnis as your client. This would prevent the server-side execution from running and hang Omnis.

$start()

Runs the worker on the background thread. Returns true if the worker was successfully started. The callback $completed will be called with the results of the request, or alternatively $cancelled will be called if the request is cancelled.

$cancel()

If required, cancels execution of the worker on the background thread. Will not return until the request has been cancelled.

Method names

If you add further methods to your web services object you should avoid using names that may conflict with any possible reserved words on your system.

Callbacks

The HTTPClientWorker object calls the following callbacks, which must be defined in your object class:

$cancelled

Called to report that the request has been cancelled.

$completed

Called to report completion of the request. It has a single row variable parameter with columns as follows, including the content returned in the final column:

Column Description
errorCode An integer error code indicating if the request was successful. Zero means success, i.e. the HTTP request was issued and response received - you also need to check the httpStatusCode to know if the HTTP request itself worked.
errorInfo A text string providing information about the error if any.
httpStatusCode A standard HTTP status code that indicates the result received from the HTTP server.
httpStatusText The HTTP status text received from the HTTP server.
responseHeaders A row containing the headers received in the response from the HTTP server. The header values are stored in columns of the row. The column name is the header name converted to lower case with any - (hyphen) characters removed, so for example the Content-Length header would have the column name contentlength.
responseContent A binary column containing the content received from the server.

The following is the $completed method in the oRest object in the example library:

# called by the client worker with the results
Calculate iResponse as pRow
Calculate iResponseHeaders as pRow.responseHeaders
Do OJSON.$formatjson(pRow.responseContent) Returns iReturnStr
Do OJSON.$jsontolistorrow(pRow.responseContent) Returns iJSONRow

The JSON external component can be used to process the JSON output.

CA Certificates

To make your RESTful based connection secure, you need to add a certificate file (.crt) to the appropriate place on your operating system. On Linux, the default installation for handling certificates is Open SSL; the CA certs are typically installed in /etc/ssl/certs/ca-certificates.crt.

The client certificates are specified by the last three arguments of the $setsecureoptions() method (which must be called before calling $run or $start).

  1. $setsecureoptions()
    Sets the options that affect how secure connections are established
    $setsecureoptions([bVerifyPeer=kTrue,bVerifyHost=kTrue,cCertFile='',cPrivKeyFile='',cPrivKeyPassword=''])

The last three arguments are:

  1. cCertFile
    Empty if client cert not required
    For macOS, pathname of .p12 file containing client cert and private key, or its keychain name.
    For Windows, certificate store path expression (CurrentUser\\MY\\<thumbprint>).
    For Linux, pathname of client cert .pem file

  2. cPrivKeyFile (Only required for Linux)
    The pathname of the private key .pem file. Empty if client certificate not required

  3. cPrivKeyPassword (Only required for Linux)
    The private key file password. Empty if client certificate not required

You can find more information about valid certificate store path expressions on the CURL website (https://curl.haxx.se).

Creating your own Web Services

There is a Web Server plug-in which allows you to expose the code in your Omnis applications and provide them as Web Services for any clients to consume. The interface for the web services you can create and provide to clients is exposed as an API or set of APIs. The key requirements for Omnis to act as a server or provider of RESTful based Web Services are:

  1. Allow an HTTP client to submit and retrieve resources using various HTTPmethods.

  2. Expose the HTTP headers that arrive with a request, and allow headers to bespecified for the response.

  3. A means to manipulate the important media types for RESTful resources: XML and JSON.

  4. Support for HTTPS, and for HTTP basic and digest authentication.

These requirements can be met with a combination of the Omnis App Server and a standard HTTP Web Server.

There is a tech note and example library on the Omnis website to show how you can implement a Web Service from your Omnis code: TNWS0003: “Getting Started with RESTful Web Services, Tomcat and Swagger UI”.

To deploy your Omnis-based Web Service, you will need to setup the Omnis App Server, using the Web Services Server plug-in (mod_OmnisREST.so) rather than the standard plug-in, which is detailed in the tech note: TNJS0003: “Setting Up The Omnis App Server”.

Omnis RESTful APIs

Omnis RESTful APIs (or ORAs) can be fully defined using a “Swagger” definition (see https://github.com/wordnik/swagger-spec/), which is the most widely used standard for defining RESTful APIs. Omnis Studio supports Swagger 2.0 for RESTful web services (rather than 1.2). This only affects the Swagger files Omnis generates, and there is just one definition per service. The Web Service Server library also has a link to save the Swagger file for a service to disk. The reasons for choosing swagger include:

  1. It makes it easier to document and test them

  2. It has tools to generate clients for various languages using the swagger definition

  3. It simplifies the generation of RESTful APIs in Omnis, so you can concentrate on application logic rather than lower level protocol related issues

Note however that there is nothing in the current implementation that requires a developer to use the Swagger definition in REST based web services.

Omnis uses the first non-empty description it can find for a remote task in the service as the description of the service in the Swagger file.

Web Services in the Omnis IDE

Omnis RESTful APIs are visible in the Studio Browser as children of the library node beneath the Web Service Server node (the same as previous implementations of Web Services based on WSDL files). Each ORA is shown a separate node icon in the tree, and various options or actions are shown as hyperlinks when an ORA is selected. Omnis RESTful APIs have a method list and an error log that you can use to manage the service.

Omnis RESTful APIs have Swagger definitions rather than WSDL files, and the Swagger definitions can be viewed using the IDE browser hyperlinks for the ORA. There is a hyperlink for the top-level resource listing, and a separate hyperlink for each top-level URI component. Clicking on a link displays the relevant Swagger data for the link (building it if necessary first). In the top of the panels is a read-only URL; you can select the text for the URL, and paste it into a browser or into Swagger UI (in the latter case, the resource listing URL is the only URL you would use). Note that you need to be aware of potential CORS issues when using these links in Swagger UI (see the later section on CORS).

Creating an Omnis RESTful API

To create a Web Service or Omnis RESTful API you need to set some properties of a remote task and add some RESTful methods. The remote task class has two properties to allow you to setup the Web Service:

  1. $restful
    If true, the remote task is RESTful, it can have URI objects, and can be part of a RESTful API by setting $restfulapiname. This property can only be set to kFalse when the remote task and superclasses have no URI objects

  2. $restfulapiname
    If not empty, this is the name of the RESTful API in the library containing the remote task (cannot equal $webservice for remote tasks in lib). The RESTful API name in this property must start with an alphanumeric (a-z) and can only contain a-z, 0-9 and _

To create an Omnis RESTful API (ORA), set the $restful property of a remote task to kTrue, and provide a name in $restfulapiname (this name will appear in the Studio Browser when you have added some methods). Note: the $restful property is an inherited property, so if you create a subclass of a remote task with $restful set to kTrue, the subclass will also be $restful. Further note a remote task with $restful set to kTrue is not yet a member of an ORA. For each remote task that is to belong to an ORA (meaning that it provides URIs and methods for clients to call) set $restfulapiname. Note that all remote tasks in an ORA must be in the same library. The $restful and $restfulapiname properties are available at runtime in remote task instances.

After setting $restful and $restfulapiname for a remote task class, the new ORA will not appear in the browser, because it has not implemented any RESTful methods. Therefore, the next step is to open the method editor for the remote task, in order to add objects and methods.

When a remote task is RESTful, the remote task has a group of objects (named $objs). These objects are the URIs exposed by the remote task to clients. Inheritance works with these objects and their methods in the same way that it works with other Omnis classes that support inheritance. However, $cfield and $cobj are not resolved for URI objects.

URIs

A URI must have one or more components starting with /. Parameter place-holders can be included as component two or later as {paramName} where paramName is unique (case-insensitive) in the URI. The URI cannot have a trailing / and cannot be duplicated. For example:

  1. /users

  2. /user/{userId}

In the second case, userId is a parameter place-holder, meaning that the RESTful methods implemented for the URI must all have a parameter named userId which Omnis populates with the userId from the addressed URI.

A URI is considered to be a duplicate (and therefore not allowed in the remote task) if it has the same number of components of another URI in the remote task or one of its superclasses, and all components match; components match if neither is a parameter place-holder and they have the same case-insensitive value, or if either of the components is a parameter place-holder.

HTTP methods

URIs are like other class objects in classes with instances, in that they can have their own methods. There are some special methods supported for URIs, called HTTP methods. These correspond directly to the HTTP protocol methods used by a RESTful API, and they are:

  1. $delete, $get, $head, $options, $patch, $post, $put

The HTTP methods are named with a leading $ (unlike the HTTP protocol methods) so that they work with the usual Omnis inheritance mechanism. URIs can also have other methods, but these are not HTTP methods and are not part of the public ORA. The name is the only property that determines if a URI method is an HTTP method, so renaming a method can make it become HTTP or non-HTTP accordingly.

Query Parameters

HTTP methods of a URI have some special features and properties. The first parameter for all HTTP methods must be named pHeaders, and defined as a Field reference. This references a row which contains the HTTP headers received in the RESTful request from the server. The row has a column for each HTTP header. The column names are created by converting the HTTP header name to lower case and removing any - characters e.g. Content-type becomes contenttype as a column name. If more than one header exists with the same name, the headers are combined into a single comma-separated value.

For methods which accept content with the request ($patch, $post, $put) the second parameter must be named pContent, which is a Field reference to the content received in the request.

When you create a new HTTP method, Omnis creates the parameters pHeaders and pContent automatically, and it also adds a character parameter for each parameter place-holder in the URI. In addition, you can add further parameters to the method (which must be of type character, Boolean, integer or number). Each further parameter is then expected to be part of the query string in the full URL used to make the RESTful call to the method; if you provide an initial value for the parameter, the parameter is optional in the query string.

When the RESTful call reaches the remote task method, pHeaders, pContent, the place-holder parameters and the query string parameters are all automatically populated by Omnis.

Note that once you have created the method, you can delete parameters which are required at runtime, e.g. pHeaders. However, Omnis will detect this and generate an error, either at the ORA level (see the error log in the browser) or when the client attempts to call the method.

An HTTP method has some additional properties:

  1. Nickname
    A name which must be unique in the set of all HTTP methods for the ORA. The nickname is used to uniquely identify the method in the Swagger definition for the RESTful API. Clients generated from the Swagger definition typically use the nickname as the method name to call in the client interface. When you create a new HTTP method, Omnis automatically assigns a default nickname

  2. Input type
    Methods which accept content with the request ($patch, $post, $put) have a property called input type, where the value is one of:
    - empty if no content is to be supplied with the request
    - a MIME type e.g. application/xml
    - the name of an Omnis schema class in either the same library as the remote task, or another library. A schema input type is identified by the absence of a / (forward slash), and the supplied content must be application/json.
    The JSON input is automatically converted to an Omnis Row or List (based on the provided JSON, not the Schema).
    The request is automatically denied if the JSON does not contain members named the same as columns in the Schema which have No Nulls set to kTrue. The No Nulls check only happens for Columns at the Schema's top level, Omnis doesn’t currently do this validation on nested Schemas (e.g. if you have a List/Row column type).
    You can use an empty Schema and Omnis will automatically convert the JSON to a List or Row. This may be preferable in many cases over using 'application/json' as the Input Type.
    Note that the columns in the schema class must be character, Boolean, integer, number, list or row, and when using list or row, the list or row must have a schema class subtype which also conforms to these type rules
    - "application/x-www-form-urlencoded” content type, such as that generated by a form on a web page. Therefore, in addition to URL place-holder parameters, you can populate parameters using either the query string or application/x-www-form-urlencoded content. You cannot use both the query string and application/x-www-form-urlencoded content. To use application/x-www-form-urlencoded, set the RESTful input type to application/x-www-form-urlencoded. Omnis then expects application/x-www-form-urlencoded content containing each of the non-optional non-place-holder parameters. The raw application/x-www-form-urlencoded content is also supplied in the pContent parameter of the RESTful method: application/x-www-form-urlencoded content can only be used with HTTP methods that can send content to the server.

  3. Output type
    This specifies the type of content returned by the method when it returns the HTTP status of 200 (OK). One of:
    - empty if no content is to be returned
    - a MIME type e.g. application/xml
    - the name of an Omnis schema class in either the same library as the remote task, or another library. The notes regarding schema classes and the input type also apply to the output type. To return an array of JSON objects you can use schema[] as the RESTful output type, or the return type for one of the HTTP status codes, e.g. mySchema[]. The RESTful method must then return a list defined from the schema rather than a row (see note below about $sendlistsasobjectarray)

  4. HTTP response codes
    A list of codes which can be returned by the method. These are the application codes that can be significant to clients; in addition, the Omnis server will return other codes such as internal server error, which should not be specified here. With each code you can specify optional status text and an optional schema class used to specify some JSON that you will return when the method returns this status code

The HTTP method properties affect how Omnis interacts with the HTTP method:

  1. When calling a method which accepts content with the request, then there are two possibilities:
    The input type is either empty or a MIME type. pContent is a field reference to a binary variable containing the content if any.
    The input type is a schema class. Omnis parses the JSON content and generates a row. In addition, Omnis checks that every column marked as “No nulls” in the input type schema is present in the row. If parsing fails, or the column check fails, Omnis returns an error to the client. Otherwise, pContent is a field reference to the row generated by parsing the JSON.

  2. When an HTTP method returns, Omnis treats the return value as follows:
    If the HTTP status code set using $sethttpstatus is 200, then the output type property determines the type of the output.
    If the HTTP status code set using $sethttpstatus is another value, then Omnis looks up the status code in the HTTP response codes, and uses the return type for the status code.
    Omnis uses the output type determined from the HTTP status code as follows:
    If the output type is not empty, then there must be some returned content. Omnis automatically sets the content-type header for the response to either the output type, or application/json if the output type is a schema. In addition, if the output type is a schema, then the return value from the method can either be:
    Binary (not recommended). Omnis looks at the first character of the content, and checks that it is {, as a sanity check to see if it is probably JSON (if the check fails, the client receives an error).
    A row (recommended). Omnis checks that the row is defined from a class with the same name (excluding the library) as the output type (if the check fails, the client receives an error). Omnis then automatically converts the row to JSON.
    If the output type is empty, then there can only be returned content if the method has already added a content-type header using $addhttpresponseheader; otherwise Omnis returns an error to the client.

Object array output type

When $sendlistsasobjectarray is set to true, the JSON generated by Omnis for a returned row or list that contains lists, contains arrays of objects rather than arrays of arrays (in this case the lists must only contain columns with simple types). There is one exception to this rule. If the list to be converted to JSON has a single column named "<array>", Omnis outputs the list as an array.

There is a checkbox on the RESTful panel for the HTTP method in the method editor, that allows you to select this option.

Note that this option applies to both rows returned by the method, and lists returned by the method when the return type is schema[]. In the latter case, the top-level array returned is always an array of objects, therefore you should note that the new option applies to lists contained in the returned list.

Unknown Query String Parameters

RESTful methods can allow unknown query string parameters. The RESTful panel for a RESTful method in the Method Editor has a checkbox option "Allow unknown query string parameters" (the default is unchecked). When checked, it means the RESTful server will accept requests that contain query string parameters that are not specified in the method parameters. The remote task instance can access these unknown parameters using the notation $cinst.$unknownquerystringparams. The properties are:

  1. $allowunknownquerystringparams
    If true, the RESTful method allows query string parameters that are not present in the method parameters. You access these unknown parameters using the property $unknownquerystringparams of the remote task instance.

  2. $unknownquerystringparams
    If unknown query string parameters are allowed, then this property is a row with a character column for each unknown parameter (the column name is the parameter name in lower case and the column value is the parameter value).

Simple Types

In a schema class, a list column can have a so-called simple type as its sub-type. Valid values are <character>, <integer>, <boolean> and <number>. These allow ORAs to define JSON that contains arrays of simple types.

Method Editor

The method editor has additional features for a RESTful remote task. There are menu items that allow you to:

  1. Insert a new URI

  2. Delete a URI

  3. Insert a new HTTP method

  4. Rename a URI

These menu items are on the context menu for the method tree, and also in the modify menu in the toolbar, provided that the method tree has the focus.

In addition, when the currently selected method is an HTTP method, the variables panel has two additional tabs: RESTful and RESTful notes:

  1. RESTful allows you to set the input type, output type and HTTP response codes. The status code grid has a context menu you can use to manage its entries.

  2. RESTful notes allows you to add documentation notes about the method which Omnis writes to the Swagger definition.

Find and replace works with the RESTful properties; double clicking on a RESTful entry in the find and replace log will open the method editor with the property selected.

The Code Assistant lists column names after you enter the name of a list or row variable with a schema or table class as its subtype.

Server Properties and URLs

The Server Configuration dialog allows two properties to be configured:

  1. RESTful URL
    The base URL used to call Omnis RESTful Web Services, e.g. http://www.test.com/scripts/omnisrestisapi.dll Omnis uses this in the Swagger definitions it generates. If empty, Omnis uses http://127.0.0.1:$serverport

  2. RESTful connection
    [POOL,][IPADDR:][PORT]. Controls how the Omnis RESTful Web Server plugin connects to Omnis. POOL is a load sharing process pool name;IPADDR and PORT identify Omnis or load sharing process; if empty, defaults to $serverport

These properties are stored in the config.json file in the Studio folder of the Omnis tree. These properties affect the URLs stored in the Swagger definitions for ORAs implemented in the server.

If you do not set these properties, then the API will be defined to connect directly to the built-in HTTP server in Omnis.

In order to make a call to an ORA, you need a URI. If you look at an ORA in the IDE browser, you can see how the URIs are constructed by looking at the Swagger definition for a top-level URI path (using one of the hyperlinks immediately below the Resource listing hyperlink). The base path will be something like:

http://localhost:8080/omnisrestservlet/ws/5988/api/phase2/myapi

The initial part of the URL (http://localhost:8080/omnisrestservlet) gets the request as far as the Web Server plugin. The next two components of the URL (/ws/5988) tell the Web Server plugin how to connect to Omnis or the load sharing process. These two components are optional, and can be replaced with the Omnis-server header property described in the Phase 1 documentation; however, if you are likely to be doing cross-domain requests, then it is better to use the /ws/5988 form, since it is guaranteed to be sent with an OPTIONS method request. (Note that the “ws” is a fixed value). The second component (5988) has the general syntax definition:

  1. nnnn (a port number)

  2. or ipaddress:nnnn (IP address and port number)

  3. or serverpool,ipaddress:nnnn

The remaining components are forwarded to the Omnis server: /api/phase2/myapi. The first of these remaining components is a fixed value, which tells the Omnis server that this is a call to an ORA (this first component can also have the fixed value swagger as part of a URL to request a Swagger definition, or it can be of the form LIB.RT, as used in Phase 1 of the RESTful server implementation). The next two components are the library name and the ORA name.

When connecting directly to the Omnis server, the base URL is something like:

http://localhost:5988/api/phase2/myapi

Finally, when combined with the URI in a remote task in the server, the URL used to call an HTTP method for URI /users/{id} (with no query string parameters) is something like:

http://localhost:8080/omnisrestservlet/ws/5988/api/phase2/myapi/users/1234

ORA Properties and Methods

There are various properties and objects to support ORAs. As described earlier, the remote task has the properties $restful and $restfulapiname. RESTful remote tasks have a $objs group. Specific methods in this group are:

  1. $add()
    $add([cUri]) inserts a URI into a RESTful remote task and returns an item reference to it. cUri must be a valid remote task URI starting with a /

  2. $remove()
    $remove(rItem) delete the URI; rItem is an item reference to the URI to delete

HTTP methods in a RESTful remote task have the following properties:

  1. $httpnickname (note this was $httpoperationid in previous versions)
    A simple name for the RESTful remote task method exposed via a URI and HTTP method; it must be unique in the RESTful API; it cannot be empty, must start with an alpha character (a-z or A-Z) and can only contain a-z, A-Z, 0-9 and _

  2. $httpinputtype
    Only applies to RESTful remote task HTTP methods. Empty if no input content is required, or the name of a schema class describing the JSON input object if application/json input is required, or a MIME type if other input content is required

  3. $httpoutputtype
    Only applies to RESTful remote task HTTP methods when they return HTTP OK (200). Either empty if no content is output, or the name of a schema class that describes the output JSON object,or a MIME type for other output content

  4. $httpnotes
    Applies to RESTful remote task HTTP methods only. Notes about the method functionality

In addition, HTTP methods have a group:

  1. $httpresponses
    Applies to RESTful remote task HTTP methods only. The group of HTTP response objects that define the possible response codes (not including 200 OK) for the HTTP method

To add a new response code to the group, use:

  1. $add(iCode[,cText,cType])
    Adds a new HTTP response code definition for the method and returns an item reference to it

The members of the HTTP response codes group have properties as follows:

  1. $httpresponsecode
    An HTTP response code, in the range 201-599 (informational status codes 100-199, plus 200, are not reported)

  2. $httpresponsetext
    Text describing the HTTP response code

  3. $httpresponsetype
    This is the name of a schema class that describes the JSON object to be returned as the result of a RESTful call to the HTTP method which returns the associated response code. Empty means no content is returned

There are two properties in $root.$prefs (which are also in config.json):

  1. $restfulurl
    The base URL used to call Omnis RESTful Web Services, e.g. http://www.test.com/scripts/omnisrestisapi.dll. Omnis uses this in the Swagger definitions it generates. If empty, Omnis uses http://127.0.0.1:$serverport

  2. $restfulconnection
    [POOL,][IPADDR:][PORT]. Controls how the Omnis RESTful Web Server plugin connects to Omnis.POOL is a load sharing process pool name; IPADDR and PORT identify Omnis or load sharing process; if empty, defaults to $serverport

A RESTful remote task $construct method receives a row variable parameter with two columns:

  1. url and method
    where url is the partial url starting with the Omnis library component, and method is the name of the HTTP method.

Swagger Definitions

Omnis populates the Swagger definitions using the properties of the remote task. The Swagger method summary is the Omnis method description. A schema column with no nulls set to kTrue is marked as a required JSON member in the Swagger model object.

The Swagger resource listing contains various fields that need to be populated e.g. API version number, contact email etc. In order to do this, the Omnis tree contains a default template, and you can also create specific templates for specific ORAs. The default template is the file ‘default.json’ in the folder clientserver/server/restful/swaggertemplates in the Studio tree. You can edit this, or alternatively copy it and create an ORA specific template, which must have the name <restfulapiname>.json, and be stored in a sub-folder of swaggertemplates named with the library name e.g.

clientserver/server/restful/swaggertemplates/lib/myapi.json

Omnis reads the template each time it generates a new resource listing. Omnis keeps the Swagger definitions in step with changes in the environment e.g. when you save a remote task or relevant schema class, or change the RESTful URL or connection property.

You can use swagger-ui (https://github.com/wordnik/swagger-ui) with the built-in web server to test your ORAs. Take the dist folder for swagger-ui, drop it into the webapps folder of your web server tree, and rename it swagger-ui. Restart the web server. You can then use the URL http://localhost:8080/swagger-ui/index.html#!/path in a browser to open swagger-ui.

If you also place omnisrestservlet in web server ‘webapps’ folder (and restart the web server), and set Omnis server properties restfulconnection to your server port, and restful URL to http://localhost:8080/omnisrestservlet, you can use swagger-ui without any cross-domain issues.

If you select your ORA in the Web Service Server node of the IDE browser, you can click on the Resource listing hyperlink, and copy the URL from the top of the panel showing the Swagger definition. Paste the URL into swagger-ui and press Explore - you should see your ORA.

Managing Return Values

There may be occasions where RESTful API remote tasks are not able to generate their content as the return value of the HTTP method. For these cases, content generation can be deferred until later, for example, until a threaded worker object completes, or to allow push support, possibly using server sent events and text/event-stream content. In order to do this, there are additional steps. Before returning from the HTTP method (where you would usually return content):

Calculate $cinst.$restfulapiwillclose as kFalse

This prevents the remote task from closing when you return, and it means that you are responsible for closing the remote task by calling $close() at a later point, or by using the remote task timeout mechanism. Note that it is essential to close the remote task, so that the data connection to the client is closed.

Note that setting $restfulapiwillclose to kFalse will be ignored if an error is detected by the Omnis server as part of request processing.

$restfulapiwillclose has the following definition: If true,the RESTful API remote task will close when the Omnis RESTful HTTP method returns. Defaults to kTrue in a new RESTful API remote task. kFalse only applies when the method executes successfully; you must eventually call $close().

After setting $cinst.$restfulapiwillclose to kFalse, you do not need to return any content, headers or status from the method. If you do return content though, then you also need to set the HTTP status and add any response headers before returning the content. Note that the Omnis server no longer automatically adds the content-length header - this becomes your responsibility if this header is required (in many cases like this it is not).

Sending HTTP Content

The $sendhttpcontent() remote task method lets you send HTTP content back to the client:

  1. $sendhttpcontent()
    $sendhttpcontent(xData) sends the next block of HTTP content (xData) to the client for a RESTful API remote task that did not close

When you are ready to generate the response e.g. in a worker callback, call $sendhttpcontent. The xData parameter differs from content returned from a RESTful HTTP method, in that it is always binary (meaning that you are responsible for generating JSON or encoding characters for example).

You can call $sendhttpcontent more than once, to incrementally send content. However, before the first call, you must set the HTTP status and supply the HTTP response headers (including content-length or transfer-encoding chunked if required).

When using $cinst.$restfulapiwillclose set to kFalse, the Omnis server does not attempt to validate the content returned as it does for JSON content when using $cinst.$restfulapiwillclose set to kTrue.

$sendhttpcontent cannot be used in the initial RESTful API HTTP method call.

The $sendhttpcontent() method can be used to send character data (converted to UTF-8 before sending) and list or row data (converted to JSON before sending). In addition, the method has an optional second argument, bChunk, which defaults to kFalse (the current behaviour). When true, bChunk formats the data as a chunk (removing the need to call the formatchunk() function). This improves performance a little, and also allows you to handle web servers which automatically chunk the response. A call to $sendhttpcontent with empty data and bChunk passed as kTrue must be used to terminate the content.

Transfer-encoding chunked

You can return content in multiple blocks using transfer-encoding chunked by using $sendhttpcontent. To facilitate this, there is a built-in function:

  1. formatchunk()
    formatchunk(data) formats the data as a chunk suitable for sending to the client using chunked transfer encoding.Data can be character (which Omnis converts to UTF-8) or binary

Each data block to be sent can be sent with code such as:

$ctask.$sendhttpcontent(formatchunk(data))

These calls need to be followed by a call to send a zero-length chunk (which terminates) the content:

$ctask.$sendhttpcontent(formatchunk())

Server Sent Events

You can use $sendhttpcontent to handle a push connection from a client using Server Sent Events. To do this, set the output type for a get method to text/event-stream. Note: you do not need a content-length header for this. You can then send events to the client using $sendhttpcontent. To facilitate this, there is a built-in function:

  1. formatserversentevent()
    formatserversentevent(fieldname,fielddata[,fieldname,fielddata]...): formats data suitable for sending as an event when generating text/event-stream content.Parameters can be character (which Omnis converts to UTF-8) or binary (UTF-8)

For example:

Do $ctask.$sendhttpcontent(formatserversentevent("id",1,"data",”my event dat))

The protocol field names in the example are data and id, with values 1 and “my event data” respectively.

Date and Date-time values

Support for date and date-time values has been added to REST-based Web Services support. RESTful services typically use a subset of ISO8601 to exchange date and date-time values, which are supported in Swagger which is used to define web services in Omnis. ISO8601 represents the date or date-time as a character string. See http://swagger.io/specification/ and search for RFC3339 in the page - a link from there takes you to http://xml2rfc.ietf.org/public/rfc/html/rfc3339.html#anchor14.

Swagger has two format specifiers for character string values: date and date-time. The Swagger generator has been enhanced so that for fields (either in a schema or in the HTTP method parameters) of type character, the description can contain a swagger tag that specifies the format, e.g. the description for query string parameter startDate could be “<swagger-date> This parameter is the start date for the requested work”. When Omnis generates the swagger definition for the web service, it looks for these swagger tags, and uses them to set the format for Swagger string types. This has the additional benefit that you can use other supported Swagger string formats, e.g. password and byte.

Dates and date-time values are still exchanged as character values. The application code therefore needs to parse and generate the ISO8601 date and date-time values. To support this, there are two new functions to manipulate ISO8601 dates, or at least the subset of ISO8601 needed to work with Swagger and the Omnis RESTful server:

  1. iso8601toomnis()
    iso8601toomnis(cISO8601,bNeedTime,bNeedTimeZone,cErrorText]) converts ISO8601 date/date-time string to Omnis date-time and returns result (in UTC time if cISO8601 contains time and time zone).
    Returns #NULL and sets cErrorText if an error occurs

  2. omnistoiso8601()
    omnistoiso8601(dOmnisDateTime,bNeedTime[,cErrorText]) converts dOmnisDateTime (assumed to be in UTC) to an ISO8601 date or date-time string (depending on bNeedTime) and returns the result.Returns #NULL and sets cErrorText if an error occurs

Note that for a RESTful service, you should always use time zones for input date time values, so you would always pass bNeedTimeZone as kTrue to iso8601toomnis if you are passing bNeedTime as kTrue.

omnistoiso8601() always outputs the timezone using the “Z” UTC time indicator.

Web Services Functions

HTTP Headers

The following functions facilitate using date HTTP header values.

  1. parsehttpdate()
    parsehttpdate(httpDate) parses a date value in HTTP header format (e.g. Sun, 06 Nov 1994 08:49:37 GMT) and returns an Omnis date-time value (in UTC) or NULL if the value cannot be parsed successfully.

  2. formathttpdate()
    formathttpdate(omnisDate) formats the Omnis date-time value (assumed to be in UTC) as an HTTP date header value and returns the resulting string.

  3. parsehttpauth()
    parsehttpauth(auth) parses the HTTP Authorization header value auth and returns a row variable containing the extracted information. See Authorization section for more details.

BASE64 encoding

The following functions are for handling BASE64 encoded data. You are recommended to use these with RESTful requests that require them, rather than the functions in OXML.

  1. bintobase64()
    bintobase64(vData) encodes vData as BASE64 and returns the result. vData can be either binary or character. If vData is character, Omnis converts it to UTF-8 before encoding it as BASE64.

  2. binfrombase64()
    binfrombase64(vData) decodes the binary or character vData from BASE64 and returns the resulting binary data. Returns NULL if vData is not valid BASE64.

Cross Origin Resource Sharing

Cross Origin Resource Sharing (CORS) “is a mechanism that allows many resources (e.g., fonts, JavaScript, etc.) on a web page to be requested from another domain outside the domain the resource originated from” (wikipedia). An Omnis RESTful API can handle CORS by implementing the $options HTTP method, and by handling the Origin and other headers when processing other HTTP methods (see above for details). In addition, you can configure the Omnis Server (in both the development and server runtime versions) to automatically handle CORS. This means that the Omnis Server can be configured to automatically send the response to OPTIONS, and to add the correct CORS headers to the response buffer before passing a simple or actual request to the application.

The configuration of CORS for RESTful-based web services is stored in a separate configuration file, ‘cors.json’ which should be added to the ‘Studio’ folder: there is no CORS configuration if the cors.json file is not present in the Studio folder. (In previous versions the CORS configuration was stored in a “CORS” section in config.json which is now redundant: since it is a separate file you can edit it while the Omnis Server is running.)

There is a template cors.json file containing the required settings located in the ‘config’ folder in the Studio folder: you can make a copy of this file and place the copy in the Studio folder, making any necessary changes.

The cors.json file can be changed while Omnis is open, but you need to inform Omnis of the change. This can be done using a button on the server configuration dialog, “Reload CORS Config”.

The CORS object can have the following members (note that everything here is optional, and the most likely result of omitting data is that a request will be passed to the application to handle, or a method not supported error will be returned to the client if the application does not implement the method):

  1. originLists: Each member of originLists is a named list of origins, i.e. possible values for the HTTP Origin header. (Each list is an array)

  2. headerLists: Each member of headerLists is a named list of HTTP headers (Each list is an array)

  3. exposedHeaderLists: Each member of exposedHeaderLists is a named list of HTTP headers (Each list is an array)

  4. APIS: This object has members as follows:
        *: Server wildcard CORS entry. See below for the definition of CORS entry
        Swagger: Server Swagger CORS entry
        libraryname.apiname. Each library.api object has members as follows:
            *: API wildcard CORS entry
            Swagger: API Swagger CORS entry
            CORS entries named using a URI string. These URI strings need to
            match URI object names in the API

A CORS entry has members as follows:

  1. origins: This has either the value * (meaning that when this CORS entry is used, all origins are allowed), or the name of a member of originLists (meaning that when this CORS entry is used, only the origins in the list are allowed).

  2. headers: This has either the value * (meaning that any header requested by the client using the Access-Control-Request-Headers header is acceptable), or the name of a member of headerLists (meaning that only headers in this list can be requested by the client using the Access-Control-Request-Headers header).

  3. exposedHeaders: The name of a member of exposedHeaderLists. Headers in this list will be returned using Access-Control-Expose-Headers when handling a simple or actual request.

  4. supportsCredentials: If true, and the origin is allowed, the server adds Access-Control-Allow-Credentials with value true.

  5. maxAge: The number of seconds that a client is allowed to cache the result of an OPTIONS method.

CORS processing in the Omnis server occurs when a request with an Origin header arrives. The server tries to locate a CORS entry for the request. There are two cases:

  1. When the client is requesting Swagger data, the server looks for the API Swagger CORS entry. If the API has no configuration, or no API Swagger CORS entry, the server looks for the Server Swagger CORS entry.

  2. When the client is executing an API method (resulting in either the method call or an OPTIONS method call), the server looks for the CORS entry exactly matching the URI that will be used to make the request; if that is missing, the server looks for the API wildcard CORS entry; and if the latter is missing, the server looks for the Server wildcard CORS entry.

If the above processing does not locate a CORS entry, then the server does not carry out any CORS processing, and the request continues as it would without CORS. If however the above processing locates a CORS entry:

  1. The server will attempt to generate the response to OPTIONS, provided that the logic in section 6.2 of the W3C Recommendation referenced earlier applies.

  2. The server will add CORS headers to the response buffer for other requests, provided that the logic in section 6.1 of the W3C Recommendation referenced earlier applies.

In order to understand what is going on, there is a new log type that you can specify in the datatolog member of the main Omnis log configuration: "cors". Using this will cause the server to log CORS issues that mean the CORS processing in the server has not handled the request, and is passing it on to the application if possible.

Logging

You can add a “log” member to the Omnis configuration file (config.json) to add logging to the Omnis Server including the ability to monitor or debug REST requests and responses. The “datatolog” array can be added to “log” member using the following format:

    "log": {
        "logcomp": "logToFile",
        "datatolog": [
            "restrequestheaders",
            "restrequestcontent",
            "restresponseheaders",
            "restresponsecontent",
            "tracelog",
            "seqnlog",
            "soapfault",
            "soaprequesturi",
            "soaprequest",
            "soapresponse",
            "cors"
            "headlessmessage",
            "headlesserror",
            "systemevent"
        ],
        "overrideWebServicesLog": true,
        "logToFile": {
            "folder": "logs",
            "rollingcount": 10
        },
        "windowssystemdragdrop": true
    },

You can get this member text from the template config.json in the studio/config folder in the main Omnis tree.

Including the cors item will cause the server to log CORS issues that mean the CORS processing in the server has not handled the request, and is passing it on to the application if possible.

Further information about Logging in the Omnis Server is available in the Deployment chapter in the ‘Creating Web & Mobile Apps’ manual.

Authentication

You must be responsible for setting up authentication in your Omnis library. When using a real Web Server, rather than the built-in web server, you can configure the URL for the web service to support basic or digest authentication. There is also the option of using https, and also client certificates to further secure connections.

The parsehttpauth(auth) function parses the HTTP Authorization header value auth and returns a row variable containing the extracted information. Column 1 of the returned row (named scheme) is the scheme (e.g. basic). Other columns are scheme dependent. Examples for various auth header values:

  1. Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
    Returned row has three columns:
    scheme: basic
    username: Aladdin
    password: open sesame

  2. Digest username="Mufasa",realm="testrealm@host.com",nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",uri="/dir/index.html",qop=auth,nc=00000001,cnonce="0a4f113b",response="6629fae49393a05397450978507c4ef1",opaque="5ccc069c403ebaf9f0171e9517f40e41"
    Returned row has 10 columns:
    scheme: digest
    username: Mufasa
    realm: testrealm@host.com
    nonce: dcd98b7102dd2f0e8b11d0f600bfb0c093
    uri: /dir/index.html
    qop: auth
    nc: 00000001
    cnonce: 0a4f113b
    response: 6629fae49393a05397450978507c4ef1
    opaque: 5ccc069c403ebaf9f0171e9517f40e41

  3. OAuth realm="Example",oauth_consumer_key="0685bd9184jfhq22",oauth_token="ad180jjd733klru7",oauth_signature_method="HMAC-SHA1",oauth_signature="wOJIO9A2W5mFwDgiDvZbTSMK%2FPY%3D",oauth_timestamp="137131200",oauth_nonce="4572616e48616d6d65724c61686176",oauth_version="1.0"
    Returned row has 9 columns:
    scheme: oauth
    realm: Example
    oauth_consumer_key: 0685bd9184jfhq22
    oauth_token: ad180jjd733klru7
    oauth_signature_method: HMAC-SHA1
    oauth_signature: wOJIO9A2W5mFwDgiDvZbTSMK%2FPY%3D
    oauth_timestamp: 137131200
    oauth_nonce: 4572616e48616d6d65724c61686176
    oauth_version: 1.0

  4. Bearer 0b79bab50daca910b000d4f1a2b675d604257e42
    Returned row has 2 columns:
    scheme: bearer
    token: 0b79bab50daca910b000d4f1a2b675d604257e42

  5. Any other scheme:
    Returned row has 2 columns:
    scheme: scheme name in lower case
    data: the rest of the header data

Web Server Configuration for Authentication

If you want to setup Basic and/or Digest authentication for your web services running on Apache Web Server or IIS, please refer to the tech notes section on the Omnis website at: www.omnis.net/technotes

Manipulating Resources

The server can use the same mechanism for manipulating resources as the client, so refer to the sections above. Configuring and logging the Omnis RESTful API server is achieved in the Omnis configuration file (config.json).