Commit 3a9a24ad authored by Alzhan Turlybekov's avatar Alzhan Turlybekov Committed by Ondrej Sejvl
Browse files

Advanced training

parent 473741ee
# -*- mode: qore; indent-tabs-mode: nil -*-
#
# Qorus Integration Engine
%new-style
%strict-args
%require-types
%enable-all-warnings
our string format_version = "2.6";
our hash<auto> groups."ADVANCED-EXAMPLES".desc = "Qorus advanced examples";
our hash<auto> queues."adv_examples_queue" = {"desc": "asynchronous queue for advanced examples"};
const async_step = {
"name": "adv_examples_async_step:1.0",
"valname": "adv_examples_async_step_val:1.0",
"endname": "adv_examples_async_step_end:1.0",
"queue": "adv_examples_queue",
};
const save_data_to_csv_file = {
"name": "adv_examples_async_step_save_data_to_csv_file:1.0",
};
const steps = (
async_step,
save_data_to_csv_file
);
our hash<auto> workflows."ADV-EXAMPLES-ASYNC_STEP"."1.0" = {
"desc" : "Example of asynchronous steps",
"author" : "Alzhan Turlybekov (Qore Technologies)",
"steps" : steps,
"groups" : ("ADVANCED-EXAMPLES",),
"autostart" : 1,
};
# -*- mode: qore; indent-tabs-mode: nil -*-
%requires qore >= 0.8.10
module AdvancedExamplesSchema {
version = "1.0";
desc = "Schema for exchange rates application";
author = "Alzhan Turlybekov (Qore Technologies)";
url = "http://www.qoretechnologies.com";
}
# here we add fallback paths to the QORE_MODULE_DIR search path,
# in case QORE_MODULE_DIR is not set properly for Qorus
%append-module-path /var/opt/qorus/qlib:$OMQ_DIR/qlib:/opt/qorus/qlib
%requires Schema
%requires SqlUtil
%new-style
%require-types
%strict-args
%enable-all-warnings
# private namespace for private schema declarations
namespace Private {
const AdvancedExamplesTestTable = {
"columns": {
"id": c_int(True, "PK ID field"),
"creation_date": c_date(True, "input creation date"),
"message": c_varchar(200, False, "input messag"),
},
"primary_key": ("name": "pk_adv_examples_test", "columns": ("id")),
};
const Tables = {
"advanced_examples_test": AdvancedExamplesTestTable,
};
}
public namespace AdvancedExamplesSchema {
public string sub get_datasource_name() {
return "omquser";
}
public AdvancedExamplesSchema sub get_user_schema(AbstractDatasource ds, *string dts, *string its) {
return new AdvancedExamplesSchema(ds, dts, its);
}
public class AdvancedExamplesSchema inherits AbstractSchema {
public {
const SchemaName = "AdvancedExamplesSchema";
const SchemaVersion = "1.0";
}
constructor(AbstractDatasource ds, *string dts, *string its) : AbstractSchema(ds, dts, its) {
}
private string getNameImpl() {
return SchemaName;
}
private string getVersionImpl() {
return SchemaVersion;
}
private *hash getTablesImpl() {
return Tables;
}
}
}
# Asynchronous Workflow Step
Asynchronous steps allow Qorus to efficiently process asynchronous actions. In the context of Qorus workflow processing, asynchronous actions are actions that take a significant amount of time to complete. It is not necessary to know in advance how much time the action will take to complete to define an asynchronous step. In this example we will create a workflow with one asynchronous step, which waits for data in the database.
## Workflow definition
To define an asynchronous step, the step definition must define an asynchronous back-end function and a queue, and either [wf_submit_async_key()](https://www.qoretechnologies.com/manual/qorus/latest/qorus/namespaceOMQ_1_1UserApi_1_1Workflow.html#aae61f0cc389c81a9d7743bbc339c5ba5) or [wf_skip_async_step()](https://www.qoretechnologies.com/manual/qorus/latest/qorus/namespaceOMQ_1_1UserApi_1_1Workflow.html#a0fdb22028cc1ccc8956d680dd09efc8f) must be called during the execution of the primary step function.
```php
$queues."adv_examples_queue" = ("desc": "asynchronous queue for advance examples");
const async_step = (
"name": "adv_examples_async_step:1.0",
"valname": "adv_examples_async_step_val:1.0",
"endname": "adv_examples_async_step_end:1.0",
"queue": "adv_examples_queue",
);
```
## Asynchronous step definition
In the primary step function [wf_submit_async_key()](https://www.qoretechnologies.com/manual/qorus/latest/qorus/namespaceOMQ_1_1UserApi_1_1Workflow.html#aae61f0cc389c81a9d7743bbc339c5ba5) function is called with generated unique key ([wf_generate_unique_key() function](https://www.qoretechnologies.com/manual/qorus/latest/qorus/namespaceOMQ_1_1UserApi_1_1Workflow.html#a8f5f61b14c5e4d1bbcbd78f8a2e0c56a)):
```php
sub adv_examples_async_step() {
log (LL_INFO, "Check data completion");
wf_submit_async_key(wf_generate_unique_key());
}
```
The [wf_submit_async_key()](https://www.qoretechnologies.com/manual/qorus/latest/qorus/namespaceOMQ_1_1UserApi_1_1Workflow.html#aae61f0cc389c81a9d7743bbc339c5ba5) function saves a reference to the step's action as a unique key in the namespace of the queue; all keys submitted to a queue of the same name must be unique (this is enforced by an index in the Qorus database).
The system option [async_delay](https://www.qoretechnologies.com/manual/qorus/latest/qorus/systemoptions.html#async_delay) determines when the system will retry the step if the step's queue has not been updated in time. If there is no validation function, the default behavior for the system will be to delete any queue data for the step, and re-run the step's primary step function. If this is not the desired behavior, then you must implement a validation function to control how the system reacts to an asynchronous timeout.
In this example the validation function checks if data are present in the database:
```php
auto sub adv_examples_async_step_val(string id) {
DatasourcePool ds = get_datasource_pool("adv_example_db");
on_success ds.commit();
on_error ds.rollback();
*hash query = ds.selectRow("select count(*) cnt from test");
if (query.cnt > 0) {
return OMQ::StatComplete;
}
return OMQ::StatAsyncWaiting;
}
```
## The second step definition
In the second step data received from the database are saved into a CSV file using [CsvUtil module](https://qoretechnologies.com/manual/qorus/latest/qore/modules/CsvUtil/html/). The [CsvFileWriter](https://qoretechnologies.com/manual/qorus/latest/qore/modules/CsvUtil/html/class_csv_util_1_1_csv_file_writer.html) class provides [**write() function](https://qoretechnologies.com/manual/qorus/latest/qore/modules/CsvUtil/html/class_csv_util_1_1_abstract_csv_writer.html#a33187f88b03208ab019837e6a99ae8d4) to write data to the csv file in the easy way:
```php
%requires CsvUtil
const FIELDS = {
"id": {"type": "int"},
"creation_date": {"type": "date", "format": "YYYY-MM-DDTHH:mm:ss"},
"msg": {"type": "string"},
};
const CSV_OPTS = {
"separator": ",",
"eol": "\r\n",
"fields": FIELDS,
"write_headers": True
};
sub adv_examples_async_step_save_data_to_csv_file() {
DatasourcePool ds = get_datasource_pool("omquser");
on_success ds.commit();
on_error ds.rollback();
SqlUtil::AbstractTable table = get_sql_table(ds, "advanced_examples_test");
string path = get_user_connection("adv_example_test_sftp").path();
CsvUtil::CsvFileWriter csv(path + Qore::DirSep + "data.csv", CSV_OPTS);
SQLStatement statement = table.getRowIterator();
csv.write(statement);
}
```
## Testing
To test the workflow it's needed to prepare the database. It can be done using prepared SQL scripts in the **db_scripts** directory of this example. These scripts allow to create the table in the **"omquser"** database and *advanced_examples_test* table and to add some random data to it and to delete them.
The workflow also uses the sftp user connection called *adv-examples-test-sftp* to save the csv file. The connection can be defined by loading the *adv-examples-test-sftp.qconn* file to Qorus.
**Note:** it's good to set the **async_delay** system option for example to 10-15 seconds (default is 3600). You can set it by UI or in the options configuration file in the etc/ directory of the Qorus installation (there is a need to restart Qorus).
In case there are no data in the test table in the adv_example database the workflow order will be in the StatAsyncWaiting state. It can be tested by deleting all data before creating a workflow order. Then when workflow order is created the order should be in the StatAsyncWaiting state. At this moment adding some data to the database using the SQL script will end with the workflow order in the StatComplete state.
To create a workflow order Postman application or [qrest](https://www.qoretechnologies.com/manual/qorus/latest/qorus/commandline.html#qrest) command-line tool can be used. Also the prepared *create_order.qsrcipt* can be used. The following [POST REST request](https://qoretechnologies.com/manual/qorus/latest/qorus/rest_api_page_latest.html#rest_api_latest_workflows__id_or_name_) shows how a workflow can be created:
```php
POST /api/latest/workflows/{id_or_name}?action=createOrder
```
**Parameters (JSON):**
```php
{
"staticdata": {}
}
```
---
<table>
<tr>
<td>[&uarr; Go Back to: Advanced Qorus examples](../)</td>
<td>[Next: Subworkflow step &rarr;](../02_subworkflow_step)</td>
</tr>
</table>
adv-examples-test-sftp = (
desc = Test SFTP for advanced examples,
url = file://tmp,
)
\ No newline at end of file
# -*- mode: qore; indent-tabs-mode: nil -*-
# type: STEP
# name: adv_examples_async_step
# version: 1.0
# desc: Creates the asynchronous WF that must wait for the completion of processing enriched data in DB
# author: Alzhan Turlybekov (Qore Technologies)
%new-style
%require-types
%strict-args
%enable-all-warnings
sub adv_examples_async_step() {
log (LL_INFO, "Check data completion");
wf_submit_async_key(wf_generate_unique_key());
}
#END
# type: VALIDATION
# name: adv_examples_async_step_val
# version: 1.0
# desc: Asynchronously checks whether data have been enriched in DB
# author: Alzhan Turlybekov (Qore Technologies)
%new-style
%require-types
%strict-args
%enable-all-warnings
auto sub adv_examples_async_step_val(string id) {
log (LL_INFO, "Check data completion for id %y", id);
DatasourcePool ds = get_datasource_pool("omquser");
on_success ds.commit();
on_error ds.rollback();
*hash query = ds.selectRow("select count(*) cnt from advanced_examples_test");
if (query.cnt > 0) {
return OMQ::StatComplete;
}
return OMQ::StatAsyncWaiting;
}
#END
# type: ASYNC-END
# name: adv_examples_async_step_end
# version: 1.0
# desc: Confirm completeness of data enrichment
# author: Alzhan Turlybekov (Qore Technologies)
%new-style
%require-types
%strict-args
%enable-all-warnings
sub adv_examples_async_step_end() {
log(LL_INFO, "All data have been enriched");
}
# END
# -*- mode: qore; indent-tabs-mode: nil -*-
# type: STEP
# name: adv_examples_async_step_save_data_to_csv_file
# version: 1.0
# desc: Saves data from the DB to csv file
# author: Alzhan Turlybekov (Qore Technologies)
%new-style
%require-types
%strict-args
%enable-all-warnings
%requires CsvUtil
const FIELDS = {
"id": {"type": "int"},
"creation_date": {"type": "date", "format": "YYYY-MM-DDTHH:mm:ss"},
"msg": {"type": "string"},
};
const CSV_OPTS = {
"separator": ",",
"eol": "\r\n",
"fields": FIELDS,
"write_headers": True
};
sub adv_examples_async_step_save_data_to_csv_file() {
DatasourcePool ds = get_datasource_pool("omquser");
on_success ds.commit();
on_error ds.rollback();
SqlUtil::AbstractTable table = get_sql_table(ds, "advanced_examples_test");
string path = get_user_connection("adv-examples-test-sftp").path();
CsvUtil::CsvFileWriter csv(path + Qore::DirSep + "data.csv", CSV_OPTS);
SQLStatement statement = table.getRowIterator();
csv.write(statement);
}
#END
#!/usr/bin/env qore
%new-style
%strict-args
%require-types
%enable-all-warnings
%requires QorusClientCore
const WORKFLOW_NAME = "ADV-EXAMPLES-ASYNC_STEP";
const ORDER_DATA = {
"staticdata": {
"test": "data"
}
};
hash response = qrest.post("workflows/" + WORKFLOW_NAME + "?action=createOrder", ORDER_DATA);
printf("Response: %N", response);
\ No newline at end of file
CREATE TABLE advanced_examples_test (id INTEGER NOT NULL PRIMARY KEY,
creation_date DATE NOT NULL,
message VARCHAR);
\ No newline at end of file
DELETE FROM advanced_examples_test;
\ No newline at end of file
INSERT INTO advanced_examples_test (id,creation_date,message) VALUES
(1,'2018-04-13 20:15:38','nisl arcu iaculis enim,'),
(2,'2019-01-31 15:44:11','semper pretium neque. Morbi quis urna.'),
(3,'2017-12-25 11:33:23','ut quam vel sapien'),
(4,'2019-06-16 14:59:51','euismod ac, fermentum vel, mauris. Integer sem'),
(5,'2018-12-31 23:28:58','justo faucibus lectus, a sollicitudin orci'),
(6,'2018-02-08 07:11:41','lectus.'),
(7,'2019-01-17 07:58:15','turpis egestas. Fusce aliquet magna a neque.'),
(8,'2018-12-18 18:14:42','Nulla tempor augue ac ipsum.'),
(9,'2018-02-07 00:17:58','metus'),
(10,'2017-10-28 17:54:51','Donec');
INSERT INTO advanced_examples_test (id,creation_date,message) VALUES
(11,'2019-07-31 02:54:34','turpis egestas. Fusce'),
(12,'2019-07-04 12:47:03','luctus vulputate, nisi sem semper erat, in'),
(13,'2018-12-12 10:08:41','fames ac'),
(14,'2018-07-19 12:30:19','In scelerisque scelerisque dui. Suspendisse ac metus'),
(15,'2019-07-16 03:43:23','eget nisi dictum augue malesuada malesuada.'),
(16,'2018-08-08 00:02:32','tellus non magna. Nam'),
(17,'2018-10-17 10:18:25','gravida mauris ut mi.'),
(18,'2019-07-22 17:31:15','dictum cursus. Nunc mauris'),
(19,'2018-09-13 04:20:56','sapien, cursus in, hendrerit consectetuer, cursus et,'),
(20,'2018-07-21 09:44:28','et libero. Proin mi. Aliquam gravida mauris');
INSERT INTO advanced_examples_test (id,creation_date,message) VALUES
(21,'2019-02-02 09:58:01','mi.'),
(22,'2019-08-07 14:53:53','eget laoreet'),
(23,'2019-09-02 10:02:11','porttitor scelerisque neque. Nullam nisl.'),
(24,'2017-10-19 00:09:55','sit amet ante. Vivamus'),
(25,'2018-11-05 17:01:42','magna a'),
(26,'2019-09-30 13:32:28','luctus felis purus ac tellus. Suspendisse sed'),
(27,'2018-06-12 06:44:41','aliquet vel, vulputate eu,'),
(28,'2018-06-22 12:33:29','luctus et'),
(29,'2017-12-12 12:53:12','hendrerit neque. In ornare sagittis'),
(30,'2018-08-27 19:43:06','velit. Quisque varius.');
INSERT INTO advanced_examples_test (id,creation_date,message) VALUES
(31,'2018-03-13 17:20:55','egestas. Sed pharetra, felis eget varius'),
(32,'2019-01-27 21:02:43','purus mauris a nunc. In'),
(33,'2018-08-08 12:07:12','molestie arcu. Sed eu nibh vulputate mauris'),
(34,'2019-07-25 13:59:51','ligula. Donec luctus aliquet odio. Etiam ligula'),
(35,'2018-05-30 09:39:50','porttitor'),
(36,'2019-07-03 19:14:40','Nunc sollicitudin commodo ipsum.'),
(37,'2018-04-07 00:41:04','vehicula risus. Nulla eget metus eu erat'),
(38,'2019-05-08 10:16:26','netus'),
(39,'2018-08-11 07:44:49','Morbi non'),
(40,'2018-11-27 23:49:10','magna. Duis dignissim tempor arcu. Vestibulum');
INSERT INTO advanced_examples_test (id,creation_date,message) VALUES
(41,'2019-03-08 19:59:54','mattis. Cras eget nisi'),
(42,'2019-02-09 18:12:16','a, magna. Lorem ipsum dolor sit amet,'),
(43,'2018-06-14 04:45:16','tellus. Suspendisse sed dolor. Fusce'),
(44,'2018-03-04 14:39:31','lorem ipsum sodales purus,'),
(45,'2018-07-04 14:04:11','massa rutrum magna.'),
(46,'2018-07-06 09:46:09','Aliquam'),
(47,'2019-05-23 19:16:33','Donec'),
(48,'2019-05-01 22:47:22','a feugiat tellus'),
(49,'2018-10-25 17:05:42','sem'),
(50,'2018-08-28 02:06:13','dui. Suspendisse ac metus vitae');
INSERT INTO advanced_examples_test (id,creation_date,message) VALUES
(51,'2018-08-10 02:35:53','amet, consectetuer adipiscing elit.'),
(52,'2019-03-30 08:28:07','Suspendisse aliquet molestie tellus.'),
(53,'2019-02-05 04:45:04','orci luctus et ultrices posuere cubilia'),
(54,'2019-06-07 09:08:59','erat nonummy ultricies'),
(55,'2019-02-12 20:48:37','auctor quis, tristique ac, eleifend vitae, erat.'),
(56,'2017-10-12 13:11:44','Curabitur vel lectus. Cum sociis natoque'),
(57,'2019-01-20 19:52:30','nibh dolor, nonummy ac, feugiat non, lobortis'),
(58,'2018-12-01 20:31:11','arcu'),
(59,'2017-10-08 22:13:24','Duis gravida. Praesent'),
(60,'2017-11-29 23:04:58','volutpat ornare, facilisis eget, ipsum.');
INSERT INTO advanced_examples_test (id,creation_date,message) VALUES
(61,'2018-05-07 18:08:40','nonummy ac,'),
(62,'2018-05-24 12:14:45','nulla.'),
(63,'2018-12-26 14:17:14','nisi.'),
(64,'2019-01-02 02:04:59','Nullam scelerisque neque sed'),
(65,'2018-10-26 01:03:54','luctus et ultrices posuere cubilia Curae;'),
(66,'2019-03-06 15:36:45','urna convallis erat, eget tincidunt dui'),
(67,'2019-05-08 02:18:49','elit, a feugiat'),
(68,'2019-04-30 18:13:48','leo. Cras vehicula'),
(69,'2018-10-25 01:14:44','auctor non, feugiat'),
(70,'2018-07-12 18:44:42','volutpat. Nulla dignissim. Maecenas');
INSERT INTO advanced_examples_test (id,creation_date,message) VALUES
(71,'2019-06-07 10:28:18','id ante dictum'),
(72,'2018-01-05 12:47:07','ligula. Donec'),
(73,'2018-04-25 09:26:55','sed tortor. Integer aliquam adipiscing'),
(74,'2018-09-11 14:47:26','Nunc sollicitudin commodo ipsum. Suspendisse non'),
(75,'2019-07-04 19:14:52','enim. Mauris quis turpis vitae purus'),
(76,'2019-03-15 02:44:02','dignissim magna'),
(77,'2019-02-26 01:37:35','mattis ornare, lectus ante'),
(78,'2019-06-10 03:32:12','tempor lorem, eget mollis lectus pede'),
(79,'2017-12-12 16:34:25','vulputate,'),
(80,'2019-02-15 15:01:07','adipiscing non, luctus sit amet, faucibus');
INSERT INTO advanced_examples_test (id,creation_date,message) VALUES
(81,'2017-12-25 03:19:13','Quisque ac libero nec ligula'),
(82,'2018-10-18 07:54:55','dictum. Proin eget odio. Aliquam'),
(83,'2019-03-30 13:41:49','natoque'),
(84,'2019-06-26 20:46:24','neque. In ornare'),
(85,'2018-07-25 20:02:52','In condimentum. Donec at arcu.'),
(86,'2018-06-30 13:57:34','eu dolor egestas rhoncus. Proin'),
(87,'2018-03-06 01:58:05','semper et, lacinia'),
(88,'2019-06-27 02:32:42','lectus'),
(89,'2019-01-25 15:21:24','lorem ipsum sodales purus, in'),
(90,'2018-05-04 12:31:49','elit, pellentesque a, facilisis non, bibendum');
INSERT INTO advanced_examples_test (id,creation_date,message) VALUES
(91,'2019-09-22 04:09:07','a felis ullamcorper viverra.'),
(92,'2018-10-05 06:45:06','Suspendisse eleifend. Cras sed leo.'),
(93,'2017-11-30 20:32:19','Donec elementum, lorem ut aliquam iaculis, lacus'),
(94,'2019-01-08 01:42:45','lacus.'),
(95,'2018-12-25 19:21:16','scelerisque mollis. Phasellus libero mauris, aliquam'),
(96,'2019-04-17 10:06:38','justo'),
(97,'2018-02-17 23:11:32','sed,'),
(98,'2019-10-01 07:21:53','eget nisi dictum'),
(99,'2018-11-15 02:32:35','Curabitur vel'),
(100,'2018-07-23 20:50:55','urna, nec luctus felis');
\ No newline at end of file
# -*- mode: qore; indent-tabs-mode: nil -*-
#
# Qorus Integration Engine
%new-style
%strict-args
%require-types
%enable-all-warnings
our string format_version = "2.6";
our hash<auto> groups."ADVANCED-EXAMPLES".desc = "Qorus advanced examples";
const step_action = {"name": "adv_examples_subwf_step_action_create_file:1.0"};
const step_next_subwf = {"name": "adv_examples_subwf_step_next_subwf:1.0",
"subworkflow": True};
const steps = (
step_action,
step_next_subwf,
);
our hash<auto> workflows."ADV-EXAMPLES-SUBWF-STEP-CREATE-FILE"."1.0" = (
"desc" : "Creates file in the filesystem and calls next subworkflow",
"author" : "Alzhan Turlybekov (Qore Technologies)",
"steps" : steps,
"groups" : ("ADVANCED-EXAMPLES",),
"keylist" : ("filename",),
"autostart" : 1,
);
# -*- mode: qore; indent-tabs-mode: nil -*-
#
# Qorus Integration Engine
%new-style
%strict-args
%require-types
%enable-all-warnings
our string format_version = "2.6";
our hash<auto> groups."ADVANCED-EXAMPLES".desc = "Qorus advanced examples";
const step_action = {"name": "adv_examples_subwf_step_action_delete_file:1.0"};
const step_next_subwf = {"name": "adv_examples_subwf_step_next_subwf:1.0",
"subworkflow": True};
const steps = (
step_action,
step_next_subwf,
);
our hash<auto> workflows."ADV-EXAMPLES-SUBWF-STEP-DELETE-FILE"."1.0" = {
"desc" : "Deletes file in the filesystem and calls next subworkflow",
"author" : "Alzhan Turlybekov (Qore Technologies)",
"steps" : steps,
"groups" : ("ADVANCED-EXAMPLES",),
"keylist" : ("filename",),
"autostart" : 1,
};
# Subworkflow step
A subworkflow step binds a child workflow (called a subworkflow) to a step. The child workflow's status will be bound to the step's status; that is; whatever status the child workflow has will be reflected as the step's status. This is how Qorus supports logical branching in workflows, where one branch of processing is optionally executed based on a logical condition.
Subworkflow steps are defined by setting the [subworkflow attribute](https://www.qoretechnologies.com/manual/qorus/latest/qorus/designimplworkflows.html#subworkflowtag) to *True* in the workflow configuration file:
```php
const step_next_subwf = {"name": "adv_examples_next_subwf:1.0",
"subworkflow": True};
```
Subworkflow steps are not bound to any particular workflow type; the only rule for a subworkflow step is that either [wf_bind_subworkflow()](https://www.qoretechnologies.com/manual/qorus/latest/qorus/namespaceOMQ_1_1UserApi_1_1Workflow.html#af606b1c9c91a08b0efcf71e217dc9e0c) or [wf_skip_subworkflow()](https://www.qoretechnologies.com/manual/qorus/latest/qorus/namespaceOMQ_1_1UserApi_1_1Workflow.html#a9a7e6fbab1b4658da7ac924eef8c782a) must be called in the [primary step function](https://www.qoretechnologies.com/manual/qorus/latest/qorus/designimplworkflows.html#primarystepfunc) for the step.
Please note that subworkflow steps may not have a [validation function](https://www.qoretechnologies.com/manual/qorus/latest/qorus/designimplworkflows.html#validationfunc).
# Workflow Definitions
In this example there are two workflows: ADV-EXAMPLES-SUBWF-STEP-CREATE-FILE workflow which creates a file with given content and name and ADV-EXAMPLES-SUBWF-STEP-DELETE-FILE workflow which deletes a file with located in the given path. These workflows uses *adv-examples-test-sftp* user connection which can be created by loading the *adv-examples-test-sftp.qconn* file to Qorus.
# Testing
To test this example a workflow order can be created using Postman application or using [qrest](https://www.qoretechnologies.com/manual/qorus/latest/qorus/commandline.html#qrest) command-line tool. Also the prepared *create_order.qscript* can be used. The following [POST REST request](https://qoretechnologies.com/manual/qorus/latest/qorus/rest_api_page_latest.html#rest_api_latest_workflows__id_or_name_) shows how a workflow can be created:
```php
POST /api/latest/workflows/{id_or_name}?action=createOrder
```
**Parameters (JSON):**
```php
{
"staticdata": {
"action": {
"action": "CREATE",
"file_name": "file_name1",
"directory": "test",
"file_content": "SGVsbG8gd29ybGQ="
},
"next_actions": [
{
"action": "CREATE",
"file_name": "file_name2",
"directory": "test",
"file_content": "SGVsbG8gd29ybGQ="
},
{
"action": "DELETE",
"file_name": "file_name1",
"directory": "test"
}