REST API Service provides an easy-to-use and powerful way to create REST APIs.

1 What Are RESTful Web Services

REST, or REpresentational State Transfer, is an architectural style for providing standards between computer systems on the web, making it easier for systems to communicate with each other.

  • Resource identification through URI: A RESTful web service exposes a set of resources that identify the targets of the interaction with its clients. Resources are identified by URIs, which provide a global addressing space for resource and service discovery.
  • Uniform interface: Resources are manipulated using a fixed set of four create, read, update, delete operations: PUT, GET, POST, and DELETE. POST creates a new resource, which can be then deleted by using DELETE. GET retrieves the current state of a resource in some representation. PUT transfers a new state onto a resource.
  • Self-descriptive messages: Resources are decoupled from their representation so that their content can be accessed in a variety of formats, such as HTML, XML, plain text, PDF, XLS, JSON, and others.
  • Stateful interactions through hyperlinks: Every interaction with a resource is stateless; that is, request messages are self-contained. Stateful interactions are based on the concept of explicit state transfer.

REST requires that a client makes a request to the server in order to retrieve or modify data on the server. A request generally consists of:

  • an HTTP verb, which defines what kind of operation to perform
  • a header, which allows the client to pass along information about the request
  • a path or URI to a resource
  • an optional message body containing data

2 Configuring RESTful Web Services with Axional Studio

When a request arrives at the system, the http method, the context and the path in the request URL are used to determine which JS or SQL function is going to be called.

In Axional Studio REST resources are defined in two tables:

Table name Description
wic_rest_context The context allows you to create groups of Rest APIs under the same context path. It includes the definition of the version, getting the benefits associated with version control.
wic_rest_api Each record of the table defines a PATH, an HTTP method, and a statement that provides the resource. The primary key of the table wic_rest_api is composed of the columns api_path, api_method, api_context_type, api_stmt_type, api_stmt, api_sql_mode

The parameters in the URL are:

  • server-ip: it is the domain or IP of the server
  • server-port: it is the port where the server is listening
  • dbms: it is the database logical code to connect
  • context_path: it is the path of the context
  • version: it is the version number of the context
  • path: it is the path of the API that will be matched against wic_rest_api . This parameter can contain multiple slashes /
URL Model example

http://[server-ip]:[server-port]/rest/{dbms}/{context_path}/v{version}/api/{path}

In the image above, the URL corresponds to:

http://web1/service/rest/{dbms}/sample/v1/api/customer/{customerid}

Additionally, requests should include the HTTP header Accept to assist the REST service content negotiation.

Required permission

API service permission must be assigned to the database connection group.

2.1 Rest Context definition

Menu path:
Web Services / Contexts

The Rest API Context form includes the following options:

  • Path*: define the context path. Format:/{context_path}
  • Version*: change version when modifying context in order to avoid overwriting. Format: /v/{version}. At the bottom of the screen, use the button New version to change version.
  • Filter JS Function: this optional filter will be applied previously to the api request. It is usually used, for instance, for security and safety reasons. This JavaScrip function is catalogued in the table wic_dbscripts_js_funcs (Business logic/Server JS code).
  • Description: create an optional description to define APIs aggrupation
  • WADL Show code*: WADL describes the RESTful Web services. However, only if this flag is enabled, the descriptor will include the code of the javascript function.
  • File: upload a JAR file. This is a type of compressed zip file that can contain multiple resources. The HTLM file included in the JAR file will provide the path to the resources inside this file. This path is mandatory and has to follow this format: resource/{resource_path}

    As a way of example, see the following image of the demo_studio.jar file:

    The html index contains the resource path to inner JAR files:

Example

HTML index content

Copy
<html>
    <head>
        <meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge">
        <meta name=viewport content="width=device-width,initial-scale=1"><title>Vue App</title>
        <link href=resource/css/app.css rel=stylesheet>
        <link href=resource/app2.css rel=stylesheet>
    </head>
    <body>
        <pre id="container"></pre>
        <script src="resource/js/app.js" ></script>
    </body>
</html>

Use the button Delete file to delete the JAR file.

2.2 Rest API Defintion

Menu path:
Web Services / API

