How-to: Interaction via HTTP protocol
Example 1
Task
We have a certain set of cities associated with their countries.
CLASS Country 'Country';
id 'Code' = DATA STRING[20] (Country) IN id;
name 'Name' = DATA ISTRING[100] (Country) IN id;
country (STRING[20] id) = GROUP AGGR Country c BY id(c);
CLASS City 'City';
name 'Name' = DATA ISTRING[100] (City) IN id;
country 'Country' = DATA Country (City);
nameCountry 'Country' (City c) = name(country(c));
FORM cities 'Cities'
OBJECTS c = City
PROPERTIES(c) name, nameCountry, NEW, DELETE
;
NAVIGATOR {
NEW cities;
}
We need to send an HTTP request for adding a city in the JSON format to a certain url.
Solution
postCity 'Send' (City c) {
EXPORT JSON FROM countryId = id(country(c)), name = name(c);
LOCAL result = FILE();
EXTERNAL HTTP 'http://localhost:7651/exec?action=Location.createCity' PARAMS exportFile() TO result;
LOCAL code = STRING[10]();
LOCAL message = STRING[100]();
IMPORT JSON FROM result() TO() code, message;
IF NOT code() == '0' THEN {
MESSAGE 'Error: ' + message();
}
}
EXTEND FORM cities
PROPERTIES(c) postCity
;
The EXPORT
operator will create a JSON in the FILE
format and then will write it to the exportFile
property. Here is an example of the generated file:
{"countryId":"123","name":"San Francisco"}
Then we call the EXTERNAL
operator, which sends a request to the specified url passing there the contents of the generated file as Body. In this case, since the property in the FROM
block has the type JSON, application/json will be used as the content type. <namespace>.<property name>
is encoded in the url. In this case, the namespace of the action being called createCity
is Location
. All parameters are passed consequently with the ID p
. The response from the server will be written to the result
property. Suppose that the response is received in the JSON format and has one of the following types:
{"code":"0","message":"OK"}
{"code":"1","message":"Invalid country code"}
The response is handled by the IMPORT
operator which parses the corresponding parameters into the code
and message
properties respectively. If any error occurs, the user will see a corresponding error message.
Example 2
Task
Similar to Example 1.
We need to handle the incoming HTTP request and create a new city in the database with the parameters provided in the request.
Solution
createCity (FILE f) {
LOCAL cy = STRING[20] ();
LOCAL ne = STRING[100] ();
IMPORT JSON FROM f AS FILE TO() cy = countryId, ne = name;
IF NOT country(cy()) THEN {
EXPORT JSON FROM code = '1', message = 'Invalid country code';
RETURN;
}
NEW c = City {
name(c) <- ne();
country(c) <- country(cy());
APPLY;
}
EXPORT JSON FROM code = '0', message = 'OK';
}
Since the property is named createCity
and located in the module with the namespace Location
, the url on which the request will be handled looks like this:
http://localhost:7651/exec?action=Location.createCity
Body of the HTTP request will be passed as a parameter of the type FILE
. The values read from the countryId
and name
parameters are written to the local properties cy
and ne
respectively.
If there is no country with the corresponding code, then a JSON file is generated similar to that described in the previous example, and the RETURN
operator is called to abort execution. By default, the response message value is also stored in the exportFile
property.
If all the actions are completed successfully, the corresponding "OK message" is generated in response.
Example 3
Task
We have the logic of book orders.
CLASS Book 'Book';
id 'Code' = DATA STRING[10] (Book) IN id;
name 'Name' = DATA ISTRING[100] (Book) IN id;
book (STRING[10] id) = GROUP AGGR Book b BY id(b);
CLASS Order 'Order';
date 'Date' = DATA DATE (Order);
number 'Number' = DATA STRING[10] (Order);
CLASS OrderDetail 'Order line';
order 'Order' = DATA Order (OrderDetail) NONULL DELETE;
book 'Book' = DATA Book (OrderDetail) NONULL;
nameBook 'Book' (OrderDetail d) = name(book(d));
quantity 'Quantity' = DATA INTEGER (OrderDetail);
price 'Price' = DATA NUMERIC[14,2] (OrderDetail);
FORM order 'Order'
OBJECTS o = Order PANEL
PROPERTIES(o) date, number
OBJECTS d = OrderDetail
PROPERTIES(d) nameBook, quantity, price, NEW, DELETE
FILTERS order(d) == o
EDIT Order OBJECT o
;
FORM orders 'Orders'
OBJECTS i = Order
PROPERTIES(i) READONLY date, number
PROPERTIES(i) NEWSESSION NEW, EDIT, DELETE
;
NAVIGATOR {
NEW orders;
}
We need to send an HTTP request for creating a new order in the JSON format to a certain url.
Solution
FORM exportOrder
OBJECTS order = Order PANEL
PROPERTIES dt = date(order), nm = number(order)
OBJECTS detail = OrderDetail
PROPERTIES id = id(book(detail)), qn = quantity(detail), pr = price(detail)
FILTERS order(detail) == order
;
exportOrder 'Send' (Order o) {
EXPORT exportOrder OBJECTS order = o JSON;
LOCAL result = FILE();
EXTERNAL HTTP 'http://localhost:7651/exec?action=Location.importOrder' PARAMS exportFile() TO result;
}
EXTEND FORM orders
PROPERTIES(i) exportOrder;
;
To create a JSON with nested tags, we need to create a form with the corresponding objects linked via the FILTERS
block of operators. Based on the dependencies between objects, the system will generate a JSON file with the corresponding structure. In the considering example, the output JSON structure will look like this:
{
"dt":"20.08.18",
"nm":"1",
"detail":[
{
"pr":5.99,
"id":"b1",
"qn":3
},
{
"pr":6.99,
"id":"b2",
"qn":2
}
]
}
We do not create a custom tag for order
, since the object value is passed as an argument to the EXPORT
operator.
In this example, the response to the HTTP request is ignored.
Example 4
Task
Similar to Example 3.
We need to handle the incoming HTTP request and create a new order in the database with the parameters provided in the request.
Solution
date = DATA LOCAL DATE();
number = DATA LOCAL STRING[10]();
id = DATA LOCAL STRING[10] (INTEGER);
quantity = DATA LOCAL INTEGER (INTEGER);
price = DATA LOCAL NUMERIC[14,2] (INTEGER);
FORM importOrder
PROPERTIES dt = date(), nm = number()
OBJECTS detail = INTEGER
PROPERTIES id = id(detail), qn = quantity(detail), pr = price(detail)
;
importOrder (FILE f) {
IMPORT importOrder JSON FROM f;
NEW o = Order {
date(o) <- date();
number(o) <- number();
FOR id(INTEGER detail) DO NEW d = OrderDetail {
order(d) <- o;
book(d) <- book(id(detail));
quantity(d) <- quantity(detail);
price(d) <- price(detail);
}
APPLY;
}
}
To import the corresponding file in the JSON format, we need to create a form of a similar structure, except that the INTEGER
type will be used as object classes. During the import process, the tag values will be placed in the properties with the corresponding names. The date
and number
properties have no parameters, since their values in JSON are provided at the topmost level.
Example 5
Task
Similar to Example 4.
We need to send an HTTP request to create an order in the JSON format to a certain url as in the previous example, except that everything must be wrapped in the order
tag.
Solution
GROUP order;
FORM exportOrderNew
OBJECTS o = Order
PROPERTIES IN order dt = date(o), nm = number(o)
OBJECTS detail = OrderDetail IN order
PROPERTIES id = id(book(detail)), qn = quantity(detail), pr = price(detail)
FILTERS order(detail) == o
;
exportOrderNew 'Send (new)' (Order o) {
EXPORT exportOrderNew OBJECTS o = o JSON;
LOCAL result = FILE();
EXTERNAL HTTP 'http://localhost:7651/exec?action=Location.importOrderNew' PARAMS exportFile() TO result;
}
EXTEND FORM orders
PROPERTIES(i) exportOrderNew;
;
Unlike the previous example, here we create a property group named order
using the GROUP
operator. When declaring a form, we put all the properties of the purchase order as well as the detail
object into this property group. The result JSON will look like this:
{
"order":{
"dt":"20.08.18",
"nm":"1",
"detail":[
{
"pr":5.99,
"id":"b1",
"qn":3
},
{
"pr":6.99,
"id":"b2",
"qn":2
}
]
}
}
Example 6
Task
Similar to Example 5.
We need to handle the incoming HTTP request and create a new order in the database with the parameters provided in the request.
Solution
FORM importOrderNew
PROPERTIES IN order dt = date(), nm = number()
OBJECTS detail = INTEGER IN order
PROPERTIES id = id(detail), qn = quantity(detail), pr = price(detail)
;
importOrderNew (FILE f) {
IMPORT importOrderNew JSON FROM f;
NEW o = Order {
date(o) <- date();
number(o) <- number();
FOR id(INTEGER detail) DO NEW d = OrderDetail {
order(d) <- o;
book(d) <- book(id(detail));
quantity(d) <- quantity(detail);
price(d) <- price(detail);
}
APPLY;
}
}
Just as in the export process, we put all the properties and the detail
object to the order
group to correctly receive the new version of JSON.
Example 7
Task
Similar to Example 3.
We need to return a list of order numbers for a given date using an HTTP GET request in which this date is provided.
Solution
FORM exportOrders
OBJECTS date = DATE PANEL
OBJECTS order = Order
PROPERTIES nm = number(order)
FILTERS date(order) = date
;
getOrdersByDate (DATE d) {
EXPORT exportOrders OBJECTS date = d JSON;
}
The url to which the HTTP request should be sent will look like this: http://localhost:7651/exec?action=Location.getOrdersByDate&p=12.11.2018
.
The response JSON will look like this:
{
"order": [
{
"nm": "42"
},
{
"nm": "65"
}
]
}
Example 8
Task
Similar to [Example 3](#example 3).
For each order, there is a list of files attached to it.
CLASS Attachment 'Attachment';
order = DATA Order (Attachment) NONULL DELETE;
name 'Name' = DATA STRING (Attachment);
file = DATA FILE (Attachment);
We need to implement HTTP GET request that will return for the passed internal identifier of the order its parameters and a list of files. In addition, we need to implement a separate request to retrieve the content of a specific file.
Solution
FORM orderAttachments
OBJECTS o = Order
PROPERTIES(o) number, date
OBJECTS attachments = Attachment
PROPERTIES id = VALUE(attachments), name(attachments)
;
getOrderAttachments (LONG orderId) {
FOR LONG(Order o AS Order) = orderId DO {
EXPORT orderAttachments OBJECTS o = o JSON;
}
}
To generate an HTTP request, use the following url : http://localhost:7651/exec?action=getOrderAttachments&p=32178. In it p parameter contains internal order identifier.
The response will return for example the following JSON :
{
"date": "20.10.2021",
"number": "12",
"attachments": [
{
"name": "File 1",
"id": 32180
},
{
"name": "File 2",
"id": 32183
}
]
}
The id attributes will contain internal file identifiers. The contents of these files can then be read by a query using the following url : http://localhost:7651/exec?action=getOrderAttachment&p=32180. The getOrderAttachment action is declared as follows :
getOrderAttachment (LONG id) {
FOR LONG(Attachment a AS Attachment) = id DO
exportFile() <- file(a);
}
The query will return a file with Content-Type corresponding to the extension of the file property.