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 make 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 to the system, the http method, the context and the path in the request URL are used to determine which JS function is going to be called.

In Axional Studio REST resources are defined in three 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 JS method 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
wic_dbscripts_js_funcs JS functions are catalogued in the table wic_dbscripts_js_funcs and will receive a map with the arguments of the request. Select an existing JS funtion or create a new one through the menu Business logic/Sever JS code.

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

The Rest API Context form includes following options:

  • Name*: select an existing name or define a new name for the context
  • Path*: define the context path. Format:/{context_path}
  • Version*: change version when modifying context in order to avoid overwriting. Format: /v/{version}
  • Description: create an optional description to define APIs aggrupation
  • Filter JS Function*: this filter will be applied previous to the api request. It is usually used, for instance, for security and safety reasons. This JavaScrip function is also catalogued in the table wic_dbscripts_js_funcs.
  • File: upload a JAR file. This is a type of compressed zip file that can containg 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 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>

2.2 Rest API Defintion

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).
  • JS Code*: select an existing JavaScript code or create a new one through the menu Business logic/Sever JS code. IMPORTANT: when creating a new code the name of function has to be the same as the JavaScript name.
  • Path*: create the Rest API path. Format: /{path}. It can contain multiple slashes /
  • 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.
  • Rest Call Parameters (recommended): it limits response parameters to those reported here. Define name, type and origen. Type refers to the type of data (String, Integer, Long, Float, Double, Boolean, Date, DateTime, Binary). Origen distinguishes between the following parameter types based on the parameter location within the request:
    • form/json: they are located in the body of 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.

    Click the tick box Repeating (array) when the parameter will be received more than one, in an array.

    Optionally, add a description to the Rest Call Parameters.
  • 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.
  • 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.
 

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 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 the 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 in order 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 highest 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 highest 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 model of the deployed resources, their structure, supported media types, HTTP methods and so on. In a sense, WADL is a 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 to 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 to 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>

                    
>

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

/api/{dbms}/{path}

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

Produces

  • application/json
  • text/csv
  • application/vnd.ms-excel
  • text/plain

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

Produces

  • application/json
  • text/csv
  • application/vnd.ms-excel
  • text/plain

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

Produces

  • application/json
  • text/csv
  • application/vnd.ms-excel
  • text/plain

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 uploafing 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

  • application/json
  • text/csv
  • application/vnd.ms-excel
  • text/plain

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. In addition, 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 methods allows us :

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

4 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 bellow.

  • Access the Rest Context form through the menu.
  • 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, version and path. Then press insert.
  • Access the tab Rest APIs.
  • Fill in the Rest API fields:
    • HTTP method*: select between GET/PUT/POST/DELETE. In this example GET in order to retrieve information.
    • JS Code: select an existing JavaScript code or create a new one. IMPORTANT: the name of function has to be the same as the JavaScript name.
      Copy
      function sample_get_customer(data) {
          
          
          return Ax.db.executeQuery(`
          
              SELECT * FROM customer WHERE 
              customerid = ?
          `, data.customerid
          
          )
          
      }

      Now come back to the REST APIs form and assign the new JS function to the Rest API.

    • Path*: create the Rest API path.
    • Context type*: select between the different options to define the output format. In this case application/pdf.
  • Press insert button. The Rest API sample_get_customer 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 Origen (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.