The Rest API form includes following options:

  • HTTP method*: select between GET (retrieve information), PUT (transfer a new state onto a resource), POST (create a new resource), DELETE (delete a resource).
  • Information: optional brief description.
  • Path*: create the Rest API path. Format: /{path}. It can contain multiple slashes /

    IMPORTANT

    Rest apis can't repeat the same method for the same rest api path

  • Cache max -age (seconds): during this period only the previous resources on the cache will be used in response to this request. Once the time expires, the server will fulfill the same request. This will improve server performance.
  • Content type: select between the different options to define the output format (plain, csv, html, json, xml, octet-stream, pdf, vnd.ms-excel, msword, image). If */* is selected, then no concrete output format will be selected.
  • Concurrecy limit*: it determines the number of users who can run the RestApi simultaneously. The number 0 indicates infinite users.
  • Security*: it constrains access through the authentication context. Select between BOTH (no access restrictions), SERVICE (path access from /service context), APPLICATION (path access from the root / context). For further information, see section Authentication.
  • Row expression: the client can freely use this expression for whatever he needs, for example, to hide rows. Therefore, in addition to the information about each method endpoint, the expression defined here to apply row by row is included.
  • Disable validation: when this option is activated, the parameters Type, Repeating (array) or Required are not validated.
  • Role: it constrains access through the user ROLE defined in the api_role from the table wic_rest_api . For further information, see section Security.
  • Parameters (recommended): it limits response parameters to those reported here.
    • Order: specifies the order in which the fields will appear.
    • Tabname: this field act as a driver to retrieve logical data like labels,...
    • Name: name of the column
    • Default: it allows specifying a default value, usually the most common value.
    • Type: it refers to the type of data (String, Integer, Long, Float, Double, Boolean, Date, DateTime, Binary).
    • Origin: it distinguishes between the following parameter types based on the parameter location within the request:
      • form/json: they are located in the body of the request in a form or json format
      • query: they are the most common type and appear at the end of the request URL after a question mark (?). Example: /users?role=admin.
      • path: they are variable parts of a URL path, each denoted with curly braces { }. Example: as/users/{id}.
      • header: in the header parameters, such as X-MyHeader: Value.
    • Repeating (array): click the tick box Repeating (array) when the parameter will be received more than one, in an array.


  • JS/SQL: define the JavaScript or SQL language statement. Depending on the used language, choose between JS or SQL options in the api_stmt_type field.
    • If JS is selected, write the function in the statement field. The function will receive 2 parameters. The first one is a map containing the parameters defined in the API, whereas the second one is the raw body of the request.

      Copy
      function main(params, rawBody) {
      
          // params is a map containing the parameters
          // rawBody is the request body as received
      
      }

      IMPORTANT

      When JS is selected, the name of the function must be main .

    • If SQL language is selected, the response will be different depending on the chosen output option that will be displayed.
      • Resulset: the response will include the rowset and the metadata in an array (standard output).
      • Array: the response includes a key value of the rowset in an array.
      • Row: the response includes a key value of the first row of the rowset.
      • 1x1: usually used for blobs, the response will only include the bytes of the blob, which are contained in the first column of the first row.
  • The button Change context: this button moves the RestAPI to a different context.
  • The button Copy: this button can copy the current RestAPI to create a new one. The user can select which attributes will be copied.
 

2.3 Authentication

The service is mounted on two separate contexts in the system, and therefore is accessible from two separate URLs:

  • Service context /service/* url path. The authentication for this context can be either BASIC or DIGEST, depending on the wic_conf setup. This context is designed for communication between machines.
    Copy
    http://[server-ip]:[server-port]/service/rest/{dbms}/{context_path}/v{version}/api/{path}
  • Root context: this is the main context and is accessed thought via FORM authentication
    Copy
    http://[server-ip]:[server-port]/rest/{dbms}/{context_path}/v{version}/api/{path}

    REST API calls to the root context only make sense when developing embedded component within studio such as HTML pages or JS box applications.

Indeed, the field api_access from the table wic_rest_api can have the following values:

  • Both: No access restrictions
  • Service: The path can only be accessed from the /service context
  • Application: The path can only be accessed from the root / context
 

2.4 Security

It's possible to constraint the paths a user can access by defining the required roles in the API path definition. The field used to define the security constraint is api_role from the table wic_rest_api . An empty api_role means that no security is enforced, whereas a non-empty value means that the user must have the security role defined in wic_conf tables wic_role_api and wic_role_api_path.

It's also possible to restrict or grant access to API paths from the different context explained in the previous section Authentication.

2.5 Resource matching

2.5.1 URI path matching

URI path templates are URIs with variables embedded within the URI syntax. These variables are substituted at runtime for a resource to respond to a request based on the substituted URI. Variables are denoted by braces ({ and }). For example, look at the following Path definition:

/users/{username}

In this kind of example, a user is prompted to type his or her name, and then the Axional Studio web service configured to respond to requests to this URI path template responds. For example, if the user types the user name “Galileo,” the web service responds to the following URL:

http://example.com/service/rest/context/v1/api/users/Galileo

The value of the user name is added to the arguments map passed to the method

Copy
{ username : 'Galileo' }

2.5.2 Content type matching

The following graph displays the content negotiation rules used to match incoming requests with catalogued wic_rest_api records

Matching

Loading...

Mimes priorities

When matching requested mime types with records in wic_rest_api, the following priorities are used when more than one record is matched

Lowest priority

  • */*

Low priority

  • application/octet-stream
  • text/plain
  • text/csv
  • text/html

Medium priority

  • application/json
  • application/xml

High priority

  • application/pdf
  • application/vnd.ms-excel
  • application/msword
  • image/*
Example

Imagine that a user request has the HTTP header Accept : */*, and that multiple records in wic_rest_api there are defined for the same path with the following content types:

  • application/vnd.ms-excel
  • */*
  • application/json

The response for this request would contain the HTTP header Content-Type : application/vnd.ms-excel since it has higher priority compared to application/json and */*

Example

Imagine that a user request has the HTTP header Accept : application/json, and that multiple records in wic_rest_api there are defined for the same path with the following content types:

  • application/vnd.ms-excel
  • application/xml
  • application/json

The response for this request would contain the HTTP header Content-Type : application/vnd.ms-excel since it has higher priority compared to application/json and */*

3 Reference

Example

All the examples given in the following docs assume that in the table wic_rest_api the following endpoints have been catalogued

Copy
+---------------------+-----------+------------------------+
|api_path             |api_method |api_js_code             |
+---------------------+-----------+------------------------+
|/v1/test/echo        |GET        |echo                    |
|/v1/test/info/table  |GET        |get_table_info          |
|/v1/test/invoice     |POST       |new_invoice             |    
|/v1/test/invoice     |PUT        |update_invoice_state    |      
|/v1/test/invoice     |DELETE     |delete_invoice          |
+---------------------+-----------+------------------------+

We assume that the function echo is catalogued

Copy
function echo(args) {
    return args
}

as well as a function get_table_info that returns the number of rows of a table passed via argument

Copy
function get_table_info(args) {
    if (!args.table)
        throw new Error("Invalid arguments. Table name is empty")
    return Ax.db.executeQuery("SELECT * FROM systables WHERE tabname = ? ", args.table)
}

new_invoice

Copy
function new_invoice(json) {
    let sqlca = Ax.db.insert("studio_rest_api_invoice_docs", json);
    return sqlca;
}

update_invoice_state

Copy
function update_invoice_state(json) {

    let pk = {
        invoice_id : json.invoice_id
    }
    
    let data  = {
        invoice_state : json.invoice_state
    }
 
    return Ax.db.update("studio_rest_api_invoice_docs", data, pk);
}
delete_invoice
Copy
function delete_invoice(json) {
    // console.log  and console.err will be sent back inside HTTP headers
    console.log("JSON = " + JSON.stringify(json));
    return Ax.db.delete("studio_rest_api_invoice_docs", {
        invoice_id : json.invoice_id 
    });
}

In addition, let's define the table

Copy
CREATE TABLE studio_rest_api_invoice_docs (
    invoice_id varchar(30) not null PRIMARY KEY CONSTRAINT p_invoice_id,
    invoice_customer varchar(30) not null,
    invoice_date date not null,
    invoice_state char(1) default 'O' not null,
    invoice_document blob 
);

3.1 WADL descriptor

Axional Studio contains support for Web Application Description Language (WADL). WADL is a XML description of a deployed RESTful web application. It contains a model of the deployed resources, their structure, supported media types, HTTP methods and so on. In a sense, WADL is similar to the WSDL (Web Service Description Language) which describes SOAP web services. WADL is however specifically designed to describe RESTful Web resources.

The REST API service provides a WADL descriptor by accessing the following url:

/service/rest/{database}/{context_path}/v{version}/wadl

This url serves both XML and HTML documentation of the REST API, providing a friendly overview of the service.

Example
Copy
<?xml version="1.0" encoding="UTF-8"?>
<ns0:application xmlns:ns0="http://wadl.dev.java.net/2009/02">
   <ns0:resources base="/rest/api/test_junit">
      <ns0:resource path="/v1/test/echo">
         <ns0:method id="do_get" name="GET">
            <ns0:doc>Doc echo (GET)</ns0:doc>
            <ns0:request>
               <ns0:representation mediaType="application/json"/>
            </ns0:request>
            <ns0:response>
               <ns0:representation mediaType="*/*"/>
            </ns0:response>
         </ns0:method>
         <ns0:method id="do_post" name="POST">
            <ns0:doc>Doc echo (DELETE)</ns0:doc>
            <ns0:request>
               <ns0:representation mediaType="multipart/form-data"/>
            </ns0:request>
            <ns0:response>
               <ns0:representation mediaType="*/*"/>
            </ns0:response>
         </ns0:method>
         <ns0:method id="do_post" name="POST">
            <ns0:doc>Doc echo (DELETE)</ns0:doc>
            <ns0:request>
               <ns0:representation mediaType="application/x-www-form-urlencoded"/>
            </ns0:request>
            <ns0:response>
               <ns0:representation mediaType="*/*"/>
            </ns0:response>
         </ns0:method>
         <ns0:method id="do_put" name="PUT">
            <ns0:doc/>
            <ns0:request>
               <ns0:representation mediaType="multipart/form-data"/>
            </ns0:request>
            <ns0:response>
               <ns0:representation mediaType="*/*"/>
            </ns0:response>
         </ns0:method>
         <ns0:method id="do_put" name="PUT">
            <ns0:doc/>
            <ns0:request>
               <ns0:representation mediaType="application/x-www-form-urlencoded"/>
            </ns0:request>
            <ns0:response>
               <ns0:representation mediaType="*/*"/>
            </ns0:response>
         </ns0:method>
         <ns0:method id="do_delete" name="DELETE">
            <ns0:doc>Doc echo (DELETE)</ns0:doc>
            <ns0:request>
               <ns0:representation mediaType="*/*"/>
            </ns0:request>
            <ns0:response>
               <ns0:representation mediaType="*/*"/>
            </ns0:response>
         </ns0:method>
      </ns0:resource>
      <ns0:resource path="/v1/test/invoice">
         <ns0:method id="do_post" name="POST">
            <ns0:doc>Create an invoice</ns0:doc>
            <ns0:request>
               <ns0:representation mediaType="multipart/form-data"/>
            </ns0:request>
            <ns0:response>
               <ns0:representation mediaType="*/*"/>
            </ns0:response>
         </ns0:method>
         <ns0:method id="do_post" name="POST">
            <ns0:doc>Create an invoice</ns0:doc>
            <ns0:request>
               <ns0:representation mediaType="application/x-www-form-urlencoded"/>
            </ns0:request>
            <ns0:response>
               <ns0:representation mediaType="*/*"/>
            </ns0:response>
         </ns0:method>
         <ns0:method id="do_put" name="PUT">
            <ns0:doc>Update and invoice</ns0:doc>
            <ns0:request>
               <ns0:representation mediaType="multipart/form-data"/>
            </ns0:request>
            <ns0:response>
               <ns0:representation mediaType="*/*"/>
            </ns0:response>
         </ns0:method>
         <ns0:method id="do_put" name="PUT">
            <ns0:doc>Update and invoice</ns0:doc>
            <ns0:request>
               <ns0:representation mediaType="application/x-www-form-urlencoded"/>
            </ns0:request>
            <ns0:response>
               <ns0:representation mediaType="*/*"/>
            </ns0:response>
         </ns0:method>
         <ns0:method id="do_delete" name="DELETE">
            <ns0:doc>Delete an invoice</ns0:doc>
            <ns0:request>
               <ns0:representation mediaType="*/*"/>
            </ns0:request>
            <ns0:response>
               <ns0:representation mediaType="*/*"/>
            </ns0:response>
         </ns0:method>
      </ns0:resource>
      <ns0:resource path="/v1/test/file_upload">
         <ns0:method id="do_post" name="POST">
            <ns0:doc>Test filem upload</ns0:doc>
            <ns0:request>
               <ns0:representation mediaType="multipart/form-data"/>
            </ns0:request>
            <ns0:response>
               <ns0:representation mediaType="*/*"/>
            </ns0:response>
         </ns0:method>
         <ns0:method id="do_post" name="POST">
            <ns0:doc>Test filem upload</ns0:doc>
            <ns0:request>
               <ns0:representation mediaType="application/x-www-form-urlencoded"/>
            </ns0:request>
            <ns0:response>
               <ns0:representation mediaType="*/*"/>
            </ns0:response>
         </ns0:method>
      </ns0:resource>
      <ns0:resource path="/v1/test/sql_error">
         <ns0:method id="do_get" name="GET">
            <ns0:doc>Test SQL error propagation</ns0:doc>
            <ns0:request>
               <ns0:representation mediaType="application/json"/>
            </ns0:request>
            <ns0:response>
               <ns0:representation mediaType="application/json"/>
            </ns0:response>
         </ns0:method>
      </ns0:resource>
      <ns0:resource path="/v1/test/xlsdoc2">
         <ns0:method id="do_get" name="GET">
            <ns0:doc>Test xls generation</ns0:doc>
            <ns0:request>
               <ns0:representation mediaType="application/json"/>
            </ns0:request>
            <ns0:response>
               <ns0:representation mediaType="application/vnd.ms-excel"/>
            </ns0:response>
         </ns0:method>
      </ns0:resource>
      <ns0:resource path="/v1/test/xlsdoc">
         <ns0:method id="do_get" name="GET">
            <ns0:doc>Test xls generation</ns0:doc>
            <ns0:request>
               <ns0:representation mediaType="application/json"/>
            </ns0:request>
            <ns0:response>
               <ns0:representation mediaType="*/*"/>
            </ns0:response>
         </ns0:method>
         <ns0:method id="do_get" name="GET">
            <ns0:doc>Test xls generation</ns0:doc>
            <ns0:request>
               <ns0:representation mediaType="application/json"/>
            </ns0:request>
            <ns0:response>
               <ns0:representation mediaType="application/vnd.ms-excel"/>
            </ns0:response>
         </ns0:method>
         <ns0:method id="do_get" name="GET">
            <ns0:doc>Test xls generation</ns0:doc>
            <ns0:request>
               <ns0:representation mediaType="application/json"/>
            </ns0:request>
            <ns0:response>
               <ns0:representation mediaType="application/octet-stream"/>
            </ns0:response>
         </ns0:method>
      </ns0:resource>
      <ns0:resource path="/v1/test/info/table">
         <ns0:method id="do_get" name="GET">
            <ns0:doc>Return info about table</ns0:doc>
            <ns0:request>
               <ns0:representation mediaType="application/json"/>
            </ns0:request>
            <ns0:response>
               <ns0:representation mediaType="*/*"/>
            </ns0:response>
         </ns0:method>
      </ns0:resource>
      <ns0:resource path="/v1/test/role">
         <ns0:method id="do_get" name="GET">
            <ns0:doc>Test role security</ns0:doc>
            <ns0:request>
               <ns0:representation mediaType="application/json"/>
            </ns0:request>
            <ns0:response>
               <ns0:representation mediaType="application/json"/>
            </ns0:response>
         </ns0:method>
      </ns0:resource>
      <ns0:resource path="/v1/test/js_error">
         <ns0:method id="do_get" name="GET">
            <ns0:doc>Test error</ns0:doc>
            <ns0:request>
               <ns0:representation mediaType="application/json"/>
            </ns0:request>
            <ns0:response>
               <ns0:representation mediaType="application/json"/>
            </ns0:response>
         </ns0:method>
      </ns0:resource>
      <ns0:resource path="/v1/employees">
         <ns0:method id="do_get" name="GET">
            <ns0:doc>Returns the employees</ns0:doc>
            <ns0:request>
               <ns0:representation mediaType="application/json"/>
            </ns0:request>
            <ns0:response>
               <ns0:representation mediaType="*/*"/>
            </ns0:response>
         </ns0:method>
      </ns0:resource>
      <ns0:resource path="/v1/test/echo_n_times">
         <ns0:method id="do_get" name="GET">
            <ns0:doc/>
            <ns0:request>
               <ns0:representation mediaType="application/json"/>
            </ns0:request>
            <ns0:response>
               <ns0:representation mediaType="*/*"/>
            </ns0:response>
         </ns0:method>
      </ns0:resource>
   </ns0:resources>
</ns0:application>

                    
>

The WADL descriptor is also accessible through the Application Explorer. If you have the corresponding permission, each database will show information by clicking on the information icon. Among other information, the system indicates if the application has RestApi and which ones. Once the RestApi is selected, the system shows its WADL descriptor. Use the Download option to get the corresponding *.xml file.

3.2 GET /api/{dbms}/{path}

/api/{dbms}/{path}

GET Execute a js function. GET methods are usually used to fetch data

Produces

  • text/plain
  • text/csv
  • text/html
  • application/json
  • application/xml
  • application/octet-stream
  • application/vnd.ms-excel
  • application/pdf
  • application/msword
  • image/*

Path parameters

  • {dbms} : The database to use in the process.
  • {path} : API call path defined in wic_rest_api

Responses

  • 200 Success
  • 404 API path not found in wic_rest_api or js script not found
  • 500 An unexpected error
Example

Retrieve the number of rows of a table via systables

Copy
curl -u user:password -X GET  -H "Accept: */*"  \   -L http://web1/service/rest/{database}/docs/v1/api/info/table
Copy
Client client = ClientBuilder.newClient();
client.register( HttpAuthenticationFeature.basic(StudioTestConstants.USER_CODE, StudioTestConstants.USER_PASSWORD));
WebTarget target = client.target("http://www.mydeister.com/service/rest/api/test_junit/v1/test/info/table").queryParam("table", "systables");
Invocation.Builder invocationBuilder  = target.request().accept(MediaType.APPLICATION_JSON);
Response res = invocationBuilder.get();
String responseStr = res.readEntity(String.class);
System.err.println(responseStr);
Copy
{
  "rowset": [
    [
      "systables",
      1,
      251.0
    ]
  ],
  "total": 1,
  "maxrows": -1,
  "metadata": [
    {
      "columnName": "tabname",
      "catalogName": " ",
      "columnClassName": "java.lang.String",
      "columnDisplaySize": 128,
      "columnViewSize": 9,
      "columnLabel": "tabname",
      "columnType": 12,
      "columnTypeName": "varchar",
      "precision": 128,
      "scale": 0,
      "schemaName": " ",
      "tableName": "systables",
      "isNullable": 1,
      "isRowid": false,
      "isNumeric": false,
      "isGeometry": false,
      "refresh": 0,
      "combo": null,
      "header": null,
      "tooltip": null,
      "metainfo": null,
      "action": null,
      "isHidden": false,
      "isPercent": false,
      "isTotal": false
    },
    {
      "columnName": "tabid",
      "catalogName": " ",
      "columnClassName": "java.lang.Integer",
      "columnDisplaySize": 10,
      "columnViewSize": 1,
      "columnLabel": "tabid",
      "columnType": 4,
      "columnTypeName": "serial",
      "precision": 10,
      "scale": 0,
      "schemaName": " ",
      "tableName": "systables",
      "isNullable": 0,
      "isRowid": false,
      "isNumeric": true,
      "isGeometry": false,
      "refresh": 0,
      "combo": null,
      "header": null,
      "tooltip": null,
      "metainfo": null,
      "action": null,
      "isHidden": false,
      "isPercent": false,
      "isTotal": false
    },
    {
      "columnName": "nrows",
      "catalogName": " ",
      "columnClassName": "java.lang.Double",
      "columnDisplaySize": 17,
      "columnViewSize": 5,
      "columnLabel": "nrows",
      "columnType": 8,
      "columnTypeName": "float",
      "precision": 15,
      "scale": 0,
      "schemaName": " ",
      "tableName": "systables",
      "isNullable": 1,
      "isRowid": false,
      "isNumeric": true,
      "isGeometry": false,
      "refresh": 0,
      "combo": null,
      "header": null,
      "tooltip": null,
      "metainfo": null,
      "action": null,
      "isHidden": false,
      "isPercent": false,
      "isTotal": false
    }
  ],
  "attributes": null,
  "title": null,
  "totals": [],
  "empty": [],
  "pivot_metadata": null
}

3.3 POST /api/{dbms}/{path}

/api/{dbms}/{path}

POST Execute a js function. POST methods are usually used to execute a process or insert data

Consumes

  • multipart/form-data
  • application/x-www-form-urlencoded
  • application/json
  • text/plain
  • text/html
  • application/xml
  • application/octet-stream

Produces

  • text/plain
  • text/csv
  • text/html
  • application/json
  • application/xml
  • application/octet-stream
  • application/vnd.ms-excel
  • application/pdf
  • application/msword
  • image/*

Path parameters

  • {dbms} : The database to use in the process.
  • {path} : API call path defined in wic_rest_api

Responses

  • 200 Success
  • 404 API path not found in wic_rest_api or js script not found
  • 500 An unexpected error
Example

Upload an invoice with data and a file fields to the table of invoices consuming a multipart POST:

Copy
curl -u user:password -X POST  \
  -d invoice_id=value \
  -d invoice_customer=value \
  -d invoice_date=value \
  -d invoice_state=value \
  WARNING BINARY FILE CANNOT BE SENT IN URL ENCODED  \
   -H "dummy: value"  \
   -H "Accept: */*"  \
   -L http://web1/service/rest/{database}/docs/v1/api/invoice
Copy
MultiPart multiPart = new MultiPart();
multiPart.setMediaType(MediaType.MULTIPART_FORM_DATA_TYPE);

multiPart
	.bodyPart(new FormDataBodyPart("invoice_id", "I-000001"))
	.bodyPart(new FormDataBodyPart("invoice_customer", "John Doe"))
	.bodyPart(new FormDataBodyPart("invoice_date", JDBCDateFormat.format(Calendar.getInstance().getTime())))
	.bodyPart(new FormDataBodyPart("invoice_state", "O"))
	.bodyPart(new FileDataBodyPart("invoice_document", new File("/tmp/invoice.pdf")));


Client client = ClientBuilder.newClient();
client.register( HttpAuthenticationFeature.basic(StudioTestConstants.USER_CODE, StudioTestConstants.USER_PASSWORD));
WebTarget target = client.target("http://www.mydeister.com/service/rest/api/test_junit/v1/test/invoice").register(MultiPartFeature.class);
Invocation.Builder invocationBuilder  = target.request().accept(MediaType.APPLICATION_JSON);
Response res = invocationBuilder.post(Entity.entity(multiPart, multiPart.getMediaType()));
String responseStr = res.readEntity(String.class);
System.err.println(responseStr);
Copy
{
  "serial": 0,
  "warnings": null,
  "count": 1,
  "resultset": false,
  "time": 11,
  "type": "INSERT",
  "sql": "INSERT INTO studio_rest_api_invoice_docs (invoice_customer,invoice_state,invoice_id,invoice_document,invoice_date) VALUES (?,?,?,?,?)"
}
Example

POST an invoice using url encoded form. No files uploaded.

Copy
curl -u user:password -X POST  \
   -F invoice_id="value"  \
   -F invoice_customer="value"  \
   -F invoice_date="value"  \
   -F invoice_state="value"  \
   -F invoice_document=@/file/path/filename.ext  \
   -H "dummy: value"  \
   -H "Accept: */*"  \
   -L http://web1/service/rest/{database}/docs/v1/api/invoice
Copy
Form form = new Form();

form.param("invoice_id", "I-000001")
	.param("invoice_customer", "John Doe")
	.param("invoice_date", JDBCDateFormat.format(Calendar.getInstance().getTime()))
	.param("invoice_state", "O");


Client client = ClientBuilder.newClient();
client.register( HttpAuthenticationFeature.basic(StudioTestConstants.USER_CODE, StudioTestConstants.USER_PASSWORD));
WebTarget target = client.target("http://www.mydeister.com/service/rest/api/test_junit/v1/test/invoice").register(MultiPartFeature.class);
Invocation.Builder invocationBuilder  = target.request().accept(MediaType.APPLICATION_JSON);
Response res = invocationBuilder.post(Entity.form(form));
Copy
{
  "serial": 0,
  "warnings": null,
  "count": 1,
  "resultset": false,
  "time": 11,
  "type": "INSERT",
  "sql": "INSERT INTO studio_rest_api_invoice_docs (invoice_customer,invoice_state,invoice_id,invoice_document,invoice_date) VALUES (?,?,?,?,?)"
}
Example

POST an invoice as a json. No files uploaded.

Copy
curl -u user:password -X POST  \
   -d  '{"invoice_id":"value","invoice_customer":"value","invoice_date":"value","invoice_state":"value","invoice_document":"value"}' \
   -H "dummy: value"  \
   -H "Accept: */*"  \
   -L http://web1/service/rest/{database}/docs/v1/api/invoice
Copy
Form form = new Form();

form.param("invoice_id", "I-000001")
	.param("invoice_customer", "John Doe")
	.param("invoice_date", JDBCDateFormat.format(Calendar.getInstance().getTime()))
	.param("invoice_state", "O");


Client client = ClientBuilder.newClient();
client.register( HttpAuthenticationFeature.basic(StudioTestConstants.USER_CODE, StudioTestConstants.USER_PASSWORD));
WebTarget target = client.target("http://www.mydeister.com/service/rest/api/test_junit/v1/test/invoice").register(MultiPartFeature.class);
Invocation.Builder invocationBuilder  = target.request().accept(MediaType.APPLICATION_JSON);
Response res = invocationBuilder.post(Entity.form(form));
Copy
{
  "serial": 0,
  "warnings": null,
  "count": 1,
  "resultset": false,
  "time": 11,
  "type": "INSERT",
  "sql": "INSERT INTO studio_rest_api_invoice_docs (invoice_customer,invoice_state,invoice_id,invoice_document,invoice_date) VALUES (?,?,?,?,?)"
}

3.4 PUT /api/{dbms}/{path}

/api/{dbms}/{path}

PUT Execute a js function. PUT methods are usually used to modify data

Consumes

  • multipart/form-data
  • application/x-www-form-urlencoded
  • application/json
  • text/plain
  • text/html
  • application/xml
  • application/octet-stream

Produces

  • text/plain
  • text/csv
  • text/html
  • application/json
  • application/xml
  • application/octet-stream
  • application/vnd.ms-excel
  • application/pdf
  • application/msword
  • image/*

Path parameters

  • {dbms} : The database to use in the process.
  • {path} : API call path defined in wic_rest_api

Responses

  • 200 Success
  • 404 API path not found in wic_rest_api or js script not found
  • 500 An unexpected error
Example

Sample multipart PUT. Update an invoice uploading a new file and setting a new state:

Copy
curl -u user:password -X PUT  -H "Accept: */*"  \   -L http://web1/service/rest/{database}/docs/v1/api/invoice
Copy
MultiPart multiPart = new MultiPart();
multiPart.setMediaType(MediaType.MULTIPART_FORM_DATA_TYPE);

multiPart
	.bodyPart(new FormDataBodyPart("invoice_id", "I-000001"))
	.bodyPart(new FormDataBodyPart("invoice_state", "P"))
	.bodyPart(new FileDataBodyPart("invoice_document", new File("/tmp/invoice.pdf")));


Client client = ClientBuilder.newClient();
client.register( HttpAuthenticationFeature.basic(StudioTestConstants.USER_CODE, StudioTestConstants.USER_PASSWORD));
WebTarget target = client.target("http://www.mydeister.com/service/rest/api/test_junit/v1/test/invoice").register(MultiPartFeature.class);
Invocation.Builder invocationBuilder  = target.request().accept(MediaType.APPLICATION_JSON);
Response res = invocationBuilder.put(Entity.entity(multiPart, multiPart.getMediaType()));
String responseStr = res.readEntity(String.class);
System.err.println(responseStr);
Copy
{
  "serial": 0,
  "warnings": null,
  "count": 1,
  "resultset": false,
  "time": 11,
  "type": "INSERT",
  "sql": "INSERT INTO studio_rest_api_invoice_docs (invoice_customer,invoice_state,invoice_id,invoice_document,invoice_date) VALUES (?,?,?,?,?)"
}
Example

Sample URL encoded PUT. Change an invoice, no file uploaded.

Copy
curl -u user:password -X PUT  -H "Accept: */*"  \ -L http://web1/service/rest/{database}/docs/v1/api/invoice
Copy
Form form = new Form();

form.param("invoice_id", "I-000001"))
	.param("invoice_state", "P");

Client client = ClientBuilder.newClient();
client.register( HttpAuthenticationFeature.basic(StudioTestConstants.USER_CODE, StudioTestConstants.USER_PASSWORD));
WebTarget target = client.target("http://www.mydeister.com/service/rest/api/test_junit/v1/test/invoice").register(MultiPartFeature.class);
Invocation.Builder invocationBuilder  = target.request().accept(MediaType.APPLICATION_JSON);
Response res = invocationBuilder.put(Entity.form(form));
Copy
{
  "serial": 0,
  "warnings": null,
  "count": 1,
  "resultset": false,
  "time": 11,
  "type": "INSERT",
  "sql": "INSERT INTO studio_rest_api_invoice_docs (invoice_customer,invoice_state,invoice_id,invoice_document,invoice_date) VALUES (?,?,?,?,?)"
}
Example

Sample application/json
.

Copy
curl  -u user:password  -X PUT -d "invoice_id=I-000001&invoice_customer=John Doe&invoice_date=2019-03-08&invoice_state=O"  -H "Content-Type: application/x-www-form-urlencoded" -H "Accept: application/json" -L http://www.mydeister.com/service/rest/api/est_junit/v1/test/invoice
Copy
Form form = new Form();

form.param("invoice_id", "I-000001"))
	.param("invoice_state", "P");

Client client = ClientBuilder.newClient();
client.register( HttpAuthenticationFeature.basic(StudioTestConstants.USER_CODE, StudioTestConstants.USER_PASSWORD));
WebTarget target = client.target("http://www.mydeister.com/service/rest/api/test_junit/v1/test/invoice").register(MultiPartFeature.class);
Invocation.Builder invocationBuilder  = target.request().accept(MediaType.APPLICATION_JSON);
Response res = invocationBuilder.put(Entity.form(form));
Copy
{
  "serial": 0,
  "warnings": null,
  "count": 1,
  "resultset": false,
  "time": 11,
  "type": "INSERT",
  "sql": "INSERT INTO studio_rest_api_invoice_docs (invoice_customer,invoice_state,invoice_id,invoice_document,invoice_date) VALUES (?,?,?,?,?)"
}

3.5 DELETE /api/{dbms}/{path}

/api/{dbms}/{path}

DELETE Execute a js function. DELETE methods are usually used to delete data

Consumes

Produces

  • text/plain
  • text/csv
  • text/html
  • application/json
  • application/xml
  • application/octet-stream
  • application/vnd.ms-excel
  • application/pdf
  • application/msword
  • image/*

Path parameters

  • {dbms} : The database to use in the process.
  • {path} : API call path defined in wic_rest_api

Responses

  • 200 Success
  • 404 API path not found in wic_rest_api or js script not found
  • 500 An unexpected error
Example

Delete all invoices in a certain state

Copy
curl  -u user:password  -X DELETE -G  -H "Accept: application/json"  -d 'invoice_id=P'  -H "Content-Type: application/json"  -L http://www.mydeister.com/service/rest/api/test_junit/v1/test/invoice
Copy
Client client = ClientBuilder.newClient();
client.register( HttpAuthenticationFeature.basic(StudioTestConstants.USER_CODE, StudioTestConstants.USER_PASSWORD));
WebTarget target = client.target("http://www.mydeister.com/service/rest/api/test_junit/v1/test/invoice").queryParam("invoice_state", "P");
Invocation.Builder invocationBuilder  = target.request().accept(MediaType.APPLICATION_JSON);
Response res = invocationBuilder.delete();
String responseStr = res.readEntity(String.class);
Copy
{
  "serial": 0,
  "warnings": null,
  "count": 0,
  "resultset": false,
  "time": 1,
  "type": "DELETE",
  "sql": "DELETE FROM studio_rest_api_invoice_docs WHERE invoice_state \u003d ?"
}

3.6 The response object

The response comes in application/json format. Besides, three HTTP headers are provided:

  • x-result-type: Contains the java class of the response.
  • x-stdout: Contains the standard output generated by the invocation
  • x-stderr: Contains the standard error generated by the invocation
Example

Using the option -v from the curl command we can see the headers

Copy
curl   -u user:password  -X DELETE -G  -H "Accept: application/json"  -d 'invoice_id=P'  -H "Content-Type: application/json"  -L http://www.mydeister.com/service/rest/api/test_junit/v1/test/invoice
*   Trying 192.168.10.109...
* TCP_NODELAY set
* Connected to mordor (192.168.10.109) port 8080 (#0)
* Server auth using Basic with user 'jab'
> DELETE /service/rest/api/test_junit/v1/test/invoice?invoice_id=P HTTP/1.1
> Host: mordor:8080
> Authorization: Basic amFiOmphYjEyMzQ=
> User-Agent: curl/7.54.0
> Accept: application/json
> Content-Type: application/json
> 
< HTTP/1.1 200 OK
< Date: Fri, 08 Mar 2019 16:10:35 GMT
< x-result-type: deister.axional.server.dbstudio.script.js.ax.db.JSSQLCA
< x-stdout: "JSON = {invoice_id=P}",  
< Content-Type: application/json;charset=UTF-8
< Cache-Control: private, no-cache, no-store
< Content-Length: 189
< 
{
  "serial": 0,
  "warnings": null,
  "count": 12,
  "resultset": false,
  "time": 3,
  "type": "DELETE",
  "sql": "DELETE FROM studio_rest_api_invoice_docs WHERE invoice_state \u003d ?"
* Connection #0 to host mordor left intact
}

The response object can be customized by using the JS function Ax.ext.response.builder . This method allows us to:

  • Set status code (20, 500, 404, ..)
  • Perform redirects (seeOther
  • Control cache
  • Set response content type
  • Add custom headers
  • Return a custom entity

4 Context Filter

As explained before, an HTTP filter can be set in a REST api context by setting a JS filter function in the context.

The function will be executed before executing the called REST endpoint, That means that for all endpoints called from a context, a JS function will be executed.

Keep in mind that filters are always executed, regardless of whether the resource was found or not.

The function receives an instance of JSContainerRequestContext

If the function returns an instance of a HttpResponseBuilder or Response then the request is aborted with the provided reponse

5 Decorating ResultSet responses

When a request-response is a resultset, it is possible to decorate resultset metadata in order to customize the output. To that end, the following tab options are available: HEADER, OUTPUT, COLOR, FONT, LINKS.

5.1 Header

The groups of headers allow making groupings of columns under the same title. The utility in the use of this type of group is to provide the user with a more didactic and intelligible presentation of information.

The grouping criteria of the columns respond to the existence of a common link between them, which can be of a very different nature: the header describes that link. The output fields that are going to belong to the same grouping must be assigned the same header group.

Procedure:

  • Access the tab header.
  • Define a header code: it must be a unique identifier.
  • Assing an existing label from the wic_jdic_lbldata: it will use the idiomatic descriptions of the label in the header group.
  • Headers grid: it defines the form layout of the headers. The default value is zero (null). This field accepts a javascript syntax or integer numbers, which matches the number of columns. The javascript syntax allows a responsive design to reflow content to match different screen sizes. Example: {xs: 1, sm: 1, md: 3, lg: 12}.
  • Access the tab output.
  • Insert the header code in the desired column. The system will display that column under the header inserted.

5.2 Output

Through this option, we access the definition of the output fields. In this tab, in addition to recording the output fields corresponding to the columns of the resulset, a whole series of properties and their characteristics are also defined, both from a presentation and functional point of view.

5.2.1 Parameters: table and column

Name of the table to which the column to be registered as the output field belongs.

The table reported here should be defined in the wic_jdic_tabdata table dictionary, and in combination with the column to register, in the wic_jdic_coldata column dictionary. The fact that they are registered in this last dictionary is important because it will depend on the system showing the descriptions abbreviated from the dictionary, such as column titles.

ATTENTION

Keep in mind that some character has a special treatment: ¡!""#$%&'`´()*+,-.¨·/:;<=>¿?^@|\\[{}; the system will replace them with _ (underlined); these characters are not allowed for the name of a table.

Three colored circles appear next to the table name. Each circle is a link to an option of the table or column. If this circle is shown in gray, that option does not exist and the link is not available.

  • ⬤ Red: link to the physical schema of the table.

  • ⬤ Magenta: link to the attributes of the column.

  • ⬤ Purple: link to the column dictionary of the table.

  • ⬤ Grey: no link is available.

5.2.2 Form layout: header and grid column

It is possible to define layouts through these options.

Header: columns with a defined header will be grouped under that header. To establish a header, it must be previously defined in the tab header (see previous section Headers).

Grid column: it defines the layout of the form. The default value is zero (null). This field accepts a javascript syntax or integer numbers, which matches the number of columns. The javascript syntax allows a responsive design to reflow content to match different screen sizes. Example: {xs: 1, sm: 1, md: 3, lg: 12}.

5.2.3 Configuration

  • Decimals: using this parameter, we indicate to the system the number of decimal places of precision that we want to show us in a numerical field (up to 6 decimal places).

    When you want the system to display the same number of decimals as that of the field's own physical definition within the table to which it belongs, then we will indicate the value AUTO here. This value is the one presented by the default system. In fact, for those fields that are alphanumeric, it will be the value used, regardless of the one that has been reported.

    If the physical definition of the field does not support decimals, the system will not display decimals even if it is prompted to do so. For this, it would be necessary to vary the physical definition of the field itself.

  • Hide column: the system will not display the columns that have the hide column indicator on.
  • Read only: the read only attribute specifies that an input field is read-only. A read-only output field cannot be modified, but it can you can highlight it or copy the text from it.
  • Column note: it is a message that appears when the cursor is positioned over a row. To define it, it is necessary to indicate the name of the column in the column note field.
  • Trend: instead of numeric data, a column’s cells can display trends based on that data (only numerical fields). Select an option:
    • up_is_good: the higher, the better
    • up_is_good + trend: the higher, the better; includes the direction symbol
    • up_is_bad: the higher, the worse
    • up_is_bad + trend: the higher, the worse; includes the direction symbol
  • Colorizer: the report outputs can colorize data cells depending on its content, generally to emphasize the importance of a cell among a group of cells or to classify the dataset when it contains numeric values.

    Just by indicating the color rule definition rule_code in the out_palette field is enough. Use the Table Colorizer tool and you will make it easier to analyze reports at a glance.

    Do not use colorizer when using tab COLOR options.

5.3 Color

In this tab, it is possible to define the color of the text (foreground) and the background of the cells. To do this, expressions are assigned to columns: the foreground or background of the cells defined by the field column will change color when the conditions of the expression field are met. It should be noted that:

Do not use COLOR when using OUTPUT colorizer options.
  • The column of the field column and the column of the field expression can be different or the same.
  • If the field column is not defined (null) and the expression is defined (not null), the color definition will affect all columns.
  • If the field column is defined (not null) and the expression is defined (not null), the color definition will affect only that column.
  • If the field column is defined (not null) and the expression is not (null), the color definition will be always applied to that column.
  • If two expressions apply to the same cell and there is no order, the system will apply the color of the first expression established.
  • If two expressions apply to the same cell and it has been established order in the corresponding field, the system will apply the colors depending on the order established.
  • It is possible to renumber the order by clicking on the reorder button.

It is NOT possible to define the column by using the metacharacter *.

5.4 Font

In this tab, it is possible to define the font of the cell text. To do this, expressions are assigned to columns: the cells defined by the field column will change text font when the conditions of the expression field are met.

The definition of font works the same as the definition of color, so all the notes that apply to color should be taken into account for the font application.

  • Font family: this field specifies a list of preferred fonts that a browser should use to write the content. Font names should be capitalized. The most common are: Activa, Arial, Arial Narrow, Brush Script MT, Candara, Calibri, Cambria, Comic Sans MS, Courier, Courier New, Copperplate, Cursive, Fantasy, Garamond, Geneva, Helvetica, Liberation Sans, Lucida Bright, Monaco, Optima, Perpetua, Symbol, Times, Times New Roman, Verdana ...
  • Weight: the weight of a font is how bold it is. The font-weight property affects how heavily the text is drawn on a page. Choose between normal, bold or define a numerical value: 100, 200, 300, 400, 500, 600, 700, 800, 900. Each value is one degree bolder than the previous one. Normal is equivalent to 400. Bold is equivalent to 700.
  • Style: this property, provides control over several different effects on text. They can be Normal, Italic, Oblique or Backslash.
  • Size: it specifies how large text appearance in pt. The point size refers to the height of a character.
  • Transform: it allows you to specify the capitalization of text. There are various types of capitalization: uppercase, where every letter is in uppercase; capitalize, where every word is capitalized; lowercase, where every letter is in lowercase; initial, where every initial letter of every word is capitalized; and inherit, it inherits this property from its parent element.
  • Decoration: this property provides control over several different effects on text. They can be None, Underline, Overline or Line through.
  • Align: the alignment possibilities are none, left, right, center, justified, initial or inherit (from parent element).

5.5 Links

A hyperlink represents a way or resource to connect a certain field with the universe of possible queries related to any of the existing information shown. Any hyperlink that is defined must be assigned to one of the output fields of the restApi, regardless of the field in question. Generally, the object behind the hyperlink will provide related information with the output field in which it has been defined.

  • Order: it is possible to place more than one hyperlink to a field, so with the order, we define the system the position in which we want to place this hyperlink. This will be the order in which the hyperlinks will be presented in a certain output field; hence, for each field, it is a new numbering.
  • Column: name of the column where you want to define the hyperlink. The column indicated here must correspond to one of the output fields of the object; otherwise, it could not carry out the definition of the hyperlink.
  • Expression: expressions are assigned to columns, so the cells defined by the field column will show a link when the conditions of the expression field are met.
  • Modal mode: there are four different possibilities of opening links.
    • New window: opens the report link on a new window.
    • Current window: opens the report link on the same window.
    • Modal window: opens the report link on a modal window, but does not refresh data after closing it.
      A modal window creates a mode that disables the main window but keeps it visible, with the modal window as a child window in front of it. Users must interact with the modal window before they can return to the parent application. This avoids interrupting the workflow on the main window.
    • Process: opens the report link on a modal window and refreshes data after the modal window closes.
  • Link href:

    the link or target consists of an hiperlink that will provide information related to the source or with any of the concepts involved in it.

6 Creating a Rest API Example

As an example, we will create a Rest API that returns the information related to a customer in a *.pdf file, from the table customer, when its ID number is provided. You can also display the sample video below.

  • Access the Rest Context form through the menu Webservices/REST Context.
  • Select an existing Rest Context or create a new one, since each Rest API requires having a context assigned. To create a new one, define name (Sample), version (1) and path (/sample). Add an optional description. Then press insert.
  • Access the tab Rest APIs.
  • Fill in the Rest API fields:
    • HTTP method*: choose between GET/PUT/POST/DELETE. In this example GET in order to retrieve information.
    • Path*: create the Rest API path. In this example, /customer/{customerid}
    • Context type*: choose between the different options to define the output format. In this case application/pdf.
  • Select the api_stmt_type option: in this case JavaScript, which is a default value.
  • Statement: in this case, write the JavaScript statement.
    Copy
    function main(params, rawBody) {
        return Ax.db.executeQuery(`
            SELECT * FROM customer WHERE customerid = ?
        `, params.customerid
        
        )
    }
  • Press insert button. The new Rest API has been created. Now it is possible to edit the REST CALL PARAMETERS box.
  • Rest Call Parameters (optional): they will limit response parameters to those reported here. Define name, type (String, Integer, Long, Float, Double, Boolean, Date, DateTime, Binary) and Origin (form/json, query, path, header). Optionally, add a description. In this example parameters are: customerid/Integer/path/Customer ID.
    Now mark on Required.
    Press insert on the rest call parameters box.

Now, it is possible to check the correct functioning of this Rest API.

  • Copy the url located in the CURL URL ENCODED box. In this case http://web1/service/rest/{database}/sample/v1/api/customer/{customerid}
  • Paste URL in the web browser.
  • Replace {database} with your database name.
  • Replace {customerid} with an appropriate ID number. In this case, 27.
  • Press enter.

You can see in the video that, as a response to the request, the JavaScript function has been executed, and the browser downloads a file with the information of the customer number 27 in a pdf format.