diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000000000000000000000000000000000..bddc9f221adca60c6a5ab3a3c72b1cafce1219bd --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "building-blocks"] + path = building-blocks + url = git@git.qoretechnologies.com:qorus/building-blocks.git diff --git a/03_basics_building_blocks/01_exchange_rates_app/01_job/BASIC-TRAINING-EXCHANGE-APP.qgroup.yaml b/03_basics_building_blocks/01_exchange_rates_app/01_job/BASIC-TRAINING-EXCHANGE-APP.qgroup.yaml new file mode 100644 index 0000000000000000000000000000000000000000..30349632a7486b567b7b2a44bf2aa2f3f668d6eb --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/01_job/BASIC-TRAINING-EXCHANGE-APP.qgroup.yaml @@ -0,0 +1,4 @@ +# This is a generated file, don't edit! +type: group +name: BASIC-TRAINING-EXCHANGE-APP +desc: "Basic training exchange application objects" diff --git a/03_basics_building_blocks/01_exchange_rates_app/01_job/README.md b/03_basics_building_blocks/01_exchange_rates_app/01_job/README.md new file mode 100644 index 0000000000000000000000000000000000000000..a42fb20db482bc6e2c5d1f409e527a3096f1504b --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/01_job/README.md @@ -0,0 +1,101 @@ +# Implementing Qorus Jobs + +Jobs are simple tasks executed on a schedule, similar to a cron job. The information and status of processing is stored in the database and is reported through system APIs. + +# Job Definition Files + +Qorus jobs are defined using two files, one file for the job metadata in the YAML format and the other for the job code. In the YAML file there is code tag that is used to reference to the job's code, the path is relative. Job definition files are used to create job definitions in the Qorus schema that will be run according to their schedule. + +**NOTE**: It's recommended to have YAML definition files in the same directory with the user code. + +Jobs can be easily created using the Qorus Developer Tools extension that will generate job metadata and code: + +![alt-text](img/testjob.gif) + +**NOTE**: the YAML file generated by the extension should not be manually edited otherwise it may cause code and metadata misalignments and hence problems with the extension usability. + +The metadata file consists of metadata tags defining the job. The code file contains Qore or Java language code that makes up the job. + +## Example Job Definition + +Metadata: +```yaml +# This is a generated file, don't edit! +type: job +name: basics-logging-job +desc: "Job implementation example. The job simply logs every hour." +author: + - Qore Technologies, s.r.o. +base-class-name: QorusJob +class-name: BasicsLoggingJob +groups: + - BASIC-TRAINING-EXCHANGE-APP +lang: qore +schedule: + minutes: "0" + hours: "*" + days: "*" + months: "*" + dow: "*" +version: "1.0" +code: basics-logging-job-1.0.qjob +``` + +The training job in this section simply logs a message in the job log file; it is built with a no-code approach using the `BBM_LogMessage` building block. + +Handcoded Qore equivalent: +```php +%strict-args +%require-types +%new-style +%enable-all-warnings + +class BasicsLoggingJob inherits QorusJob { + run() { + logInfo("job info: %y", getInfo()); + } +} +``` + +Information on how to implement Qorus Objects using YAML format can be found [here](https://qoretechnologies.com/manual/qorus/latest/qorus/implementingqorusobjectsusingyaml.html). + +The YAML schema used for validation of the job metadata can be downloaded [here](https://qoretechnologies.com/manual/qorus/latest/qorus/job_schema.yaml). + +The job's code must have a **run()** method. This function will be called when the job is scheduled. In the example above the run() method will be called every hour. + +Whenever a job is executed, a record in the **JOB_INSTANCE** table is created. If an error is raised by calling [errWithInfo()](https://qoretechnologies.com/manual/qorus/latest/qorus/classOMQ_1_1UserApi_1_1Job_1_1JobApi.html#a88cd263ee9118e7735838ca3aac2990f) with a severity greater than [OMQ::ES_Warning](https://qoretechnologies.com/manual/qorus/latest/qorus/group__JobStatusDescriptions.html), the job instance will get a status of [OMQ::JS_Error](https://qoretechnologies.com/manual/qorus/latest/qorus/group__JobStatusDescriptions.html#gac0c70076e9afec841a5455b36cb4713c), otherwise the job instance will get a status of [OMQ::JS_Complete](https://qoretechnologies.com/manual/qorus/latest/qorus/group__JobStatusDescriptions.html#ga814061c22780aa392c167ba7b776a75a). In the case that the Qorus system crashes while the job has a [OMQ::JS_InProgress](https://qoretechnologies.com/manual/qorus/latest/qorus/group__JobStatusDescriptions.html#ga959be579cfa577351bfcd12ba55fa840) status, then, when the system is recovered, the job_instance row will get a [OMQ::JS_Crash](https://qoretechnologies.com/manual/qorus/latest/qorus/group__JobStatusDescriptions.html#gab536e747502e01363a328ecb99e2efb7) status. + +Job instances can never be recovered; the status is saved for information purposes only. + +To save information about processing status, call the [saveInfo()](https://qoretechnologies.com/manual/qorus/latest/qorus/classOMQ_1_1UserApi_1_1Job_1_1JobApi.html#a4b7d0fa64af46b39d37fbebeff622131) function. + +The log() function is used to save information in the job's log file; see [System, Service, Workflow, and Job Logging](https://qoretechnologies.com/manual/qorus/latest/qorus/logging.html) for more information about log file locations and file formats. + +# Job Cron Schedule + +It basically follows the format of a cron job time specification; it is made up of 5 fields separated by spaces as follows: + + + + + + + + + + + + + +
Field Values
minutes0-59
hours0-23
days of month1-31
months1-12 (or 3-letter English month abbreviations: Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec)
days of week 0-7 (0 and 7 are Sunday, or 3-letter English day abbreviations: Sun, Mon, Tue, Wed, Thu, Fri, Sat)
+ +[More about job cron schedule](https://qoretechnologies.com/manual/qorus/latest/qorus/implementingjobs.html#jobschedule) + +--- + + + + + + +
[← Go Back to: exchange rates application](../)[Next: Implementing Qorus services →](../02_service)
diff --git a/03_basics_building_blocks/01_exchange_rates_app/01_job/img/testjob.gif b/03_basics_building_blocks/01_exchange_rates_app/01_job/img/testjob.gif new file mode 100644 index 0000000000000000000000000000000000000000..83ce0b47ee41e08e03372de0c85d84f2a70ddc81 Binary files /dev/null and b/03_basics_building_blocks/01_exchange_rates_app/01_job/img/testjob.gif differ diff --git a/03_basics_building_blocks/01_exchange_rates_app/01_job/java/bb-basics-logging-job-java-1.0.qjob.yaml b/03_basics_building_blocks/01_exchange_rates_app/01_job/java/bb-basics-logging-job-java-1.0.qjob.yaml new file mode 100644 index 0000000000000000000000000000000000000000..da7b2910cef4213d40f26f403fa4cebbb7f08a00 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/01_job/java/bb-basics-logging-job-java-1.0.qjob.yaml @@ -0,0 +1,47 @@ +# This is a generated file, don't edit! +type: job +name: bb-basics-logging-job-java +desc: Job implementation example. The job simply logs every hour. +lang: java +author: + - Qore Technologies, s.r.o. +base-class-name: QorusJob +class-name: BasicsLoggingJobJava +classes: + - BBM_LogMessage +groups: + - BASIC-TRAINING-EXCHANGE-APP +schedule: + minutes: "0" + hours: "0" + days: "*" + months: "*" + dow: "*" +version: '1.0' +code: bb_basics_logging_job_java_1_0_job/BasicsLoggingJobJava.java +class-connections: + log: + - class: BBM_LogMessage + connector: logMessage + trigger: run +config-items: + - name: log-message-level + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + - name: log-message-string + value: + "job info: %y" + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + - name: log-message-args + value: + "$info:*" + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + value_true_type: string diff --git a/03_basics_building_blocks/01_exchange_rates_app/01_job/java/bb_basics_logging_job_java_1_0_job/BasicsLoggingJobJava.java b/03_basics_building_blocks/01_exchange_rates_app/01_job/java/bb_basics_logging_job_java_1_0_job/BasicsLoggingJobJava.java new file mode 100644 index 0000000000000000000000000000000000000000..c005939576dd901e5fbf445d2f665d1388b34a4e --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/01_job/java/bb_basics_logging_job_java_1_0_job/BasicsLoggingJobJava.java @@ -0,0 +1,75 @@ +import qore.OMQ.*; +import qore.OMQ.UserApi.*; +import qore.OMQ.UserApi.Job.*; +import org.qore.jni.QoreJavaApi; +import org.qore.jni.QoreObject; +import java.util.Map; +import org.qore.jni.Hash; +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; +import qore.BBM_LogMessage; + +class BasicsLoggingJobJava extends QorusJob { + // ==== GENERATED SECTION! DON'T EDIT! ==== // + ClassConnections_BasicsLoggingJobJava classConnections; + + // ======== GENERATED SECTION END ========= // + public BasicsLoggingJobJava() throws Throwable { + super(); + // ==== GENERATED SECTION! DON'T EDIT! ==== // + classConnections = new ClassConnections_BasicsLoggingJobJava(); + // ======== GENERATED SECTION END ========= // + } + + // ==== GENERATED SECTION! DON'T EDIT! ==== // + public void run() throws Throwable { + classConnections.log(null); + } + // ======== GENERATED SECTION END ========= // +} + +// ==== GENERATED SECTION! DON'T EDIT! ==== // +class ClassConnections_BasicsLoggingJobJava { + // map of prefixed class names to class instances + private final Hash classMap; + + ClassConnections_BasicsLoggingJobJava() throws Throwable { + classMap = new Hash(); + UserApi.startCapturingObjectsFromJava(); + try { + classMap.put("BBM_LogMessage", QoreJavaApi.newObjectSave("BBM_LogMessage")); + } finally { + UserApi.stopCapturingObjectsFromJava(); + } + } + + Object callClassWithPrefixMethod(final String prefixedClass, final String methodName, + Object params) throws Throwable { + UserApi.logDebug("ClassConnections_BasicsLoggingJobJava: callClassWithPrefixMethod: method: %s class: %y", methodName, prefixedClass); + final Object object = classMap.get(prefixedClass); + + if (object instanceof QoreObject) { + QoreObject qoreObject = (QoreObject)object; + return qoreObject.callMethod(methodName, params); + } else { + final Method method = object.getClass().getMethod(methodName, Object.class); + try { + return method.invoke(object, params); + } catch (InvocationTargetException ex) { + throw ex.getCause(); + } + } + } + + public Object log(Object params) throws Throwable { + // convert varargs to a single argument if possible + if (params != null && params.getClass().isArray() && ((Object[])params).length == 1) { + params = ((Object[])params)[0]; + } + UserApi.logDebug("log called with data: %y", params); + + UserApi.logDebug("calling logMessage: %y", params); + return callClassWithPrefixMethod("BBM_LogMessage", "logMessage", params); + } +} +// ======== GENERATED SECTION END ========= // diff --git a/03_basics_building_blocks/01_exchange_rates_app/01_job/python/bb-basics-logging-job-python-1.0.qjob.py b/03_basics_building_blocks/01_exchange_rates_app/01_job/python/bb-basics-logging-job-python-1.0.qjob.py new file mode 100644 index 0000000000000000000000000000000000000000..219baf8172ced1af8f4c062d132a227191c84ae0 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/01_job/python/bb-basics-logging-job-python-1.0.qjob.py @@ -0,0 +1,38 @@ +from job import QorusJob +from qore.__root__ import BBM_LogMessage + +class BasicsLoggingJobPython(QorusJob): + def __init__(self): + ####### GENERATED SECTION! DON'T EDIT! ######## + self.class_connections = ClassConnections_BasicsLoggingJobPython() + ############ GENERATED SECTION END ############ + + ####### GENERATED SECTION! DON'T EDIT! ######## + def run(self): + self.class_connections.Connection_1() + ############ GENERATED SECTION END ############ + +####### GENERATED SECTION! DON'T EDIT! ######## +class ClassConnections_BasicsLoggingJobPython: + def __init__(self): + UserApi.startCapturingObjectsFromPython() + # map of prefixed class names to class instances + self.class_map = { + 'BBM_LogMessage': BBM_LogMessage(), + } + UserApi.stopCapturingObjectsFromPython() + + def callClassWithPrefixMethod(self, prefixed_class, method, *argv): + UserApi.logDebug("ClassConnections_BasicsLoggingJobPython: callClassWithPrefixMethod: method: %s class: %y", method, prefixed_class) + return getattr(self.class_map[prefixed_class], method)(*argv) + + def Connection_1(self, *params): + UserApi.logDebug("Connection_1 called with data: %y", params) + # convert varargs to a single argument if possible + if (type(params) is list or type(params) is tuple) and (len(params) == 1): + params = params[0] + UserApi.logDebug("calling logMessage: %y", params) + params = self.callClassWithPrefixMethod("BBM_LogMessage", "logMessage", params) + UserApi.logDebug("output from logMessage: %y", params) + return params +############ GENERATED SECTION END ############ diff --git a/03_basics_building_blocks/01_exchange_rates_app/01_job/python/bb-basics-logging-job-python-1.0.qjob.yaml b/03_basics_building_blocks/01_exchange_rates_app/01_job/python/bb-basics-logging-job-python-1.0.qjob.yaml new file mode 100644 index 0000000000000000000000000000000000000000..bc0bdd7e62a923e48b64918f2ecfb3f9df92d561 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/01_job/python/bb-basics-logging-job-python-1.0.qjob.yaml @@ -0,0 +1,47 @@ +# This is a generated file, don't edit! +type: job +name: bb-basics-logging-job-python +desc: Job implementation example. The job simply logs every hour. +lang: python +author: + - Qore Technologies, s.r.o. +base-class-name: QorusJob +class-name: BasicsLoggingJobPython +classes: + - BBM_LogMessage +groups: + - BASIC-TRAINING-EXCHANGE-APP +schedule: + minutes: "0" + hours: "0" + days: "*" + months: "*" + dow: "*" +version: '1.0' +code: bb-basics-logging-job-python-1.0.qjob.py +class-connections: + Connection_1: + - class: BBM_LogMessage + connector: logMessage + trigger: run +config-items: + - name: log-message-level + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + - name: log-message-string + value: + "job info: %y" + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + - name: log-message-args + value: + "$info:*" + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + value_true_type: string diff --git a/03_basics_building_blocks/01_exchange_rates_app/01_job/qore/bb-basics-logging-job-1.0.qjob b/03_basics_building_blocks/01_exchange_rates_app/01_job/qore/bb-basics-logging-job-1.0.qjob new file mode 100644 index 0000000000000000000000000000000000000000..e382198e43b97404753c81841398b212caca97d1 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/01_job/qore/bb-basics-logging-job-1.0.qjob @@ -0,0 +1,45 @@ +%new-style +%strict-args +%require-types +%enable-all-warnings + +class BasicsLoggingJob inherits QorusJob { + private { + ####### GENERATED SECTION! DON'T EDIT! ######## + ClassConnections_BasicsLoggingJob class_connections(); + ############ GENERATED SECTION END ############ + } + + ####### GENERATED SECTION! DON'T EDIT! ######## + run() { + class_connections.run(); + } + ############ GENERATED SECTION END ############ +} + +####### GENERATED SECTION! DON'T EDIT! ######## +class ClassConnections_BasicsLoggingJob { + private { + # map of prefixed class names to class instances + hash class_map; + } + + constructor() { + class_map = { + "BBM_LogMessage": new BBM_LogMessage(), + }; + } + + auto callClassWithPrefixMethod(string prefixed_class, string method) { + UserApi::logDebug("ClassConnections_BasicsLoggingJob: callClassWithPrefixMethod: method: %s class: %y", method, prefixed_class); + return call_object_method_args(class_map{prefixed_class}, method, argv); + } + + auto run(auto params) { + UserApi::logDebug("run called with data: %y", params); + + UserApi::logDebug("calling logMessage: %y", params); + return callClassWithPrefixMethod("BBM_LogMessage", "logMessage", params); + } +} +############ GENERATED SECTION END ############ diff --git a/03_basics_building_blocks/01_exchange_rates_app/01_job/qore/bb-basics-logging-job-1.0.qjob.yaml b/03_basics_building_blocks/01_exchange_rates_app/01_job/qore/bb-basics-logging-job-1.0.qjob.yaml new file mode 100644 index 0000000000000000000000000000000000000000..ac51a4873ed859162156529ab9061e8071667dd6 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/01_job/qore/bb-basics-logging-job-1.0.qjob.yaml @@ -0,0 +1,47 @@ +# This is a generated file, don't edit! +type: job +name: bb-basics-logging-job +desc: Job implementation example. The job simply logs every hour. +lang: qore +author: + - Qore Technologies, s.r.o. +base-class-name: QorusJob +class-name: BasicsLoggingJob +groups: + - BASIC-TRAINING-EXCHANGE-APP +schedule: + minutes: "0" + hours: "0" + days: "*" + months: "*" + dow: "*" +version: '1.0' +classes: + - BBM_LogMessage +code: bb-basics-logging-job-1.0.qjob +class-connections: + run: + - class: BBM_LogMessage + connector: logMessage + trigger: run +config-items: + - name: log-message-level + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + - name: log-message-string + value: + "job info: %y" + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + - name: log-message-args + value: + "$info:*" + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + value_true_type: string diff --git a/03_basics_building_blocks/01_exchange_rates_app/02_service/README.md b/03_basics_building_blocks/01_exchange_rates_app/02_service/README.md new file mode 100644 index 0000000000000000000000000000000000000000..7a19c9508b21ddacf2e84212905e3bd2f6a97cbb --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/02_service/README.md @@ -0,0 +1,99 @@ +# Implementing Qorus Services + +User services are like named and versioned API sets that can be live-upgraded. User service methods are available to all workflows and jobs (and to other services) and can be automatically exported to other applications through lightweight web services (RPC protocols and REST) through the [HTTP server](https://qoretechnologies.com/manual/qorus/latest/qorus/sysarch.html#httpserver) (in fact, this is the default behavior, but can be inhibited by setting the internal flag on the service method). + +Additionally, services can also have one or more background threads, and therefore do not have to wait for input data to perform some processing. + +Services are loaded from the database into their own program objects and have their own [internal API](https://qoretechnologies.com/manual/qorus/latest/qorus/serviceapi.html). All user-defined services must be of the type "user". System services are delivered with Qorus and should not be modified, as this is likely to affect system stability. + +The following diagram illustrates the attributes of a service: + +![alt text](https://qoretechnologies.com/manual/qorus/latest/qorus/service-diagram.png "Service Metadata Diagram") + +[Service methods](https://qoretechnologies.com/manual/qorus/latest/qorus/implementingservices.html#servicemethods) define the user-visible interface of the service. The logic in a service program is defined by the method code and any library objects (functions, classes, or constant definitions) loaded into the service's program object. + +Besides the name, version, type (always "user" for user-defined services), and description of the service, there is the *"autostart"* flag. If the *"autostart"* flag is set to True, then the service will be started automatically whenever Qorus is started or whenever its RBAC access group is renabled. + +## Service Loading + +Otherwise, a service is loaded and initialized any time a method of that service is called and the service has not already been loaded. In this case all the service's methods are loaded and parsed into the same program object, along with any library function, classes, and constants associated with the service. Any parse exceptions during service loading will prohibit the service from being loaded. + +## Service Initialization + +If a service has an **init()** method, this method is called as soon as the service has been loaded and parsed, and before any call to a service method is made. For example, if a service with an **init()** method is loaded because a call to another method is made, the **init()** method will be called first, and then the called method will be called and the value returned to the caller. + +An exception when running the **init()** method will cause the service to be unloaded, and the exception will be returned to the caller. + +If the service has a **start()** method, then it must also have a **stop()** method. The **start()** method will be run in a separate thread and is expected to run until the **stop()** method is called. The **stop()** method should signal the routine running in the **start()** method to exit. + +When the **start()** method returns, the service is automatically unloaded. To start threads in user code in a service, use the [**startThread()**](https://qoretechnologies.com/manual/qorus/latest/qorus/classOMQ_1_1UserApi_1_1Service_1_1ServiceApi.html#a80a104bcc6723c7bf68a1a950f365372) or [**startThreadArgs()**](https://qoretechnologies.com/manual/qorus/latest/qorus/classOMQ_1_1UserApi_1_1Service_1_1ServiceApi.html#a68268ee2277e4a0bdcda70b10643c44e) functions (in which case, to use these functions, the service must also have a **stop()** method). + +## Service Definition File + +Qorus services are defined using two files. Service metadata tags are defined in the YAML format and service code written in Java/Qore language. In the YAML file there is code tag that is used to reference to the service's code, the path is relative. + +**NOTE**: It's recommended to have YAML definition files in the same directory with the user code. + +Service definition files are used to create service objects in the Qorus schema that can then be loaded and their methods can be called from any Qorus code, and, for external methods, from external applications through lightweight web-service protocols exported through the [HTTP server](https://qoretechnologies.com/manual/qorus/latest/qorus/sysarch.html#httpserver) as well. + +Services can be easily created using the Qorus Developer Tools extension that will generate service metadata and code: + +![alt-text](img/testservice.gif) + +**NOTE**: the YAML file generated by the extension should not be manually edited otherwise it may cause code and metadata misalignments and hence problems with the extension usability. + +### Example Service Definition + +Metadata: +```yaml +# This is a generated file, don't edit! +type: service +name: basics-simple-service +desc: "Service simple example" +author: + - Qore Technologies, s.r.o. +base-class-name: QorusService +class-name: BasicsSimpleService +groups: + - BASIC-TRAINING-EXCHANGE-APP +lang: qore +autostart: true +version: "1.0" +servicetype: USER +code: basics-simple-service-1.0.qsd +methods: + - name: init + desc: initialization of the service +``` + +The training job in this section simply logs a message in the job log file; it is built with a no-code approach using the `BBM_LogMessage` building block. + +Handcoded Qore equivalent: +```php +%new-style +%strict-args +%require-types +%enable-all-warnings + +class BasicsSimpleService inherits QorusService { + init() { + logInfo("service is initialized"); + } +} +``` + +Information on how to implement Qorus Objects using YAML format can be found [here](https://qoretechnologies.com/manual/qorus/latest/qorus/implementingqorusobjectsusingyaml.html). + +The YAML schema used for validation of the service metadata can be downloaded [here](https://qoretechnologies.com/manual/qorus/latest/qorus/service_schema.yaml). + +Service must have at least one method defined. The tags are parsed and validated according to the schema mentioned above by the [oload](https://qoretechnologies.com/manual/qorus/latest/qorus/commandline.html#oload) program, which will create the service objects in the Qorus database according to the definitions in the file. + +Service method definitions are defined in the service metadata under the `methods` tag. + +--- + + + + + +
[← Previous: Implementing Qorus jobs](../01_job)[Next: Implementing Qorus workflows →](../03_workflow)
diff --git a/03_basics_building_blocks/01_exchange_rates_app/02_service/img/testservice.gif b/03_basics_building_blocks/01_exchange_rates_app/02_service/img/testservice.gif new file mode 100644 index 0000000000000000000000000000000000000000..c52812c1a3874a7cbd38e2dbecaae1046390d14f Binary files /dev/null and b/03_basics_building_blocks/01_exchange_rates_app/02_service/img/testservice.gif differ diff --git a/03_basics_building_blocks/01_exchange_rates_app/02_service/java/bb-basics-simple-service-java-1.0.qsd.yaml b/03_basics_building_blocks/01_exchange_rates_app/02_service/java/bb-basics-simple-service-java-1.0.qsd.yaml new file mode 100644 index 0000000000000000000000000000000000000000..dc6944586c4d4dd54225ca2b5cb9b3c6bb7631ab --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/02_service/java/bb-basics-simple-service-java-1.0.qsd.yaml @@ -0,0 +1,43 @@ +# This is a generated file, don't edit! +type: service +name: bb-basics-simple-service-java +desc: Service simple example +lang: java +author: + - Qore Technologies, s.r.o. +autostart: true +base-class-name: QorusService +class-name: BasicsSimpleServiceJava +groups: + - BASIC-TRAINING-EXCHANGE-APP +version: '1.0' +classes: + - BBM_LogMessage +servicetype: USER +code: bb_basics_simple_service_java_1_0_service/BasicsSimpleServiceJava.java +class-connections: + init_log: + - class: BBM_LogMessage + connector: logMessage + trigger: init +config-items: + - name: log-message-level + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + - name: log-message-string + value: + "service is initialized" + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + - name: log-message-args + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' +methods: + - name: init + desc: initialization of the service diff --git a/03_basics_building_blocks/01_exchange_rates_app/02_service/java/bb_basics_simple_service_java_1_0_service/BasicsSimpleServiceJava.java b/03_basics_building_blocks/01_exchange_rates_app/02_service/java/bb_basics_simple_service_java_1_0_service/BasicsSimpleServiceJava.java new file mode 100644 index 0000000000000000000000000000000000000000..cc7991b26e4d8a83c0b5936ee2e11587779a0211 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/02_service/java/bb_basics_simple_service_java_1_0_service/BasicsSimpleServiceJava.java @@ -0,0 +1,75 @@ +import qore.OMQ.*; +import qore.OMQ.UserApi.*; +import qore.OMQ.UserApi.Service.*; +import org.qore.jni.QoreJavaApi; +import org.qore.jni.QoreObject; +import java.util.Map; +import org.qore.jni.Hash; +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; +import qore.BBM_LogMessage; + +class BasicsSimpleServiceJava extends QorusService { + // ==== GENERATED SECTION! DON'T EDIT! ==== // + ClassConnections_BasicsSimpleServiceJava classConnections; + + // ======== GENERATED SECTION END ========= // + public BasicsSimpleServiceJava() throws Throwable { + super(); + // ==== GENERATED SECTION! DON'T EDIT! ==== // + classConnections = new ClassConnections_BasicsSimpleServiceJava(); + // ======== GENERATED SECTION END ========= // + } + + // ==== GENERATED SECTION! DON'T EDIT! ==== // + public void init() throws Throwable { + classConnections.init_log(null); + } + // ======== GENERATED SECTION END ========= // +} + +// ==== GENERATED SECTION! DON'T EDIT! ==== // +class ClassConnections_BasicsSimpleServiceJava { + // map of prefixed class names to class instances + private final Hash classMap; + + ClassConnections_BasicsSimpleServiceJava() throws Throwable { + classMap = new Hash(); + UserApi.startCapturingObjectsFromJava(); + try { + classMap.put("BBM_LogMessage", QoreJavaApi.newObjectSave("BBM_LogMessage")); + } finally { + UserApi.stopCapturingObjectsFromJava(); + } + } + + Object callClassWithPrefixMethod(final String prefixedClass, final String methodName, + Object params) throws Throwable { + UserApi.logDebug("ClassConnections_BasicsSimpleServiceJava: callClassWithPrefixMethod: method: %s class: %y", methodName, prefixedClass); + final Object object = classMap.get(prefixedClass); + + if (object instanceof QoreObject) { + QoreObject qoreObject = (QoreObject)object; + return qoreObject.callMethod(methodName, params); + } else { + final Method method = object.getClass().getMethod(methodName, Object.class); + try { + return method.invoke(object, params); + } catch (InvocationTargetException ex) { + throw ex.getCause(); + } + } + } + + public Object init_log(Object params) throws Throwable { + // convert varargs to a single argument if possible + if (params != null && params.getClass().isArray() && ((Object[])params).length == 1) { + params = ((Object[])params)[0]; + } + UserApi.logDebug("init_log called with data: %y", params); + + UserApi.logDebug("calling logMessage: %y", params); + return callClassWithPrefixMethod("BBM_LogMessage", "logMessage", params); + } +} +// ======== GENERATED SECTION END ========= // diff --git a/03_basics_building_blocks/01_exchange_rates_app/02_service/python/bb-basics-simple-service-python-1.0.qsd.py b/03_basics_building_blocks/01_exchange_rates_app/02_service/python/bb-basics-simple-service-python-1.0.qsd.py new file mode 100644 index 0000000000000000000000000000000000000000..b8ae5e241042443c314ee4d7a7fb692281d4f457 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/02_service/python/bb-basics-simple-service-python-1.0.qsd.py @@ -0,0 +1,38 @@ +from svc import QorusService +from qore.__root__ import BBM_LogMessage + +class BasicsSimpleServicePython(QorusService): + def __init__(self): + ####### GENERATED SECTION! DON'T EDIT! ######## + self.class_connections = ClassConnections_BasicsSimpleServicePython() + ############ GENERATED SECTION END ############ + + ####### GENERATED SECTION! DON'T EDIT! ######## + def init(self): + self.class_connections.log_init() + ############ GENERATED SECTION END ############ + +####### GENERATED SECTION! DON'T EDIT! ######## +class ClassConnections_BasicsSimpleServicePython: + def __init__(self): + UserApi.startCapturingObjectsFromPython() + # map of prefixed class names to class instances + self.class_map = { + 'BBM_LogMessage': BBM_LogMessage(), + } + UserApi.stopCapturingObjectsFromPython() + + def callClassWithPrefixMethod(self, prefixed_class, method, *argv): + UserApi.logDebug("ClassConnections_BasicsSimpleServicePython: callClassWithPrefixMethod: method: %s class: %y", method, prefixed_class) + return getattr(self.class_map[prefixed_class], method)(*argv) + + def log_init(self, *params): + UserApi.logDebug("log_init called with data: %y", params) + # convert varargs to a single argument if possible + if (type(params) is list or type(params) is tuple) and (len(params) == 1): + params = params[0] + UserApi.logDebug("calling logMessage: %y", params) + params = self.callClassWithPrefixMethod("BBM_LogMessage", "logMessage", params) + UserApi.logDebug("output from logMessage: %y", params) + return params +############ GENERATED SECTION END ############ diff --git a/03_basics_building_blocks/01_exchange_rates_app/02_service/python/bb-basics-simple-service-python-1.0.qsd.yaml b/03_basics_building_blocks/01_exchange_rates_app/02_service/python/bb-basics-simple-service-python-1.0.qsd.yaml new file mode 100644 index 0000000000000000000000000000000000000000..f4f46f2c033716a5f22f9e8d1b51a33f02179c28 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/02_service/python/bb-basics-simple-service-python-1.0.qsd.yaml @@ -0,0 +1,43 @@ +# This is a generated file, don't edit! +type: service +name: bb-basics-simple-service-python +desc: Service simple example +lang: python +author: + - Qore Technologies, s.r.o. +autostart: true +base-class-name: QorusService +class-name: BasicsSimpleServicePython +groups: + - BASIC-TRAINING-EXCHANGE-APP +version: '1.0' +classes: + - BBM_LogMessage +servicetype: USER +code: bb-basics-simple-service-python-1.0.qsd.py +class-connections: + log_init: + - class: BBM_LogMessage + connector: logMessage + trigger: init +config-items: + - name: log-message-level + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + - name: log-message-string + value: + "service is initialized" + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + - name: log-message-args + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' +methods: + - name: init + desc: initialization of the service diff --git a/03_basics_building_blocks/01_exchange_rates_app/02_service/qore/bb-basics-simple-service-1.0.qsd b/03_basics_building_blocks/01_exchange_rates_app/02_service/qore/bb-basics-simple-service-1.0.qsd new file mode 100644 index 0000000000000000000000000000000000000000..75812f70352cf42da0342e7618f503f6c607e084 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/02_service/qore/bb-basics-simple-service-1.0.qsd @@ -0,0 +1,45 @@ +%new-style +%strict-args +%require-types +%enable-all-warnings + +class BasicsSimpleService inherits QorusService { + private { + ####### GENERATED SECTION! DON'T EDIT! ######## + ClassConnections_BasicsSimpleService class_connections(); + ############ GENERATED SECTION END ############ + } + + ####### GENERATED SECTION! DON'T EDIT! ######## + init() { + class_connections.log_init(); + } + ############ GENERATED SECTION END ############ +} + +####### GENERATED SECTION! DON'T EDIT! ######## +class ClassConnections_BasicsSimpleService { + private { + # map of prefixed class names to class instances + hash class_map; + } + + constructor() { + class_map = { + "BBM_LogMessage": new BBM_LogMessage(), + }; + } + + auto callClassWithPrefixMethod(string prefixed_class, string method) { + UserApi::logDebug("ClassConnections_BasicsSimpleService: callClassWithPrefixMethod: method: %s class: %y", method, prefixed_class); + return call_object_method_args(class_map{prefixed_class}, method, argv); + } + + auto log_init(auto params) { + UserApi::logDebug("log_init called with data: %y", params); + + UserApi::logDebug("calling logMessage: %y", params); + return callClassWithPrefixMethod("BBM_LogMessage", "logMessage", params); + } +} +############ GENERATED SECTION END ############ diff --git a/03_basics_building_blocks/01_exchange_rates_app/02_service/qore/bb-basics-simple-service-1.0.qsd.yaml b/03_basics_building_blocks/01_exchange_rates_app/02_service/qore/bb-basics-simple-service-1.0.qsd.yaml new file mode 100644 index 0000000000000000000000000000000000000000..2d4fd2fa2925a087cbd233a08f1fa286aac0bcbd --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/02_service/qore/bb-basics-simple-service-1.0.qsd.yaml @@ -0,0 +1,43 @@ +# This is a generated file, don't edit! +type: service +name: bb-basics-simple-service +desc: Service simple example +lang: qore +author: + - Qore Technologies, s.r.o. +autostart: true +base-class-name: QorusService +class-name: BasicsSimpleService +groups: + - BASIC-TRAINING-EXCHANGE-APP +version: '1.0' +classes: + - BBM_LogMessage +servicetype: USER +code: bb-basics-simple-service-1.0.qsd +class-connections: + log_init: + - class: BBM_LogMessage + connector: logMessage + trigger: init +config-items: + - name: log-message-level + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + - name: log-message-string + value: + "service is initialized" + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + - name: log-message-args + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' +methods: + - name: init + desc: initialization of the service diff --git a/03_basics_building_blocks/01_exchange_rates_app/03_workflow/README.md b/03_basics_building_blocks/01_exchange_rates_app/03_workflow/README.md new file mode 100644 index 0000000000000000000000000000000000000000..2397f00451915084e41f82fb927d574ee1584a5a --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/03_workflow/README.md @@ -0,0 +1,106 @@ +# Implementing Qorus Workflows + +Workflows are made up a set of interdependent steps that each perform one traced and restartable action. To design a workflow, a series of logical steps and their dependencies must be defined and then represented by a workflow definition file. Once the high-level design for the workflow has been done, then the logic for the steps can be implemented and the workflow definition and the functions can be loaded into the database and executed. + +The following table defines the major elements used when designing and implementing an Qorus workflow: + + + + + + + + + + + + + + + + + + + + + + + +
ElementDescription
[Step](#step-definition)The lowest element in a workflow, represents one traced and restartable action. Each step is defined by at least a primary step code containing the logic for the step, and optionally other code (such as a validation code, run when the step is run in error recovery mode, or an asynchronous back-end code, required for asynchronous steps) and other option attributes.
[Workflow](#workflow-definition)The workflow is the highest level element defining the logic as a set of steps and inter-step dependencies, along with other attributes; workflows process workflow order data instances that in turn contain the data and the status of processing (status of all steps). A running workflow is called a workflow execution instance and can be run either in batch mode (OMQ::WM_Normal), batch recovery mode (OMQ::WM_Recovery), or synchronous mode.
QueueAsynchronous steps require a queue for linking the associated step data created by the front-end logic with the back-end logic. +
Workflow Synchronization EventsWorkflow synchronization events allow multiple workflow orders to synchronize their processing based on a single event
+ +## Workflow Definition +Workflow definition files define workflow metadata including the steps and dependencies between steps. + +Workflows are defined using two files. One file for defining workflow metadata tags in the YAML format and workflow code written in Java/Qore language. In the YAML file there is code tag that is used to reference to the workflow's code, the path is relative. The code tag is optional. + +**NOTE**: It's recommended to have YAML definition files in the same directory with the user code. + +### Workflow Parameters + +The workflow object consists of the step dependencies and other attributes. The following diagram illustrates the attributes of a workflow. + +![alt text](https://qoretechnologies.com/manual/qorus/latest/qorus/workflow-diagram.png) + +Workflows can be easily created using the Qorus Developer Tools extension that will generate workflow metadata and code: + +![alt-text](img/testworkflow.gif) + +During workflow creation process it's possible to use existing steps and also to define new one as shown above. + +**NOTE**: the YAML file generated by the extension should not be manually edited otherwise it may cause code and metadata misalignments and hence problems with the extension usability. + +### Example Workflow Definition + +```yaml +# This is a generated file, don't edit! +type: workflow +name: BASICS-SIMPLE-WORKFLOW +desc: "Simple workflow example" +author: + - Qore Technologies, s.r.o. +groups: + - BASIC-TRAINING-EXCHANGE-APP +steps: + [ + "BasicsSimpleWorkflowStep:1.0" + ] +version: "1.0" +autostart: 1 +#code: Workflow.qwf #optional +``` + +The inter-step dependencies are defined in the steps key in the workflow definition. The basic format of this data structure is a list. In the example above, 1 step will be created (or updated if the step already exists) with the name and version specifiers and placed in simple linear dependency in the steps list. + +Information on how to implement Qorus Objects using YAML format can be found [here](https://qoretechnologies.com/manual/qorus/latest/qorus/implementingqorusobjectsusingyaml.html). + +The YAML schema used for validation of the workflow metadata can be downloaded [here](https://qoretechnologies.com/manual/qorus/latest/qorus/workflow_schema.yaml). + +### Primary Method + +Every step base class has an abstract primary() method where the primary step logic must be defined. See the class documentation for the specific step class for more information on requirements for the primary step method. + +```php +%new-style +%strict-args +%require-types +%enable-all-warnings + +class BasicsSimpleWorkflowStep inherits QorusNormalStep { + primary() { + logInfo("BasicsSimpleWorkflowStep was called"); + } +} +``` + +# Testing +To test the workflow the **create_order-post_reload.qscript** script can be used. The script will automatically create a workflow order at the end of the deployment. The deployment can be done by Qorus extension for Visual Studio Code. Right click on the folder you want to deploy and then click on *"deploy the directory"*. + +--- + + + + + + +
[← Previous: Implementing Qorus services](../02_service)[Next: Workflow serial steps →](../04_workflow_serial_steps)
\ No newline at end of file diff --git a/03_basics_building_blocks/01_exchange_rates_app/03_workflow/img/testworkflow.gif b/03_basics_building_blocks/01_exchange_rates_app/03_workflow/img/testworkflow.gif new file mode 100644 index 0000000000000000000000000000000000000000..893483e0ae699838fad88f81a8ac98cac11b0d59 Binary files /dev/null and b/03_basics_building_blocks/01_exchange_rates_app/03_workflow/img/testworkflow.gif differ diff --git a/03_basics_building_blocks/01_exchange_rates_app/03_workflow/java/BB-BASICS-SIMPLE-WORKFLOW-JAVA-1.0.qwf.yaml b/03_basics_building_blocks/01_exchange_rates_app/03_workflow/java/BB-BASICS-SIMPLE-WORKFLOW-JAVA-1.0.qwf.yaml new file mode 100644 index 0000000000000000000000000000000000000000..10352bc54f85fa0e41ae05a88847db95ee92419b --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/03_workflow/java/BB-BASICS-SIMPLE-WORKFLOW-JAVA-1.0.qwf.yaml @@ -0,0 +1,14 @@ +# This is a generated file, don't edit! +type: workflow +name: BB-BASICS-SIMPLE-WORKFLOW-JAVA +desc: Simple workflow example for java +author: + - Qore Technologies, s.r.o. +autostart: 1 +groups: + - BASIC-TRAINING-EXCHANGE-APP +version: "1.0" +steps: + [ + "BB_BasicsSimpleWorkflowStepJava:1.0" + ] diff --git a/03_basics_building_blocks/01_exchange_rates_app/03_workflow/java/BB_BasicsSimpleWorkflowStepJava-1.0.qstep.yaml b/03_basics_building_blocks/01_exchange_rates_app/03_workflow/java/BB_BasicsSimpleWorkflowStepJava-1.0.qstep.yaml new file mode 100644 index 0000000000000000000000000000000000000000..a2705652c7666025b126c7b742335a0b1e05aeae --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/03_workflow/java/BB_BasicsSimpleWorkflowStepJava-1.0.qstep.yaml @@ -0,0 +1,37 @@ +# This is a generated file, don't edit! +type: step +name: BB_BasicsSimpleWorkflowStepJava +desc: Simple worklfow step example +lang: java +author: + - Qore Technologies, s.r.o. +base-class-name: QorusNormalStep +class-name: BasicsSimpleWorkflowStepJava +classes: + - BBM_LogMessage +version: '1.0' +steptype: NORMAL +code: bb_basicssimpleworkflowstepjava_1_0_step/BasicsSimpleWorkflowStepJava.java +class-connections: + primary: + - class: BBM_LogMessage + connector: logMessage + trigger: primary +config-items: + - name: log-message-level + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + - name: log-message-string + value: + "BasicsSimpleWorkflowStep was called" + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + - name: log-message-args + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' diff --git a/03_basics_building_blocks/01_exchange_rates_app/03_workflow/java/bb_basicssimpleworkflowstepjava_1_0_step/BasicsSimpleWorkflowStepJava.java b/03_basics_building_blocks/01_exchange_rates_app/03_workflow/java/bb_basicssimpleworkflowstepjava_1_0_step/BasicsSimpleWorkflowStepJava.java new file mode 100644 index 0000000000000000000000000000000000000000..23c00c4875098d4a35cd0d026091005ee0dddfe0 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/03_workflow/java/bb_basicssimpleworkflowstepjava_1_0_step/BasicsSimpleWorkflowStepJava.java @@ -0,0 +1,79 @@ +import qore.OMQ.*; +import qore.OMQ.UserApi.*; +import qore.OMQ.UserApi.Workflow.*; +import org.qore.jni.QoreJavaApi; +import org.qore.jni.QoreObject; +import java.util.Map; +import org.qore.jni.Hash; +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; +import qore.BBM_LogMessage; + +class BasicsSimpleWorkflowStepJava extends QorusNormalStep { + // ==== GENERATED SECTION! DON'T EDIT! ==== // + ClassConnections_BasicsSimpleWorkflowStepJava classConnections; + + // ======== GENERATED SECTION END ========= // + public BasicsSimpleWorkflowStepJava() throws Throwable { + super(); + // ==== GENERATED SECTION! DON'T EDIT! ==== // + classConnections = new ClassConnections_BasicsSimpleWorkflowStepJava(); + // ======== GENERATED SECTION END ========= // + } + + // ==== GENERATED SECTION! DON'T EDIT! ==== // + public void primary() throws Throwable { + classConnections.primary(null); + } + + public String validation() throws Throwable { + return qore.OMQ.$Constants.StatRetry; + } + // ======== GENERATED SECTION END ========= // +} + +// ==== GENERATED SECTION! DON'T EDIT! ==== // +class ClassConnections_BasicsSimpleWorkflowStepJava { + // map of prefixed class names to class instances + private final Hash classMap; + + ClassConnections_BasicsSimpleWorkflowStepJava() throws Throwable { + classMap = new Hash(); + UserApi.startCapturingObjectsFromJava(); + try { + classMap.put("BBM_LogMessage", QoreJavaApi.newObjectSave("BBM_LogMessage")); + } finally { + UserApi.stopCapturingObjectsFromJava(); + } + } + + Object callClassWithPrefixMethod(final String prefixedClass, final String methodName, + Object params) throws Throwable { + UserApi.logDebug("ClassConnections_BasicsSimpleWorkflowStepJava: callClassWithPrefixMethod: method: %s class: %y", methodName, prefixedClass); + final Object object = classMap.get(prefixedClass); + + if (object instanceof QoreObject) { + QoreObject qoreObject = (QoreObject)object; + return qoreObject.callMethod(methodName, params); + } else { + final Method method = object.getClass().getMethod(methodName, Object.class); + try { + return method.invoke(object, params); + } catch (InvocationTargetException ex) { + throw ex.getCause(); + } + } + } + + public Object primary(Object params) throws Throwable { + // convert varargs to a single argument if possible + if (params != null && params.getClass().isArray() && ((Object[])params).length == 1) { + params = ((Object[])params)[0]; + } + UserApi.logDebug("primary called with data: %y", params); + + UserApi.logDebug("calling logMessage: %y", params); + return callClassWithPrefixMethod("BBM_LogMessage", "logMessage", params); + } +} +// ======== GENERATED SECTION END ========= // diff --git a/03_basics_building_blocks/01_exchange_rates_app/03_workflow/java/create_order-post_reload.qscript b/03_basics_building_blocks/01_exchange_rates_app/03_workflow/java/create_order-post_reload.qscript new file mode 100755 index 0000000000000000000000000000000000000000..9e62f1b1f7a304623bc717d8ac7a20b49cf1866e --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/03_workflow/java/create_order-post_reload.qscript @@ -0,0 +1,20 @@ +#!/usr/bin/env qore +%new-style +%strict-args +%require-types +%enable-all-warnings + +%requires QorusClientCore + +QorusClient::initFast(); + +const WORKFLOW_NAME = "BB-BASICS-SIMPLE-WORKFLOW-JAVA"; + +const ORDER_DATA = { + "staticdata": { + "test": "data" + } +}; + +hash response = qrest.post("workflows/" + WORKFLOW_NAME + "?action=createOrder", ORDER_DATA); +printf("Response: %N\n", response); \ No newline at end of file diff --git a/03_basics_building_blocks/01_exchange_rates_app/03_workflow/python/BB-BASICS-SIMPLE-WORKFLOW-PYTHON-1.0.qwf.yaml b/03_basics_building_blocks/01_exchange_rates_app/03_workflow/python/BB-BASICS-SIMPLE-WORKFLOW-PYTHON-1.0.qwf.yaml new file mode 100644 index 0000000000000000000000000000000000000000..581f8fc6a1a9fd246ed19e1ee635238e456b1bab --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/03_workflow/python/BB-BASICS-SIMPLE-WORKFLOW-PYTHON-1.0.qwf.yaml @@ -0,0 +1,14 @@ +# This is a generated file, don't edit! +type: workflow +name: BB-BASICS-SIMPLE-WORKFLOW-PYTHON +desc: Simple workflow example in python +author: + - Qore Technologies, s.r.o. +autostart: 1 +groups: + - BASIC-TRAINING-EXCHANGE-APP +version: "1.0" +steps: + [ + "BB_BasicsSimpleWorkflowStepPython:1.0" + ] diff --git a/03_basics_building_blocks/01_exchange_rates_app/03_workflow/python/BB_BasicsSimpleWorkflowStepPython-1.0.qstep.py b/03_basics_building_blocks/01_exchange_rates_app/03_workflow/python/BB_BasicsSimpleWorkflowStepPython-1.0.qstep.py new file mode 100644 index 0000000000000000000000000000000000000000..e22fe12d5e1501d3dfc8351c602ec45d7d4bc953 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/03_workflow/python/BB_BasicsSimpleWorkflowStepPython-1.0.qstep.py @@ -0,0 +1,41 @@ +from wf import QorusNormalStep +from qore.__root__ import BBM_LogMessage + +class BasicsSimpleWorkflowStepPython(QorusNormalStep): + def __init__(self): + ####### GENERATED SECTION! DON'T EDIT! ######## + self.class_connections = ClassConnections_BasicsSimpleWorkflowStepPython() + ############ GENERATED SECTION END ############ + + ####### GENERATED SECTION! DON'T EDIT! ######## + def primary(self): + self.class_connections.primary() + + def validation(self): + return StatRetry + ############ GENERATED SECTION END ############ + +####### GENERATED SECTION! DON'T EDIT! ######## +class ClassConnections_BasicsSimpleWorkflowStepPython: + def __init__(self): + UserApi.startCapturingObjectsFromPython() + # map of prefixed class names to class instances + self.class_map = { + 'BBM_LogMessage': BBM_LogMessage(), + } + UserApi.stopCapturingObjectsFromPython() + + def callClassWithPrefixMethod(self, prefixed_class, method, *argv): + UserApi.logDebug("ClassConnections_BasicsSimpleWorkflowStepPython: callClassWithPrefixMethod: method: %s class: %y", method, prefixed_class) + return getattr(self.class_map[prefixed_class], method)(*argv) + + def primary(self, *params): + UserApi.logDebug("primary called with data: %y", params) + # convert varargs to a single argument if possible + if (type(params) is list or type(params) is tuple) and (len(params) == 1): + params = params[0] + UserApi.logDebug("calling logMessage: %y", params) + params = self.callClassWithPrefixMethod("BBM_LogMessage", "logMessage", params) + UserApi.logDebug("output from logMessage: %y", params) + return params +############ GENERATED SECTION END ############ diff --git a/03_basics_building_blocks/01_exchange_rates_app/03_workflow/python/BB_BasicsSimpleWorkflowStepPython-1.0.qstep.yaml b/03_basics_building_blocks/01_exchange_rates_app/03_workflow/python/BB_BasicsSimpleWorkflowStepPython-1.0.qstep.yaml new file mode 100644 index 0000000000000000000000000000000000000000..22efcfa77dd371d4f7c7bd4e6ec286faf6731a19 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/03_workflow/python/BB_BasicsSimpleWorkflowStepPython-1.0.qstep.yaml @@ -0,0 +1,37 @@ +# This is a generated file, don't edit! +type: step +name: BB_BasicsSimpleWorkflowStepPython +desc: Simple workflow step example in python +lang: python +author: + - Qore Technologies, s.r.o. +base-class-name: QorusNormalStep +class-name: BasicsSimpleWorkflowStepPython +version: '1.0' +classes: + - BBM_LogMessage +steptype: NORMAL +code: BB_BasicsSimpleWorkflowStepPython-1.0.qstep.py +class-connections: + primary: + - class: BBM_LogMessage + connector: logMessage + trigger: primary +config-items: + - name: log-message-level + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + - name: log-message-string + value: + "BasicsSimpleWorkflowStep was called" + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + - name: log-message-args + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' diff --git a/03_basics_building_blocks/01_exchange_rates_app/03_workflow/python/create_order-post_reload.qscript b/03_basics_building_blocks/01_exchange_rates_app/03_workflow/python/create_order-post_reload.qscript new file mode 100755 index 0000000000000000000000000000000000000000..5614af8b347f656af29f30e7dc7f176cc07754db --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/03_workflow/python/create_order-post_reload.qscript @@ -0,0 +1,20 @@ +#!/usr/bin/env qore +%new-style +%strict-args +%require-types +%enable-all-warnings + +%requires QorusClientCore + +QorusClient::initFast(); + +const WORKFLOW_NAME = "BB-BASICS-SIMPLE-WORKFLOW-PYTHON"; + +const ORDER_DATA = { + "staticdata": { + "test": "data" + } +}; + +hash response = qrest.post("workflows/" + WORKFLOW_NAME + "?action=createOrder", ORDER_DATA); +printf("Response: %N\n", response); \ No newline at end of file diff --git a/03_basics_building_blocks/01_exchange_rates_app/03_workflow/qore/BB-BASICS-SIMPLE-WORKFLOW-1.0.yaml b/03_basics_building_blocks/01_exchange_rates_app/03_workflow/qore/BB-BASICS-SIMPLE-WORKFLOW-1.0.yaml new file mode 100644 index 0000000000000000000000000000000000000000..5cf131f86fec0a0ad663d277ef0da31527168ec9 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/03_workflow/qore/BB-BASICS-SIMPLE-WORKFLOW-1.0.yaml @@ -0,0 +1,14 @@ +# This is a generated file, don't edit! +type: workflow +name: BB-BASICS-SIMPLE-WORKFLOW +desc: "Simple workflow example" +author: + - Qore Technologies, s.r.o. +groups: + - BASIC-TRAINING-EXCHANGE-APP +steps: + [ + "BB_BasicsSimpleWorkflowStep:1.0" + ] +version: "1.0" +autostart: 1 diff --git a/03_basics_building_blocks/01_exchange_rates_app/03_workflow/qore/BB_BasicsSimpleWorkflowStep-1.0.qstep b/03_basics_building_blocks/01_exchange_rates_app/03_workflow/qore/BB_BasicsSimpleWorkflowStep-1.0.qstep new file mode 100644 index 0000000000000000000000000000000000000000..5094558d97dc98cee438a0f55dd7f6819fde9ab9 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/03_workflow/qore/BB_BasicsSimpleWorkflowStep-1.0.qstep @@ -0,0 +1,49 @@ +%new-style +%strict-args +%require-types +%enable-all-warnings + +class BasicsSimpleWorkflowStep inherits QorusNormalStep { + private { + ####### GENERATED SECTION! DON'T EDIT! ######## + ClassConnections_BasicsSimpleWorkflowStep class_connections(); + ############ GENERATED SECTION END ############ + } + + ####### GENERATED SECTION! DON'T EDIT! ######## + primary() { + class_connections.primary(); + } + + string validation() { + return OMQ::StatRetry; + } + ############ GENERATED SECTION END ############ +} + +####### GENERATED SECTION! DON'T EDIT! ######## +class ClassConnections_BasicsSimpleWorkflowStep { + private { + # map of prefixed class names to class instances + hash class_map; + } + + constructor() { + class_map = { + "BBM_LogMessage": new BBM_LogMessage(), + }; + } + + auto callClassWithPrefixMethod(string prefixed_class, string method) { + UserApi::logDebug("ClassConnections_BasicsSimpleWorkflowStep: callClassWithPrefixMethod: method: %s class: %y", method, prefixed_class); + return call_object_method_args(class_map{prefixed_class}, method, argv); + } + + auto primary(auto params) { + UserApi::logDebug("primary called with data: %y", params); + + UserApi::logDebug("calling logMessage: %y", params); + return callClassWithPrefixMethod("BBM_LogMessage", "logMessage", params); + } +} +############ GENERATED SECTION END ############ diff --git a/03_basics_building_blocks/01_exchange_rates_app/03_workflow/qore/BB_BasicsSimpleWorkflowStep-1.0.qstep.yaml b/03_basics_building_blocks/01_exchange_rates_app/03_workflow/qore/BB_BasicsSimpleWorkflowStep-1.0.qstep.yaml new file mode 100644 index 0000000000000000000000000000000000000000..f9a673ce0d5a466a84c7c8b9ab32fe1647986100 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/03_workflow/qore/BB_BasicsSimpleWorkflowStep-1.0.qstep.yaml @@ -0,0 +1,37 @@ +# This is a generated file, don't edit! +type: step +name: BB_BasicsSimpleWorkflowStep +desc: Simple worklfow step example +lang: qore +author: + - Qore Technologies, s.r.o. +base-class-name: QorusNormalStep +class-name: BasicsSimpleWorkflowStep +version: '1.0' +classes: + - BBM_LogMessage +steptype: NORMAL +code: BB_BasicsSimpleWorkflowStep-1.0.qstep +class-connections: + primary: + - class: BBM_LogMessage + connector: logMessage + trigger: primary +config-items: + - name: log-message-level + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + - name: log-message-string + value: + "BasicsSimpleWorkflowStep was called" + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + - name: log-message-args + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' diff --git a/03_basics_building_blocks/01_exchange_rates_app/03_workflow/qore/create_order-post_reload.qscript b/03_basics_building_blocks/01_exchange_rates_app/03_workflow/qore/create_order-post_reload.qscript new file mode 100755 index 0000000000000000000000000000000000000000..e484a9ced7e62aba03a9e7959aa39262ab229a4d --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/03_workflow/qore/create_order-post_reload.qscript @@ -0,0 +1,20 @@ +#!/usr/bin/env qore +%new-style +%strict-args +%require-types +%enable-all-warnings + +%requires QorusClientCore + +QorusClient::initFast(); + +const WORKFLOW_NAME = "BB-BASICS-SIMPLE-WORKFLOW"; + +const ORDER_DATA = { + "staticdata": { + "test": "data" + } +}; + +hash response = qrest.post("workflows/" + WORKFLOW_NAME + "?action=createOrder", ORDER_DATA); +printf("Response: %N\n", response); \ No newline at end of file diff --git a/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/README.md b/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/README.md new file mode 100644 index 0000000000000000000000000000000000000000..2994ef3f2838fd973e91c1566ebb1e510f31c711 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/README.md @@ -0,0 +1,71 @@ +# Workflow Serial Steps + +## Workflow Definition with Serial Steps +```yaml +# This is a generated file, don't edit! +type: workflow +name: BASICS-WORKFLOW-SERIAL-STEPS +desc: "Workflow serial steps example" +author: + - Qore Technologies, s.r.o. +groups: + - BASIC-TRAINING-EXCHANGE-APP +steps: + [ + "BasicsWorkflowSerialStep1:1.0", + "BasicsWorkflowSerialStep2:1.0" + ] +version: "1.0" +autostart: 1 +``` + +In this example; the workflow **BASICS-WORKFLOW-SERIAL-STEPS** 1.0 is created. The step dependencies are linear: *BasicsWorkflowSerialStep1* has no dependencies and therefore is the starting step, *BasicsWorkflowSerialStep2* is dependent only on *BasicsWorkflowSerialStep1* and is the final step in the workflow. + + +## Workflow Autostart Parameter + +The workflow "autostart" parameter sets the number of workflow execution instances to be started when the system is started; if the system should ensure that this workflow is generally running, then set this key to a value greater than zero. + +If no value is provided for this option, the system will not start the workflow automatically; any workflow execution instances for this workflow must be started manually. + +If a non-zero value is provided for this workflow, then the system will attempt to start the workflow at all times if all its dependencies are met, and it is not disabled. Additionally, if the workflow cannot be started for any reason (for example, due to an error in the [onetimeinit](https://qoretechnologies.com/manual/qorus/latest/qorus/designimplworkflows.html#onetimeinit) function or a dependency error), an ongoing system alert will be raised, which is only cleared when the workflow is successfully started (or the autostart parameter is set to zero). + +## Workflows and Order Data + +Because a running workflow execution instance can be working on several different orders at once in different threads, accessing workflow data is performed through API calls in order to guarantee that the workflow's program code accesses only the correct data for the current order being processed at all times. + +Accessing and processing data is done using the Qorus API as outlined in this section; these APIs set up the data context for each thread so that the correct data is accessed. + +### Workflow Static Order Data + +Static data represents the workflow order data being processed. Workflow static order data cannot be updated or deleted by the Qorus workflow API; it is read-only data representing the order data to be processed or fulfilled by the workflow. + +API support: + +[getStaticData()](https://qoretechnologies.com/manual/qorus/latest/qorus/classOMQ_1_1UserApi_1_1Workflow_1_1WorkflowApi.html#a684678e62bafdbd00f9d5ffbaa13ef90) + +In the first step of the example static data are printed into a log file as a hash using the method above. + +### Workflow Dynamic Data + +Dynamic data is associated with the workflow order data instance being processed, but it can be updated and is persistent. Any changes made to dynamic data will be committed to the database before the update method returns, therefore any changes will be available in the future, even in the case of errors and later recovery processing. + +Dynamic data is appropriate for storing identifiers and references generated during order processing that are needed in subsequent steps, for example. + +API support: + +* [getDynamicData()](https://qoretechnologies.com/manual/qorus/latest/qorus/classOMQ_1_1UserApi_1_1Workflow_1_1WorkflowApi.html#a43e88d6e61a5b069b4c21f3d404a7653) +* [deleteDynamicDataKey()](https://qoretechnologies.com/manual/qorus/latest/qorus/classOMQ_1_1UserApi_1_1Workflow_1_1WorkflowApi.html#a4492e6d697f7644a47d7d2c889f2e188) +* [updateDynamicData()](https://qoretechnologies.com/manual/qorus/latest/qorus/classOMQ_1_1UserApi_1_1Workflow_1_1WorkflowApi.html#a707e6a020e3b81876977a6ac5ed9d5f4) +* [DynamicDataHelper class](https://qoretechnologies.com/manual/qorus/latest/qorus/classOMQ_1_1UserApi_1_1Workflow_1_1DynamicDataHelper.html) + +In the second step of the example static data are also printed into a log file as in the first step and the second step prints dynamic data, which were changed by the first step. + +--- + + + + + + +
[← Previous: Implementing Qorus workflows](../03_workflow)[Next: Workflow parallel steps →](../05_workflow_parallel_steps)
\ No newline at end of file diff --git a/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/java/BB-BASICS-WORKFLOW-SERIAL-STEPS-JAVA-1.0.qwf.yaml b/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/java/BB-BASICS-WORKFLOW-SERIAL-STEPS-JAVA-1.0.qwf.yaml new file mode 100644 index 0000000000000000000000000000000000000000..e467378d46e765d8a37d4469c763d36877f8e5f4 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/java/BB-BASICS-WORKFLOW-SERIAL-STEPS-JAVA-1.0.qwf.yaml @@ -0,0 +1,13 @@ +# This is a generated file, don't edit! +type: workflow +name: BB-BASICS-WORKFLOW-SERIAL-STEPS-JAVA +desc: Workflow serial steps example +author: + - Qore Technologies, s.r.o. +autostart: 1 +version: "1.0" +steps: + [ + "BB_BasicsWorkflowSerialStep1Java:1.0", + "BB_BasicsWorkflowSerialStep2Java:1.0" + ] diff --git a/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/java/BB_BasicsWorkflowSerialStep1Java-1.0.qstep.yaml b/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/java/BB_BasicsWorkflowSerialStep1Java-1.0.qstep.yaml new file mode 100644 index 0000000000000000000000000000000000000000..8aa50c9cc9a9487002625bbc6f0842f213f42c3c --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/java/BB_BasicsWorkflowSerialStep1Java-1.0.qstep.yaml @@ -0,0 +1,52 @@ +# This is a generated file, don't edit! +type: step +name: BB_BasicsWorkflowSerialStep1Java +desc: |- + ## Simple worklfow step example + It logs static data of the order and updates dynamic data +lang: java +author: + - Qore Technologies, s.r.o. +base-class-name: QorusNormalStep +class-name: BasicsWorkflowSerialStep1Java +classes: + - BBM_LogMessage + - BBM_OutputData +version: '1.0' +steptype: NORMAL +code: bb_basicsworkflowserialstep1java_1_0_step/BasicsWorkflowSerialStep1Java.java +class-connections: + primary: + - class: BBM_LogMessage + connector: logMessage + trigger: primary + - class: BBM_OutputData + connector: writeOutputData +config-items: + - name: log-message-level + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + - name: log-message-string + value: + "BasicsWorkflowSerialStep1Java was called, static data: %N" + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + - name: log-message-args + value: + "$static:*" + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + value_true_type: string + - name: output-data-hash + value: + '$qore-expr:{"data"}': '$dynamic:some_key' + parent: + interface-type: class + interface-name: BBM_OutputData + interface-version: '1.0' diff --git a/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/java/BB_BasicsWorkflowSerialStep2Java-1.0.qstep.yaml b/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/java/BB_BasicsWorkflowSerialStep2Java-1.0.qstep.yaml new file mode 100644 index 0000000000000000000000000000000000000000..b2a7dac2bbff9ddbbc95e48075d96283968b4d66 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/java/BB_BasicsWorkflowSerialStep2Java-1.0.qstep.yaml @@ -0,0 +1,43 @@ +# This is a generated file, don't edit! +type: step +name: BB_BasicsWorkflowSerialStep2Java +desc: |- + ## Simple worklfow step example + The step logs static and dynamic data of the order +lang: java +author: + - Qore Technologies, s.r.o. +base-class-name: QorusNormalStep +class-name: BasicsWorkflowSerialStep2Java +classes: + - BBM_LogMessage +version: '1.0' +steptype: NORMAL +code: bb_basicsworkflowserialstep2java_1_0_step/BasicsWorkflowSerialStep2Java.java +class-connections: + primary: + - class: BBM_LogMessage + connector: logMessage + trigger: primary +config-items: + - name: log-message-level + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + - name: log-message-string + value: + "BasicsWorkflowSerialStep2Java was called - Static data: %N - Dynamic data: %N" + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + - name: log-message-args + value: + - '$static:*' + - '$dynamic:*' + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + value_true_type: list diff --git a/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/java/bb_basicsworkflowserialstep1java_1_0_step/BasicsWorkflowSerialStep1Java.java b/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/java/bb_basicsworkflowserialstep1java_1_0_step/BasicsWorkflowSerialStep1Java.java new file mode 100644 index 0000000000000000000000000000000000000000..dcfc92be2f7920be57d2de402b412fa92a201fa3 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/java/bb_basicsworkflowserialstep1java_1_0_step/BasicsWorkflowSerialStep1Java.java @@ -0,0 +1,84 @@ +import qore.OMQ.*; +import qore.OMQ.UserApi.*; +import qore.OMQ.UserApi.Workflow.*; +import org.qore.jni.QoreJavaApi; +import org.qore.jni.QoreObject; +import java.util.Map; +import org.qore.jni.Hash; +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; +import qore.BBM_LogMessage; +import qore.BBM_OutputData; + +class BasicsWorkflowSerialStep1Java extends QorusNormalStep { + // ==== GENERATED SECTION! DON'T EDIT! ==== // + ClassConnections_BasicsWorkflowSerialStep1Java classConnections; + + // ======== GENERATED SECTION END ========= // + public BasicsWorkflowSerialStep1Java() throws Throwable { + super(); + // ==== GENERATED SECTION! DON'T EDIT! ==== // + classConnections = new ClassConnections_BasicsWorkflowSerialStep1Java(); + // ======== GENERATED SECTION END ========= // + } + + // ==== GENERATED SECTION! DON'T EDIT! ==== // + public void primary() throws Throwable { + classConnections.primary(null); + } + + public String validation() throws Throwable { + return qore.OMQ.$Constants.StatRetry; + } + // ======== GENERATED SECTION END ========= // +} + +// ==== GENERATED SECTION! DON'T EDIT! ==== // +class ClassConnections_BasicsWorkflowSerialStep1Java { + // map of prefixed class names to class instances + private final Hash classMap; + + ClassConnections_BasicsWorkflowSerialStep1Java() throws Throwable { + classMap = new Hash(); + UserApi.startCapturingObjectsFromJava(); + try { + classMap.put("BBM_LogMessage", QoreJavaApi.newObjectSave("BBM_LogMessage")); + classMap.put("BBM_OutputData", QoreJavaApi.newObjectSave("BBM_OutputData")); + } finally { + UserApi.stopCapturingObjectsFromJava(); + } + } + + Object callClassWithPrefixMethod(final String prefixedClass, final String methodName, + Object params) throws Throwable { + UserApi.logDebug("ClassConnections_BasicsWorkflowSerialStep1Java: callClassWithPrefixMethod: method: %s class: %y", methodName, prefixedClass); + final Object object = classMap.get(prefixedClass); + + if (object instanceof QoreObject) { + QoreObject qoreObject = (QoreObject)object; + return qoreObject.callMethod(methodName, params); + } else { + final Method method = object.getClass().getMethod(methodName, Object.class); + try { + return method.invoke(object, params); + } catch (InvocationTargetException ex) { + throw ex.getCause(); + } + } + } + + public Object primary(Object params) throws Throwable { + // convert varargs to a single argument if possible + if (params != null && params.getClass().isArray() && ((Object[])params).length == 1) { + params = ((Object[])params)[0]; + } + UserApi.logDebug("primary called with data: %y", params); + + UserApi.logDebug("calling logMessage: %y", params); + params = callClassWithPrefixMethod("BBM_LogMessage", "logMessage", params); + + UserApi.logDebug("calling writeOutputData: %y", params); + return callClassWithPrefixMethod("BBM_OutputData", "writeOutputData", params); + } +} +// ======== GENERATED SECTION END ========= // diff --git a/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/java/bb_basicsworkflowserialstep2java_1_0_step/BasicsWorkflowSerialStep2Java.java b/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/java/bb_basicsworkflowserialstep2java_1_0_step/BasicsWorkflowSerialStep2Java.java new file mode 100644 index 0000000000000000000000000000000000000000..e983612aabdd6bfc385d8796f2d89f848cc5eefd --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/java/bb_basicsworkflowserialstep2java_1_0_step/BasicsWorkflowSerialStep2Java.java @@ -0,0 +1,79 @@ +import qore.OMQ.*; +import qore.OMQ.UserApi.*; +import qore.OMQ.UserApi.Workflow.*; +import org.qore.jni.QoreJavaApi; +import org.qore.jni.QoreObject; +import java.util.Map; +import org.qore.jni.Hash; +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; +import qore.BBM_LogMessage; + +class BasicsWorkflowSerialStep2Java extends QorusNormalStep { + // ==== GENERATED SECTION! DON'T EDIT! ==== // + ClassConnections_BasicsWorkflowSerialStep2Java classConnections; + + // ======== GENERATED SECTION END ========= // + public BasicsWorkflowSerialStep2Java() throws Throwable { + super(); + // ==== GENERATED SECTION! DON'T EDIT! ==== // + classConnections = new ClassConnections_BasicsWorkflowSerialStep2Java(); + // ======== GENERATED SECTION END ========= // + } + + // ==== GENERATED SECTION! DON'T EDIT! ==== // + public void primary() throws Throwable { + classConnections.primary(null); + } + + public String validation() throws Throwable { + return qore.OMQ.$Constants.StatRetry; + } + // ======== GENERATED SECTION END ========= // +} + +// ==== GENERATED SECTION! DON'T EDIT! ==== // +class ClassConnections_BasicsWorkflowSerialStep2Java { + // map of prefixed class names to class instances + private final Hash classMap; + + ClassConnections_BasicsWorkflowSerialStep2Java() throws Throwable { + classMap = new Hash(); + UserApi.startCapturingObjectsFromJava(); + try { + classMap.put("BBM_LogMessage", QoreJavaApi.newObjectSave("BBM_LogMessage")); + } finally { + UserApi.stopCapturingObjectsFromJava(); + } + } + + Object callClassWithPrefixMethod(final String prefixedClass, final String methodName, + Object params) throws Throwable { + UserApi.logDebug("ClassConnections_BasicsWorkflowSerialStep2Java: callClassWithPrefixMethod: method: %s class: %y", methodName, prefixedClass); + final Object object = classMap.get(prefixedClass); + + if (object instanceof QoreObject) { + QoreObject qoreObject = (QoreObject)object; + return qoreObject.callMethod(methodName, params); + } else { + final Method method = object.getClass().getMethod(methodName, Object.class); + try { + return method.invoke(object, params); + } catch (InvocationTargetException ex) { + throw ex.getCause(); + } + } + } + + public Object primary(Object params) throws Throwable { + // convert varargs to a single argument if possible + if (params != null && params.getClass().isArray() && ((Object[])params).length == 1) { + params = ((Object[])params)[0]; + } + UserApi.logDebug("primary called with data: %y", params); + + UserApi.logDebug("calling logMessage: %y", params); + return callClassWithPrefixMethod("BBM_LogMessage", "logMessage", params); + } +} +// ======== GENERATED SECTION END ========= // diff --git a/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/java/create_order-post_reload.qscript b/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/java/create_order-post_reload.qscript new file mode 100755 index 0000000000000000000000000000000000000000..045b408e4a9f763e54bc213a64e5e227898069b6 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/java/create_order-post_reload.qscript @@ -0,0 +1,20 @@ +#!/usr/bin/env qore +%new-style +%strict-args +%require-types +%enable-all-warnings + +%requires QorusClientCore + +QorusClient::initFast(); + +const WORKFLOW_NAME = "BB-BASICS-WORKFLOW-SERIAL-STEPS-JAVA"; + +const ORDER_DATA = { + "staticdata": { + "test": "data" + } +}; + +hash response = qrest.post("workflows/" + WORKFLOW_NAME + "?action=createOrder", ORDER_DATA); +printf("Response: %N\n", response); \ No newline at end of file diff --git a/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/python/BB-BASICS-WORKFLOW-SERIAL-STEPS-PYTHON-1.0.qwf.yaml b/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/python/BB-BASICS-WORKFLOW-SERIAL-STEPS-PYTHON-1.0.qwf.yaml new file mode 100644 index 0000000000000000000000000000000000000000..459529b9d9c6200d94013a7a3a7c45516f0843d8 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/python/BB-BASICS-WORKFLOW-SERIAL-STEPS-PYTHON-1.0.qwf.yaml @@ -0,0 +1,13 @@ +# This is a generated file, don't edit! +type: workflow +name: BASICS-WORKFLOW-SERIAL-STEPS-PYTHON +desc: Workflow serial steps example +author: + - Qore Technologies, s.r.o. +autostart: 1 +version: "1.0" +steps: + [ + "BasicsWorkflowSerialStep1Python:1.0", + "BasicsWorkflowSerialStep2Python:1.0" + ] diff --git a/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/python/BB_BasicsWorkflowSerialStep1Python-1.0.qstep.py b/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/python/BB_BasicsWorkflowSerialStep1Python-1.0.qstep.py new file mode 100644 index 0000000000000000000000000000000000000000..b66e5f4822a72318001622816133704d367e8c22 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/python/BB_BasicsWorkflowSerialStep1Python-1.0.qstep.py @@ -0,0 +1,46 @@ +from wf import QorusNormalStep +from qore.__root__ import BBM_LogMessage +from qore.__root__ import BBM_OutputData + +class BasicsWorkflowSerialStep1Python(QorusNormalStep): + def __init__(self): + ####### GENERATED SECTION! DON'T EDIT! ######## + self.class_connections = ClassConnections_BasicsWorkflowSerialStep1Python() + ############ GENERATED SECTION END ############ + + ####### GENERATED SECTION! DON'T EDIT! ######## + def primary(self): + self.class_connections.primary() + + def validation(self): + return StatRetry + ############ GENERATED SECTION END ############ + +####### GENERATED SECTION! DON'T EDIT! ######## +class ClassConnections_BasicsWorkflowSerialStep1Python: + def __init__(self): + UserApi.startCapturingObjectsFromPython() + # map of prefixed class names to class instances + self.class_map = { + 'BBM_LogMessage': BBM_LogMessage(), + 'BBM_OutputData': BBM_OutputData(), + } + UserApi.stopCapturingObjectsFromPython() + + def callClassWithPrefixMethod(self, prefixed_class, method, *argv): + UserApi.logDebug("ClassConnections_BasicsWorkflowSerialStep1Python: callClassWithPrefixMethod: method: %s class: %y", method, prefixed_class) + return getattr(self.class_map[prefixed_class], method)(*argv) + + def primary(self, *params): + UserApi.logDebug("primary called with data: %y", params) + # convert varargs to a single argument if possible + if (type(params) is list or type(params) is tuple) and (len(params) == 1): + params = params[0] + UserApi.logDebug("calling logMessage: %y", params) + params = self.callClassWithPrefixMethod("BBM_LogMessage", "logMessage", params) + UserApi.logDebug("output from logMessage: %y", params) + UserApi.logDebug("calling writeOutputData: %y", params) + params = self.callClassWithPrefixMethod("BBM_OutputData", "writeOutputData", params) + UserApi.logDebug("output from writeOutputData: %y", params) + return params +############ GENERATED SECTION END ############ diff --git a/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/python/BB_BasicsWorkflowSerialStep1Python-1.0.qstep.yaml b/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/python/BB_BasicsWorkflowSerialStep1Python-1.0.qstep.yaml new file mode 100644 index 0000000000000000000000000000000000000000..4a929fb24418cd1e9036ce922fbde089565a889a --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/python/BB_BasicsWorkflowSerialStep1Python-1.0.qstep.yaml @@ -0,0 +1,52 @@ +# This is a generated file, don't edit! +type: step +name: BB_BasicsWorkflowSerialStep1Python +desc: |- + ## Simple worklfow step example + It logs static data of the order and updates dynamic data +lang: python +author: + - Qore Technologies, s.r.o. +base-class-name: QorusNormalStep +class-name: BasicsWorkflowSerialStep1Python +classes: + - BBM_LogMessage + - BBM_OutputData +version: '1.0' +steptype: NORMAL +code: BB_BasicsWorkflowSerialStep1Python-1.0.qstep.py +class-connections: + primary: + - class: BBM_LogMessage + connector: logMessage + trigger: primary + - class: BBM_OutputData + connector: writeOutputData +config-items: + - name: log-message-level + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + - name: log-message-string + value: + "BasicsWorkflowSerialStep1Java was called, static data: %N" + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + - name: log-message-args + value: + "$static:*" + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + value_true_type: string + - name: output-data-hash + value: + '$qore-expr:{"data"}': '$dynamic:some_key' + parent: + interface-type: class + interface-name: BBM_OutputData + interface-version: '1.0' diff --git a/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/python/BB_BasicsWorkflowSerialStep2Python-1.0.qstep.py b/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/python/BB_BasicsWorkflowSerialStep2Python-1.0.qstep.py new file mode 100644 index 0000000000000000000000000000000000000000..ae482414cd0f9193b93c6576e50a614330c0b0c6 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/python/BB_BasicsWorkflowSerialStep2Python-1.0.qstep.py @@ -0,0 +1,41 @@ +from wf import QorusNormalStep +from qore.__root__ import BBM_LogMessage + +class BasicsWorkflowSerialStep2Python(QorusNormalStep): + def __init__(self): + ####### GENERATED SECTION! DON'T EDIT! ######## + self.class_connections = ClassConnections_BasicsWorkflowSerialStep2Python() + ############ GENERATED SECTION END ############ + + ####### GENERATED SECTION! DON'T EDIT! ######## + def primary(self): + self.class_connections.primary() + + def validation(self): + return StatRetry + ############ GENERATED SECTION END ############ + +####### GENERATED SECTION! DON'T EDIT! ######## +class ClassConnections_BasicsWorkflowSerialStep2Python: + def __init__(self): + UserApi.startCapturingObjectsFromPython() + # map of prefixed class names to class instances + self.class_map = { + 'BBM_LogMessage': BBM_LogMessage(), + } + UserApi.stopCapturingObjectsFromPython() + + def callClassWithPrefixMethod(self, prefixed_class, method, *argv): + UserApi.logDebug("ClassConnections_BasicsWorkflowSerialStep2Python: callClassWithPrefixMethod: method: %s class: %y", method, prefixed_class) + return getattr(self.class_map[prefixed_class], method)(*argv) + + def primary(self, *params): + UserApi.logDebug("primary called with data: %y", params) + # convert varargs to a single argument if possible + if (type(params) is list or type(params) is tuple) and (len(params) == 1): + params = params[0] + UserApi.logDebug("calling logMessage: %y", params) + params = self.callClassWithPrefixMethod("BBM_LogMessage", "logMessage", params) + UserApi.logDebug("output from logMessage: %y", params) + return params +############ GENERATED SECTION END ############ diff --git a/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/python/BB_BasicsWorkflowSerialStep2Python-1.0.qstep.yaml b/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/python/BB_BasicsWorkflowSerialStep2Python-1.0.qstep.yaml new file mode 100644 index 0000000000000000000000000000000000000000..fa9da531dcf130c72610c1f4498dbe2da1636f74 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/python/BB_BasicsWorkflowSerialStep2Python-1.0.qstep.yaml @@ -0,0 +1,43 @@ +# This is a generated file, don't edit! +type: step +name: BB_BasicsWorkflowSerialStep2Python +desc: |- + ## Simple worklfow step example + The step logs static and dynamic data of the order +lang: python +author: + - Qore Technologies, s.r.o. +base-class-name: QorusNormalStep +class-name: BasicsWorkflowSerialStep2Python +version: '1.0' +classes: + - BBM_LogMessage +steptype: NORMAL +code: BB_BasicsWorkflowSerialStep2Python-1.0.qstep.py +class-connections: + primary: + - class: BBM_LogMessage + connector: logMessage + trigger: primary +config-items: + - name: log-message-level + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + - name: log-message-string + value: + "BasicsWorkflowSerialStep2Java was called\\nStatic data: %N\\nDynamic data: %N" + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + - name: log-message-args + value: + - '$static:*' + - '$dynamic:*' + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + value_true_type: list diff --git a/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/python/create_order-post_reload.qscript b/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/python/create_order-post_reload.qscript new file mode 100755 index 0000000000000000000000000000000000000000..1085ada395c5fc38b8d09182dd923aa8a0396feb --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/python/create_order-post_reload.qscript @@ -0,0 +1,20 @@ +#!/usr/bin/env qore +%new-style +%strict-args +%require-types +%enable-all-warnings + +%requires QorusClientCore + +QorusClient::initFast(); + +const WORKFLOW_NAME = "BB-BASICS-WORKFLOW-SERIAL-STEPS-PYTHON"; + +const ORDER_DATA = { + "staticdata": { + "test": "data" + } +}; + +hash response = qrest.post("workflows/" + WORKFLOW_NAME + "?action=createOrder", ORDER_DATA); +printf("Response: %N\n", response); \ No newline at end of file diff --git a/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/qore/BB-BASICS-WORKFLOW-SERIAL-STEPS-1.0.yaml b/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/qore/BB-BASICS-WORKFLOW-SERIAL-STEPS-1.0.yaml new file mode 100644 index 0000000000000000000000000000000000000000..88d4e5fe27e691231aa945903a0eeb790599938c --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/qore/BB-BASICS-WORKFLOW-SERIAL-STEPS-1.0.yaml @@ -0,0 +1,15 @@ +# This is a generated file, don't edit! +type: workflow +name: BB-BASICS-WORKFLOW-SERIAL-STEPS +desc: "Workflow serial steps example" +author: + - Qore Technologies, s.r.o. +groups: + - BASIC-TRAINING-EXCHANGE-APP +steps: + [ + "BB_BasicsWorkflowSerialStep1:1.0", + "BB_BasicsWorkflowSerialStep2:1.0" + ] +version: "1.0" +autostart: 1 diff --git a/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/qore/BB_BasicsWorkflowSerialStep1-1.0.qstep b/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/qore/BB_BasicsWorkflowSerialStep1-1.0.qstep new file mode 100644 index 0000000000000000000000000000000000000000..fe0f7f31eb5bf31c782859bf542373390b5b0c4b --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/qore/BB_BasicsWorkflowSerialStep1-1.0.qstep @@ -0,0 +1,53 @@ +%new-style +%strict-args +%require-types +%enable-all-warnings + +class BasicsWorkflowSerialStep1 inherits QorusNormalStep { + private { + ####### GENERATED SECTION! DON'T EDIT! ######## + ClassConnections_BasicsWorkflowSerialStep1 class_connections(); + ############ GENERATED SECTION END ############ + } + + ####### GENERATED SECTION! DON'T EDIT! ######## + primary() { + class_connections.primary(); + } + + string validation() { + return OMQ::StatRetry; + } + ############ GENERATED SECTION END ############ +} + +####### GENERATED SECTION! DON'T EDIT! ######## +class ClassConnections_BasicsWorkflowSerialStep1 { + private { + # map of prefixed class names to class instances + hash class_map; + } + + constructor() { + class_map = { + "BBM_LogMessage": new BBM_LogMessage(), + "BBM_OutputData": new BBM_OutputData(), + }; + } + + auto callClassWithPrefixMethod(string prefixed_class, string method) { + UserApi::logDebug("ClassConnections_BasicsWorkflowSerialStep1: callClassWithPrefixMethod: method: %s class: %y", method, prefixed_class); + return call_object_method_args(class_map{prefixed_class}, method, argv); + } + + auto primary(auto params) { + UserApi::logDebug("primary called with data: %y", params); + + UserApi::logDebug("calling logMessage: %y", params); + params = callClassWithPrefixMethod("BBM_LogMessage", "logMessage", params); + + UserApi::logDebug("calling writeOutputData: %y", params); + return callClassWithPrefixMethod("BBM_OutputData", "writeOutputData", params); + } +} +############ GENERATED SECTION END ############ diff --git a/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/qore/BB_BasicsWorkflowSerialStep1-1.0.qstep.yaml b/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/qore/BB_BasicsWorkflowSerialStep1-1.0.qstep.yaml new file mode 100644 index 0000000000000000000000000000000000000000..b8010a52137d51b66b730c5f22fd0d19bbe209df --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/qore/BB_BasicsWorkflowSerialStep1-1.0.qstep.yaml @@ -0,0 +1,52 @@ +# This is a generated file, don't edit! +type: step +name: BB_BasicsWorkflowSerialStep1 +desc: |- + ## Simple worklfow step example + The step logs static data of the order and updates dynamic data +lang: qore +author: + - Qore Technologies, s.r.o. +base-class-name: QorusNormalStep +class-name: BasicsWorkflowSerialStep1 +version: '1.0' +classes: + - BBM_LogMessage + - BBM_OutputData +steptype: NORMAL +code: BB_BasicsWorkflowSerialStep1-1.0.qstep +class-connections: + primary: + - class: BBM_LogMessage + connector: logMessage + trigger: primary + - class: BBM_OutputData + connector: writeOutputData +config-items: + - name: log-message-level + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + - name: log-message-string + value: + "BasicsWorkflowSerialStep1Java was called, static data: %N" + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + - name: log-message-args + value: + "$static:*" + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + value_true_type: string + - name: output-data-hash + value: + '$qore-expr:{"data"}': '$dynamic:some_key' + parent: + interface-type: class + interface-name: BBM_OutputData + interface-version: '1.0' diff --git a/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/qore/BB_BasicsWorkflowSerialStep2-1.0.qstep b/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/qore/BB_BasicsWorkflowSerialStep2-1.0.qstep new file mode 100644 index 0000000000000000000000000000000000000000..e2593a268382325277b8a6b88017146fecfca083 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/qore/BB_BasicsWorkflowSerialStep2-1.0.qstep @@ -0,0 +1,49 @@ +%new-style +%strict-args +%require-types +%enable-all-warnings + +class BasicsWorkflowSerialStep2 inherits QorusNormalStep { + private { + ####### GENERATED SECTION! DON'T EDIT! ######## + ClassConnections_BasicsWorkflowSerialStep2 class_connections(); + ############ GENERATED SECTION END ############ + } + + ####### GENERATED SECTION! DON'T EDIT! ######## + primary() { + class_connections.primary(); + } + + string validation() { + return OMQ::StatRetry; + } + ############ GENERATED SECTION END ############ +} + +####### GENERATED SECTION! DON'T EDIT! ######## +class ClassConnections_BasicsWorkflowSerialStep2 { + private { + # map of prefixed class names to class instances + hash class_map; + } + + constructor() { + class_map = { + "BBM_LogMessage": new BBM_LogMessage(), + }; + } + + auto callClassWithPrefixMethod(string prefixed_class, string method) { + UserApi::logDebug("ClassConnections_BasicsWorkflowSerialStep2: callClassWithPrefixMethod: method: %s class: %y", method, prefixed_class); + return call_object_method_args(class_map{prefixed_class}, method, argv); + } + + auto primary(auto params) { + UserApi::logDebug("primary called with data: %y", params); + + UserApi::logDebug("calling logMessage: %y", params); + return callClassWithPrefixMethod("BBM_LogMessage", "logMessage", params); + } +} +############ GENERATED SECTION END ############ diff --git a/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/qore/BB_BasicsWorkflowSerialStep2-1.0.qstep.yaml b/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/qore/BB_BasicsWorkflowSerialStep2-1.0.qstep.yaml new file mode 100644 index 0000000000000000000000000000000000000000..40ecb09e9f851654f305ca4588ce8c1a4cf41972 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/qore/BB_BasicsWorkflowSerialStep2-1.0.qstep.yaml @@ -0,0 +1,43 @@ +# This is a generated file, don't edit! +type: step +name: BB_BasicsWorkflowSerialStep2 +desc: |- + ## Simple worklfow step example + The step logs static and dynamic data of the order +lang: qore +author: + - Qore Technologies, s.r.o. +base-class-name: QorusNormalStep +class-name: BasicsWorkflowSerialStep2 +version: '1.0' +classes: + - BBM_LogMessage +steptype: NORMAL +code: BB_BasicsWorkflowSerialStep2-1.0.qstep +class-connections: + primary: + - class: BBM_LogMessage + connector: logMessage + trigger: primary +config-items: + - name: log-message-level + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + - name: log-message-string + value: + "BasicsWorkflowSerialStep2Java was called\\nStatic data: %N\\nDynamic data: %N" + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + - name: log-message-args + value: + - '$static:*' + - '$dynamic:*' + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + value_true_type: list diff --git a/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/qore/create_order-post_reload.qscript b/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/qore/create_order-post_reload.qscript new file mode 100755 index 0000000000000000000000000000000000000000..62b2ca7194e4d1dfc33024ac04e0b1c84b435bbe --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/04_workflow_serial_steps/qore/create_order-post_reload.qscript @@ -0,0 +1,20 @@ +#!/usr/bin/env qore +%new-style +%strict-args +%require-types +%enable-all-warnings + +%requires QorusClientCore + +QorusClient::initFast(); + +const WORKFLOW_NAME = "BB-BASICS-WORKFLOW-SERIAL-STEPS"; + +const ORDER_DATA = { + "staticdata": { + "test": "data" + } +}; + +hash response = qrest.post("workflows/" + WORKFLOW_NAME + "?action=createOrder", ORDER_DATA); +printf("Response: %N\n", response); \ No newline at end of file diff --git a/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/README.md b/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/README.md new file mode 100644 index 0000000000000000000000000000000000000000..784e78dedb1e5483205210f4ec9bf07a291fa752 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/README.md @@ -0,0 +1,90 @@ +# Workflow Parallel Steps + +It is possible to create more complex step dependencies. For example, if one of the entries in the top-level steps list is itself a list, then all entries in the sublist have the same dependency and therefore will be executed in parallel threads. + +Furthermore, of one of the entries in the sublist is also a list, then they will be assigned sequential dependencies (i.e.: be executed sequentially) within this sublist. + +Using this simple syntax, it is possible to define a complex multithreaded workflow with minimum effort. + +## Workflow Definition + +Here is a simple example of workflow with parallel steps: + +```yaml +# This is a generated file, don't edit! +type: workflow +name: BASICS-WORKFLOW-PARALLEL-STEPS +desc: "Example of simple workflow with parallel steps" +author: + - Qore Technologies, s.r.o. +groups: + - BASIC-TRAINING-EXCHANGE-APP +steps: + [ + [ + "BasicsWorkflowParallelStep1:1.0", + "BasicsWorkflowParallelStep2:1.0" + ] + ] +version: "1.0" +autostart: 1 +``` + +In this example, 2 threads will start in parallel. In one thread, *BasicsWorkflowParallelStep1* will be executed. In the other thread, *BasicsWorkflowParallelStep2* will be executed. Only when all of these steps reach a OMQ::StatComplete status, workflow order will be completed. + +## Step Definitions + +Steps can be easily created using the Qorus Developer Tools extension that will generate workflow metadata and code: + +![alt-text](img/teststep.gif) + +Also it's possible to define new steps during workflow creation. + +Metadata: +```yaml +# This is a generated file, don't edit! +type: step +name: BasicsWorkflowParallelStep1 +desc: "Simple worklfow step example" +author: + - Qore Technologies, s.r.o. +base-class-name: QorusNormalStep +class-name: BasicsWorkflowParallelStep1 +lang: qore +version: "1.0" +steptype: NORMAL +code: BasicsWorkflowParallelStep1-1.0.qstep +``` + +Code: +```php +%new-style +%strict-args +%require-types +%enable-all-warnings + +class BasicsWorkflowParallelStep1 inherits QorusNormalStep { + primary() { + logInfo("BasicsWorkflowParallelStep1 was called"); + + int i = 0; + while (i < 10) { + i++; + logInfo("BasicsWorkflowParallelStep1 is going to sleep"); + sleep(1s); + } + } +} + +``` + +Step definitions are simple. The first step waits 10 seconds and then it will reach a OMQ::StatComplete status. Also, it logs the message "BasicsWorkflowParallelStep1 is going to sleep" every second before the thread goes to sleep ([sleep method](https://qoretechnologies.com/manual/qorus/latest/qorus/classOMQ_1_1UserApi_1_1UserApi.html#a79394c28aa9efcffecc364b08130d99d)). Definition of the second step is similar to the first step except for the fact that it will wait 6 seconds instead of 10. + +--- + + + + + + +
[← Previous: Workflow serial steps](../04_workflow_serial_steps)[Next: Get exchange rates using job →](../06_exchange_api_user_connection)
\ No newline at end of file diff --git a/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/img/teststep.gif b/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/img/teststep.gif new file mode 100644 index 0000000000000000000000000000000000000000..19b819a36a9ad7a73ae1cb4457038de6b605098d Binary files /dev/null and b/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/img/teststep.gif differ diff --git a/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/java/BB-BASICS-WORKFLOW-PARALLEL-STEPS-JAVA-1.0.yaml b/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/java/BB-BASICS-WORKFLOW-PARALLEL-STEPS-JAVA-1.0.yaml new file mode 100644 index 0000000000000000000000000000000000000000..71246104445a726f3c21a10cac6fb99cbda21a3f --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/java/BB-BASICS-WORKFLOW-PARALLEL-STEPS-JAVA-1.0.yaml @@ -0,0 +1,17 @@ +# This is a generated file, don't edit! +type: workflow +name: BB-BASICS-WORKFLOW-PARALLEL-STEPS-JAVA +desc: "Example of simple workflow with parallel steps" +author: + - Qore Technologies, s.r.o. +groups: + - BASIC-TRAINING-EXCHANGE-APP +steps: + [ + [ + "BB_BasicsWorkflowParallelStep1Java:1.0", + "BB_BasicsWorkflowParallelStep2Java:1.0" + ] + ] +version: "1.0" +autostart: 1 diff --git a/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/java/BB_BasicsWorkflowParallelStep1Java-1.0.qstep.yaml b/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/java/BB_BasicsWorkflowParallelStep1Java-1.0.qstep.yaml new file mode 100644 index 0000000000000000000000000000000000000000..971faf348c44afdab866900236ce77200a6fa36a --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/java/BB_BasicsWorkflowParallelStep1Java-1.0.qstep.yaml @@ -0,0 +1,12 @@ +# This is a generated file, don't edit! +type: step +name: BB_BasicsWorkflowParallelStep1Java +desc: "Simple workflow step example" +author: + - Qore Technologies, s.r.o. +base-class-name: QorusNormalStep +class-name: BasicsWorkflowParallelStep1Java +lang: java +version: "1.0" +steptype: NORMAL +code: BB_BasicsWorkflowParallelStep1Java.java diff --git a/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/java/BB_BasicsWorkflowParallelStep1Java.java b/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/java/BB_BasicsWorkflowParallelStep1Java.java new file mode 100644 index 0000000000000000000000000000000000000000..9d4cb9b4d270246029aa6d82d3c9783c2bdd0f32 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/java/BB_BasicsWorkflowParallelStep1Java.java @@ -0,0 +1,20 @@ +import qore.OMQ.*; +import qore.OMQ.UserApi.*; +import qore.OMQ.UserApi.Workflow.*; + +class BasicsWorkflowParallelStep1Java extends QorusNormalStep { + public BasicsWorkflowParallelStep1Java() throws Throwable { + super(); + } + + public void primary() throws Throwable{ + logInfo("BasicsWorkflowParallelStep1Java was called"); + + int i = 0; + while (i < 10) { + ++i; + logInfo("BasicsWorkflowParallelStep1Java is going to sleep"); + Thread.sleep(1000); + } + } +} \ No newline at end of file diff --git a/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/java/BB_BasicsWorkflowParallelStep2Java-1.0.qstep.yaml b/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/java/BB_BasicsWorkflowParallelStep2Java-1.0.qstep.yaml new file mode 100644 index 0000000000000000000000000000000000000000..6d61a01fe2c5955e13def45316b1a0f65389eee4 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/java/BB_BasicsWorkflowParallelStep2Java-1.0.qstep.yaml @@ -0,0 +1,12 @@ +# This is a generated file, don't edit! +type: step +name: BB_BasicsWorkflowParallelStep2Java +desc: "Simple worklfow step example" +author: + - Qore Technologies, s.r.o. +base-class-name: QorusNormalStep +class-name: BasicsWorkflowParallelStep2Java +lang: java +version: "1.0" +steptype: NORMAL +code: BB_BasicsWorkflowParallelStep2Java.java diff --git a/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/java/BB_BasicsWorkflowParallelStep2Java.java b/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/java/BB_BasicsWorkflowParallelStep2Java.java new file mode 100644 index 0000000000000000000000000000000000000000..824550c3196c73068ca874aee16f6a7c623c795f --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/java/BB_BasicsWorkflowParallelStep2Java.java @@ -0,0 +1,20 @@ +import qore.OMQ.*; +import qore.OMQ.UserApi.*; +import qore.OMQ.UserApi.Workflow.*; + +class BasicsWorkflowParallelStep2Java extends QorusNormalStep { + public BasicsWorkflowParallelStep2Java() throws Throwable { + super(); + } + + public void primary() throws Throwable { + logInfo("BasicsWorkflowParallelStep2Java was called"); + + int i = 0; + while (i < 4) { + ++i; + logInfo("BasicsWorkflowParallelStep2Java is going to sleep"); + Thread.sleep(1000); + } + } +} diff --git a/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/java/create_order-post_reload.qscript b/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/java/create_order-post_reload.qscript new file mode 100755 index 0000000000000000000000000000000000000000..87b4f050a9ca11d0ff969b3ac4f1122caee7c778 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/java/create_order-post_reload.qscript @@ -0,0 +1,20 @@ +#!/usr/bin/env qore +%new-style +%strict-args +%require-types +%enable-all-warnings + +%requires QorusClientCore + +QorusClient::initFast(); + +const WORKFLOW_NAME = "BB-BASICS-WORKFLOW-PARALLEL-STEPS-JAVA"; + +const ORDER_DATA = { + "staticdata": { + "test": "data" + } +}; + +hash response = qrest.post("workflows/" + WORKFLOW_NAME + "?action=createOrder", ORDER_DATA); +printf("Response: %N\n", response); \ No newline at end of file diff --git a/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/python/BB-BASICS-WORKFLOW-PARALLEL-STEPS-PYTHON-1.0.qwf.yaml b/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/python/BB-BASICS-WORKFLOW-PARALLEL-STEPS-PYTHON-1.0.qwf.yaml new file mode 100644 index 0000000000000000000000000000000000000000..5bff75b76464f8dd1a7b7b8c0ea2792efd232706 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/python/BB-BASICS-WORKFLOW-PARALLEL-STEPS-PYTHON-1.0.qwf.yaml @@ -0,0 +1,17 @@ +# This is a generated file, don't edit! +type: workflow +name: BB-BASICS-WORKFLOW-PARALLEL-STEPS-PYTHON +desc: Example of simple workflow with parallel steps +author: + - Qore Technologies, s.r.o. +autostart: 1 +groups: + - BASIC-TRAINING-EXCHANGE-APP +version: "1.0" +steps: + [ + [ + "BB_BasicsWorkflowParallelStep1Python:1.0", + "BB_BasicsWorkflowParallelStep2Python:1.0" + ] + ] diff --git a/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/python/BB_BasicsWorkflowParallelStep1Python-1.0.qstep.py b/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/python/BB_BasicsWorkflowParallelStep1Python-1.0.qstep.py new file mode 100644 index 0000000000000000000000000000000000000000..ba1de6d0f314f31963fd0b7051091e45d0e75534 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/python/BB_BasicsWorkflowParallelStep1Python-1.0.qstep.py @@ -0,0 +1,12 @@ +from wf import QorusNormalStep +import time + +class BasicsWorkflowParallelStep1Python(QorusNormalStep): + def primary(self): + UserApi.logInfo("BasicsWorkflowParallelStep1Python was called") + + i = 0 + while (i < 10): + i += 1 + UserApi.logInfo("BasicsWorkflowParallelStep1Python is going to sleep") + time.sleep(1) diff --git a/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/python/BB_BasicsWorkflowParallelStep1Python-1.0.qstep.yaml b/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/python/BB_BasicsWorkflowParallelStep1Python-1.0.qstep.yaml new file mode 100644 index 0000000000000000000000000000000000000000..1714052e6f6c3ac9187c39bedc37b27f17644c66 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/python/BB_BasicsWorkflowParallelStep1Python-1.0.qstep.yaml @@ -0,0 +1,12 @@ +# This is a generated file, don't edit! +type: step +name: BB_BasicsWorkflowParallelStep1Python +desc: '"Simple workflow step example"' +lang: python +base-class-name: QorusNormalStep +class-name: BasicsWorkflowParallelStep1Python +version: "1.0" +author: + - Qore Technologies, s.r.o. +steptype: NORMAL +code: BB_BasicsWorkflowParallelStep1Python-1.0.qstep.py diff --git a/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/python/BB_BasicsWorkflowParallelStep2Python-1.0.qstep.py b/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/python/BB_BasicsWorkflowParallelStep2Python-1.0.qstep.py new file mode 100644 index 0000000000000000000000000000000000000000..eb4f3a478a19d5dfdc15a718f30a64f096f597cd --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/python/BB_BasicsWorkflowParallelStep2Python-1.0.qstep.py @@ -0,0 +1,12 @@ +from wf import QorusNormalStep +import time + +class BasicsWorkflowParallelStep2Python(QorusNormalStep): + def primary(self): + UserApi.logInfo("BasicsWorkflowParallelStep2Python was called") + + i = 0 + while (i < 4): + i += 1 + UserApi.logInfo("BasicsWorkflowParallelStep2Python is going to sleep") + time.sleep(1) diff --git a/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/python/BB_BasicsWorkflowParallelStep2Python-1.0.qstep.yaml b/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/python/BB_BasicsWorkflowParallelStep2Python-1.0.qstep.yaml new file mode 100644 index 0000000000000000000000000000000000000000..82db4c14a3c3a0b8713ec54f5ae873356868a8e4 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/python/BB_BasicsWorkflowParallelStep2Python-1.0.qstep.yaml @@ -0,0 +1,12 @@ +# This is a generated file, don't edit! +type: step +name: BB_BasicsWorkflowParallelStep2Python +desc: '"Simple workflow step example"' +lang: python +base-class-name: QorusNormalStep +class-name: BasicsWorkflowParallelStep2Python +version: "1.0" +author: + - Qore Technologies, s.r.o. +steptype: NORMAL +code: BB_BasicsWorkflowParallelStep2Python-1.0.qstep.py diff --git a/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/python/create_order-post_reload.qscript b/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/python/create_order-post_reload.qscript new file mode 100755 index 0000000000000000000000000000000000000000..7b342bcd8413da71148e7156c4bb08648a146319 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/python/create_order-post_reload.qscript @@ -0,0 +1,20 @@ +#!/usr/bin/env qore +%new-style +%strict-args +%require-types +%enable-all-warnings + +%requires QorusClientCore + +QorusClient::initFast(); + +const WORKFLOW_NAME = "BB-BASICS-WORKFLOW-PARALLEL-STEPS-PYTHON"; + +const ORDER_DATA = { + "staticdata": { + "test": "data" + } +}; + +hash response = qrest.post("workflows/" + WORKFLOW_NAME + "?action=createOrder", ORDER_DATA); +printf("Response: %N\n", response); \ No newline at end of file diff --git a/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/qore/BB-BASICS-WORKFLOW-PARALLEL-STEPS-1.0.yaml b/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/qore/BB-BASICS-WORKFLOW-PARALLEL-STEPS-1.0.yaml new file mode 100644 index 0000000000000000000000000000000000000000..217a65760914cdfd590342c8e00bff90570b0e86 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/qore/BB-BASICS-WORKFLOW-PARALLEL-STEPS-1.0.yaml @@ -0,0 +1,17 @@ +# This is a generated file, don't edit! +type: workflow +name: BB-BASICS-WORKFLOW-PARALLEL-STEPS +desc: "Example of simple workflow with parallel steps" +author: + - Qore Technologies, s.r.o. +groups: + - BASIC-TRAINING-EXCHANGE-APP +steps: + [ + [ + "BB_BasicsWorkflowParallelStep1:1.0", + "BB_BasicsWorkflowParallelStep2:1.0" + ] + ] +version: "1.0" +autostart: 1 diff --git a/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/qore/BB_BasicsWorkflowParallelStep1-1.0.qstep b/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/qore/BB_BasicsWorkflowParallelStep1-1.0.qstep new file mode 100644 index 0000000000000000000000000000000000000000..21c81c1f6c9ec962a3f8e26eabfcaefa71b01f06 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/qore/BB_BasicsWorkflowParallelStep1-1.0.qstep @@ -0,0 +1,17 @@ +%new-style +%strict-args +%require-types +%enable-all-warnings + +class BasicsWorkflowParallelStep1 inherits QorusNormalStep { + primary() { + logInfo("BasicsWorkflowParallelStep1 was called"); + + int i = 0; + while (i < 10) { + i++; + logInfo("BasicsWorkflowParallelStep1 is going to sleep"); + sleep(1s); + } + } +} diff --git a/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/qore/BB_BasicsWorkflowParallelStep1-1.0.qstep.yaml b/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/qore/BB_BasicsWorkflowParallelStep1-1.0.qstep.yaml new file mode 100644 index 0000000000000000000000000000000000000000..15e202845c462cb546a2acf27328873a98dec0fa --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/qore/BB_BasicsWorkflowParallelStep1-1.0.qstep.yaml @@ -0,0 +1,12 @@ +# This is a generated file, don't edit! +type: step +name: BB_BasicsWorkflowParallelStep1 +desc: "Simple worklfow step example" +author: + - Qore Technologies, s.r.o. +base-class-name: QorusNormalStep +class-name: BasicsWorkflowParallelStep1 +lang: qore +version: "1.0" +steptype: NORMAL +code: BB_BasicsWorkflowParallelStep1-1.0.qstep diff --git a/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/qore/BB_BasicsWorkflowParallelStep2-1.0.qstep b/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/qore/BB_BasicsWorkflowParallelStep2-1.0.qstep new file mode 100644 index 0000000000000000000000000000000000000000..2f7e7a92362009de65380a31f7f1f55fc3665c10 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/qore/BB_BasicsWorkflowParallelStep2-1.0.qstep @@ -0,0 +1,17 @@ +%new-style +%strict-args +%require-types +%enable-all-warnings + +class BasicsWorkflowParallelStep2 inherits QorusNormalStep { + primary() { + logInfo("BasicsWorkflowParallelStep2 was called"); + + int i = 0; + while (i < 4) { + i++; + logInfo("BasicsWorkflowParallelStep2 is going to sleep"); + sleep(1s); + } + } +} diff --git a/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/qore/BB_BasicsWorkflowParallelStep2-1.0.qstep.yaml b/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/qore/BB_BasicsWorkflowParallelStep2-1.0.qstep.yaml new file mode 100644 index 0000000000000000000000000000000000000000..bb44aa91af6917de4b5191734b3dd2475aff9acc --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/qore/BB_BasicsWorkflowParallelStep2-1.0.qstep.yaml @@ -0,0 +1,12 @@ +# This is a generated file, don't edit! +type: step +name: BB_BasicsWorkflowParallelStep2 +desc: "Simple worklfow step example" +author: + - Qore Technologies, s.r.o. +base-class-name: QorusNormalStep +class-name: BasicsWorkflowParallelStep2 +lang: qore +version: "1.0" +steptype: NORMAL +code: BB_BasicsWorkflowParallelStep2-1.0.qstep diff --git a/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/qore/create_order-post_reload.qscript b/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/qore/create_order-post_reload.qscript new file mode 100755 index 0000000000000000000000000000000000000000..2c2a9302f29926ffb029a19baf99b94f8d39e8ae --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/05_workflow_parallel_steps/qore/create_order-post_reload.qscript @@ -0,0 +1,20 @@ +#!/usr/bin/env qore +%new-style +%strict-args +%require-types +%enable-all-warnings + +%requires QorusClientCore + +QorusClient::initFast(); + +const WORKFLOW_NAME = "BB-BASICS-WORKFLOW-PARALLEL-STEPS"; + +const ORDER_DATA = { + "staticdata": { + "test": "data" + } +}; + +hash response = qrest.post("workflows/" + WORKFLOW_NAME + "?action=createOrder", ORDER_DATA); +printf("Response: %N\n", response); \ No newline at end of file diff --git a/03_basics_building_blocks/01_exchange_rates_app/06_exchange_api_user_connection/README.md b/03_basics_building_blocks/01_exchange_rates_app/06_exchange_api_user_connection/README.md new file mode 100644 index 0000000000000000000000000000000000000000..43e4f6ec72b19b0442b78eb4812b9cd171e1daee --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/06_exchange_api_user_connection/README.md @@ -0,0 +1,200 @@ +# Exchange Rates API User Connection + +## Introduction to Connections + +Qorus supports automatic connection monitoring and dependent interface management based on connection status. The following connection types are monitored: + ++ [Qorus to Qorus Connections](https://qoretechnologies.com/manual/qorus/latest/qorus/connmon.html#remoteconn) ++ [User Connections](#user-connections) ++ [Datasource Connections](https://qoretechnologies.com/manual/qorus/latest/qorus/connmon.html#dsconn) + +## User Connections + +User connections are defined in the database (in the CONNECTIONS table) and are usually created as a part of a user release, but can also be created via REST API calls (ex: POST /api/latest/remote/user). + +User connections are acquired in user code by the following API: + ++ [**getUserConnection()**](https://qoretechnologies.com/manual/qorus/latest/qorus/classOMQ_1_1UserApi_1_1UserApi.html#a6b6cf455c01fc92c3176074faa85a202): returns the connection object described by the user connection + +User connection objects are of the type defined by the connection; the connection URLs and connection objects in the following table are supported by Qorus (see connection-modules for an option allowing for user-defined connection types). + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
SchemeObjectDocumentation
file, dirDirFilesystem/Directory User Connections
ftp, ftpsFtpClientFTP User Connections
http, httpsHTTPClientHTTP User Connections
jsonrpc, jsonrpcsJsonRpcClientJSON-RPC User Connections
pop3, pop3sPop3ClientPOP3 User Connections
rest, restsRestClientREST User Connections
sewiorest, sewiorestsSewioRestClientSewio.net RTLS Studio REST API User Connections
sewiows, sewiowssSewioWebSocketConnection Sewio WebSocket User Connections
sftpSFTPClient SFTP Connections
sfrestsSalesforceRestClientSalesforce.com REST User Connections
smtp, smtps, smtptls,
+ esmtp, esmtptls +
SmtpClient SMTP Connections
soap, soapsSoapClient SOAP Connections
telnetTelnetClientTELNET User Connections
ws, wssWebSocketClientWebSocket User Connections
xmlrpc, xmlrpcsXmlRpcClientXML-RPC User Connections
yamlrpc, yamlrpcsYamlRpcClientYAML-RPC User Connections
+ +## Creating User Connection to Exchange Rates API + +New user connection can be added to Qorus by deploying the following YAML: + +```yaml +type: connection +name: exchange-rates-api +desc: Exchange rates API +url: rests://api.exchangeratesapi.io, +options: + timeout: 60000 + connect_timeout: 30000 +``` + +Information on how to implement Qorus Objects using YAML format can be found [here](https://qoretechnologies.com/manual/qorus/latest/qorus/implementingqorusobjectsusingyaml.html). + +The YAML schema used for validation of the connection definition can be downloaded [here](https://qoretechnologies.com/manual/qorus/latest/qorus/connection_schema.yaml). + +### Schemes + + + + + + + + + + + + + + + + +
URI SchemeDescription
"rest"non-encrypted REST HTTP connections; default port 80 if not present in the URL
"rests"encrypted REST HTTPS connections; default port 443 if not present in the URL
+ +### Connection Options + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OptionDescription
"connect_timeout"connection timeout to use in milliseconds
"content_encoding"this sets the send encoding (if the "send_encoding" option is not set) and the requested response encoding; for possible values, see EncodingSupport
"data"see DataSerializationOptions for possible values; the default is "auto"; note that it's recommended to use "yaml" when talking to Qorus
"http_version"HTTP version to use ("1.0" or "1.1", defaults to "1.1")
"max_redirects"maximum redirects to support
"proxy"proxy URL to use
"send_encoding"a send data encoding option or the value "auto" which means to use automatic encoding; if not present defaults to no content-encoding on sent message bodies
"timeout"transfer timeout to use in milliseconds
+ +--- + + + + + + +
[← Previous: Workflow parallel steps](../05_workflow_parallel_steps)[Next: Get exchange rates using job →](../07_job_get_exchange_rates)
diff --git a/03_basics_building_blocks/01_exchange_rates_app/06_exchange_api_user_connection/exchange-rates-api.qconn.yaml b/03_basics_building_blocks/01_exchange_rates_app/06_exchange_api_user_connection/exchange-rates-api.qconn.yaml new file mode 100644 index 0000000000000000000000000000000000000000..dde84250f9600fea1f9968dbf85497d85b324e40 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/06_exchange_api_user_connection/exchange-rates-api.qconn.yaml @@ -0,0 +1,7 @@ +type: connection +name: exchange-rates-api +desc: Exchange rates API +url: rests://cdn.jsdelivr.net/gh/fawazahmed0/currency-api@1/latest/currencies +options: + timeout: 10000 + connect_timeout: 10000 \ No newline at end of file diff --git a/03_basics_building_blocks/01_exchange_rates_app/07_job_get_exchange_rates/README.md b/03_basics_building_blocks/01_exchange_rates_app/07_job_get_exchange_rates/README.md new file mode 100644 index 0000000000000000000000000000000000000000..42623ce455e04719126073dd1d680f0714566018 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/07_job_get_exchange_rates/README.md @@ -0,0 +1,58 @@ +# Get Exchange Rates using Job + +## Job Definition + +Metadata: +```yaml +# This is a generated file, don't edit! +type: job +name: basics-get-exchange-rates-job +desc: "Job logs exchange rates every 10th minute" +author: + - Qore Technologies, s.r.o. +base-class-name: QorusJob +class-name: BasicsGetExchangeRatesJob +groups: + - BASIC-TRAINING-EXCHANGE-APP +lang: qore +schedule: + minutes: "*/10" + hours: "*" + days: "*" + months: "*" + dow: "*" +version: "1.0" +code: basics-get-exchange-rates-job-1.0.qjob +``` + +Code: +```php +%new-style +%strict-args +%require-types +%enable-all-warnings + +%requires RestClient + +class BasicsGetExchangeRatesJob inherits QorusJob { + run() { + logInfo("Job is run"); + + RestClient exchange_rates_api = getUserConnection("exchange-rates-api"); + hash result = exchange_rates_api.get("/latest"); + logInfo("rates: %N", result.body); + } +} + +``` + +This job gets the user connection created in the previous section by [**getUserConnection()**](https://qoretechnologies.com/manual/qorus/latest/qorus/classOMQ_1_1UserApi_1_1UserApi.html#a6b6cf455c01fc92c3176074faa85a202) method. Then it gets actual rates using [**get()**](https://docs.qore.org/current/modules/RestClient/html/class_rest_client_1_1_rest_client.html) method of the [RestClient](https://qoretechnologies.com/manual/qorus/latest/qore/modules/RestClient/html/class_rest_client_1_1_rest_client.html) class. Then it simply logs exchange rates for euro from the resulting hash. + +--- + + + + + + +
[← Previous: Exchange API user connection](../06_exchange_api_user_connection)[Next: Exchange rates datasource Connection →](../08_exchange_rates_datasource_connection)
diff --git a/03_basics_building_blocks/01_exchange_rates_app/07_job_get_exchange_rates/java/bb-basics-get-exchange-rates-job-java-1.0.qjob.yaml b/03_basics_building_blocks/01_exchange_rates_app/07_job_get_exchange_rates/java/bb-basics-get-exchange-rates-job-java-1.0.qjob.yaml new file mode 100644 index 0000000000000000000000000000000000000000..716e1e09eeeaa5341803c4230c03cebfc647d888 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/07_job_get_exchange_rates/java/bb-basics-get-exchange-rates-job-java-1.0.qjob.yaml @@ -0,0 +1,128 @@ +# This is a generated file, don't edit! +type: job +name: bb-basics-get-exchange-rates-job-java +desc: Job logs exchange rates every 10th minute +lang: java +author: + - Qore Technologies, s.r.o. +base-class-name: QorusJob +class-name: BasicsGetExchangeRatesJobJava +classes: + - BBM_RestAction + - BBM_LogMessage +groups: + - BASIC-TRAINING-EXCHANGE-APP +schedule: + minutes: "0" + hours: "0" + days: "*" + months: "*" + dow: "*" +version: '1.0' +code: bb_basics_get_exchange_rates_job_java_1_0_job/BasicsGetExchangeRatesJobJava.java +class-connections: + run: + - class: BBM_RestAction + connector: restActionFromConfig + trigger: run + - class: BBM_LogMessage + connector: logMessage +config-items: + - name: rest-connection-name + value: + "exchange-rates-api" + parent: + interface-type: class + interface-name: BBM_RestAction + interface-version: '1.0' + - name: rest-method + value: + "GET" + parent: + interface-type: class + interface-name: BBM_RestAction + interface-version: '1.0' + - name: rest-api-mapper-name + parent: + interface-type: class + interface-name: BBM_RestAction + interface-version: '1.0' + - name: rest-uri-path + value: + "/eur/usd.json" + parent: + interface-type: class + interface-name: BBM_RestAction + interface-version: '1.0' + - name: rest-id-location + parent: + interface-type: class + interface-name: BBM_RestAction + interface-version: '1.0' + - name: rest-headers + parent: + interface-type: class + interface-name: BBM_RestAction + interface-version: '1.0' + - name: rest-swagger-schema-location + parent: + interface-type: class + interface-name: BBM_RestAction + interface-version: '1.0' + - name: rest-body-remove-nulls + parent: + interface-type: class + interface-name: BBM_RestAction + interface-version: '1.0' + - name: rest-output-data + value: + body: '$var:body' + parent: + interface-type: class + interface-name: BBM_RestAction + interface-version: '1.0' + - name: rest-id-log-msg + parent: + interface-type: class + interface-name: BBM_RestAction + interface-version: '1.0' + - name: rest-body + parent: + interface-type: class + interface-name: BBM_RestAction + interface-version: '1.0' + - name: rest-action-retries + parent: + interface-type: class + interface-name: BBM_RestAction + interface-version: '1.0' + - name: rest-action-retry-delay + parent: + interface-type: class + interface-name: BBM_RestAction + interface-version: '1.0' + - name: rest-action-retry-errors + parent: + interface-type: class + interface-name: BBM_RestAction + interface-version: '1.0' + - name: log-message-level + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + - name: log-message-string + value: + "rates: %N" + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + - name: log-message-args + value: + "$var:body" + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + value_true_type: string diff --git a/03_basics_building_blocks/01_exchange_rates_app/07_job_get_exchange_rates/java/bb_basics_get_exchange_rates_job_java_1_0_job/BasicsGetExchangeRatesJobJava.java b/03_basics_building_blocks/01_exchange_rates_app/07_job_get_exchange_rates/java/bb_basics_get_exchange_rates_job_java_1_0_job/BasicsGetExchangeRatesJobJava.java new file mode 100644 index 0000000000000000000000000000000000000000..2f77f6c870238e4a3955c072abab55a70d451bfc --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/07_job_get_exchange_rates/java/bb_basics_get_exchange_rates_job_java_1_0_job/BasicsGetExchangeRatesJobJava.java @@ -0,0 +1,80 @@ +import qore.OMQ.*; +import qore.OMQ.UserApi.*; +import qore.OMQ.UserApi.Job.*; +import org.qore.jni.QoreJavaApi; +import org.qore.jni.QoreObject; +import java.util.Map; +import org.qore.jni.Hash; +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; +import qore.BBM_RestAction; +import qore.BBM_LogMessage; + +class BasicsGetExchangeRatesJobJava extends QorusJob { + // ==== GENERATED SECTION! DON'T EDIT! ==== // + ClassConnections_BasicsGetExchangeRatesJobJava classConnections; + + // ======== GENERATED SECTION END ========= // + public BasicsGetExchangeRatesJobJava() throws Throwable { + super(); + // ==== GENERATED SECTION! DON'T EDIT! ==== // + classConnections = new ClassConnections_BasicsGetExchangeRatesJobJava(); + // ======== GENERATED SECTION END ========= // + } + + // ==== GENERATED SECTION! DON'T EDIT! ==== // + public void run() throws Throwable { + classConnections.run(null); + } + // ======== GENERATED SECTION END ========= // +} + +// ==== GENERATED SECTION! DON'T EDIT! ==== // +class ClassConnections_BasicsGetExchangeRatesJobJava { + // map of prefixed class names to class instances + private final Hash classMap; + + ClassConnections_BasicsGetExchangeRatesJobJava() throws Throwable { + classMap = new Hash(); + UserApi.startCapturingObjectsFromJava(); + try { + classMap.put("BBM_RestAction", QoreJavaApi.newObjectSave("BBM_RestAction")); + classMap.put("BBM_LogMessage", QoreJavaApi.newObjectSave("BBM_LogMessage")); + } finally { + UserApi.stopCapturingObjectsFromJava(); + } + } + + Object callClassWithPrefixMethod(final String prefixedClass, final String methodName, + Object params) throws Throwable { + UserApi.logDebug("ClassConnections_BasicsGetExchangeRatesJobJava: callClassWithPrefixMethod: method: %s class: %y", methodName, prefixedClass); + final Object object = classMap.get(prefixedClass); + + if (object instanceof QoreObject) { + QoreObject qoreObject = (QoreObject)object; + return qoreObject.callMethod(methodName, params); + } else { + final Method method = object.getClass().getMethod(methodName, Object.class); + try { + return method.invoke(object, params); + } catch (InvocationTargetException ex) { + throw ex.getCause(); + } + } + } + + public Object run(Object params) throws Throwable { + // convert varargs to a single argument if possible + if (params != null && params.getClass().isArray() && ((Object[])params).length == 1) { + params = ((Object[])params)[0]; + } + UserApi.logDebug("run called with data: %y", params); + + UserApi.logDebug("calling restActionFromConfig: %y", params); + params = callClassWithPrefixMethod("BBM_RestAction", "makeRestRequestConnectorFromConfig", params); + + UserApi.logDebug("calling logMessage: %y", params); + return callClassWithPrefixMethod("BBM_LogMessage", "logMessage", params); + } +} +// ======== GENERATED SECTION END ========= // diff --git a/03_basics_building_blocks/01_exchange_rates_app/07_job_get_exchange_rates/python/basics-get-exchange-rates-job-python-1.0.qjob.py b/03_basics_building_blocks/01_exchange_rates_app/07_job_get_exchange_rates/python/basics-get-exchange-rates-job-python-1.0.qjob.py new file mode 100644 index 0000000000000000000000000000000000000000..aafa16be82978c44bf893a1cdd58301ac3ace723 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/07_job_get_exchange_rates/python/basics-get-exchange-rates-job-python-1.0.qjob.py @@ -0,0 +1,43 @@ +from job import QorusJob +from qore.__root__ import BBM_RestAction +from qore.__root__ import BBM_LogMessage + +class BasicsGetExchangeRatesJobPython(QorusJob): + def __init__(self): + ####### GENERATED SECTION! DON'T EDIT! ######## + self.class_connections = ClassConnections_BasicsGetExchangeRatesJobPython() + ############ GENERATED SECTION END ############ + + ####### GENERATED SECTION! DON'T EDIT! ######## + def run(self): + self.class_connections.run() + ############ GENERATED SECTION END ############ + +####### GENERATED SECTION! DON'T EDIT! ######## +class ClassConnections_BasicsGetExchangeRatesJobPython: + def __init__(self): + UserApi.startCapturingObjectsFromPython() + # map of prefixed class names to class instances + self.class_map = { + 'BBM_RestAction': BBM_RestAction(), + 'BBM_LogMessage': BBM_LogMessage(), + } + UserApi.stopCapturingObjectsFromPython() + + def callClassWithPrefixMethod(self, prefixed_class, method, *argv): + UserApi.logDebug("ClassConnections_BasicsGetExchangeRatesJobPython: callClassWithPrefixMethod: method: %s class: %y", method, prefixed_class) + return getattr(self.class_map[prefixed_class], method)(*argv) + + def run(self, *params): + UserApi.logDebug("run called with data: %y", params) + # convert varargs to a single argument if possible + if (type(params) is list or type(params) is tuple) and (len(params) == 1): + params = params[0] + UserApi.logDebug("calling restActionFromConfig: %y", params) + params = self.callClassWithPrefixMethod("BBM_RestAction", "makeRestRequestConnectorFromConfig", params) + UserApi.logDebug("output from restActionFromConfig: %y", params) + UserApi.logDebug("calling logMessage: %y", params) + params = self.callClassWithPrefixMethod("BBM_LogMessage", "logMessage", params) + UserApi.logDebug("output from logMessage: %y", params) + return params +############ GENERATED SECTION END ############ diff --git a/03_basics_building_blocks/01_exchange_rates_app/07_job_get_exchange_rates/python/basics-get-exchange-rates-job-python-1.0.qjob.yaml b/03_basics_building_blocks/01_exchange_rates_app/07_job_get_exchange_rates/python/basics-get-exchange-rates-job-python-1.0.qjob.yaml new file mode 100644 index 0000000000000000000000000000000000000000..aadf61cd24343a39ffb85c20f7069c78b24d3408 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/07_job_get_exchange_rates/python/basics-get-exchange-rates-job-python-1.0.qjob.yaml @@ -0,0 +1,128 @@ +# This is a generated file, don't edit! +type: job +name: bb-basics-get-exchange-rates-job-python +desc: Job logs exchange rates every 10th minute +lang: python +author: + - Qore Technologies, s.r.o. +base-class-name: QorusJob +class-name: BasicsGetExchangeRatesJobPython +groups: + - BASIC-TRAINING-EXCHANGE-APP +schedule: + minutes: "0" + hours: "0" + days: "*" + months: "*" + dow: "*" +version: '1.0' +classes: + - BBM_RestAction + - BBM_LogMessage +code: basics-get-exchange-rates-job-python-1.0.qjob.py +class-connections: + run: + - class: BBM_RestAction + connector: restActionFromConfig + trigger: run + - class: BBM_LogMessage + connector: logMessage +config-items: + - name: rest-connection-name + value: + "exchange-rates-api" + parent: + interface-type: class + interface-name: BBM_RestAction + interface-version: '1.0' + - name: rest-method + value: + "GET" + parent: + interface-type: class + interface-name: BBM_RestAction + interface-version: '1.0' + - name: rest-api-mapper-name + parent: + interface-type: class + interface-name: BBM_RestAction + interface-version: '1.0' + - name: rest-uri-path + value: + "/eur/usd.json" + parent: + interface-type: class + interface-name: BBM_RestAction + interface-version: '1.0' + - name: rest-id-location + parent: + interface-type: class + interface-name: BBM_RestAction + interface-version: '1.0' + - name: rest-headers + parent: + interface-type: class + interface-name: BBM_RestAction + interface-version: '1.0' + - name: rest-swagger-schema-location + parent: + interface-type: class + interface-name: BBM_RestAction + interface-version: '1.0' + - name: rest-body-remove-nulls + parent: + interface-type: class + interface-name: BBM_RestAction + interface-version: '1.0' + - name: rest-output-data + value: + body: '$var:body' + parent: + interface-type: class + interface-name: BBM_RestAction + interface-version: '1.0' + - name: rest-id-log-msg + parent: + interface-type: class + interface-name: BBM_RestAction + interface-version: '1.0' + - name: rest-body + parent: + interface-type: class + interface-name: BBM_RestAction + interface-version: '1.0' + - name: rest-action-retries + parent: + interface-type: class + interface-name: BBM_RestAction + interface-version: '1.0' + - name: rest-action-retry-delay + parent: + interface-type: class + interface-name: BBM_RestAction + interface-version: '1.0' + - name: rest-action-retry-errors + parent: + interface-type: class + interface-name: BBM_RestAction + interface-version: '1.0' + - name: log-message-level + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + - name: log-message-string + value: + "rates: %N" + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + - name: log-message-args + value: + "$var:body" + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + value_true_type: string diff --git a/03_basics_building_blocks/01_exchange_rates_app/07_job_get_exchange_rates/python/bb-basics-get-exchange-rates-job-python-1.0.qjob.py b/03_basics_building_blocks/01_exchange_rates_app/07_job_get_exchange_rates/python/bb-basics-get-exchange-rates-job-python-1.0.qjob.py new file mode 100644 index 0000000000000000000000000000000000000000..ae40aec3bca61d16ffdda60d8a1ab00f51f66086 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/07_job_get_exchange_rates/python/bb-basics-get-exchange-rates-job-python-1.0.qjob.py @@ -0,0 +1,9 @@ +from job import QorusJob + +class BasicsGetExchangeRatesJobPython(QorusJob): + def run(self): + UserApi.logInfo("Job is run") + + exchange_rates_api = UserApi.getUserConnection("exchange-rates-api") + result = exchange_rates_api.get("/eur/usd.json") + UserApi.logInfo("rates: %N", result["body"]) diff --git a/03_basics_building_blocks/01_exchange_rates_app/07_job_get_exchange_rates/qore/basics-get-exchange-rates-job-1.0.qjob b/03_basics_building_blocks/01_exchange_rates_app/07_job_get_exchange_rates/qore/basics-get-exchange-rates-job-1.0.qjob new file mode 100644 index 0000000000000000000000000000000000000000..20111bebf668e37e4a111edd4380055580ab45ca --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/07_job_get_exchange_rates/qore/basics-get-exchange-rates-job-1.0.qjob @@ -0,0 +1,49 @@ +%new-style +%strict-args +%require-types +%enable-all-warnings + +class BasicsGetExchangeRatesJob inherits QorusJob { + private { + ####### GENERATED SECTION! DON'T EDIT! ######## + ClassConnections_BasicsGetExchangeRatesJob class_connections(); + ############ GENERATED SECTION END ############ + } + + ####### GENERATED SECTION! DON'T EDIT! ######## + run() { + class_connections.run(); + } + ############ GENERATED SECTION END ############ +} + +####### GENERATED SECTION! DON'T EDIT! ######## +class ClassConnections_BasicsGetExchangeRatesJob { + private { + # map of prefixed class names to class instances + hash class_map; + } + + constructor() { + class_map = { + "BBM_RestAction": new BBM_RestAction(), + "BBM_LogMessage": new BBM_LogMessage(), + }; + } + + auto callClassWithPrefixMethod(string prefixed_class, string method) { + UserApi::logDebug("ClassConnections_BasicsGetExchangeRatesJob: callClassWithPrefixMethod: method: %s class: %y", method, prefixed_class); + return call_object_method_args(class_map{prefixed_class}, method, argv); + } + + auto run(auto params) { + UserApi::logDebug("run called with data: %y", params); + + UserApi::logDebug("calling restActionFromConfig: %y", params); + params = callClassWithPrefixMethod("BBM_RestAction", "makeRestRequestConnectorFromConfig", params); + + UserApi::logDebug("calling logMessage: %y", params); + return callClassWithPrefixMethod("BBM_LogMessage", "logMessage", params); + } +} +############ GENERATED SECTION END ############ diff --git a/03_basics_building_blocks/01_exchange_rates_app/07_job_get_exchange_rates/qore/basics-get-exchange-rates-job-1.0.qjob.yaml b/03_basics_building_blocks/01_exchange_rates_app/07_job_get_exchange_rates/qore/basics-get-exchange-rates-job-1.0.qjob.yaml new file mode 100644 index 0000000000000000000000000000000000000000..652cb60817d8c4c8e412ad1b34c605221882967e --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/07_job_get_exchange_rates/qore/basics-get-exchange-rates-job-1.0.qjob.yaml @@ -0,0 +1,128 @@ +# This is a generated file, don't edit! +type: job +name: bb-basics-get-exchange-rates-job +desc: Job logs exchange rates every 10th minute +lang: qore +author: + - Qore Technologies, s.r.o. +base-class-name: QorusJob +class-name: BasicsGetExchangeRatesJob +groups: + - BASIC-TRAINING-EXCHANGE-APP +schedule: + minutes: "0" + hours: "0" + days: "*" + months: "*" + dow: "*" +version: '1.0' +classes: + - BBM_RestAction + - BBM_LogMessage +code: basics-get-exchange-rates-job-1.0.qjob +class-connections: + run: + - class: BBM_RestAction + connector: restActionFromConfig + trigger: run + - class: BBM_LogMessage + connector: logMessage +config-items: + - name: rest-connection-name + value: + "exchange-rates-api" + parent: + interface-type: class + interface-name: BBM_RestAction + interface-version: '1.0' + - name: rest-method + value: + "GET" + parent: + interface-type: class + interface-name: BBM_RestAction + interface-version: '1.0' + - name: rest-api-mapper-name + parent: + interface-type: class + interface-name: BBM_RestAction + interface-version: '1.0' + - name: rest-uri-path + value: + "/eur/usd.json" + parent: + interface-type: class + interface-name: BBM_RestAction + interface-version: '1.0' + - name: rest-id-location + parent: + interface-type: class + interface-name: BBM_RestAction + interface-version: '1.0' + - name: rest-headers + parent: + interface-type: class + interface-name: BBM_RestAction + interface-version: '1.0' + - name: rest-swagger-schema-location + parent: + interface-type: class + interface-name: BBM_RestAction + interface-version: '1.0' + - name: rest-body-remove-nulls + parent: + interface-type: class + interface-name: BBM_RestAction + interface-version: '1.0' + - name: rest-output-data + value: + body: '$var:body' + parent: + interface-type: class + interface-name: BBM_RestAction + interface-version: '1.0' + - name: rest-id-log-msg + parent: + interface-type: class + interface-name: BBM_RestAction + interface-version: '1.0' + - name: rest-body + parent: + interface-type: class + interface-name: BBM_RestAction + interface-version: '1.0' + - name: rest-action-retries + parent: + interface-type: class + interface-name: BBM_RestAction + interface-version: '1.0' + - name: rest-action-retry-delay + parent: + interface-type: class + interface-name: BBM_RestAction + interface-version: '1.0' + - name: rest-action-retry-errors + parent: + interface-type: class + interface-name: BBM_RestAction + interface-version: '1.0' + - name: log-message-level + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + - name: log-message-string + value: + "rates: %N" + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + - name: log-message-args + value: + "$var:body" + parent: + interface-type: class + interface-name: BBM_LogMessage + interface-version: '1.0' + value_true_type: string diff --git a/03_basics_building_blocks/01_exchange_rates_app/07_job_get_exchange_rates/qore/bb-basics-get-exchange-rates-job-1.0.qjob b/03_basics_building_blocks/01_exchange_rates_app/07_job_get_exchange_rates/qore/bb-basics-get-exchange-rates-job-1.0.qjob new file mode 100644 index 0000000000000000000000000000000000000000..454ae7627978b6ab72f9a35add84e6b60fd242ac --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/07_job_get_exchange_rates/qore/bb-basics-get-exchange-rates-job-1.0.qjob @@ -0,0 +1,16 @@ +%new-style +%strict-args +%require-types +%enable-all-warnings + +%requires RestClient + +class BasicsGetExchangeRatesJob inherits QorusJob { + run() { + logInfo("Job is run"); + + RestClient exchange_rates_api = getUserConnection("exchange-rates-api"); + hash result = exchange_rates_api.get("/eur/usd.json"); + logInfo("rates: %N", result.body); + } +} diff --git a/03_basics_building_blocks/01_exchange_rates_app/08_exchange_rates_datasource_connection/ExchangeRatesSchema.qsm b/03_basics_building_blocks/01_exchange_rates_app/08_exchange_rates_datasource_connection/ExchangeRatesSchema.qsm new file mode 100644 index 0000000000000000000000000000000000000000..fdaa2a7381f5d5d92daaf40c71ae7770d8e91555 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/08_exchange_rates_datasource_connection/ExchangeRatesSchema.qsm @@ -0,0 +1,82 @@ +# -*- mode: qore; indent-tabs-mode: nil -*- + +%requires qore >= 0.8.10 + +module ExchangeRatesSchema { + 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 ExchangeOrdersTable = { + "columns": { + "id": c_int(True, "PK ID field"), + "currency_to_buy": c_varchar(3, True, "input currency to buy"), + "currency_to_sell": c_varchar(3, True, "input currency to sell"), + "amount": c_number(True, "input amount"), + "creation_date": c_date(True, "input creation date"), + "status": c_varchar(15, True, "input status"), + "comment": c_varchar(200, False, "input comment"), + }, + "primary_key": {"name": "pk_exchange_orders", "columns": ("id")}, + }; + + const Tables = { + "exchange_orders": ExchangeOrdersTable, + }; + + # Could be added, but we want to retrive id of the order from frontend + # const Sequences = { + # "seq_exchange_orders_id": {}, + # }; +} + +public namespace ExchangeRatesSchema { + public string sub get_datasource_name() { + return "omquser"; + } + + public ExchangeRatesSchema sub get_user_schema(AbstractDatasource ds, *string dts, *string its) { + return new ExchangeRatesSchema(ds, dts, its); + } + + public class ExchangeRatesSchema inherits AbstractSchema { + public { + const SchemaName = "ExchangeRatesSchema"; + 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; + } + + # private *hash getSequencesImpl() { + # return Sequences; + # } + } +} diff --git a/03_basics_building_blocks/01_exchange_rates_app/08_exchange_rates_datasource_connection/README.md b/03_basics_building_blocks/01_exchange_rates_app/08_exchange_rates_datasource_connection/README.md new file mode 100644 index 0000000000000000000000000000000000000000..becde7e91ff43df53e148d13021fcbaedaa345d4 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/08_exchange_rates_datasource_connection/README.md @@ -0,0 +1,37 @@ +# Exchange Rates Datasource Connection + +## Datasource Connections + +[Datasources](https://qoretechnologies.com/manual/qorus/latest/qorus/connmon.html#dsconn) are system-wide named connections to external databases. Qorus datasources are defined in the [System Options File](https://qoretechnologies.com/manual/qorus/latest/qorus/sysarch.html#options); from these definitions the recommended way to get a database connection is following: + +[**getDatasourcePool()**](https://qoretechnologies.com/manual/qorus/latest/qorus/classOMQ_1_1UserApi_1_1UserApi.html#a80461179fe81c80d10c46cf0b47ad1f0): gets a [DatasourcePool](https://qoretechnologies.com/manual/qorus/latest/qore/lang/html/class_qore_1_1_s_q_l_1_1_datasource_pool.html) object for the given system datasource. + +In this example the **omquser** datasource connection will be used. Before it can be used tables should be prepared. To do that a user schema module can be used which is described in the next chapter. + +**NOTE** You must create the `omquser` datasource in Qorus before you can deploy the user schema. The `omquser.qconn.yaml` +file contains a definition of an `omquser` datasource, however it assumes that there is an accessible PostgreSQL database already present called `omquser`. Ensure that this database is created or update the file to reference another accessible database with a user with read and write permissions on the DB. + +## Database Preparation + +### User Schema Management + +User schema module files allow Qorus developers to manage user schemas automatically by provided a user schema module file describing the schema. When the system loads such a schema file, the schema will be automatically aligned in the target datasource as described by the user schema module. + +Qorus user schema module files are [Qore user modules](https://www.qoretechnologies.com/manual/qorus/latest/qore/lang/html/qore_modules.html#user_modules) that use the [Schema module](https://www.qoretechnologies.com/manual/qorus/latest/qore/modules/Schema/html/index.html#schemaintro) to provide the configuration and management of user schemas. A Qorus user schema module must have the extension ***.qsm**. + +The user schema module must provide the following public (ie exported) functions: + ++ **public string sub get_datasource_name()**: this function must return the name of the datasource to be used for schema management ++ **AbstractSchema sub get_user_schema(AbstractDatasource ds, *string dts, *string its)**: this function must return the schema object to be managed; the arguments to the function correspond to the arguments to the [AbstractSchema::constructor()](https://www.qoretechnologies.com/manual/qorus/latest/qore/modules/Schema/html/class_schema_1_1_abstract_schema.html#afb3d82ba918ab2eaa594d45001e51311) method +The [AbstractSchema](https://www.qoretechnologies.com/manual/qorus/latest/qore/modules/Schema/html/class_schema_1_1_abstract_schema.html) object returned by [**get_user_schema()**]() represents the schema that will be managed. + +To create needed table in the **"omquser"** database the ExchangeRatesSchema module can be used. This file will create the needed table for the exchange rates application. + +--- + + + + + + +
[← Previous: Get exchange rates using Job](../07_job_get_exchange_rates)[Next: Exchange currency workflow →](../09_exchange_currency_workflow)
diff --git a/03_basics_building_blocks/01_exchange_rates_app/08_exchange_rates_datasource_connection/omquser.qconn.yaml b/03_basics_building_blocks/01_exchange_rates_app/08_exchange_rates_datasource_connection/omquser.qconn.yaml new file mode 100644 index 0000000000000000000000000000000000000000..bc9894ace685ff78068be4f6d73938e781412c2a --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/08_exchange_rates_datasource_connection/omquser.qconn.yaml @@ -0,0 +1,4 @@ +type: connection +name: omquser +desc: omquser datasource connection, +url: db://pgsql:omquser/omquser@omquser \ No newline at end of file diff --git a/03_basics_building_blocks/01_exchange_rates_app/09_exchange_currency_workflow/README.md b/03_basics_building_blocks/01_exchange_rates_app/09_exchange_currency_workflow/README.md new file mode 100644 index 0000000000000000000000000000000000000000..e5ab87f16058af9caff08375006e641e617f9076 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/09_exchange_currency_workflow/README.md @@ -0,0 +1,81 @@ +# Exchange Currency Workflow + +## Workflow Definition + +```yaml +# This is a generated file, don't edit! +type: workflow +name: BASICS-EXCHANGE-CURRENCY-WORKFLOW +desc: "Exchange currency workflow" +author: + - Qore Technologies, s.r.o. +groups: + - BASIC-TRAINING-EXCHANGE-APP +steps: + [ + "BasicsExchangeRatesInsertToOrdersTable:1.0" + ] +version: "1.0" +``` + +The workflow consists of one step, which inserts data into the **"exchange_orders"** table of the **"omquser"** datasource. + +## Workflow Definition Keylist Key + +The workflow definition option keylist defines one or more "order keys" that can be used to quickly look up workflow order data instances. In this example the **keylist** parameter will be used to prevent creating workflow instances with the same data. + +## Step definition + +Metadata: +```yaml +# This is a generated file, don't edit! +type: step +name: BasicsExchangeRatesInsertToOrdersTable +desc: "Inserts orders from static data into orders table" +author: + - Qore Technologies, s.r.o. +base-class-name: QorusNormalStep +class-name: BasicsExchangeRatesInsertToOrdersTable +lang: qore +version: "1.0" +steptype: NORMAL +code: BasicsExchangeRatesInsertToOrdersTable-1.0.qstep +``` + +Code: +```php +%new-style +%strict-args +%require-types +%enable-all-warnings + +class BasicsExchangeRatesInsertToOrdersTable inherits QorusNormalStep { + primary() { + DatasourcePool omquser_ds = getDatasourcePool("omquser"); + on_success omquser_ds.commit(); + on_error omquser_ds.rollback(); + + SqlUtil::AbstractTable orders_table = getSqlTable(omquser_ds, "exchange_orders"); + + list> orders = getStaticData("orders"); + foreach hash row in (orders) { + logInfo("inserting row: %N", row); + orders_table.insert(row); + } + } +} + +``` + +This step inserts data received in *orders* list of **static data** of a workflow order into the **exchange_orders** table of the **omquser** database. Each element of the *orders* list is a hash containing one row of the table. + +To obtain the datasource [**getDatasourcePool()**](https://qoretechnologies.com/manual/qorus/latest/qorus/classOMQ_1_1UserApi_1_1UserApi.html#a80461179fe81c80d10c46cf0b47ad1f0) method is used. + +--- + + + + + + +
[← Previous: Exchange rates datasource connection](../08_exchange_rates_datasource_connection)[Next: Exchange currency workflow with mapper →](../10_exchange_currency_workflow_with_mapper)
diff --git a/03_basics_building_blocks/01_exchange_rates_app/09_exchange_currency_workflow/basics-exchange-rates-insert-to-orders-table.qfsm.yaml b/03_basics_building_blocks/01_exchange_rates_app/09_exchange_currency_workflow/basics-exchange-rates-insert-to-orders-table.qfsm.yaml new file mode 100644 index 0000000000000000000000000000000000000000..b7463271c9a849f8ec45470b14d7a64abe509f21 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/09_exchange_currency_workflow/basics-exchange-rates-insert-to-orders-table.qfsm.yaml @@ -0,0 +1,82 @@ +# This is a generated file, don't edit! +type: fsm +name: basics-exchange-rates-insert-to-orders-table +desc: Inserts static data to the `exchange_orders` table +groups: + - BASIC-TRAINING-EXCHANGE-APP +states: + '1': + position: + x: 88 + 'y': 45 + initial: true + name: Loop over static data + desc: '' + type: block + id: s8lzBUiGY + execution_order: 1 + block-config: + loop: + type: string + value: '$qore-expr:{$qore-expr-value:{$static:orders}.pairIterator()}' + block-type: foreach + states: + '1': + position: + x: 55 + 'y': 31 + initial: true + name: Insert Row + desc: '' + type: state + id: CYU66uSIC + action: + type: connector + value: + class: BBM_DataProviderRecordCreate + connector: DataProvider Record Create From Config + execution_order: 1 + config-items: + - name: dataprovider-create-provider-path + value: + "datasource/omquser/exchange_orders" + parent: + interface-type: class + interface-name: BBM_DataProviderRecordCreate + interface-version: '1.0' + - name: dataprovider-create-input + value: + "$qore-expr:{$qore-expr-value:{$local:input.value} + {\"id\": $local:{input.key}.toInt()}}" + parent: + interface-type: class + interface-name: BBM_DataProviderRecordCreate + interface-version: '1.0' + value_true_type: string + - name: dataprovider-create-upsert + parent: + interface-type: class + interface-name: BBM_DataProviderRecordCreate + interface-version: '1.0' + - name: dataprovider-create-mapper + parent: + interface-type: class + interface-name: BBM_DataProviderRecordCreate + interface-version: '1.0' + - name: dataprovider-create-options + parent: + interface-type: class + interface-name: BBM_DataProviderRecordCreate + interface-version: '1.0' + - name: dataprovider-create-duplicate-handling + parent: + interface-type: class + interface-name: BBM_DataProviderRecordCreate + interface-version: '1.0' + allowed_values: + - "SUCCESS" + - "DUPLICATE" + - name: dataprovider-create-output-data + parent: + interface-type: class + interface-name: BBM_DataProviderRecordCreate + interface-version: '1.0' diff --git a/03_basics_building_blocks/01_exchange_rates_app/09_exchange_currency_workflow/java/BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW-JAVA-1.0.yaml b/03_basics_building_blocks/01_exchange_rates_app/09_exchange_currency_workflow/java/BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW-JAVA-1.0.yaml new file mode 100644 index 0000000000000000000000000000000000000000..c5f3fa6a7d51a4676463676d899c100acb2750c7 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/09_exchange_currency_workflow/java/BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW-JAVA-1.0.yaml @@ -0,0 +1,13 @@ +# This is a generated file, don't edit! +type: workflow +name: BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW-JAVA +desc: "Exchange currency workflow" +author: + - Qore Technologies, s.r.o. +groups: + - BASIC-TRAINING-EXCHANGE-APP +steps: + [ + "BB_BasicsExchangeRatesInsertToOrdersTableJava:1.0" + ] +version: "1.0" diff --git a/03_basics_building_blocks/01_exchange_rates_app/09_exchange_currency_workflow/java/BB_BasicsExchangeRatesInsertToOrdersTableJava-1.0.qstep.yaml b/03_basics_building_blocks/01_exchange_rates_app/09_exchange_currency_workflow/java/BB_BasicsExchangeRatesInsertToOrdersTableJava-1.0.qstep.yaml new file mode 100644 index 0000000000000000000000000000000000000000..b31aee8d5995d7141c1d38ed0dc900ce5ca2460a --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/09_exchange_currency_workflow/java/BB_BasicsExchangeRatesInsertToOrdersTableJava-1.0.qstep.yaml @@ -0,0 +1,23 @@ +# This is a generated file, don't edit! +type: step +name: BB_BasicsExchangeRatesInsertToOrdersTableJava +desc: Inserts orders from static data into orders table +lang: java +author: + - Qore Technologies, s.r.o. +base-class-name: QorusNormalStep +class-name: BasicsExchangeRatesInsertToOrdersTableJava +version: '1.0' +fsm: + [ + { + "name": "basics-exchange-rates-insert-to-orders-table", + "triggers": [ + { + "method": "primary" + } + ] + } + ] +steptype: NORMAL +code: bb_basicsexchangeratesinserttoorderstablejava_1_0_step/BasicsExchangeRatesInsertToOrdersTableJava.java diff --git a/03_basics_building_blocks/01_exchange_rates_app/09_exchange_currency_workflow/java/bb_basicsexchangeratesinserttoorderstablejava_1_0_step/BasicsExchangeRatesInsertToOrdersTableJava.java b/03_basics_building_blocks/01_exchange_rates_app/09_exchange_currency_workflow/java/bb_basicsexchangeratesinserttoorderstablejava_1_0_step/BasicsExchangeRatesInsertToOrdersTableJava.java new file mode 100644 index 0000000000000000000000000000000000000000..c59ebc9e7a3bbbbd15d50bc43730736534c62429 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/09_exchange_currency_workflow/java/bb_basicsexchangeratesinserttoorderstablejava_1_0_step/BasicsExchangeRatesInsertToOrdersTableJava.java @@ -0,0 +1,17 @@ +import qore.OMQ.*; +import qore.OMQ.UserApi.*; +import qore.OMQ.UserApi.Workflow.*; + +import qore.SqlUtil.AbstractTable; +import qore.Qore.SQL.AbstractDatasource; + +import org.qore.jni.Hash; + +class BasicsExchangeRatesInsertToOrdersTableJava extends QorusNormalStep { + public BasicsExchangeRatesInsertToOrdersTableJava() throws Throwable { + super(); + } + + public void primary() throws Throwable { + } +} diff --git a/03_basics_building_blocks/01_exchange_rates_app/09_exchange_currency_workflow/java/create_order-post_reload.qscript b/03_basics_building_blocks/01_exchange_rates_app/09_exchange_currency_workflow/java/create_order-post_reload.qscript new file mode 100755 index 0000000000000000000000000000000000000000..4005051cfa62f6b2322ad0fc39f49d2246a68686 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/09_exchange_currency_workflow/java/create_order-post_reload.qscript @@ -0,0 +1,29 @@ +#!/usr/bin/env qore +%new-style +%strict-args +%require-types +%enable-all-warnings + +%requires QorusClientCore + +QorusClient::initFast(); + +const WORKFLOW_NAME = "BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW-JAVA"; + +const ORDER_DATA = { + "staticdata": { + "orders": { + "1": { + "currency_to_buy": "czk", "currency_to_sell": "eur", + "creation_date": now(), "status": "new", "amount": 1500 + }, + "2": { + "currency_to_buy": "eur", "currency_to_sell": "czk", + "creation_date": now(), "status": "new", "amount": 60 + } + } + } +}; + +hash response = qrest.post("workflows/" + WORKFLOW_NAME + "?action=createOrder", ORDER_DATA); +printf("Response: %N\n", response); \ No newline at end of file diff --git a/03_basics_building_blocks/01_exchange_rates_app/09_exchange_currency_workflow/python/BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW-PYTHON-1.0.qwf.yaml b/03_basics_building_blocks/01_exchange_rates_app/09_exchange_currency_workflow/python/BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW-PYTHON-1.0.qwf.yaml new file mode 100644 index 0000000000000000000000000000000000000000..c235cd092de9c5e2fd54889057147289451b872d --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/09_exchange_currency_workflow/python/BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW-PYTHON-1.0.qwf.yaml @@ -0,0 +1,13 @@ +# This is a generated file, don't edit! +type: workflow +name: BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW-PYTHON +desc: '"Exchange currency workflow"' +version: "1.0" +author: + - Qore Technologies, s.r.o. +groups: + - BASIC-TRAINING-EXCHANGE-APP +steps: + [ + "BB_BasicsExchangeRatesInsertToOrdersTablePython:1.0" + ] diff --git a/03_basics_building_blocks/01_exchange_rates_app/09_exchange_currency_workflow/python/BB_BasicsExchangeRatesInsertToOrdersTablePython-1.0.qstep.py b/03_basics_building_blocks/01_exchange_rates_app/09_exchange_currency_workflow/python/BB_BasicsExchangeRatesInsertToOrdersTablePython-1.0.qstep.py new file mode 100644 index 0000000000000000000000000000000000000000..a02394bcaf02ca8bcf87d00c5cec66f5313a7122 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/09_exchange_currency_workflow/python/BB_BasicsExchangeRatesInsertToOrdersTablePython-1.0.qstep.py @@ -0,0 +1,5 @@ +from wf import QorusNormalStep + +class BasicsExchangeRatesInsertToOrdersTablePython(QorusNormalStep): + def primary(self): + pass diff --git a/03_basics_building_blocks/01_exchange_rates_app/09_exchange_currency_workflow/python/BB_BasicsExchangeRatesInsertToOrdersTablePython-1.0.qstep.yaml b/03_basics_building_blocks/01_exchange_rates_app/09_exchange_currency_workflow/python/BB_BasicsExchangeRatesInsertToOrdersTablePython-1.0.qstep.yaml new file mode 100644 index 0000000000000000000000000000000000000000..5ff57ed70012efc42bc638702569816133485ebf --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/09_exchange_currency_workflow/python/BB_BasicsExchangeRatesInsertToOrdersTablePython-1.0.qstep.yaml @@ -0,0 +1,23 @@ +# This is a generated file, don't edit! +type: step +name: BB_BasicsExchangeRatesInsertToOrdersTablePython +desc: Inserts orders from static data into orders table +lang: python +author: + - Qore Technologies, s.r.o. +base-class-name: QorusNormalStep +class-name: BasicsExchangeRatesInsertToOrdersTablePython +version: '1.0' +fsm: + [ + { + "name": "basics-exchange-rates-insert-to-orders-table", + "triggers": [ + { + "method": "primary" + } + ] + } + ] +steptype: NORMAL +code: BB_BasicsExchangeRatesInsertToOrdersTablePython-1.0.qstep.py diff --git a/03_basics_building_blocks/01_exchange_rates_app/09_exchange_currency_workflow/python/create_order-post_reload.qscript b/03_basics_building_blocks/01_exchange_rates_app/09_exchange_currency_workflow/python/create_order-post_reload.qscript new file mode 100755 index 0000000000000000000000000000000000000000..0da6583ed22a3ddfbf045b331f6cce82d2a0b6c1 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/09_exchange_currency_workflow/python/create_order-post_reload.qscript @@ -0,0 +1,29 @@ +#!/usr/bin/env qore +%new-style +%strict-args +%require-types +%enable-all-warnings + +%requires QorusClientCore + +QorusClient::initFast(); + +const WORKFLOW_NAME = "BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW-PYTHON"; + +const ORDER_DATA = { + "staticdata": { + "orders": { + "3": { + "currency_to_buy": "czk", "currency_to_sell": "eur", + "creation_date": now(), "status": "new", "amount": 1500 + }, + "4": { + "currency_to_buy": "eur", "currency_to_sell": "czk", + "creation_date": now(), "status": "new", "amount": 60 + } + } + } +}; + +hash response = qrest.post("workflows/" + WORKFLOW_NAME + "?action=createOrder", ORDER_DATA); +printf("Response: %N\n", response); \ No newline at end of file diff --git a/03_basics_building_blocks/01_exchange_rates_app/09_exchange_currency_workflow/qore/BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW-1.0.yaml b/03_basics_building_blocks/01_exchange_rates_app/09_exchange_currency_workflow/qore/BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW-1.0.yaml new file mode 100644 index 0000000000000000000000000000000000000000..4ecd84e9acb570486ac52fc9aa41821888812cfc --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/09_exchange_currency_workflow/qore/BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW-1.0.yaml @@ -0,0 +1,13 @@ +# This is a generated file, don't edit! +type: workflow +name: BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW +desc: "Exchange currency workflow" +author: + - Qore Technologies, s.r.o. +groups: + - BASIC-TRAINING-EXCHANGE-APP +steps: + [ + "BB_BasicsExchangeRatesInsertToOrdersTable:1.0" + ] +version: "1.0" diff --git a/03_basics_building_blocks/01_exchange_rates_app/09_exchange_currency_workflow/qore/BB_BasicsExchangeRatesInsertToOrdersTable-1.0.qstep b/03_basics_building_blocks/01_exchange_rates_app/09_exchange_currency_workflow/qore/BB_BasicsExchangeRatesInsertToOrdersTable-1.0.qstep new file mode 100644 index 0000000000000000000000000000000000000000..f3c5cd94149776bf09774942919b1bf44503eda5 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/09_exchange_currency_workflow/qore/BB_BasicsExchangeRatesInsertToOrdersTable-1.0.qstep @@ -0,0 +1,9 @@ +%new-style +%strict-args +%require-types +%enable-all-warnings + +class BasicsExchangeRatesInsertToOrdersTable inherits QorusNormalStep { + primary() { + } +} diff --git a/03_basics_building_blocks/01_exchange_rates_app/09_exchange_currency_workflow/qore/BB_BasicsExchangeRatesInsertToOrdersTable-1.0.qstep.yaml b/03_basics_building_blocks/01_exchange_rates_app/09_exchange_currency_workflow/qore/BB_BasicsExchangeRatesInsertToOrdersTable-1.0.qstep.yaml new file mode 100644 index 0000000000000000000000000000000000000000..714b8538da828230c1eb3c47771d173443818dde --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/09_exchange_currency_workflow/qore/BB_BasicsExchangeRatesInsertToOrdersTable-1.0.qstep.yaml @@ -0,0 +1,23 @@ +# This is a generated file, don't edit! +type: step +name: BB_BasicsExchangeRatesInsertToOrdersTable +desc: Inserts orders from static data into orders table +lang: qore +author: + - Qore Technologies, s.r.o. +base-class-name: QorusNormalStep +class-name: BasicsExchangeRatesInsertToOrdersTable +version: '1.0' +fsm: + [ + { + "name": "basics-exchange-rates-insert-to-orders-table", + "triggers": [ + { + "method": "primary" + } + ] + } + ] +steptype: NORMAL +code: BB_BasicsExchangeRatesInsertToOrdersTable-1.0.qstep diff --git a/03_basics_building_blocks/01_exchange_rates_app/09_exchange_currency_workflow/qore/create_order-post_reload.qscript b/03_basics_building_blocks/01_exchange_rates_app/09_exchange_currency_workflow/qore/create_order-post_reload.qscript new file mode 100755 index 0000000000000000000000000000000000000000..eca683da679d528b19e2656cabcb04e9929ca8cc --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/09_exchange_currency_workflow/qore/create_order-post_reload.qscript @@ -0,0 +1,61 @@ +#!/usr/bin/env qore +%new-style +%strict-args +%require-types +%enable-all-warnings + +%requires QorusClientCore + +QorusClient::initFast(); + +const WORKFLOW_NAME = "BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW"; + +const ORDER_DATA = { + "staticdata": { + "orders": { + "41": { + "currency_to_buy": "czk", "currency_to_sell": "eur", + "creation_date": now(), "status": "new", "amount": 1500 + }, + "42": { + "currency_to_buy": "eur", "currency_to_sell": "czk", + "creation_date": now(), "status": "new", "amount": 60 + }, + "43": { + "currency_to_buy": "czk", "currency_to_sell": "eur", + "creation_date": now(), "status": "new", "amount": 1500 + }, + "44": { + "currency_to_buy": "eur", "currency_to_sell": "czk", + "creation_date": now(), "status": "new", "amount": 60 + }, + "45": { + "currency_to_buy": "czk", "currency_to_sell": "eur", + "creation_date": now(), "status": "new", "amount": 1500 + }, + "46": { + "currency_to_buy": "eur", "currency_to_sell": "czk", + "creation_date": now(), "status": "new", "amount": 60 + }, + "47": { + "currency_to_buy": "czk", "currency_to_sell": "eur", + "creation_date": now(), "status": "new", "amount": 1500 + }, + "48": { + "currency_to_buy": "eur", "currency_to_sell": "czk", + "creation_date": now(), "status": "new", "amount": 60 + }, + "49": { + "currency_to_buy": "czk", "currency_to_sell": "eur", + "creation_date": now(), "status": "new", "amount": 1500 + }, + "50": { + "currency_to_buy": "eur", "currency_to_sell": "czk", + "creation_date": now(), "status": "new", "amount": 60 + } + } + } +}; + +hash response = qrest.post("workflows/" + WORKFLOW_NAME + "?action=createOrder", ORDER_DATA); +printf("Response: %N\n", response); \ No newline at end of file diff --git a/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/README.md b/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/README.md new file mode 100644 index 0000000000000000000000000000000000000000..497ff6fdc51fbafd9dd41cbf6e3155ed47898abd --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/README.md @@ -0,0 +1,136 @@ +# Exchange Currency Workflow with Mapper + +## Mapper Introduction + +Qorus Mappers are high-level integration objects that providing a framework for runtime data transformations in Qorus interfaces. + +Mapper configurations are loaded into the database with oload and also must be explicitly associated with workflows, services, and jobs to be used in interface code. This way, the system knows (and can display in the system UI) which mappers are associated with which interfaces. + +When mappers are used at runtime, every mapper configuration generates a class descended from Mapper, and the basic configuration of Qorus mapper objects is based on the implementation of this base class (it's also possible to get an AbstractIterator object based on a mapper, but this is also based on the Mapper object generated by the mapper). + +Mappers have the following high-level attributes: + ++ *mapperid*: a unique ID for the mapper ++ *name*: the name of the mapper ++ *version*: the version of the mapper; multiple versions of a mapper can be present at any one time in the system, and interfaces must specify the mapper version that they use ++ *patch*: a patchlevel attribute that does not affect version compatibility ++ *desc*: a description of the mapper ++ *author*: the author of the mapper ++ *parse_options*: local parse options for the mapper ++ *type*: the type of mapper ++ *options*: a hash of mapper options, some mappers could require certain options ++ *fields*: a hash of mappings where the keys are output fields and the values describe how the output should be generated ++ *library objects*: a list of objects (functions, classes, and constants) that are imported into the mapper container Program + +Mappers are exposed through the REST API at the following URI path: @/api/mappers. + +## Implementing Mappers + +Mappers are developed in text files, loaded into the system schema with oload and participate in releases built with make-release. + +Each mapper has its own sandboxed Program container that contains the mapping logic and any library objects imported into it. + +Note that workflows, services, and jobs must declare mappers to be able to access them. The following api call should be used to acquire a mapper in your interface code: + +[getMapper()](https://qoretechnologies.com/manual/qorus/latest/qorus/classOMQ_1_1UserApi_1_1UserApi.html#aa661cce9be732bf43e001669f2ff75c9) +The return type of the above method will be compatible with Mapper::Mapper, but the exact type of object depends on the mapper type. + +Mappers can be created using the Qorus Developer Tools extension, which has strong support for mapper creation of any complexity. The IDE supports user-defined types, dataprovider API allowing to automatically introspect types from user connections, datasource connections and swagger schemas. Users are able to create complex mapping, where fields can be placed in a hierarchy. + +![alt-text](img/mapper.gif) + +### Mapper Files + +Mapper files defining new system mappers take the filename suffix: ".qmapper.yaml". + +As with function, class, constant objects, services, jobs, and value maps, mappers are defined by adding metadata tags in the YAML format. The tags are parsed by `oload`, which will create the mappers in the Qorus database according to the information in the file. + +### Exchange Currency Orders Mapper + +```yaml +# This is a generated file, don't edit! +type: mapper +name: bb-basics-exchange-currency-orders +desc: "Exchange Currency Orders Mapper" +author: + - Qore Technologies, s.r.o. +fields: + amount: + name: amount + creation_date: + context: '$timestamp:{YYYY-MM-DD HH:mm:SS.xx Z}' + currency_to_buy: + name: currency_to_buy + currency_to_sell: + name: currency_to_sell + id: + name: id + status: + constant: new +``` + +The full mapper example can be found in the same directory. It also defines options, where user defined input is described. + +In this example the mapper uses **"omquser"** as a datasource and **"exchange_rates"** table as an output table. The id field is filled from the same field of input data, but also it can be set using some existing sequence from the database. + +Information on how to implement Qorus Objects using YAML format can be found [here](https://qoretechnologies.com/manual/qorus/latest/qorus/implementingqorusobjectsusingyaml.html). + +The YAML schema used for validation of the workflow metadata can be downloaded [here](https://qoretechnologies.com/manual/qorus/latest/qorus/workflow_schema.yaml). + + +## Workflow Step Definition + +Metadata: +```yaml +# This is a generated file, don't edit! +type: step +name: BasicsExchangeRatesInsertToOrdersTableJava +desc: "Inserts orders from static data into orders table" +author: + - Qore Technologies, s.r.o. +base-class-name: QorusNormalStep +class-name: BasicsExchangeRatesInsertToOrdersTableJava +lang: java +version: "1.0" +steptype: NORMAL +code: BasicsExchangeRatesInsertToOrdersTableJava.java +``` + +Code: +```java +import qore.OMQ.UserApi.*; +import qore.OMQ.UserApi.Workflow.*; +import qore.Mapper.Mapper; + +import org.qore.jni.Hash; + +class BasicsExchangeRatesInsertToOrdersTableJava extends QorusNormalStep { + private final String mapperName = "bb-basics-exchange-currency-orders"; + + @SuppressWarnings("unchecked") + public void primary() throws Throwable { + Mapper mapper = getMapper(mapperName); + Hash orders = (Hash)getStaticData("orders"); + + try { + mapper.mapAll(orders); + } catch (Exception e) { + mapper.rollback(); + throw e; + } + + mapper.commit(); + } +} +``` + +Having the mapper described above it's not needed to use **getDatasourcePool()** method and insert data using **SqlUtil::AbstractTable** object. Instead of that, **getMapper()** method is used. Then **mapAll()** method is called with all orders from static data as an argument. + +--- + + + + + + +
[← Previous: Exchange currency workflow](../09_exchange_currency_workflow)[Next: Exchange currency API service →](../11_exchange_currency_api_service)
diff --git a/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/bb-basics-exchange-currency-orders-1.0.qmapper.yaml b/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/bb-basics-exchange-currency-orders-1.0.qmapper.yaml new file mode 100644 index 0000000000000000000000000000000000000000..7f747e4f3768c9807d08f4574e27fe1befa2c9c9 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/bb-basics-exchange-currency-orders-1.0.qmapper.yaml @@ -0,0 +1,163 @@ +# This is a generated file, don't edit! +type: mapper +name: bb-basics-exchange-currency-orders +desc: "Exchange Currency Orders Mapper" +author: + - Qore Technologies, s.r.o. +fields: + amount: + name: amount + creation_date: + context: '$timestamp:{YYYY-MM-DD HH:mm:SS.xx Z}' + currency_to_buy: + name: currency_to_buy + currency_to_sell: + name: currency_to_sell + id: + name: id + status: + constant: new +options: + mapper-input: + can_manage_fields: true + custom-fields: + amount: + canBeNull: false + desc: asd + firstCustomInHierarchy: true + isChild: false + isCustom: true + level: 0 + name: amount + parentPath: false + path: amount + type: + base_type: number + can_manage_fields: false + fields: {} + mandatory: true + name: number + options: + qore.no_null: true + supported_options: + qore.no_null: + desc: >- + if True then NULL is not supported on input if + NOTHING is also not accepted + type: bool + typename: number + types_accepted: + - number + - float + - int + types_returned: + - number + currency_to_buy: + canBeNull: false + desc: currency to buy + firstCustomInHierarchy: true + isChild: false + isCustom: true + level: 0 + name: currency_to_buy + parentPath: false + path: currency_to_buy + type: + base_type: string + can_manage_fields: false + fields: {} + mandatory: true + name: string + options: + qore.no_null: true + supported_options: + qore.no_null: + desc: >- + if True then NULL is not supported on input if + NOTHING is also not accepted + type: bool + string.encoding: + desc: the output encoding when writing to the type + type: string + string.max_size_chars: + desc: the maximum length of the string in characters + type: integer + typename: string + types_accepted: + - string + types_returned: + - string + currency_to_sell: + canBeNull: false + desc: currency to sell + firstCustomInHierarchy: true + isChild: false + isCustom: true + level: 0 + name: currency_to_sell + parentPath: false + path: currency_to_sell + type: + base_type: string + can_manage_fields: false + fields: {} + mandatory: true + name: string + options: + qore.no_null: true + supported_options: + qore.no_null: + desc: >- + if True then NULL is not supported on input if + NOTHING is also not accepted + type: bool + string.encoding: + desc: the output encoding when writing to the type + type: string + string.max_size_chars: + desc: the maximum length of the string in characters + type: integer + typename: string + types_accepted: + - string + types_returned: + - string + id: + canBeNull: false + desc: id of the order + firstCustomInHierarchy: true + isChild: false + isCustom: true + level: 0 + name: id + parentPath: false + path: id + type: + base_type: int + can_manage_fields: false + fields: {} + mandatory: true + name: int + options: + qore.no_null: true + supported_options: + qore.no_null: + desc: >- + if True then NULL is not supported on input if + NOTHING is also not accepted + type: bool + typename: int + types_accepted: + - int + types_returned: + - int + name: qore + path: /hash + type: type + mapper-output: + custom-fields: {} + name: omquser + path: /exchange_orders + type: datasource +mappertype: Mapper +version: "1.0" diff --git a/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/img/mapper.gif b/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/img/mapper.gif new file mode 100644 index 0000000000000000000000000000000000000000..4d5d84c2c3eac5e54d671fe9b168ea316a554e14 Binary files /dev/null and b/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/img/mapper.gif differ diff --git a/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/java/BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW-MAPPER-JAVA-1.0.qwf.yaml b/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/java/BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW-MAPPER-JAVA-1.0.qwf.yaml new file mode 100644 index 0000000000000000000000000000000000000000..c854a5e05bb3cfaa00654ca295c751eab9524e26 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/java/BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW-MAPPER-JAVA-1.0.qwf.yaml @@ -0,0 +1,18 @@ +# This is a generated file, don't edit! +type: workflow +name: BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW-MAPPER-JAVA +desc: Exchange currency workflow with mapper +author: + - Qore Technologies, s.r.o. +autostart: 1 +groups: + - BASIC-TRAINING-EXCHANGE-APP +keylist: + - id +mappers: + - bb-basics-exchange-currency-orders:1.0 +version: "1.0" +steps: + [ + "BB_BasicsExchangeRatesInsertToOrdersTableMapperJava:1.0" + ] diff --git a/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/java/BB_BasicsExchangeRatesInsertToOrdersTableMapperJava-1.0.qstep.yaml b/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/java/BB_BasicsExchangeRatesInsertToOrdersTableMapperJava-1.0.qstep.yaml new file mode 100644 index 0000000000000000000000000000000000000000..34b2713d7a58712265677ef4d724eafae1c3397e --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/java/BB_BasicsExchangeRatesInsertToOrdersTableMapperJava-1.0.qstep.yaml @@ -0,0 +1,12 @@ +# This is a generated file, don't edit! +type: step +name: BB_BasicsExchangeRatesInsertToOrdersTableMapperJava +desc: Inserts orders from static data into orders table +lang: java +author: + - Qore Technologies, s.r.o. +base-class-name: QorusNormalStep +class-name: BasicsExchangeRatesInsertToOrdersTableMapperJava +version: "1.0" +steptype: NORMAL +code: BB_BasicsExchangeRatesInsertToOrdersTableMapperJava.java diff --git a/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/java/BB_BasicsExchangeRatesInsertToOrdersTableMapperJava.java b/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/java/BB_BasicsExchangeRatesInsertToOrdersTableMapperJava.java new file mode 100644 index 0000000000000000000000000000000000000000..574357ca742408b45ae4e37b763a20e7b9b8d092 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/java/BB_BasicsExchangeRatesInsertToOrdersTableMapperJava.java @@ -0,0 +1,28 @@ +import qore.OMQ.*; +import qore.OMQ.UserApi.*; +import qore.OMQ.UserApi.Workflow.*; +import qore.Mapper.Mapper; + +class BasicsExchangeRatesInsertToOrdersTableMapperJava extends QorusNormalStep { + private final String mapperName = "bb-basics-exchange-currency-orders"; + + public BasicsExchangeRatesInsertToOrdersTableMapperJava() throws Throwable { + super(); + } + + @SuppressWarnings("unchecked") + public void primary() throws Throwable { + Mapper mapper = getMapper(mapperName); + Object orders = getStaticData("orders"); + + try { + logInfo("mapping java"); + mapper.mapAuto(orders); + } catch (Exception e) { + mapper.rollback(); + throw e; + } + + mapper.commit(); + } +} diff --git a/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/java/create_order-post_reload.qscript b/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/java/create_order-post_reload.qscript new file mode 100755 index 0000000000000000000000000000000000000000..37c9383ea18ed13170fe7f60e22ce419c9f4d0d0 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/java/create_order-post_reload.qscript @@ -0,0 +1,25 @@ +#!/usr/bin/env qore +%new-style +%strict-args +%require-types +%enable-all-warnings + +%requires QorusClientCore + +QorusClient::initFast(); + +const WORKFLOW_NAME = "BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW-MAPPER-JAVA"; + +const ORDER_DATA = { + "staticdata": { + "orders": { + "id": (7, 8), + "currency_to_buy": ("usd", "czk"), + "currency_to_sell": ("eur", "usd"), + "amount": (100, 600), + } + } +}; + +hash response = qrest.post("workflows/" + WORKFLOW_NAME + "?action=createOrder", ORDER_DATA); +printf("Response: %N\n", response); \ No newline at end of file diff --git a/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/python/BASICS-EXCHANGE-CURRENCY-WORKFLOW-MAPPER-PYTHON-1.0.qwf.yaml b/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/python/BASICS-EXCHANGE-CURRENCY-WORKFLOW-MAPPER-PYTHON-1.0.qwf.yaml new file mode 100644 index 0000000000000000000000000000000000000000..9b7922ea04b2a13cfa4268674a2f7d29328acacf --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/python/BASICS-EXCHANGE-CURRENCY-WORKFLOW-MAPPER-PYTHON-1.0.qwf.yaml @@ -0,0 +1,18 @@ +# This is a generated file, don't edit! +type: workflow +name: BASICS-EXCHANGE-CURRENCY-WORKFLOW-MAPPER-PYTHON +desc: Exchange currency workflow with mapper +version: "1.0" +author: + - Qore Technologies, s.r.o. +autostart: 1 +groups: + - BASIC-TRAINING-EXCHANGE-APP +keylist: + - id +mappers: + - bb-basics-exchange-currency-orders:1.0 +steps: + [ + "BasicsExchangeRatesInsertToOrdersTableMapperPython:1.0" + ] diff --git a/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/python/BasicsExchangeRatesInsertToOrdersTableMapperPython-1.0.qstep.py b/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/python/BasicsExchangeRatesInsertToOrdersTableMapperPython-1.0.qstep.py new file mode 100644 index 0000000000000000000000000000000000000000..6e592e81b20acab43518f22328227c96060d9690 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/python/BasicsExchangeRatesInsertToOrdersTableMapperPython-1.0.qstep.py @@ -0,0 +1,15 @@ +from wf import QorusNormalStep + +class BasicsExchangeRatesInsertToOrdersTableMapperPython(QorusNormalStep): + def primary(self): + mapper = self.getMapper("bb-basics-exchange-currency-orders") + + try: + orders = self.getStaticData("orders") + UserApi.logInfo("inserting data: %N", orders) + mapper.mapAll(orders) + except: + mapper.rollback() + raise + + mapper.commit() \ No newline at end of file diff --git a/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/python/BasicsExchangeRatesInsertToOrdersTableMapperPython-1.0.qstep.yaml b/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/python/BasicsExchangeRatesInsertToOrdersTableMapperPython-1.0.qstep.yaml new file mode 100644 index 0000000000000000000000000000000000000000..e7e92d4dfe4a4d98bf68f4198b2ab36022458e2b --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/python/BasicsExchangeRatesInsertToOrdersTableMapperPython-1.0.qstep.yaml @@ -0,0 +1,12 @@ +# This is a generated file, don't edit! +type: step +name: BasicsExchangeRatesInsertToOrdersTableMapperPython +desc: Inserts orders from static data into orders table +lang: python +base-class-name: QorusNormalStep +class-name: BasicsExchangeRatesInsertToOrdersTableMapperPython +version: "1.0" +author: + - Qore Technologies, s.r.o. +steptype: NORMAL +code: BasicsExchangeRatesInsertToOrdersTableMapperPython-1.0.qstep.py diff --git a/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/python/create_order-post_reload.qscript b/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/python/create_order-post_reload.qscript new file mode 100755 index 0000000000000000000000000000000000000000..d24a5dbe60d07fb6b47065f9bc3a78390f0c2cbb --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/python/create_order-post_reload.qscript @@ -0,0 +1,25 @@ +#!/usr/bin/env qore +%new-style +%strict-args +%require-types +%enable-all-warnings + +%requires QorusClientCore + +QorusClient::initFast(); + +const WORKFLOW_NAME = "BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW-MAPPER-PYTHON"; + +const ORDER_DATA = { + "staticdata": { + "orders": { + "id": (9, 10), + "currency_to_buy": ("usd", "czk"), + "currency_to_sell": ("eur", "usd"), + "amount": (100, 600), + } + } +}; + +hash response = qrest.post("workflows/" + WORKFLOW_NAME + "?action=createOrder", ORDER_DATA); +printf("Response: %N\n", response); \ No newline at end of file diff --git a/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/qore/BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW-MAPPER-1.0.qwf.yaml b/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/qore/BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW-MAPPER-1.0.qwf.yaml new file mode 100644 index 0000000000000000000000000000000000000000..89af83cc5f3a57823477fc10439c3d0f99b53092 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/qore/BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW-MAPPER-1.0.qwf.yaml @@ -0,0 +1,18 @@ +# This is a generated file, don't edit! +type: workflow +name: BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW-MAPPER +desc: Exchange currency workflow with mapper +author: + - Qore Technologies, s.r.o. +autostart: 1 +groups: + - BASIC-TRAINING-EXCHANGE-APP +keylist: + - id +mappers: + - bb-basics-exchange-currency-orders:1.0 +version: "1.0" +steps: + [ + "BB_BasicsExchangeRatesInsertToOrdersTableMapper:1.0" + ] diff --git a/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/qore/BB_BasicsExchangeRatesInsertToOrdersTableMapper-1.0.qstep b/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/qore/BB_BasicsExchangeRatesInsertToOrdersTableMapper-1.0.qstep new file mode 100644 index 0000000000000000000000000000000000000000..b85fc285ed55ee18bee150b79bbcbc7a371e35d1 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/qore/BB_BasicsExchangeRatesInsertToOrdersTableMapper-1.0.qstep @@ -0,0 +1,16 @@ +%new-style +%strict-args +%require-types +%enable-all-warnings + +class BasicsExchangeRatesInsertToOrdersTableMapper inherits QorusNormalStep { + primary() { + auto mapper = getMapper("bb-basics-exchange-currency-orders"); + on_error mapper.rollback(); + on_success mapper.commit(); + + hash orders = WorkflowApi::getStaticData("orders"); + logInfo("inserting data: %N", orders); + mapper.mapAll(orders); + } +} diff --git a/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/qore/BB_BasicsExchangeRatesInsertToOrdersTableMapper-1.0.qstep.yaml b/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/qore/BB_BasicsExchangeRatesInsertToOrdersTableMapper-1.0.qstep.yaml new file mode 100644 index 0000000000000000000000000000000000000000..dbf278e78b1a74c632221348ebd8b8d066956857 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/qore/BB_BasicsExchangeRatesInsertToOrdersTableMapper-1.0.qstep.yaml @@ -0,0 +1,12 @@ +# This is a generated file, don't edit! +type: step +name: BB_BasicsExchangeRatesInsertToOrdersTableMapper +desc: Inserts orders from static data into orders table +lang: qore +author: + - Qore Technologies, s.r.o. +base-class-name: QorusNormalStep +class-name: BasicsExchangeRatesInsertToOrdersTableMapper +version: "1.0" +steptype: NORMAL +code: BB_BasicsExchangeRatesInsertToOrdersTableMapper-1.0.qstep diff --git a/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/qore/create_order-post_reload.qscript b/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/qore/create_order-post_reload.qscript new file mode 100755 index 0000000000000000000000000000000000000000..95a1dd2634dc1e018dc0fde78a136ffa575b2d9b --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/10_exchange_currency_workflow_with_mapper/qore/create_order-post_reload.qscript @@ -0,0 +1,25 @@ +#!/usr/bin/env qore +%new-style +%strict-args +%require-types +%enable-all-warnings + +%requires QorusClientCore + +QorusClient::initFast(); + +const WORKFLOW_NAME = "BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW-MAPPER"; + +const ORDER_DATA = { + "staticdata": { + "orders": { + "id": (11, 12), + "currency_to_buy": ("usd", "czk"), + "currency_to_sell": ("eur", "usd"), + "amount": (100, 600), + } + } +}; + +hash response = qrest.post("workflows/" + WORKFLOW_NAME + "?action=createOrder", ORDER_DATA); +printf("Response: %N\n", response); \ No newline at end of file diff --git a/03_basics_building_blocks/01_exchange_rates_app/11_exchange_currency_api_service/README.md b/03_basics_building_blocks/01_exchange_rates_app/11_exchange_currency_api_service/README.md new file mode 100644 index 0000000000000000000000000000000000000000..d930c63690749f093b9a64b220aecfd874719f62 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/11_exchange_currency_api_service/README.md @@ -0,0 +1,46 @@ +# Exchange Currency API Service + +## Service Defintion + +This service exposes exchange method providing creation of exchange orders. The exchange method do nothing more then preparing order data and creating an order of **BASICS-EXCHANGE-CURRENCY-WORKFLOW**. It uses the `BBM_CreateOrder` building block, which allows users to implement workflow order creation logic without any need for coding. The building block inside calls the [**createOrder()**](https://qoretechnologies.com/manual/qorus/latest/qorus/classOMQ_1_1UserApi_1_1UserApi.html#a6cadda1e1395c915f157c661dec76d3a) method and passes the prepared order data to it. There is an `orders` key in the order data containing a list with one order received from a request. The order hash has the following format: + +``` +"id": integer, +"currency_to_buy": string, +"currency_to_sell": string, +"amount": float, +"comment": string +``` + +The service logic is implemented using `class-connections` (building block connections): + +![alt-text](img/class_connections.gif) + +As shown in the above animation one connection is created for connecting service's exchange method with the `createOrder` method of the building-block. Also the mapper that was created in the previous tutorial is used. + +The `CreateOrder` building block provides several configuration items in order to control its behaviour, at least the `create-workflow-name` configuration item should be set: + +![alt-text](img/config_items.gif) + +The mapper used to map the input to the workflow static data is `bb-exchange-currency-api-mapper-1.0.qmapper.yaml`, using output type `/qorus-api/workflows/create-order/request` which is what the `BBM_CreateOrder` building block requires. The mapper creates custom fields under the `staticdata` key to give structure to the workflow's static data, and also uses custom fields for the input corresponding to the data provided by the call to the `exchange()` method. Open the mapper in the IDE to check its structure. + +## Testing + +Once the service is loaded to Qorus it can be tested in Qorus UI. To do that, in the **services** tab by click on the **detail** button of the item containing **basics-simple-service** name. It'll show the details of the service. Then click on the **execute** button in the **methods** tab and fill the arguments as in the screenshots below: + +![alt-text](img/1.png) +![alt-text](img/2.png) + +Arguments to run service method from UI: +```php +id=6,currency_to_buy=czk,currency_to_sell=eur,amount=254 +``` + +--- + + + + + + +
[← Previous: Exchange currency workflow with mapper](../10_exchange_currency_workflow_with_mapper)[Next: Process orders workflow →](../12_process_orders_workflow)
diff --git a/03_basics_building_blocks/01_exchange_rates_app/11_exchange_currency_api_service/bb-exchange-currency-api-mapper-1.0.qmapper.yaml b/03_basics_building_blocks/01_exchange_rates_app/11_exchange_currency_api_service/bb-exchange-currency-api-mapper-1.0.qmapper.yaml new file mode 100644 index 0000000000000000000000000000000000000000..794296fbb4905ac7027eae1a1492fc7970f0105f --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/11_exchange_currency_api_service/bb-exchange-currency-api-mapper-1.0.qmapper.yaml @@ -0,0 +1,244 @@ +# This is a generated file, don't edit! +type: mapper +name: bb-exchange-currency-api-mapper +desc: prepares static data for BASICS-EXCHANGE-CURRENCY-WORKFLOW-MAPPER +author: + - Qore Technologies, s.r.o. +fields: + staticdata.orders.amount: + name: amount + staticdata.orders.currency_to_buy: + name: currency_to_buy + staticdata.orders.currency_to_sell: + name: currency_to_sell + staticdata.orders.id: + name: id +options: + mapper-input: + can_manage_fields: true + custom-fields: + amount: + canBeNull: false + desc: amount + firstCustomInHierarchy: true + isChild: false + isCustom: true + level: 0 + name: amount + parentPath: false + path: amount + type: + base_type: auto + can_manage_fields: true + fields: {} + mandatory: false + name: auto + options: null + supported_options: null + typename: any + types_accepted: + - any + types_returned: + - any + currency_to_buy: + canBeNull: false + desc: currency_to_buy + firstCustomInHierarchy: true + isChild: false + isCustom: true + level: 0 + name: currency_to_buy + parentPath: false + path: currency_to_buy + type: + base_type: auto + can_manage_fields: true + fields: {} + mandatory: false + name: auto + options: null + supported_options: null + typename: any + types_accepted: + - any + types_returned: + - any + currency_to_sell: + canBeNull: false + desc: currency_to_sell + firstCustomInHierarchy: true + isChild: false + isCustom: true + level: 0 + name: currency_to_sell + parentPath: false + path: currency_to_sell + type: + base_type: auto + can_manage_fields: true + fields: {} + mandatory: false + name: auto + options: null + supported_options: null + typename: any + types_accepted: + - any + types_returned: + - any + id: + canBeNull: false + desc: id + firstCustomInHierarchy: true + isChild: false + isCustom: true + level: 0 + name: id + parentPath: false + path: id + type: + base_type: auto + can_manage_fields: true + fields: {} + mandatory: false + name: auto + options: null + supported_options: null + typename: any + types_accepted: + - any + types_returned: + - any + name: qore + path: /hash + type: type + mapper-output: + can_manage_fields: false + custom-fields: + orders: + canBeNull: false + desc: orders + firstCustomInHierarchy: true + isChild: true + isCustom: true + level: 1 + name: orders + parent: staticdata + parentPath: staticdata + path: staticdata.orders + type: + base_type: hash + can_manage_fields: true + fields: + amount: + canBeNull: false + desc: amount + isChild: true + isCustom: true + level: 2 + name: amount + parent: orders + parentPath: staticdata.orders + path: staticdata.orders.amount + type: + base_type: auto + can_manage_fields: true + fields: {} + mandatory: false + name: auto + options: null + supported_options: null + typename: any + types_accepted: + - any + types_returned: + - any + currency_to_buy: + canBeNull: false + desc: currency_to_buy + isChild: true + isCustom: true + level: 2 + name: currency_to_buy + parent: orders + parentPath: staticdata.orders + path: staticdata.orders.currency_to_buy + type: + base_type: auto + can_manage_fields: true + fields: {} + mandatory: false + name: auto + options: null + supported_options: null + typename: any + types_accepted: + - any + types_returned: + - any + currency_to_sell: + canBeNull: false + desc: currency_to_sell + isChild: true + isCustom: true + level: 2 + name: currency_to_sell + parent: orders + parentPath: staticdata.orders + path: staticdata.orders.currency_to_sell + type: + base_type: auto + can_manage_fields: true + fields: {} + mandatory: false + name: auto + options: null + supported_options: null + typename: any + types_accepted: + - any + types_returned: + - any + id: + canBeNull: false + desc: id + isChild: true + isCustom: true + level: 2 + name: id + parent: orders + parentPath: staticdata.orders + path: staticdata.orders.id + type: + base_type: auto + can_manage_fields: true + fields: {} + mandatory: false + name: auto + options: null + supported_options: null + typename: any + types_accepted: + - any + types_returned: + - any + mandatory: true + name: hash + options: + qore.no_null: true + supported_options: + qore.no_null: + desc: >- + if True then NULL is not supported on input if + NOTHING is also not accepted + type: bool + typename: hash + types_accepted: + - hash + types_returned: + - hash + name: qoretechnologies + path: /qorus-api/workflows/create-order/request + type: type +mappertype: Mapper +version: "1.0" diff --git a/03_basics_building_blocks/01_exchange_rates_app/11_exchange_currency_api_service/img/1.png b/03_basics_building_blocks/01_exchange_rates_app/11_exchange_currency_api_service/img/1.png new file mode 100644 index 0000000000000000000000000000000000000000..3bdad87b64ba55d2fd1954b07c5d801fd1d878b5 Binary files /dev/null and b/03_basics_building_blocks/01_exchange_rates_app/11_exchange_currency_api_service/img/1.png differ diff --git a/03_basics_building_blocks/01_exchange_rates_app/11_exchange_currency_api_service/img/2.png b/03_basics_building_blocks/01_exchange_rates_app/11_exchange_currency_api_service/img/2.png new file mode 100644 index 0000000000000000000000000000000000000000..b0959d8046145b095730b6a0deec691df032e209 Binary files /dev/null and b/03_basics_building_blocks/01_exchange_rates_app/11_exchange_currency_api_service/img/2.png differ diff --git a/03_basics_building_blocks/01_exchange_rates_app/11_exchange_currency_api_service/img/class_connections.gif b/03_basics_building_blocks/01_exchange_rates_app/11_exchange_currency_api_service/img/class_connections.gif new file mode 100644 index 0000000000000000000000000000000000000000..fdcf0e45b355b8f4966767f9e0265df22dffe0f8 Binary files /dev/null and b/03_basics_building_blocks/01_exchange_rates_app/11_exchange_currency_api_service/img/class_connections.gif differ diff --git a/03_basics_building_blocks/01_exchange_rates_app/11_exchange_currency_api_service/img/config_items.gif b/03_basics_building_blocks/01_exchange_rates_app/11_exchange_currency_api_service/img/config_items.gif new file mode 100644 index 0000000000000000000000000000000000000000..b153c4a3d142d229ed0eee67e8448e74c98ac7bb Binary files /dev/null and b/03_basics_building_blocks/01_exchange_rates_app/11_exchange_currency_api_service/img/config_items.gif differ diff --git a/03_basics_building_blocks/01_exchange_rates_app/11_exchange_currency_api_service/java/basics_exchange_currency_api_service_java_1_0_service/BasicsExchangeCurrencyApiServiceJava.java b/03_basics_building_blocks/01_exchange_rates_app/11_exchange_currency_api_service/java/basics_exchange_currency_api_service_java_1_0_service/BasicsExchangeCurrencyApiServiceJava.java new file mode 100644 index 0000000000000000000000000000000000000000..932c254652ffaa80373947880086c6b5f38d0806 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/11_exchange_currency_api_service/java/basics_exchange_currency_api_service_java_1_0_service/BasicsExchangeCurrencyApiServiceJava.java @@ -0,0 +1,79 @@ +import qore.OMQ.*; +import qore.OMQ.UserApi.*; +import qore.OMQ.UserApi.Service.*; +import org.qore.jni.QoreJavaApi; +import org.qore.jni.QoreObject; +import java.util.Map; +import org.qore.jni.Hash; +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; +import qore.Mapper.Mapper; +import qore.BBM_CreateOrder; + +class BasicsExchangeCurrencyApiServiceJava extends QorusService { + // ==== GENERATED SECTION! DON'T EDIT! ==== // + ClassConnections_BasicsExchangeCurrencyApiServiceJava classConnections; + // ======== GENERATED SECTION END ========= // + + BasicsExchangeCurrencyApiServiceJava() throws Throwable { + // ==== GENERATED SECTION! DON'T EDIT! ==== // + classConnections = new ClassConnections_BasicsExchangeCurrencyApiServiceJava(); + // ======== GENERATED SECTION END ========= // + } + + // ==== GENERATED SECTION! DON'T EDIT! ==== // + public Object exchange(Object params) throws Throwable { + return classConnections.Connection_1(params); + } + // ======== GENERATED SECTION END ========= // +} + +// ==== GENERATED SECTION! DON'T EDIT! ==== // +class ClassConnections_BasicsExchangeCurrencyApiServiceJava { + // map of prefixed class names to class instances + private final Hash classMap; + + ClassConnections_BasicsExchangeCurrencyApiServiceJava() throws Throwable { + classMap = new Hash(); + UserApi.startCapturingObjectsFromJava(); + try { + classMap.put("BBM_CreateOrder", QoreJavaApi.newObjectSave("BBM_CreateOrder")); + } finally { + UserApi.stopCapturingObjectsFromJava(); + } + } + + Object callClassWithPrefixMethod(final String prefixedClass, final String methodName, + Object params) throws Throwable { + UserApi.logDebug("ClassConnections_BasicsExchangeCurrencyApiServiceJava: callClassWithPrefixMethod: method: %s class: %y", methodName, prefixedClass); + final Object object = classMap.get(prefixedClass); + + if (object instanceof QoreObject) { + QoreObject qoreObject = (QoreObject)object; + return qoreObject.callMethod(methodName, params); + } else { + final Method method = object.getClass().getMethod(methodName, Object.class); + try { + return method.invoke(object, params); + } catch (InvocationTargetException ex) { + throw ex.getCause(); + } + } + } + + public Object Connection_1(Object params) throws Throwable { + // convert varargs to a single argument if possible + if (params != null && params.getClass().isArray() && ((Object[])params).length == 1) { + params = ((Object[])params)[0]; + } + Mapper mapper; + UserApi.logDebug("Connection_1 called with data: %y", params); + + mapper = UserApi.getMapper("bb-exchange-currency-api-mapper"); + params = mapper.mapAuto(params); + + UserApi.logDebug("calling createOrder: %y", params); + return callClassWithPrefixMethod("BBM_CreateOrder", "createWorkflowOrderConnector", params); + } +} +// ======== GENERATED SECTION END ========= // diff --git a/03_basics_building_blocks/01_exchange_rates_app/11_exchange_currency_api_service/java/bb-basics-exchange-currency-api-service-java-1.0.qsd.yaml b/03_basics_building_blocks/01_exchange_rates_app/11_exchange_currency_api_service/java/bb-basics-exchange-currency-api-service-java-1.0.qsd.yaml new file mode 100644 index 0000000000000000000000000000000000000000..478a9761f8e35d035e94c0ae0206e7cabb96a4ec --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/11_exchange_currency_api_service/java/bb-basics-exchange-currency-api-service-java-1.0.qsd.yaml @@ -0,0 +1,59 @@ +# This is a generated file, don't edit! +type: service +name: bb-basics-exchange-currency-api-service-java +desc: Exchange currency API service +lang: java +author: + - Qore Technologies, s.r.o. +autostart: true +base-class-name: QorusService +class-name: BasicsExchangeCurrencyApiServiceJava +classes: + - BBM_CreateOrder +mappers: + - bb-exchange-currency-api-mapper:1.0 +version: '1.0' +servicetype: USER +code: basics_exchange_currency_api_service_java_1_0_service/BasicsExchangeCurrencyApiServiceJava.java +class-connections: + Connection_1: + - class: BBM_CreateOrder + connector: createOrder + mapper: bb-exchange-currency-api-mapper:1.0 + trigger: exchange +config-items: + - name: create-workflow-order-mapper + parent: + interface-type: class + interface-name: BBM_CreateOrder + interface-version: '1.0' + - name: create-workflow-name + value: + "BASICS-EXCHANGE-CURRENCY-WORKFLOW-MAPPER-JAVA" + parent: + interface-type: class + interface-name: BBM_CreateOrder + interface-version: '1.0' + - name: create-workflow-version + parent: + interface-type: class + interface-name: BBM_CreateOrder + interface-version: '1.0' + - name: create-workflow-duplicate-handling + parent: + interface-type: class + interface-name: BBM_CreateOrder + interface-version: '1.0' + - name: create-workflow-output-data + parent: + interface-type: class + interface-name: BBM_CreateOrder + interface-version: '1.0' + - name: create-workflow-order-status + parent: + interface-type: class + interface-name: BBM_CreateOrder + interface-version: '1.0' +methods: + - name: exchange + desc: exchange method diff --git a/03_basics_building_blocks/01_exchange_rates_app/11_exchange_currency_api_service/python/bb-basics-exchange-currency-api-service-python-1.0.qsd.py b/03_basics_building_blocks/01_exchange_rates_app/11_exchange_currency_api_service/python/bb-basics-exchange-currency-api-service-python-1.0.qsd.py new file mode 100644 index 0000000000000000000000000000000000000000..b5893b0f09b952477189857dc7db0b958d417ef1 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/11_exchange_currency_api_service/python/bb-basics-exchange-currency-api-service-python-1.0.qsd.py @@ -0,0 +1,35 @@ +from svc import QorusService +from qore.__root__ import BBM_CreateOrder + +class BasicsExchangeCurrencyApiServicePython(QorusService): + def __init__(self): + ####### GENERATED SECTION! DON'T EDIT! ######## + self.class_connections = ClassConnections_BasicsExchangeCurrencyApiServicePython() + ############ GENERATED SECTION END ############ + + ####### GENERATED SECTION! DON'T EDIT! ######## + def exchange(self, *params): + return self.class_connections.Connection_1(*params) + ############ GENERATED SECTION END ############ + +####### GENERATED SECTION! DON'T EDIT! ######## +class ClassConnections_BasicsExchangeCurrencyApiServicePython: + def __init__(self): + UserApi.startCapturingObjectsFromPython() + # map of prefixed class names to class instances + self.class_map = { + 'BBM_CreateOrder': BBM_CreateOrder(), + } + UserApi.stopCapturingObjectsFromPython() + + def callClassWithPrefixMethod(self, prefixed_class, method, *argv): + UserApi.logDebug("ClassConnections_BasicsExchangeCurrencyApiServicePython: callClassWithPrefixMethod: method: %s, class: %y", method, prefixed_class) + return getattr(self.class_map[prefixed_class], method)(*argv) + + def Connection_1(self, *params): + UserApi.logDebug("Connection_1 called with data: %y", *params) + mapper = UserApi.getMapper("bb-exchange-currency-api-mapper") + params = mapper.mapAuto(params) + UserApi.logDebug("calling createOrder: %y", *params) + return self.callClassWithPrefixMethod("BBM_CreateOrder", "createWorkflowOrderConnector", *params) +############ GENERATED SECTION END ############ diff --git a/03_basics_building_blocks/01_exchange_rates_app/11_exchange_currency_api_service/python/bb-basics-exchange-currency-api-service-python-1.0.qsd.yaml b/03_basics_building_blocks/01_exchange_rates_app/11_exchange_currency_api_service/python/bb-basics-exchange-currency-api-service-python-1.0.qsd.yaml new file mode 100644 index 0000000000000000000000000000000000000000..e8ab850af35d09ecff1e28d60975e85efd10db95 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/11_exchange_currency_api_service/python/bb-basics-exchange-currency-api-service-python-1.0.qsd.yaml @@ -0,0 +1,61 @@ +# This is a generated file, don't edit! +type: service +name: bb-basics-exchange-currency-api-service-python +desc: Exchange currency API service +lang: python +author: + - Qore Technologies, s.r.o. +autostart: true +base-class-name: QorusService +class-name: BasicsExchangeCurrencyApiServicePython +classes: + - BBM_CreateOrder +groups: + - BASIC-TRAINING-EXCHANGE-APP +mappers: + - bb-exchange-currency-api-mapper:1.0 +version: "1.0" +servicetype: USER +code: bb-basics-exchange-currency-api-service-python-1.0.qsd.py +class-connections: + Connection_1: + - class: BBM_CreateOrder + connector: createOrder + mapper: bb-exchange-currency-api-mapper:1.0 + trigger: exchange +config-items: + - name: create-workflow-order-mapper + parent: + interface-type: class + interface-name: BBM_CreateOrder + interface-version: "1.0" + - name: create-workflow-name + value: + "BASICS-EXCHANGE-CURRENCY-WORKFLOW-MAPPER-PYTHON" + parent: + interface-type: class + interface-name: BBM_CreateOrder + interface-version: "1.0" + - name: create-workflow-version + parent: + interface-type: class + interface-name: BBM_CreateOrder + interface-version: "1.0" + - name: create-workflow-duplicate-handling + parent: + interface-type: class + interface-name: BBM_CreateOrder + interface-version: "1.0" + - name: create-workflow-output-data + parent: + interface-type: class + interface-name: BBM_CreateOrder + interface-version: "1.0" + - name: create-workflow-order-status + parent: + interface-type: class + interface-name: BBM_CreateOrder + interface-version: "1.0" +methods: + - name: exchange + desc: exchange diff --git a/03_basics_building_blocks/01_exchange_rates_app/11_exchange_currency_api_service/qore/bb-basics-exchange-currency-api-service-1.0.qsd b/03_basics_building_blocks/01_exchange_rates_app/11_exchange_currency_api_service/qore/bb-basics-exchange-currency-api-service-1.0.qsd new file mode 100644 index 0000000000000000000000000000000000000000..257c7d6de24dd5739ca425e0b559b1cad44d4442 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/11_exchange_currency_api_service/qore/bb-basics-exchange-currency-api-service-1.0.qsd @@ -0,0 +1,49 @@ +%new-style +%strict-args +%require-types +%enable-all-warnings + +class BasicsExchangeCurrencyApiService inherits QorusService { + private { + ####### GENERATED SECTION! DON'T EDIT! ######## + ClassConnections_BasicsExchangeCurrencyApiService class_connections(); + ############ GENERATED SECTION END ############ + } + + ####### GENERATED SECTION! DON'T EDIT! ######## + auto exchange(auto params) { + return class_connections.Connection_1(params); + } + ############ GENERATED SECTION END ############ +} + +####### GENERATED SECTION! DON'T EDIT! ######## +class ClassConnections_BasicsExchangeCurrencyApiService { + private { + # map of prefixed class names to class instances + hash class_map; + } + + constructor() { + class_map = { + "BBM_CreateOrder": new BBM_CreateOrder(), + }; + } + + auto callClassWithPrefixMethod(string prefixed_class, string method) { + UserApi::logDebug("ClassConnections_BasicsExchangeCurrencyApiService: callClassWithPrefixMethod: method: %s, class: %y", method, prefixed_class); + return call_object_method_args(class_map{prefixed_class}, method, argv); + } + + auto Connection_1(auto params) { + auto mapper; + UserApi::logDebug("Connection_1 called with data: %y", params); + + mapper = UserApi::getMapper("bb-exchange-currency-api-mapper"); + params = mapper.mapAuto(params); + + UserApi::logDebug("calling createOrder: %y", params); + return callClassWithPrefixMethod("BBM_CreateOrder", "createWorkflowOrderConnector", params); + } +} +############ GENERATED SECTION END ############ diff --git a/03_basics_building_blocks/01_exchange_rates_app/11_exchange_currency_api_service/qore/bb-basics-exchange-currency-api-service-1.0.qsd.yaml b/03_basics_building_blocks/01_exchange_rates_app/11_exchange_currency_api_service/qore/bb-basics-exchange-currency-api-service-1.0.qsd.yaml new file mode 100644 index 0000000000000000000000000000000000000000..fa05aba041153c8f6cfdf9eba83b197afa6619c0 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/11_exchange_currency_api_service/qore/bb-basics-exchange-currency-api-service-1.0.qsd.yaml @@ -0,0 +1,61 @@ +# This is a generated file, don't edit! +type: service +name: bb-basics-exchange-currency-api-service +desc: Exchange currency API service +lang: qore +author: + - Qore Technologies, s.r.o. +autostart: true +base-class-name: QorusService +class-name: BasicsExchangeCurrencyApiService +classes: + - BBM_CreateOrder +groups: + - BASIC-TRAINING-EXCHANGE-APP +mappers: + - bb-exchange-currency-api-mapper:1.0 +version: "1.0" +servicetype: USER +code: bb-basics-exchange-currency-api-service-1.0.qsd +class-connections: + Connection_1: + - class: BBM_CreateOrder + connector: createOrder + mapper: bb-exchange-currency-api-mapper:1.0 + trigger: exchange +config-items: + - name: create-workflow-order-mapper + parent: + interface-type: class + interface-name: BBM_CreateOrder + interface-version: "1.0" + - name: create-workflow-name + value: + "BASICS-EXCHANGE-CURRENCY-WORKFLOW-MAPPER" + parent: + interface-type: class + interface-name: BBM_CreateOrder + interface-version: "1.0" + - name: create-workflow-version + parent: + interface-type: class + interface-name: BBM_CreateOrder + interface-version: "1.0" + - name: create-workflow-duplicate-handling + parent: + interface-type: class + interface-name: BBM_CreateOrder + interface-version: "1.0" + - name: create-workflow-output-data + parent: + interface-type: class + interface-name: BBM_CreateOrder + interface-version: "1.0" + - name: create-workflow-order-status + parent: + interface-type: class + interface-name: BBM_CreateOrder + interface-version: "1.0" +methods: + - name: exchange + desc: exchange diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/README.md b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/README.md new file mode 100644 index 0000000000000000000000000000000000000000..45d6bb675cd87f2f3b508bc6967cedde1eabc8f5 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/README.md @@ -0,0 +1,58 @@ +# Process Orders Workflow + +## Workflow Steps + +```yaml +steps: + [ + "BasicsChangeOrderStatusToInProgress:1.0", + "BasicsSendMoneyToBank:1.0", + "BasicsSendMoneyToUser:1.0", + "BasicsSendEmailToUser:1.0", + "BasicsChangeOrderStatusToCompleted:1.0" + ] +``` + +![alt-text](img/1.png) + +This workflow consists of 5 steps: + ++ The first step **BasicsChangeOrderStatusToInProgress** changes order status to in progress. This step uses the **changeStatus()** method of the **BasicsChangeOrderStatus** class, which will be loaded into the workflow's program space once the workflow is loaded to Qorus. This class is defined in the workflow definition file as following: +```yaml +classes: + - BasicsChangeOrderStatus +``` + ++ The second step **BasicsSendMoneyToBank** simulates money transaction to bank. It calculates amount that should be sent based on given exchange rates when order was created. + ++ The third step **BasicsSendMoneyToUser** just logs the amount user wanted to buy intially. + ++ The fourth step **BasicsSendEmailToUser** uses the SmtpClient module to send a notification email to a user. [SmtpClient](https://qoretechnologies.com/manual/qorus/latest/qore/modules/SmtpClient/html/class_smtp_client_1_1_smtp_client.html) object is obtained by [**getUserConnection()**](https://qoretechnologies.com/manual/qorus/latest/qorus/classOMQ_1_1UserApi_1_1UserApi.html#a6b6cf455c01fc92c3176074faa85a202) method. The name of the user connection is [**smtp-email**](#smtp-email-user-connection) (setup is described below). + ++ The fifth **BasicsChangeOrderStatusToCompleted** is called once the **BasicsSendEmailToUser** step is in completed state. This step also uses **changeStatus()** method of the **BasicsChangeOrderStatus** class to change the status of orders to completed. + +## Smtp Email User Connection + +Add the emal user connection to Qorus using the *smtp-email.qconn.yaml* file: +```yaml +type: connection +name: smtp-email +desc: SMTP email connection +url: esmtptls://test.qorus:QQQ1234qqq!!!@smtp.gmail.com +``` + +The url is in the following format: +```php +esmtptls://username:password@smtp.gmail.com +``` + +In this example test email is used and can be used for testing purposes only. [Read more about user connections from 6 tutorial](../06_exchange_api_user_connection). + +--- + + + + + + +
[← Previous: Exchange currency API service](../11_exchange_currency_api_service)[Next: Process orders job →](../13_process_orders_job)
diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/img/1.png b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/img/1.png new file mode 100644 index 0000000000000000000000000000000000000000..d2ed5b23616651d6ce03afb81ee286a900957c1e Binary files /dev/null and b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/img/1.png differ diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/java/BB-BASICS-PROCESS-ORDERS-WORKFLOW-JAVA-1.0.yaml b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/java/BB-BASICS-PROCESS-ORDERS-WORKFLOW-JAVA-1.0.yaml new file mode 100644 index 0000000000000000000000000000000000000000..2e0293eb2a997f22bfde5cde8ca4861e8d0eacf3 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/java/BB-BASICS-PROCESS-ORDERS-WORKFLOW-JAVA-1.0.yaml @@ -0,0 +1,22 @@ +# This is a generated file, don't edit! +type: workflow +name: BB-BASICS-PROCESS-ORDERS-WORKFLOW-JAVA +desc: "Processes given orders" +author: + - Qore Technologies, s.r.o. +classes: + - BB_BasicsChangeOrderStatusJava +groups: + - BASIC-TRAINING-EXCHANGE-APP +keylist: + - ids +steps: + [ + "BB_BasicsChangeOrderStatusToInProgressJava:1.0", + "BB_BasicsSendMoneyToBankJava:1.0", + "BB_BasicsSendMoneyToUserJava:1.0", + "BB_BasicsSendEmailToUserJava:1.0", + "BB_BasicsChangeOrderStatusToCompletedJava:1.0" + ] +version: "1.0" +autostart: 1 diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/java/create_order-post_reload.qscript b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/java/create_order-post_reload.qscript new file mode 100755 index 0000000000000000000000000000000000000000..23b74a28c6b75e1e8ae5be059164df1e377bcb85 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/java/create_order-post_reload.qscript @@ -0,0 +1,34 @@ +#!/usr/bin/env qore +%new-style +%strict-args +%require-types +%enable-all-warnings + +%requires QorusClientCore + +QorusClient::initFast(); + +const WORKFLOW_NAME = "BB-BASICS-PROCESS-ORDERS-WORKFLOW-JAVA:1.0"; + +const ORDER_DATA = { + "staticdata": { + "orders": { + "10": { + "currency_to_buy": "USD", "currency_to_sell": "EUR", "amount": 100.0, + "user_email": "your@email.com" + }, + "11": { + "currency_to_buy": "CZK", "currency_to_sell": "EUR", "amount": 600.0, + "user_email": "your@email.com" + } + }, + "exchange_rates": { + "EUR": 1.0, + "CZK": 25.0, + "USD": 22.0 + } + } +}; + +hash response = qrest.post("workflows/" + WORKFLOW_NAME + "?action=createOrder", ORDER_DATA); +printf("Response: %N\n", response); \ No newline at end of file diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/java/lib/BB_BasicsChangeOrderStatusJava-1.0.qclass.yaml b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/java/lib/BB_BasicsChangeOrderStatusJava-1.0.qclass.yaml new file mode 100644 index 0000000000000000000000000000000000000000..c29f71876ddc964b40c4cf27a9612e8cf00eac34 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/java/lib/BB_BasicsChangeOrderStatusJava-1.0.qclass.yaml @@ -0,0 +1,10 @@ +# This is a generated file, don't edit! +type: class +desc: "Changes the status of orders" +author: + - Qore Technologies, s.r.o. +name: BB_BasicsChangeOrderStatusJava +class-name: BB_BasicsChangeOrderStatusJava +lang: java +version: "1.0" +code: BB_BasicsChangeOrderStatusJava.java diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/java/lib/BB_BasicsChangeOrderStatusJava.java b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/java/lib/BB_BasicsChangeOrderStatusJava.java new file mode 100644 index 0000000000000000000000000000000000000000..bbb20573b04d0e2687a540babc337b74a1d8b272 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/java/lib/BB_BasicsChangeOrderStatusJava.java @@ -0,0 +1,30 @@ +import qore.OMQ.*; +import qore.OMQ.UserApi.*; +import qore.OMQ.UserApi.Workflow.*; + +import qore.SqlUtil.AbstractTable; +import qore.Qore.SQL.AbstractDatasource; + +import org.qore.jni.Hash; + +public class BB_BasicsChangeOrderStatusJava extends WorkflowApi { + public BasicsChangeOrderStatusJava() throws Throwable { + super(); + } + + @SuppressWarnings("unchecked") + public static void changeStatus(AbstractDatasource ds, String status) throws Throwable { + Hash orders = (Hash)getStaticData("orders"); + AbstractTable table = getSqlTable(ds, "exchange_orders"); + + Hash updateMap = new Hash(); + updateMap.put("status", status); + + for (String key : orders.keySet()) { + Hash cond = new Hash(); + cond.put("id", Integer.parseInt(key)); + + table.update(updateMap, cond); + } + } +} diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/java/steps/BB_BasicsChangeOrderStatusToCompletedJava-1.0.qstep.yaml b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/java/steps/BB_BasicsChangeOrderStatusToCompletedJava-1.0.qstep.yaml new file mode 100644 index 0000000000000000000000000000000000000000..816e985dde529a2e068046bca4e78bd2cc991e17 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/java/steps/BB_BasicsChangeOrderStatusToCompletedJava-1.0.qstep.yaml @@ -0,0 +1,14 @@ +# This is a generated file, don't edit! +type: step +name: BB_BasicsChangeOrderStatusToCompletedJava +desc: "Changes the status of orders to completed" +author: + - Qore Technologies, s.r.o. +base-class-name: QorusNormalStep +class-name: BasicsChangeOrderStatusToCompletedJava +classes: + - BasicsChangeOrderStatusJava +lang: java +version: "1.0" +steptype: NORMAL +code: BasicsChangeOrderStatusToCompletedJava.java diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/java/steps/BB_BasicsChangeOrderStatusToInProgressJava-1.0.qstep.yaml b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/java/steps/BB_BasicsChangeOrderStatusToInProgressJava-1.0.qstep.yaml new file mode 100644 index 0000000000000000000000000000000000000000..80ef91b0fcfe900200d092279e16714efcfe242e --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/java/steps/BB_BasicsChangeOrderStatusToInProgressJava-1.0.qstep.yaml @@ -0,0 +1,14 @@ +# This is a generated file, don't edit! +type: step +name: BB_BasicsChangeOrderStatusToInProgressJava +desc: "Changes the status of orders to in progress" +author: + - Qore Technologies, s.r.o. +base-class-name: QorusNormalStep +class-name: BasicsChangeOrderStatusToInProgressJava +classes: + - BasicsChangeOrderStatusJava +lang: java +version: "1.0" +steptype: NORMAL +code: BasicsChangeOrderStatusToInProgressJava.java diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/java/steps/BB_BasicsSendEmailToUserJava-1.0.qstep.yaml b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/java/steps/BB_BasicsSendEmailToUserJava-1.0.qstep.yaml new file mode 100644 index 0000000000000000000000000000000000000000..a84ab07ca837373dc7246200e50eeee38bc4b826 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/java/steps/BB_BasicsSendEmailToUserJava-1.0.qstep.yaml @@ -0,0 +1,12 @@ +# This is a generated file, don't edit! +type: step +name: BB_BasicsSendEmailToUserJava +desc: "Sends email to user" +author: + - Qore Technologies, s.r.o. +base-class-name: QorusNormalStep +class-name: BasicsSendEmailToUserJava +lang: java +version: "1.0" +steptype: NORMAL +code: BasicsSendEmailToUserJava.java diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/java/steps/BB_BasicsSendMoneyToBankJava-1.0.qstep.yaml b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/java/steps/BB_BasicsSendMoneyToBankJava-1.0.qstep.yaml new file mode 100644 index 0000000000000000000000000000000000000000..c97769f93e852136f87daeb69b25c02acf0b1132 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/java/steps/BB_BasicsSendMoneyToBankJava-1.0.qstep.yaml @@ -0,0 +1,20 @@ +# This is a generated file, don't edit! +type: step +name: BB_BasicsSendMoneyToBankJava +desc: "Sends money to bank" +author: + - Qore Technologies, s.r.o. +base-class-name: QorusNormalStep +class-name: BasicsSendMoneyToBankJava +lang: java +version: "1.0" +steptype: NORMAL +code: BasicsSendMoneyToBankJava.java +config-items: + - name: bank-commission + default_value: + 0.02 + config_group: Default + description: "Bank commission" + strictly_local: true + type: float diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/java/steps/BB_BasicsSendMoneyToUserJava-1.0.qstep.yaml b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/java/steps/BB_BasicsSendMoneyToUserJava-1.0.qstep.yaml new file mode 100644 index 0000000000000000000000000000000000000000..ee3c6acfc9d1eb954ceee573395a6dcf01476683 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/java/steps/BB_BasicsSendMoneyToUserJava-1.0.qstep.yaml @@ -0,0 +1,12 @@ +# This is a generated file, don't edit! +type: step +name: BB_BasicsSendMoneyToUserJava +desc: "Sends money to user" +author: + - Qore Technologies, s.r.o. +base-class-name: QorusNormalStep +class-name: BasicsSendMoneyToUserJava +lang: java +version: "1.0" +steptype: NORMAL +code: BasicsSendMoneyToUserJava.java diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/java/steps/BasicsChangeOrderStatusToCompletedJava.java b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/java/steps/BasicsChangeOrderStatusToCompletedJava.java new file mode 100644 index 0000000000000000000000000000000000000000..529afbda002e20afbcc2a998eb4009dc869f9f45 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/java/steps/BasicsChangeOrderStatusToCompletedJava.java @@ -0,0 +1,23 @@ +import qore.OMQ.*; +import qore.OMQ.UserApi.*; +import qore.OMQ.UserApi.Workflow.*; + +import qore.Qore.SQL.AbstractDatasource; + +class BasicsChangeOrderStatusToCompletedJava extends QorusNormalStep { + public BasicsChangeOrderStatusToCompletedJava() throws Throwable { + super(); + } + + public void primary() throws Throwable { + AbstractDatasource ds = getDatasourcePool("omquser"); + + try { + BasicsChangeOrderStatusJava.changeStatus(ds, "completed"); + } catch (Exception e) { + ds.rollback(); + throw e; + } + ds.commit(); + } +} diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/java/steps/BasicsChangeOrderStatusToInProgressJava.java b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/java/steps/BasicsChangeOrderStatusToInProgressJava.java new file mode 100644 index 0000000000000000000000000000000000000000..94486457968ae65a5d56506e4e7c1429f843dfbc --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/java/steps/BasicsChangeOrderStatusToInProgressJava.java @@ -0,0 +1,23 @@ +import qore.OMQ.*; +import qore.OMQ.UserApi.*; +import qore.OMQ.UserApi.Workflow.*; + +import qore.Qore.SQL.AbstractDatasource; + +class BasicsChangeOrderStatusToInProgressJava extends QorusNormalStep { + public BasicsChangeOrderStatusToInProgressJava() throws Throwable { + super(); + } + + public void primary() throws Throwable { + AbstractDatasource ds = getDatasourcePool("omquser"); + + try { + BasicsChangeOrderStatusJava.changeStatus(ds, "in-progress"); + } catch (Exception e) { + ds.rollback(); + throw e; + } + ds.commit(); + } +} diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/java/steps/BasicsSendEmailToUserJava.java b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/java/steps/BasicsSendEmailToUserJava.java new file mode 100644 index 0000000000000000000000000000000000000000..f230461f1d3a22e2ad947abbc469e964a5db52f0 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/java/steps/BasicsSendEmailToUserJava.java @@ -0,0 +1,35 @@ +import qore.OMQ.*; +import qore.OMQ.UserApi.*; +import qore.OMQ.UserApi.Workflow.*; + +import qoremod.SmtpClient.SmtpClient; +import qoremod.MailMessage.Message; + +import org.qore.jni.Hash; + + +class BasicsSendEmailToUserJava extends QorusNormalStep { + public BasicsSendEmailToUserJava() throws Throwable { + super(); + } + + @SuppressWarnings("unchecked") + public void primary() throws Throwable { + Hash orders = (Hash)getStaticData("orders"); +/* + SmtpClient smtp = (SmtpClient)getUserConnection("smtp-email"); + + for (String key : orders.keySet()) { + Hash order = orders.getHash(key); + String userEmail = order.getString("user_email"); + logInfo("Sending email to %s", userEmail); + + Message message = new Message("test.qorus@gmail.com", "Order details"); + String text = "You have received " + String.valueOf(order.getDouble("amount")) + " " + order.getString("currency_to_buy"); + message.setBody(text); + message.addTO(userEmail); + smtp.sendMessage(message); + } +*/ + } +} diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/java/steps/BasicsSendMoneyToBankJava.java b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/java/steps/BasicsSendMoneyToBankJava.java new file mode 100644 index 0000000000000000000000000000000000000000..a7f31addc907d2bc73a3433091336756163f0d42 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/java/steps/BasicsSendMoneyToBankJava.java @@ -0,0 +1,42 @@ +import qore.OMQ.*; +import qore.OMQ.UserApi.*; +import qore.OMQ.UserApi.Workflow.*; + +import org.qore.jni.Hash; + +class BasicsSendMoneyToBankJava extends QorusNormalStep { + public BasicsSendMoneyToBankJava() throws Throwable { + super(); + } + + @SuppressWarnings("unchecked") + public void primary() throws Throwable { + Hash staticData = (Hash)getStaticData(); + Hash orders = (Hash)staticData.get("orders"); + Hash exchangeRates = (Hash)staticData.get("exchange_rates"); + + for (String key : orders.keySet()) { + Hash order = orders.getHash(key); + String currencyToBuy = order.getString("currency_to_buy"); + if (!exchangeRates.containsKey(currencyToBuy)) { + throw new Exception("BUSINESS-ERROR: exchange rate for " + currencyToBuy + " is missing"); + } + + String currencyToSell = order.getString("currency_to_sell"); + if (!exchangeRates.containsKey(currencyToSell)) { + throw new Exception("BUSINESS-ERROR: exchange rate for " + currencyToSell + " is missing"); + } + + double rate = exchangeRates.getDouble(currencyToBuy); + logInfo("rate: %y", rate); + + double amount = order.getDouble("amount"); + double sellRate = exchangeRates.getDouble(currencyToSell); + double amountToSend = (amount / rate) * sellRate; + + double commission = (double)getConfigItemValue("bank-commission"); + amountToSend += amountToSend * commission; + logInfo("Money sent to bank: %f %s", amountToSend, currencyToSell); + } + } +} diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/java/steps/BasicsSendMoneyToUserJava.java b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/java/steps/BasicsSendMoneyToUserJava.java new file mode 100644 index 0000000000000000000000000000000000000000..5b1adffa2dac2bf598edd666adbd14738f5ef00c --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/java/steps/BasicsSendMoneyToUserJava.java @@ -0,0 +1,22 @@ +import qore.OMQ.*; +import qore.OMQ.UserApi.*; +import qore.OMQ.UserApi.Workflow.*; + +import org.qore.jni.Hash; + +class BasicsSendMoneyToUserJava extends QorusNormalStep { + public BasicsSendMoneyToUserJava() throws Throwable { + super(); + } + + @SuppressWarnings("unchecked") + public void primary() throws Throwable { + Hash orders = (Hash)getStaticData("orders"); + + for (String key : orders.keySet()) { + Hash order = orders.getHash(key); + logInfo("Money sent to user: %d %s", String.valueOf(order.getDouble("amount")), + order.getString("currency_to_buy")); + } + } +} diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/python/BB-BASICS-PROCESS-ORDERS-WORKFLOW-PYTHON-1.0.qwf.yaml b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/python/BB-BASICS-PROCESS-ORDERS-WORKFLOW-PYTHON-1.0.qwf.yaml new file mode 100644 index 0000000000000000000000000000000000000000..080abd53a663c6aedbf017bd5694505d041cdce3 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/python/BB-BASICS-PROCESS-ORDERS-WORKFLOW-PYTHON-1.0.qwf.yaml @@ -0,0 +1,22 @@ +# This is a generated file, don't edit! +type: workflow +name: BB-BASICS-PROCESS-ORDERS-WORKFLOW-PYTHON +desc: Processes given orders +author: + - Qore Technologies, s.r.o. +autostart: 1 +classes: + - BB_BasicsChangeOrderStatusPython +groups: + - BASIC-TRAINING-EXCHANGE-APP +keylist: + - ids +version: "1.0" +steps: + [ + "BB_BasicsChangeOrderStatusToInProgressPython:1.0", + "BB_BasicsSendMoneyToBankPython:1.0", + "BB_BasicsSendMoneyToUserPython:1.0", + "BB_BasicsSendEmailToUserPython:1.0", + "BB_BasicsChangeOrderStatusToCompletedPython:1.0" + ] diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/python/create_order-post_reload.qscript b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/python/create_order-post_reload.qscript new file mode 100755 index 0000000000000000000000000000000000000000..01e2f44b5f0b730b9434bbed3811e65e6141e5e8 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/python/create_order-post_reload.qscript @@ -0,0 +1,35 @@ +#!/usr/bin/env qore +%new-style +%strict-args +%require-types +%enable-all-warnings + +%requires QorusClientCore + +QorusClient::initFast(); + +const WORKFLOW_NAME = "BB-BASICS-PROCESS-ORDERS-WORKFLOW-PYTHON:1.0"; + +const ORDER_DATA = { + "staticdata": { + "orders": { + # Please ensure that the orders are in the database + 106: { + "id": 106, "currency_to_buy": "USD", "currency_to_sell": "EUR", "amount": 100, + "user_email": "your@email.com" + }, + 107: { + "id": 107, "currency_to_buy": "CZK", "currency_to_sell": "USD", "amount": 600, + "user_email": "your@email.com" + } + }, + "exchange_rates": { + "EUR": 1.0, + "CZK": 25.0, + "USD": 22.0 + } + } +}; + +hash response = qrest.post("workflows/" + WORKFLOW_NAME + "?action=createOrder", ORDER_DATA); +printf("Response: %N\n", response); \ No newline at end of file diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/python/lib/BB_BasicsChangeOrderStatusPython-1.0.qclass.py b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/python/lib/BB_BasicsChangeOrderStatusPython-1.0.qclass.py new file mode 100644 index 0000000000000000000000000000000000000000..7dd0437ee65fff54b8ef830866968839f6a4631b --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/python/lib/BB_BasicsChangeOrderStatusPython-1.0.qclass.py @@ -0,0 +1,7 @@ +class BB_BasicsChangeOrderStatusPython: + def changeStatus(ds, status): + orders = wfapi.getStaticData("orders") + orders_table = UserApi.getSqlTable(ds, "exchange_orders") + + for key, order in orders.items(): + orders_table.update({"status": status}, {"id": order["id"]}) diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/python/lib/BB_BasicsChangeOrderStatusPython-1.0.qclass.yaml b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/python/lib/BB_BasicsChangeOrderStatusPython-1.0.qclass.yaml new file mode 100644 index 0000000000000000000000000000000000000000..d1affed0460d4580a6691ae0126e83c2674b574b --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/python/lib/BB_BasicsChangeOrderStatusPython-1.0.qclass.yaml @@ -0,0 +1,9 @@ +# This is a generated file, don't edit! +type: class +name: BB_BasicsChangeOrderStatusPython +desc: Changes the status of orders +lang: python +author: + - Qore Technologies, s.r.o. +version: "1.0" +code: BB_BasicsChangeOrderStatusPython-1.0.qclass.py diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/python/steps/BB_BasicsChangeOrderStatusToCompletedPython-1.0.qstep.py b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/python/steps/BB_BasicsChangeOrderStatusToCompletedPython-1.0.qstep.py new file mode 100644 index 0000000000000000000000000000000000000000..a535f297ea019f020a2332eeece7edb02cbcbc22 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/python/steps/BB_BasicsChangeOrderStatusToCompletedPython-1.0.qstep.py @@ -0,0 +1,14 @@ +from wf import QorusNormalStep +from qore.__root__ import BasicsChangeOrderStatusPython + +class BB_BasicsChangeOrderStatusToCompletedPython(QorusNormalStep): + def primary(self): + ds = self.getDatasourcePool("omquser") + + try: + BB_BasicsChangeOrderStatusPython.changeStatus(ds, "completed") + except: + ds.rollback() + raise + + ds.commit() diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/python/steps/BB_BasicsChangeOrderStatusToCompletedPython-1.0.qstep.yaml b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/python/steps/BB_BasicsChangeOrderStatusToCompletedPython-1.0.qstep.yaml new file mode 100644 index 0000000000000000000000000000000000000000..bda74f1ecc3c4bdb2a0e78046de4b4f92987df24 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/python/steps/BB_BasicsChangeOrderStatusToCompletedPython-1.0.qstep.yaml @@ -0,0 +1,14 @@ +# This is a generated file, don't edit! +type: step +name: BB_BasicsChangeOrderStatusToCompletedPython +desc: Changes the status of orders to completed +lang: python +author: + - Qore Technologies, s.r.o. +base-class-name: QorusNormalStep +class-name: BB_BasicsChangeOrderStatusToCompletedPython +classes: + - BB_BasicsChangeOrderStatusPython +version: "1.0" +steptype: NORMAL +code: BB_BasicsChangeOrderStatusToCompletedPython-1.0.qstep.py diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/python/steps/BB_BasicsChangeOrderStatusToInProgressPython-1.0.qstep.py b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/python/steps/BB_BasicsChangeOrderStatusToInProgressPython-1.0.qstep.py new file mode 100644 index 0000000000000000000000000000000000000000..24616424544eb3a8824d82bd41e4273686cf10f3 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/python/steps/BB_BasicsChangeOrderStatusToInProgressPython-1.0.qstep.py @@ -0,0 +1,14 @@ +from wf import QorusNormalStep +from qore.__root__ import BasicsChangeOrderStatusPython + +class BB_BasicsChangeOrderStatusToInProgressPython(QorusNormalStep): + def primary(self): + ds = self.getDatasourcePool("omquser") + + try: + BB_BasicsChangeOrderStatusPython.changeStatus(ds, "in progress") + except: + ds.rollback() + raise + + ds.commit() diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/python/steps/BB_BasicsChangeOrderStatusToInProgressPython-1.0.qstep.yaml b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/python/steps/BB_BasicsChangeOrderStatusToInProgressPython-1.0.qstep.yaml new file mode 100644 index 0000000000000000000000000000000000000000..f5a4fa95b2bec1acf7fc64f3a253b4183bf938fc --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/python/steps/BB_BasicsChangeOrderStatusToInProgressPython-1.0.qstep.yaml @@ -0,0 +1,14 @@ +# This is a generated file, don't edit! +type: step +name: BB_BasicsChangeOrderStatusToInProgressPython +desc: Changes the status of orders to in progress +lang: python +author: + - Qore Technologies, s.r.o. +base-class-name: QorusNormalStep +class-name: BB_BasicsChangeOrderStatusToInProgressPython +classes: + - BB_BasicsChangeOrderStatusPython +version: "1.0" +steptype: NORMAL +code: BB_BasicsChangeOrderStatusToInProgressPython-1.0.qstep.py diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/python/steps/BB_BasicsSendEmailToUserPython-1.0.qstep.py b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/python/steps/BB_BasicsSendEmailToUserPython-1.0.qstep.py new file mode 100644 index 0000000000000000000000000000000000000000..ba388b450d6a8dc7f7a5fb725bee8f046e977405 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/python/steps/BB_BasicsSendEmailToUserPython-1.0.qstep.py @@ -0,0 +1,16 @@ +from wf import QorusNormalStep +from Qore.MailMessage import message + +class BB_BasicsSendEmailToUserPython(QorusNormalStep): + def primary(self): + orders = getStaticData("orders") + smtp = getUserConnection("smtp-email") + + for key, order in orders.items(): + logInfo("Sending email to %s", order["user_email"]) + + message = Message("test.qorus@gmail.com", "Order details") + text = sprintf("You have received %d %s", order["amount"], order["currency_to_buy"]) + message.setBody(text) + message.addTO(order["user_email"]) + smtp.sendMessage(message) \ No newline at end of file diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/python/steps/BB_BasicsSendEmailToUserPython-1.0.qstep.yaml b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/python/steps/BB_BasicsSendEmailToUserPython-1.0.qstep.yaml new file mode 100644 index 0000000000000000000000000000000000000000..12ca5ff4ce9d224841b13e9c0b88b88b3f03eeb6 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/python/steps/BB_BasicsSendEmailToUserPython-1.0.qstep.yaml @@ -0,0 +1,12 @@ +# This is a generated file, don't edit! +type: step +name: BB_BasicsSendEmailToUserPython +desc: '"Sends email to user"' +lang: python +base-class-name: QorusNormalStep +class-name: BB_BasicsSendEmailToUserPython +version: "1.0" +author: + - Qore Technologies, s.r.o. +steptype: NORMAL +code: BB_BasicsSendEmailToUserPython-1.0.qstep.py diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/python/steps/BB_BasicsSendMoneyToBankPython-1.0.qstep.py b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/python/steps/BB_BasicsSendMoneyToBankPython-1.0.qstep.py new file mode 100644 index 0000000000000000000000000000000000000000..630ed22b170e28ba1023ccdc21a3194411d41179 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/python/steps/BB_BasicsSendMoneyToBankPython-1.0.qstep.py @@ -0,0 +1,21 @@ +from wf import QorusNormalStep + +class BB_BasicsSendMoneyToBankPython(QorusNormalStep): + def primary(self): + static_data = self.getStaticData() + orders = static_data["orders"] + exchange_rates = static_data["exchange_rates"] + + for key, order in orders.items(): + if order["currency_to_buy"] not in exchange_rates: + raise Exception("BUSINESS-ERROR", sprintf("Exchange rate for %s is missing", order["currency_to_buy"])) + + if order["currency_to_sell"] not in exchange_rates: + raise Exception("BUSINESS-ERROR", sprintf("Exchange rate for %s is missing", order["currency_to_sell"])) + + rate = exchange_rates[order["currency_to_buy"]] + UserApi.logInfo("rate: %y", rate) + + amount_to_send = (order["amount"] / rate) * exchange_rates[order["currency_to_sell"]] + amount_to_send += amount_to_send * self.getConfigItemValue("bank-commission") + UserApi.logInfo("Money sent to bank: %f %s", amount_to_send, order["currency_to_sell"]) diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/python/steps/BB_BasicsSendMoneyToBankPython-1.0.qstep.yaml b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/python/steps/BB_BasicsSendMoneyToBankPython-1.0.qstep.yaml new file mode 100644 index 0000000000000000000000000000000000000000..0552fa0e1f851e60d8d3926e0f14dc6c9a89b9ae --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/python/steps/BB_BasicsSendMoneyToBankPython-1.0.qstep.yaml @@ -0,0 +1,20 @@ +# This is a generated file, don't edit! +type: step +name: BB_BasicsSendMoneyToBankPython +desc: '"Sends money to bank"' +lang: python +base-class-name: QorusNormalStep +class-name: BB_BasicsSendMoneyToBankPython +version: "1.0" +author: + - Qore Technologies, s.r.o. +steptype: NORMAL +code: BB_BasicsSendMoneyToBankPython-1.0.qstep.py +config-items: + - name: bank-commission + default_value: + 0.02 + description: '"Bank commission"' + config_group: Default + type: float + strictly_local: true diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/python/steps/BB_BasicsSendMoneyToUserPython-1.0.qstep.py b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/python/steps/BB_BasicsSendMoneyToUserPython-1.0.qstep.py new file mode 100644 index 0000000000000000000000000000000000000000..8bebb4f7e8043630dd4191554799b4f307598d54 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/python/steps/BB_BasicsSendMoneyToUserPython-1.0.qstep.py @@ -0,0 +1,8 @@ +from wf import QorusNormalStep + +class BB_BasicsSendMoneyToUserPython(QorusNormalStep): + def primary(self): + orders = self.getStaticData("orders") + + for key, order in orders.items(): + UserApi.logInfo("Money sent to user: %d %s", order["amount"], order["currency_to_buy"]) diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/python/steps/BB_BasicsSendMoneyToUserPython-1.0.qstep.yaml b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/python/steps/BB_BasicsSendMoneyToUserPython-1.0.qstep.yaml new file mode 100644 index 0000000000000000000000000000000000000000..120df9eb543521e0664d232fd3b7416669be2486 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/python/steps/BB_BasicsSendMoneyToUserPython-1.0.qstep.yaml @@ -0,0 +1,12 @@ +# This is a generated file, don't edit! +type: step +name: BB_BasicsSendMoneyToUserPython +desc: '"Sends money to user"' +lang: python +base-class-name: QorusNormalStep +class-name: BB_BasicsSendMoneyToUserPython +version: "1.0" +author: + - Qore Technologies, s.r.o. +steptype: NORMAL +code: BB_BasicsSendMoneyToUserPython-1.0.qstep.py diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/qore/BB-BASICS-PROCESS-ORDERS-WORKFLOW-1.0.qwf.yaml b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/qore/BB-BASICS-PROCESS-ORDERS-WORKFLOW-1.0.qwf.yaml new file mode 100644 index 0000000000000000000000000000000000000000..88ef1d4a3a8830bebe55ec06c8cb719b1fd04b0c --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/qore/BB-BASICS-PROCESS-ORDERS-WORKFLOW-1.0.qwf.yaml @@ -0,0 +1,22 @@ +# This is a generated file, don't edit! +type: workflow +name: BB-BASICS-PROCESS-ORDERS-WORKFLOW +desc: Processes given orders +author: + - Qore Technologies, s.r.o. +autostart: 1 +classes: + - BB_BasicsChangeOrderStatus +groups: + - BASIC-TRAINING-EXCHANGE-APP +keylist: + - ids +version: "1.0" +steps: + [ + "BB_BasicsChangeOrderStatusToInProgress:1.0", + "BB_BasicsSendMoneyToBank:1.0", + "BB_BasicsSendMoneyToUser:1.0", + "BB_BasicsSendEmailToUser:1.0", + "BB_BasicsChangeOrderStatusToCompleted:1.0" + ] diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/qore/create_order-post_reload.qscript b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/qore/create_order-post_reload.qscript new file mode 100755 index 0000000000000000000000000000000000000000..085f3b10e42f88b7add2311905554cb2192832cb --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/qore/create_order-post_reload.qscript @@ -0,0 +1,35 @@ +#!/usr/bin/env qore +%new-style +%strict-args +%require-types +%enable-all-warnings + +%requires QorusClientCore + +QorusClient::initFast(); + +const WORKFLOW_NAME = "BB-BASICS-PROCESS-ORDERS-WORKFLOW:1.0"; + +const ORDER_DATA = { + "staticdata": { + "orders": ( + # Please ensure that the orders are in the database + { + "id": 100, "currency_to_buy": "USD", "currency_to_sell": "EUR", "amount": 100, + "user_email": "your@email.com" + }, + { + "id": 101, "currency_to_buy": "CZK", "currency_to_sell": "EUR", "amount": 600, + "user_email": "your@email.com" + } + ), + "exchange_rates": { + "EUR": 1.0, + "CZK": 25.0, + "USD": 22.0 + } + } +}; + +hash response = qrest.post("workflows/" + WORKFLOW_NAME + "?action=createOrder", ORDER_DATA); +printf("Response: %N\n", response); \ No newline at end of file diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/qore/lib/BB_BasicsChangeOrderStatus-1.0.qclass b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/qore/lib/BB_BasicsChangeOrderStatus-1.0.qclass new file mode 100644 index 0000000000000000000000000000000000000000..8d27387186fe186aa5871b8bc1b2a9b19b7796cf --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/qore/lib/BB_BasicsChangeOrderStatus-1.0.qclass @@ -0,0 +1,17 @@ +%new-style +%strict-args +%require-types +%enable-all-warnings + +%requires SqlUtil + +class BB_BasicsChangeOrderStatus { + static changeStatus(DatasourcePool ds, string status) { + list> orders = WorkflowApi::getStaticData("orders"); + AbstractTable orders_table = UserApi::getSqlTable(ds, "exchange_orders"); + + foreach hash order in (orders) { + orders_table.update({"status": status}, {"id": order.id}); + } + } +} diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/qore/lib/BB_BasicsChangeOrderStatus-1.0.qclass.yaml b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/qore/lib/BB_BasicsChangeOrderStatus-1.0.qclass.yaml new file mode 100644 index 0000000000000000000000000000000000000000..416692e42550af38ff31bcbbc710179e74fa7099 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/qore/lib/BB_BasicsChangeOrderStatus-1.0.qclass.yaml @@ -0,0 +1,9 @@ +# This is a generated file, don't edit! +type: class +desc: "Changes the status of orders" +author: + - Qore Technologies, s.r.o. +name: BB_BasicsChangeOrderStatus +lang: qore +version: "1.0" +code: BB_BasicsChangeOrderStatus-1.0.qclass diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/qore/steps/BB_BasicsChangeOrderStatusToCompleted-1.0.qstep b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/qore/steps/BB_BasicsChangeOrderStatusToCompleted-1.0.qstep new file mode 100644 index 0000000000000000000000000000000000000000..b1e3e6d77da473324ddeae1e94adabed58d02933 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/qore/steps/BB_BasicsChangeOrderStatusToCompleted-1.0.qstep @@ -0,0 +1,14 @@ +%new-style +%strict-args +%require-types +%enable-all-warnings + +class BB_BasicsChangeOrderStatusToCompleted inherits QorusNormalStep { + primary() { + DatasourcePool ds = getDatasourcePool("omquser"); + on_success ds.commit(); + on_error ds.rollback(); + + BasicsChangeOrderStatus::changeStatus(ds, "completed"); + } +} diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/qore/steps/BB_BasicsChangeOrderStatusToCompleted-1.0.qstep.yaml b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/qore/steps/BB_BasicsChangeOrderStatusToCompleted-1.0.qstep.yaml new file mode 100644 index 0000000000000000000000000000000000000000..a6ee4c4b1122cbcdab9e1817190b93e3d3d5d197 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/qore/steps/BB_BasicsChangeOrderStatusToCompleted-1.0.qstep.yaml @@ -0,0 +1,12 @@ +# This is a generated file, don't edit! +type: step +name: BB_BasicsChangeOrderStatusToCompleted +desc: "Changes the status of orders to completed" +author: + - Qore Technologies, s.r.o. +base-class-name: QorusNormalStep +class-name: BB_BasicsChangeOrderStatusToCompleted +lang: qore +version: "1.0" +steptype: NORMAL +code: BB_BasicsChangeOrderStatusToCompleted-1.0.qstep diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/qore/steps/BB_BasicsChangeOrderStatusToInProgress-1.0.qstep b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/qore/steps/BB_BasicsChangeOrderStatusToInProgress-1.0.qstep new file mode 100644 index 0000000000000000000000000000000000000000..7f2ea84adc4e62c4c3920a3dcafd327da989d848 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/qore/steps/BB_BasicsChangeOrderStatusToInProgress-1.0.qstep @@ -0,0 +1,14 @@ +%new-style +%strict-args +%require-types +%enable-all-warnings + +class BB_BasicsChangeOrderStatusToInProgress inherits QorusNormalStep { + primary() { + DatasourcePool omquser_ds = getDatasourcePool("omquser"); + on_success omquser_ds.commit(); + on_error omquser_ds.rollback(); + + BasicsChangeOrderStatus::changeStatus(omquser_ds, "in progress"); + } +} diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/qore/steps/BB_BasicsChangeOrderStatusToInProgress-1.0.qstep.yaml b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/qore/steps/BB_BasicsChangeOrderStatusToInProgress-1.0.qstep.yaml new file mode 100644 index 0000000000000000000000000000000000000000..0044d21fc8badc42fa8c8cf848c7eca8294f0f16 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/qore/steps/BB_BasicsChangeOrderStatusToInProgress-1.0.qstep.yaml @@ -0,0 +1,12 @@ +# This is a generated file, don't edit! +type: step +name: BB_BasicsChangeOrderStatusToInProgress +desc: "Changes the status of orders to in progress" +author: + - Qore Technologies, s.r.o. +base-class-name: QorusNormalStep +class-name: BB_BasicsChangeOrderStatusToInProgress +lang: qore +version: "1.0" +steptype: NORMAL +code: BB_BasicsChangeOrderStatusToInProgress-1.0.qstep diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/qore/steps/BB_BasicsSendEmailToUser-1.0.qstep b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/qore/steps/BB_BasicsSendEmailToUser-1.0.qstep new file mode 100644 index 0000000000000000000000000000000000000000..18a4d77d7d22381c011a93dd86b41ac8e0fb7bd2 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/qore/steps/BB_BasicsSendEmailToUser-1.0.qstep @@ -0,0 +1,24 @@ +%new-style +%strict-args +%require-types +%enable-all-warnings + +%requires SmtpClient +%requires MailMessage + +class BB_BasicsSendEmailToUser inherits QorusNormalStep { + primary() { + list> orders = getStaticData("orders"); + SmtpClient smtp = getUserConnection("smtp-email"); + + foreach hash order in (orders) { + logInfo("Sending email to %s", order.user_email); + + MailMessage::Message message("test.qorus@gmail.com", "Order details"); + string text = sprintf("You have received %d %s", order.amount, order.currency_to_buy); + message.setBody(text); + message.addTO(order.user_email); + smtp.sendMessage(message); + } + } +} diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/qore/steps/BB_BasicsSendEmailToUser-1.0.qstep.yaml b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/qore/steps/BB_BasicsSendEmailToUser-1.0.qstep.yaml new file mode 100644 index 0000000000000000000000000000000000000000..6a6288528a9c0a9de9684804576688042b56dcdb --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/qore/steps/BB_BasicsSendEmailToUser-1.0.qstep.yaml @@ -0,0 +1,12 @@ +# This is a generated file, don't edit! +type: step +name: BB_BasicsSendEmailToUser +desc: "Sends email to user" +author: + - Qore Technologies, s.r.o. +base-class-name: QorusNormalStep +class-name: BB_BasicsSendEmailToUser +lang: qore +version: "1.0" +steptype: NORMAL +code: BB_BasicsSendEmailToUser-1.0.qstep diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/qore/steps/BB_BasicsSendMoneyToBank-1.0.qstep b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/qore/steps/BB_BasicsSendMoneyToBank-1.0.qstep new file mode 100644 index 0000000000000000000000000000000000000000..bd73eb1b41f2c7de51f93983a7f85bb4a596f59a --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/qore/steps/BB_BasicsSendMoneyToBank-1.0.qstep @@ -0,0 +1,29 @@ +%new-style +%strict-args +%require-types +%enable-all-warnings + +class BB_BasicsSendMoneyToBank inherits QorusNormalStep { + primary() { + hash static_data = WorkflowApi::getStaticData(); + list> orders = static_data.orders; + hash exchange_rates = static_data.exchange_rates; + + foreach hash order in (orders) { + if (!exists exchange_rates{order.currency_to_buy}) { + throw "BUSINESS-ERROR", sprintf("Exchange rate for %s is missing", order.currency_to_buy); + } + + if (!exists exchange_rates{order.currency_to_sell}) { + throw "BUSINESS-ERROR", sprintf("Exchange rate for %s is missing", order.currency_to_sell); + } + + float rate = exchange_rates{order.currency_to_buy}; + logInfo("rate: %y", rate); + + float amount_to_send = (order.amount / rate) * exchange_rates{order.currency_to_sell}; + amount_to_send += amount_to_send * getConfigItemValue("bank-commission"); + logInfo("Money sent to bank: %f %s", amount_to_send, order.currency_to_sell); + } + } +} diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/qore/steps/BB_BasicsSendMoneyToBank-1.0.qstep.yaml b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/qore/steps/BB_BasicsSendMoneyToBank-1.0.qstep.yaml new file mode 100644 index 0000000000000000000000000000000000000000..dd2d9d151b81db5934fdddfbe74eb73237e7f059 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/qore/steps/BB_BasicsSendMoneyToBank-1.0.qstep.yaml @@ -0,0 +1,19 @@ +# This is a generated file, don't edit! +type: step +name: BB_BasicsSendMoneyToBank +desc: "Sends money to bank" +author: + - Qore Technologies, s.r.o. +base-class-name: QorusNormalStep +class-name: BB_BasicsSendMoneyToBank +lang: qore +version: "1.0" +steptype: NORMAL +code: BB_BasicsSendMoneyToBank-1.0.qstep +config-items: + - name: bank-commission + default_value: + 0.02 + config_group: Default + description: "Bank commission" + type: float diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/qore/steps/BB_BasicsSendMoneyToUser-1.0.qstep b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/qore/steps/BB_BasicsSendMoneyToUser-1.0.qstep new file mode 100644 index 0000000000000000000000000000000000000000..1c5283d019a42e583bc8583b454bdc0bcead0e8f --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/qore/steps/BB_BasicsSendMoneyToUser-1.0.qstep @@ -0,0 +1,14 @@ +%new-style +%strict-args +%require-types +%enable-all-warnings + +class BasicsSendMoneyToUser inherits QorusNormalStep { + primary() { + list> orders = getStaticData("orders"); + + foreach hash order in (orders) { + logInfo("Money sent to user: %d %s", order.amount, order.currency_to_buy); + } + } +} diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/qore/steps/BB_BasicsSendMoneyToUser-1.0.qstep.yaml b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/qore/steps/BB_BasicsSendMoneyToUser-1.0.qstep.yaml new file mode 100644 index 0000000000000000000000000000000000000000..7c63f7f1e8fd77d9fc7abd1db3089bbec6d7dfc8 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/qore/steps/BB_BasicsSendMoneyToUser-1.0.qstep.yaml @@ -0,0 +1,12 @@ +# This is a generated file, don't edit! +type: step +name: BB_BasicsSendMoneyToUser +desc: "Sends money to user" +author: + - Qore Technologies, s.r.o. +base-class-name: QorusNormalStep +class-name: BB_BasicsSendMoneyToUser +lang: qore +version: "1.0" +steptype: NORMAL +code: BB_BasicsSendMoneyToUser-1.0.qstep diff --git a/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/smtp-email.qconn.yaml b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/smtp-email.qconn.yaml new file mode 100644 index 0000000000000000000000000000000000000000..be7939974769f66899683c68396b8e5c5d4f0a63 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/12_process_orders_workflow/smtp-email.qconn.yaml @@ -0,0 +1,4 @@ +type: connection +name: smtp-email +desc: SMTP email connection +url: esmtptls://test.qorus@gmail.com:Atb12345@smtp.gmail.com \ No newline at end of file diff --git a/03_basics_building_blocks/01_exchange_rates_app/13_process_orders_job/README.md b/03_basics_building_blocks/01_exchange_rates_app/13_process_orders_job/README.md new file mode 100644 index 0000000000000000000000000000000000000000..596260806d5d7ee5c34dd572ac1f80827603dd51 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/13_process_orders_job/README.md @@ -0,0 +1,49 @@ +# Process Orders Job + +This job has several methods to increase code readability: + ++ **getRates()** function returns actual rates for given currencies (string parameter where currencies are separated by comma). The function simply uses **exchange-rates-api** user connection created in [this tutorial](../06_exchange_api_user_connection). + ++ **getCurrencyList()** function providing functionality to obtain the string of currencies from orders that will be processed. Also it prepares orders: +1. It changes some currency if it's in lower case to upper case by [**upr()**](https://docs.qore.org/current/lang/html/class_qore_1_1zzz8stringzzz9.html#a0b5c301c6742fccb8645968845b00fc1) string function +2. To each order hash user email with "your@email.com" will be added. It can be changed in order to test email notifications that should be sent by the "BASICS-PROCESS-ORDERS-WORKFLOW" workflow. + ++ **prepareOrderData()** function prepares order data that should be passed when [**createOrder()**](https://qoretechnologies.com/manual/qorus/latest/qorus/classOMQ_1_1UserApi_1_1UserApi.html#a6cadda1e1395c915f157c661dec76d3a) method is called. + ++ **createWorkflowOrder()** function calls [**createOrder()**](https://qoretechnologies.com/manual/qorus/latest/qorus/classOMQ_1_1UserApi_1_1UserApi.html#a6cadda1e1395c915f157c661dec76d3a) function with order data prepared by the **prepareOrderData()** function. Also it handles the "DUPLICATE-ORDER-KEY" exception in case the job is trying to create a workflow order with the same workflow_unique_key. + +## run() function + +Job's code must have a **run()** function. This function will be called when the job is scheduled. In this example the **run()** function will be called every minute. This function fetches orders with *"new"* status from the **exchange_rates** database and calls the **createWorkflowOrder()** described above. + +```php +run() { + log(LL_INFO, "Job running"); + + DatasourcePool omquser_ds = getDatasourcePool("omquser"); + on_success omquser_ds.commit(); + on_error omquser_ds.rollback(); + + SqlUtil::AbstractTable orders_table = getSqlTable(omquser_ds, "exchange_orders"); + *list orders = orders_table.selectRows({ + "where": {"status": "new"}, + "orderby": "id", + }); + + if (!orders) { + log(LL_INFO, "No orders to be processed"); + return; + } + + createWorkflowOrder(\orders); +} +``` + +--- + + + + + + +
[← Previous: Process orders workflow](../12_process_orders_workflow)[Next: Workflow validation function →](../14_workflow_validation_code)
diff --git a/03_basics_building_blocks/01_exchange_rates_app/13_process_orders_job/bb-basics-process-orders-job-mapper-1.0.qmapper.yaml b/03_basics_building_blocks/01_exchange_rates_app/13_process_orders_job/bb-basics-process-orders-job-mapper-1.0.qmapper.yaml new file mode 100644 index 0000000000000000000000000000000000000000..d3e81170540a61df2d6b98b7c6360b8568726256 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/13_process_orders_job/bb-basics-process-orders-job-mapper-1.0.qmapper.yaml @@ -0,0 +1,144 @@ +# This is a generated file, don't edit! +type: mapper +name: bb-basics-process-orders-job-mapper +desc: Mapper for workflow order creation from the job +author: + - Qore Technologies, s.r.o. +options: + mapper-input: + type: type + name: qore + path: /hash + can_manage_fields: true + custom-fields: + exchange_rates: + name: exchange_rates + canBeNull: false + desc: exchange rates from the API + firstCustomInHierarchy: true + isChild: false + isCustom: true + level: 0 + parentPath: false + path: exchange_rates + type: + base_type: hash + can_manage_fields: true + fields: {} + mandatory: true + name: hash + options: + qore.no_null: true + supported_options: + qore.no_null: + desc: >- + if True then NULL is not supported on input if + NOTHING is also not accepted + type: bool + typename: hash + types_accepted: + - hash + types_returned: + - hash + orders: + name: orders + canBeNull: false + desc: orders to be processed + firstCustomInHierarchy: true + isChild: false + isCustom: true + level: 0 + parentPath: false + path: orders + type: + base_type: hash + can_manage_fields: true + fields: {} + mandatory: true + name: hash + options: + qore.no_null: true + supported_options: + qore.no_null: + desc: >- + if True then NULL is not supported on input if + NOTHING is also not accepted + type: bool + typename: hash + types_accepted: + - hash + types_returned: + - hash + mapper-output: + type: type + name: qoretechnologies + path: /qorus-api/workflows/create-order/request + can_manage_fields: false + custom-fields: + exchange_rates: + name: exchange_rates + canBeNull: false + desc: exchange rates from the API + firstCustomInHierarchy: true + isChild: true + isCustom: true + level: 1 + parent: staticdata + parentPath: staticdata + path: staticdata.exchange_rates + type: + base_type: hash + can_manage_fields: true + fields: {} + mandatory: true + name: hash + options: + qore.no_null: true + supported_options: + qore.no_null: + desc: >- + if True then NULL is not supported on input if + NOTHING is also not accepted + type: bool + typename: hash + types_accepted: + - hash + types_returned: + - hash + orders: + name: orders + canBeNull: false + desc: orders to be processed + firstCustomInHierarchy: true + isChild: true + isCustom: true + level: 1 + parent: staticdata + parentPath: staticdata + path: staticdata.orders + type: + base_type: hash + can_manage_fields: true + fields: {} + mandatory: true + name: hash + options: + qore.no_null: true + supported_options: + qore.no_null: + desc: >- + if True then NULL is not supported on input if + NOTHING is also not accepted + type: bool + typename: hash + types_accepted: + - hash + types_returned: + - hash +mappertype: Mapper +version: "1.0" +fields: + staticdata.exchange_rates: + name: exchange_rates + staticdata.orders: + name: orders diff --git a/03_basics_building_blocks/01_exchange_rates_app/13_process_orders_job/class-connector/BB_BasicsProcessOrdersClass-1.0.qclass b/03_basics_building_blocks/01_exchange_rates_app/13_process_orders_job/class-connector/BB_BasicsProcessOrdersClass-1.0.qclass new file mode 100644 index 0000000000000000000000000000000000000000..42a94d627a12e038f6b0280e6a709595307273a2 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/13_process_orders_job/class-connector/BB_BasicsProcessOrdersClass-1.0.qclass @@ -0,0 +1,91 @@ +%new-style +%strict-args +%require-types +%enable-all-warnings + +%requires SqlUtil +%requires RestClient + +class BB_BasicsProcessOrdersClass inherits UserApi { + *hash getParams() { + *list> orders = getOrders(); + if (!orders) { + # make sure to return no vbalue here to ensure that no wf order is created if there is no data + return; + } + prepareOrderList(\orders); + + hash orders_hash; + foreach hash order in (orders) { + orders_hash{order.id} = order; + } + logDebug("Orders: %N", orders_hash); + + hash rates = getRates(getCurrencyList(orders)); + return { + "exchange_rates": rates, + "orders": orders_hash + }; + } + + private hash getRates(list symbols) { + RestClient exchange_rates_api = getUserConnection("exchange-rates-api"); + + # maps currency codes (upper case - ex: "USD") -> rate + # NOTE: euro is the base for exchanges + hash currency_map = { + "EUR": 1.0, + }; + hash info; + on_error logError("info: %N", info); + map currency_map{$1} = + exchange_rates_api.get("/eur/" + $1.lwr() + ".json", NOTHING, \info).body{$1.lwr()}, + symbols; + log(LL_INFO, "rates (EUR): %N", currency_map); + return currency_map; + } + + private prepareOrderList(reference>> orders) { + string email = getConfigItemValue("user_email"); + foreach hash order in (\orders) { + order += {"user_email": email}; + order.currency_to_buy = order.currency_to_buy.upr(); + order.currency_to_sell = order.currency_to_sell.upr(); + } + } + + private list getCurrencyList(list> orders) { + hash currencies; + + foreach hash order in (orders) { + if (order.currency_to_buy != "EUR") { + currencies{order.currency_to_buy} = True; + } + if (order.currency_to_sell != "EUR") { + currencies{order.currency_to_sell} = True; + } + } + return keys currencies; + } + + private *list> getOrders() { + DatasourcePool ds = getDatasourcePool("omquser"); + on_success ds.commit(); + on_error ds.rollback(); + + SqlUtil::AbstractTable orders_table = getSqlTable(ds, "exchange_orders"); + *list> orders = orders_table.selectRows( + { + "where": {"status": "new"}, + "orderby": "id" + } + ); + + if (!orders) { + log(LL_INFO, "No orders to be processed"); + return NOTHING; + } + + return orders; + } +} diff --git a/03_basics_building_blocks/01_exchange_rates_app/13_process_orders_job/class-connector/BB_BasicsProcessOrdersClass-1.0.qclass.yaml b/03_basics_building_blocks/01_exchange_rates_app/13_process_orders_job/class-connector/BB_BasicsProcessOrdersClass-1.0.qclass.yaml new file mode 100644 index 0000000000000000000000000000000000000000..92b98ccccaf3616be00bc78bbc96e86acd3f502f --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/13_process_orders_job/class-connector/BB_BasicsProcessOrdersClass-1.0.qclass.yaml @@ -0,0 +1,18 @@ +# This is a generated file, don't edit! +type: class +desc: "## Helper class to get new orders from the exchange_orders table\n\nIt has one connector to be used in order to retrive new exchange orders" +author: + - Qore Technologies, s.r.o. +class-connectors: + - name: BasicsProcessOrdersClass1 + method: getParams + output-provider: + can_manage_fields: true + name: qore + path: /hash + type: type + type: output +name: BB_BasicsProcessOrdersClass +lang: qore +version: "1.0" +code: BB_BasicsProcessOrdersClass-1.0.qclass diff --git a/03_basics_building_blocks/01_exchange_rates_app/13_process_orders_job/java/basics_process_orders_job_java_1_0_job/BasicsProcessOrdersJobJava.java b/03_basics_building_blocks/01_exchange_rates_app/13_process_orders_job/java/basics_process_orders_job_java_1_0_job/BasicsProcessOrdersJobJava.java new file mode 100644 index 0000000000000000000000000000000000000000..f0499109a5b33d07a88ce2d26ea6a9e9cf225901 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/13_process_orders_job/java/basics_process_orders_job_java_1_0_job/BasicsProcessOrdersJobJava.java @@ -0,0 +1,84 @@ +import qore.OMQ.*; +import qore.OMQ.UserApi.*; +import qore.OMQ.UserApi.Job.*; +import org.qore.jni.QoreJavaApi; +import org.qore.jni.QoreObject; +import java.util.Map; +import org.qore.jni.Hash; +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; +import qore.Mapper.Mapper; +import qore.BasicsProcessOrdersClass; +import qore.BBM_CreateOrder; + +class BasicsProcessOrdersJobJava extends QorusJob { + // ==== GENERATED SECTION! DON'T EDIT! ==== // + ClassConnections_BasicsProcessOrdersJobJava classConnections; + // ======== GENERATED SECTION END ========= // + + BasicsProcessOrdersJobJava() throws Throwable { + // ==== GENERATED SECTION! DON'T EDIT! ==== // + classConnections = new ClassConnections_BasicsProcessOrdersJobJava(); + // ======== GENERATED SECTION END ========= // + } + + // ==== GENERATED SECTION! DON'T EDIT! ==== // + public void run() throws Throwable { + classConnections.Connection_1(null); + } + // ======== GENERATED SECTION END ========= // +} + +// ==== GENERATED SECTION! DON'T EDIT! ==== // +class ClassConnections_BasicsProcessOrdersJobJava { + // map of prefixed class names to class instances + private final Hash classMap; + + ClassConnections_BasicsProcessOrdersJobJava() throws Throwable { + classMap = new Hash(); + UserApi.startCapturingObjectsFromJava(); + try { + classMap.put("BasicsProcessOrdersClass", QoreJavaApi.newObjectSave("BasicsProcessOrdersClass")); + classMap.put("BBM_CreateOrder", QoreJavaApi.newObjectSave("BBM_CreateOrder")); + } finally { + UserApi.stopCapturingObjectsFromJava(); + } + } + + Object callClassWithPrefixMethod(final String prefixedClass, final String methodName, + Object params) throws Throwable { + UserApi.logDebug("ClassConnections_BasicsProcessOrdersJobJava: callClassWithPrefixMethod: method: %s class: %y", methodName, prefixedClass); + final Object object = classMap.get(prefixedClass); + + if (object instanceof QoreObject) { + QoreObject qoreObject = (QoreObject)object; + return qoreObject.callMethod(methodName, params); + } else { + final Method method = object.getClass().getMethod(methodName, Object.class); + try { + return method.invoke(object, params); + } catch (InvocationTargetException ex) { + throw ex.getCause(); + } + } + } + + public Object Connection_1(Object params) throws Throwable { + // convert varargs to a single argument if possible + if (params != null && params.getClass().isArray() && ((Object[])params).length == 1) { + params = ((Object[])params)[0]; + } + Mapper mapper; + UserApi.logDebug("Connection_1 called with data: %y", params); + + UserApi.logDebug("calling BasicsProcessOrdersClass1: %y", params); + params = callClassWithPrefixMethod("BasicsProcessOrdersClass", "getParams", params); + + mapper = UserApi.getMapper("bb-basics-process-orders-job-mapper"); + params = mapper.mapAuto(params); + + UserApi.logDebug("calling createOrder: %y", params); + return callClassWithPrefixMethod("BBM_CreateOrder", "createWorkflowOrderConnector", params); + } +} +// ======== GENERATED SECTION END ========= // diff --git a/03_basics_building_blocks/01_exchange_rates_app/13_process_orders_job/java/bb-basics-process-orders-job-java-1.0.qjob.yaml b/03_basics_building_blocks/01_exchange_rates_app/13_process_orders_job/java/bb-basics-process-orders-job-java-1.0.qjob.yaml new file mode 100644 index 0000000000000000000000000000000000000000..c13adf85706e0cfbec97bba9acd9b97f54ec1a15 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/13_process_orders_job/java/bb-basics-process-orders-job-java-1.0.qjob.yaml @@ -0,0 +1,71 @@ +# This is a generated file, don't edit! +type: job +name: bb-basics-process-orders-job-java +desc: Creates BASICS-PROCESS-ORDERS-WORKFLOW-JAVA orders +lang: java +author: + - Qore Technologies, s.r.o. +base-class-name: QorusJob +class-name: BasicsProcessOrdersJobJava +classes: + - BBM_CreateOrder + - BB_BasicsProcessOrdersClass +groups: + - BASIC-TRAINING-EXCHANGE-APP +mappers: + - bb-basics-process-orders-job-mapper:1.0 +schedule: + minutes: "0" + hours: "*" + days: "*" + months: "*" + dow: "*" +version: '1.0' +code: basics_process_orders_job_java_1_0_job/BasicsProcessOrdersJobJava.java +class-connections: + Connection_1: + - class: BasicsProcessOrdersClass + connector: BasicsProcessOrdersClass1 + trigger: run + - class: BBM_CreateOrder + connector: createOrder + mapper: bb-basics-process-orders-job-mapper:1.0 +config-items: + - name: create-workflow-order-mapper + parent: + interface-type: class + interface-name: BBM_CreateOrder + interface-version: '1.0' + - name: create-workflow-name + value: + "BASICS-PROCESS-ORDERS-WORKFLOW-JAVA" + parent: + interface-type: class + interface-name: BBM_CreateOrder + interface-version: '1.0' + - name: create-workflow-version + parent: + interface-type: class + interface-name: BBM_CreateOrder + interface-version: '1.0' + - name: create-workflow-duplicate-handling + parent: + interface-type: class + interface-name: BBM_CreateOrder + interface-version: '1.0' + - name: create-workflow-output-data + parent: + interface-type: class + interface-name: BBM_CreateOrder + interface-version: '1.0' + - name: create-workflow-order-status + parent: + interface-type: class + interface-name: BBM_CreateOrder + interface-version: '1.0' + - name: user_email + value: + "test.qorus@gmail.com" + config_group: Default + description: User email where the email notification will be sent + strictly_local: true diff --git a/03_basics_building_blocks/01_exchange_rates_app/13_process_orders_job/python/bb-basics-process-orders-job-python-1.0.qjob.py b/03_basics_building_blocks/01_exchange_rates_app/13_process_orders_job/python/bb-basics-process-orders-job-python-1.0.qjob.py new file mode 100644 index 0000000000000000000000000000000000000000..17d83166dc6dc6786fa06c40a4731d434b7226df --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/13_process_orders_job/python/bb-basics-process-orders-job-python-1.0.qjob.py @@ -0,0 +1,42 @@ +from job import QorusJob +from qore.__root__ import BasicsProcessOrdersClass +from qore.__root__ import BBM_CreateOrder + +class BasicsProcessOrdersJobPython(QorusJob): + def __init__(self): + ####### GENERATED SECTION! DON'T EDIT! ######## + self.class_connections = ClassConnections_BasicsProcessOrdersJobPython() + ############ GENERATED SECTION END ############ + + ####### GENERATED SECTION! DON'T EDIT! ######## + def run(self): + self.class_connections.Connection_1() + ############ GENERATED SECTION END ############ + +####### GENERATED SECTION! DON'T EDIT! ######## +class ClassConnections_BasicsProcessOrdersJobPython: + def __init__(self): + UserApi.startCapturingObjectsFromPython() + # map of prefixed class names to class instances + self.class_map = { + 'BasicsProcessOrdersClass': BasicsProcessOrdersClass(), + 'BBM_CreateOrder': BBM_CreateOrder(), + } + UserApi.stopCapturingObjectsFromPython() + + def callClassWithPrefixMethod(self, prefixed_class, method, *argv): + UserApi.logDebug("ClassConnections_BasicsProcessOrdersJobPython: callClassWithPrefixMethod: method: %s, class: %y", method, prefixed_class) + return getattr(self.class_map[prefixed_class], method)(*argv) + + def Connection_1(self, params): + UserApi.logDebug("Connection_1 called with data: %y", params) + + UserApi.logDebug("calling BasicsProcessOrdersClass1: %y", params) + params = self.callClassWithPrefixMethod("BasicsProcessOrdersClass", "getParams", params) + + mapper = UserApi.getMapper("bb-basics-process-orders-job-mapper") + params = mapper.mapAuto(params) + + UserApi.logDebug("calling createOrder: %y", params) + return self.callClassWithPrefixMethod("BBM_CreateOrder", "createWorkflowOrderConnector", params) +############ GENERATED SECTION END ############ diff --git a/03_basics_building_blocks/01_exchange_rates_app/13_process_orders_job/python/bb-basics-process-orders-job-python-1.0.qjob.yaml b/03_basics_building_blocks/01_exchange_rates_app/13_process_orders_job/python/bb-basics-process-orders-job-python-1.0.qjob.yaml new file mode 100644 index 0000000000000000000000000000000000000000..0a629be5fbf6d9ac40c32e4e22cb09c9e41d44a2 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/13_process_orders_job/python/bb-basics-process-orders-job-python-1.0.qjob.yaml @@ -0,0 +1,71 @@ +# This is a generated file, don't edit! +type: job +name: bb-basics-process-orders-job-python +desc: Creates BASICS-PROCESS-ORDERS-WORKFLOW-PYTHON orders +lang: python +author: + - Qore Technologies, s.r.o. +base-class-name: QorusJob +class-name: BasicsProcessOrdersJobPython +classes: + - BBM_CreateOrder + - BB_BasicsProcessOrdersClass +groups: + - BASIC-TRAINING-EXCHANGE-APP +mappers: + - bb-basics-process-orders-job-mapper:1.0 +schedule: + minutes: "0" + hours: "*" + days: "*" + months: "*" + dow: "*" +version: "1.0" +code: bb-basics-process-orders-job-python-1.0.qjob.py +class-connections: + Connection_1: + - class: BasicsProcessOrdersClass + connector: BasicsProcessOrdersClass1 + trigger: run + - class: BBM_CreateOrder + connector: createOrder + mapper: bb-basics-process-orders-job-mapper:1.0 +config-items: + - name: create-workflow-order-mapper + parent: + interface-type: class + interface-name: BBM_CreateOrder + interface-version: "1.0" + - name: create-workflow-name + value: + "BASICS-PROCESS-ORDERS-WORKFLOW-PYTHON" + parent: + interface-type: class + interface-name: BBM_CreateOrder + interface-version: "1.0" + - name: create-workflow-version + parent: + interface-type: class + interface-name: BBM_CreateOrder + interface-version: "1.0" + - name: create-workflow-duplicate-handling + parent: + interface-type: class + interface-name: BBM_CreateOrder + interface-version: "1.0" + - name: create-workflow-output-data + parent: + interface-type: class + interface-name: BBM_CreateOrder + interface-version: "1.0" + - name: create-workflow-order-status + parent: + interface-type: class + interface-name: BBM_CreateOrder + interface-version: "1.0" + - name: user_email + default_value: + "test.qorus@gmail.com" + description: User email where the email notification will be sent + config_group: Default + strictly_local: true diff --git a/03_basics_building_blocks/01_exchange_rates_app/13_process_orders_job/qore/bb-basics-process-orders-job-1.0.qjob b/03_basics_building_blocks/01_exchange_rates_app/13_process_orders_job/qore/bb-basics-process-orders-job-1.0.qjob new file mode 100644 index 0000000000000000000000000000000000000000..7d890e89ad553132e83b5510607c52f8691975ec --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/13_process_orders_job/qore/bb-basics-process-orders-job-1.0.qjob @@ -0,0 +1,53 @@ +%new-style +%strict-args +%require-types +%enable-all-warnings + +class BasicsProcessOrdersJob inherits QorusJob { + private { + ####### GENERATED SECTION! DON'T EDIT! ######## + ClassConnections_BasicsProcessOrdersJob class_connections(); + ############ GENERATED SECTION END ############ + } + + ####### GENERATED SECTION! DON'T EDIT! ######## + run() { + class_connections.Connection_1(); + } + ############ GENERATED SECTION END ############ +} + +####### GENERATED SECTION! DON'T EDIT! ######## +class ClassConnections_BasicsProcessOrdersJob { + private { + # map of prefixed class names to class instances + hash class_map; + } + + constructor() { + class_map = { + "BasicsProcessOrdersClass": new BasicsProcessOrdersClass(), + "BBM_CreateOrder": new BBM_CreateOrder(), + }; + } + + auto callClassWithPrefixMethod(string prefixed_class, string method) { + UserApi::logDebug("ClassConnections_BasicsProcessOrdersJob: callClassWithPrefixMethod: method: %s, class: %y", method, prefixed_class); + return call_object_method_args(class_map{prefixed_class}, method, argv); + } + + auto Connection_1(auto params) { + auto mapper; + UserApi::logDebug("Connection_1 called with data: %y", params); + + UserApi::logDebug("calling BasicsProcessOrdersClass1: %y", params); + params = callClassWithPrefixMethod("BasicsProcessOrdersClass", "getParams", params); + + mapper = UserApi::getMapper("bb-basics-process-orders-job-mapper"); + params = mapper.mapAuto(params); + + UserApi::logDebug("calling createOrder: %y", params); + return callClassWithPrefixMethod("BBM_CreateOrder", "createWorkflowOrderConnector", params); + } +} +############ GENERATED SECTION END ############ diff --git a/03_basics_building_blocks/01_exchange_rates_app/13_process_orders_job/qore/bb-basics-process-orders-job-1.0.qjob.yaml b/03_basics_building_blocks/01_exchange_rates_app/13_process_orders_job/qore/bb-basics-process-orders-job-1.0.qjob.yaml new file mode 100644 index 0000000000000000000000000000000000000000..128f4f024f8946ed669e6152f9d714dede48419c --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/13_process_orders_job/qore/bb-basics-process-orders-job-1.0.qjob.yaml @@ -0,0 +1,71 @@ +# This is a generated file, don't edit! +type: job +name: bb-basics-process-orders-job +desc: Creates BASICS-PROCESS-ORDERS-WORKFLOW orders +lang: qore +author: + - Qore Technologies, s.r.o. +base-class-name: QorusJob +class-name: BasicsProcessOrdersJob +classes: + - BBM_CreateOrder + - BB_BasicsProcessOrdersClass +groups: + - BASIC-TRAINING-EXCHANGE-APP +mappers: + - bb-basics-process-orders-job-mapper:1.0 +schedule: + minutes: "0" + hours: "*" + days: "*" + months: "*" + dow: "*" +version: "1.0" +code: bb-basics-process-orders-job-1.0.qjob +class-connections: + Connection_1: + - class: BasicsProcessOrdersClass + connector: BasicsProcessOrdersClass1 + trigger: run + - class: BBM_CreateOrder + connector: createOrder + mapper: bb-basics-process-orders-job-mapper:1.0 +config-items: + - name: create-workflow-order-mapper + parent: + interface-type: class + interface-name: BBM_CreateOrder + interface-version: "1.0" + - name: create-workflow-name + value: + "BASICS-PROCESS-ORDERS-WORKFLOW" + parent: + interface-type: class + interface-name: BBM_CreateOrder + interface-version: "1.0" + - name: create-workflow-version + parent: + interface-type: class + interface-name: BBM_CreateOrder + interface-version: "1.0" + - name: create-workflow-duplicate-handling + parent: + interface-type: class + interface-name: BBM_CreateOrder + interface-version: "1.0" + - name: create-workflow-output-data + parent: + interface-type: class + interface-name: BBM_CreateOrder + interface-version: "1.0" + - name: create-workflow-order-status + parent: + interface-type: class + interface-name: BBM_CreateOrder + interface-version: "1.0" + - name: user_email + value: + "test.qorus@gmail.com" + config_group: Default + description: User email where the email notification will be sent + strictly_local: true diff --git a/03_basics_building_blocks/01_exchange_rates_app/14_workflow_validation_code/README.md b/03_basics_building_blocks/01_exchange_rates_app/14_workflow_validation_code/README.md new file mode 100644 index 0000000000000000000000000000000000000000..bb722682042f2b475106f5ab8b61576d5d2ff5c8 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/14_workflow_validation_code/README.md @@ -0,0 +1,114 @@ +# Workflow Validation Code + +**Synopsis** + +The validation code is run whenever the step is recovered (for asynchronous steps, this could also be if the asynchronous step's queue entry has not been updated within the time period defined by the qorus.async_delay system option). This function's return value tells Qorus if the step function should be run again or not. Note that subworkflow steps and workflow synchronization event steps cannot have validation code. + +***Note:* Always Implement a Validation Code for Non-Repeatable Actions** + +A number of problems could prohibit a step from being updated with the correct status, for example, network problems, power outages, other application problems, etc. To handle these situations gracefully, implement validation code to verify the status of the step before running the step's primary step code again. The arguments passed to the validation code depend on the type of step. + +## Validation Method + +Some step base classes have a validation() method that can be overridden where the validation logic can be defined. See the class documentation for the specific [step class](https://qoretechnologies.com/manual/qorus/latest/qorus/designimplworkflows.html#classstepdefs) for more information on the signature and requirements for the validation method (if it's supported for the step type, not all step types support validation logic), and see Validation Code Return Value for a description of how the return value of this method affects workflow order processing. + +**Validation Code Return Value** +string: step status constant + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Return ValueSystem Behavior
OMQ::StatCompleteDo not run the step function; mark the step as "COMPLETE" and continue. For asynchronous steps, the back-end function also will not be run
OMQ::StatErrorDo not run the step function; mark the step as "ERROR" and stop running any further dependencies of this step
OMQ::StatRetryRun the step function again immediately. If the step is an asynchronous step with queue data with a OMQ::QS_Waiting status, the queue data will be deleted immediately before the step function is run again
OMQ::StatAsyncWaitingFor asynchronous steps only, do not run the step function and keep the "ASYNC-WAITING" status. For non-asynchronous steps, raises an error and the return value is treated like OMQ::StatError
any other statusan error is raised and the return value is treated like OMQ::StatError
+ +## Workflow Validation Function Example + +Step metadata: + +```yaml +# This is a generated file, don't edit! +type: step +name: BasicsExchangeRatesInsertToOrdersTable +desc: "Inserts orders from static data into orders table" +author: + - Qore Technologies, s.r.o. +base-class-name: QorusNormalStep +class-name: BasicsExchangeRatesInsertToOrdersTable +lang: qore +version: "1.0" +steptype: NORMAL +code: BasicsExchangeRatesInsertToOrdersTable-1.0.qstep +``` + +Step code: +``` +%new-style +%strict-args +%require-types +%enable-all-warnings + +class BasicsExchangeRatesInsertToOrdersTable inherits QorusNormalStep { + primary() { + InboundTableMapper mapper = getMapper("basics-exchange-currency-orders"); + on_error mapper.rollback(); + on_success mapper.commit(); + + list> orders = getStaticData("orders"); + log(LL_DEBUG_1, "inserting data: %N", orders); + mapper.queueData(orders); + } + + string validation() { + DatasourcePool ds = getDatasourcePool("omquser"); + on_success ds.commit(); + on_error ds.rollback(); + + SqlUtil::AbstractTable orders_table = getSqlTable(ds, "exchange_orders"); + + list> orders = getStaticData("orders"); + foreach hash order in (\orders) { + hash sh = {"status": "new", "id": order.id}; + if (orders_table.findSingle(sh)) { + return OMQ::StatComplete; + } + } + return OMQ::StatRetry; + } +} +``` + +This validation function is for the **BasicsExchangeRatesInsertToOrdersTable** step. This function checks whether any order with the same id is not already in the database. If yes, then the step will be skipped. In this example the *"status"* field used to filter only orders with *new* status. + +Usually, to prevent one order instance to operate with data of the other one a new field called *wfiid* is introduced in database. Basically, when a workflow order is about to operate with some data in the database it simply fills this field with its own workflow instance id (using [**getWfiid()**](https://qoretechnologies.com/manual/qorus/latest/qorus/classOMQ_1_1UserApi_1_1Workflow_1_1WorkflowApi.html#a8d857a58469c88a3a451dcdf23c53b3a) method). + +--- + + + + + + +
[← Previous: Process orders job](../13_process_orders_job)[Next: Workflow error function →](../15_workflow_error_function)
diff --git a/03_basics_building_blocks/01_exchange_rates_app/14_workflow_validation_code/java/BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW-JAVA-1.0.yaml b/03_basics_building_blocks/01_exchange_rates_app/14_workflow_validation_code/java/BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW-JAVA-1.0.yaml new file mode 100644 index 0000000000000000000000000000000000000000..8363f9d97d723daace74715f19429ae41abad66f --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/14_workflow_validation_code/java/BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW-JAVA-1.0.yaml @@ -0,0 +1,13 @@ +# This is a generated file, don't edit! +type: workflow +name: BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW-JAVA +desc: "Exchange currency workflow" +author: + - Qore Technologies, s.r.o. +groups: + - BASIC-TRAINING-EXCHANGE-APP +steps: + [ + "BB_BasicsExchangeRatesInsertToOrdersTableJava:1.1" + ] +version: "1.0" diff --git a/03_basics_building_blocks/01_exchange_rates_app/14_workflow_validation_code/java/BB_BasicsExchangeRatesInsertToOrdersTableJava-1.1.qstep.yaml b/03_basics_building_blocks/01_exchange_rates_app/14_workflow_validation_code/java/BB_BasicsExchangeRatesInsertToOrdersTableJava-1.1.qstep.yaml new file mode 100644 index 0000000000000000000000000000000000000000..6ce4460c47f0e1af11d69f05eaa8c760d4887181 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/14_workflow_validation_code/java/BB_BasicsExchangeRatesInsertToOrdersTableJava-1.1.qstep.yaml @@ -0,0 +1,12 @@ +# This is a generated file, don't edit! +type: step +name: BB_BasicsExchangeRatesInsertToOrdersTableJava +desc: "Inserts orders from static data into orders table" +author: + - Qore Technologies, s.r.o. +base-class-name: QorusNormalStep +class-name: BasicsExchangeRatesInsertToOrdersTableJava +lang: java +version: "1.1" +steptype: NORMAL +code: BasicsExchangeRatesInsertToOrdersTableJava.java diff --git a/03_basics_building_blocks/01_exchange_rates_app/14_workflow_validation_code/java/BasicsExchangeRatesInsertToOrdersTableJava.java b/03_basics_building_blocks/01_exchange_rates_app/14_workflow_validation_code/java/BasicsExchangeRatesInsertToOrdersTableJava.java new file mode 100644 index 0000000000000000000000000000000000000000..dee2272731657ea23fa466b573b3e6a3bf3bc008 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/14_workflow_validation_code/java/BasicsExchangeRatesInsertToOrdersTableJava.java @@ -0,0 +1,57 @@ +import qore.OMQ.*; +import qore.OMQ.UserApi.*; +import qore.OMQ.UserApi.Workflow.*; + +import qore.Mapper.Mapper; +import qore.Qore.SQL.AbstractDatasource; +import qore.SqlUtil.AbstractTable; + +import org.qore.jni.Hash; + +class BasicsExchangeRatesInsertToOrdersTableJava extends QorusNormalStep { + private final String mapperName = "basics-exchange-currency-orders"; + + public BasicsExchangeRatesInsertToOrdersTableJava() throws Throwable { + super(); + } + + @SuppressWarnings("unchecked") + public String validation() throws Throwable { + AbstractDatasource ds = getDatasourcePool("omquser"); + AbstractTable table = getSqlTable(ds, "exchange_orders"); + + Hash orders = (Hash)getStaticData("orders"); + try { + for (String key : orders.keySet()) { + Hash rowMap = orders.getHash(key); + rowMap.put("status", "new"); + rowMap.put("id", Integer.parseInt(key)); + Hash row = table.selectRow(rowMap); + if (!row.isEmpty()) { + return qore.OMQ.$Constants.StatComplete; + } + } + } catch (Exception e) { + ds.rollback(); + throw e; + } + + ds.commit(); + return qore.OMQ.$Constants.StatRetry; + } + + @SuppressWarnings("unchecked") + public void primary() throws Throwable { + Mapper mapper = getMapper(mapperName); + Hash orders = (Hash)getStaticData("orders"); + + try { + mapper.mapAll(orders); + } catch (Exception e) { + mapper.rollback(); + throw e; + } + + mapper.commit(); + } +} diff --git a/03_basics_building_blocks/01_exchange_rates_app/14_workflow_validation_code/java/create_order-post_reload.qscript b/03_basics_building_blocks/01_exchange_rates_app/14_workflow_validation_code/java/create_order-post_reload.qscript new file mode 100755 index 0000000000000000000000000000000000000000..a057ebf2ff2e86a278aba54db236a57fe01dc784 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/14_workflow_validation_code/java/create_order-post_reload.qscript @@ -0,0 +1,25 @@ +#!/usr/bin/env qore +%new-style +%strict-args +%require-types +%enable-all-warnings + +%requires QorusClientCore + +QorusClient::initFast(); + +const WORKFLOW_NAME = "BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW-MAPPER-JAVA"; + +const ORDER_DATA = { + "staticdata": { + "orders": { + "id": (20, 21), + "currency_to_buy": ("usd", "czk"), + "currency_to_sell": ("eur", "usd"), + "amount": (100, 600), + } + } +}; + +hash response = qrest.post("workflows/" + WORKFLOW_NAME + "?action=createOrder", ORDER_DATA); +printf("Response: %N\n", response); \ No newline at end of file diff --git a/03_basics_building_blocks/01_exchange_rates_app/14_workflow_validation_code/python/BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW-PYTHON-1.0.qwf.yaml b/03_basics_building_blocks/01_exchange_rates_app/14_workflow_validation_code/python/BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW-PYTHON-1.0.qwf.yaml new file mode 100644 index 0000000000000000000000000000000000000000..c71d10946adce99a61e039fb4bb207556af36418 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/14_workflow_validation_code/python/BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW-PYTHON-1.0.qwf.yaml @@ -0,0 +1,13 @@ +# This is a generated file, don't edit! +type: workflow +name: BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW-PYTHON +desc: '"Exchange currency workflow"' +version: "1.0" +author: + - Qore Technologies, s.r.o. +groups: + - BASIC-TRAINING-EXCHANGE-APP +steps: + [ + "BB_BasicsExchangeRatesInsertToOrdersTablePython:1.1" + ] diff --git a/03_basics_building_blocks/01_exchange_rates_app/14_workflow_validation_code/python/BB_BasicsExchangeRatesInsertToOrdersTableMapper-1.1.qstep.py b/03_basics_building_blocks/01_exchange_rates_app/14_workflow_validation_code/python/BB_BasicsExchangeRatesInsertToOrdersTableMapper-1.1.qstep.py new file mode 100644 index 0000000000000000000000000000000000000000..976f211ab0a8572c07c73772f465a5312e75ba36 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/14_workflow_validation_code/python/BB_BasicsExchangeRatesInsertToOrdersTableMapper-1.1.qstep.py @@ -0,0 +1,28 @@ +from wf import QorusNormalStep + +class BB_BasicsExchangeRatesInsertToOrdersTableMapper(QorusNormalStep): + def primary(self): + mapper = self.getMapper("basics-exchange-currency-orders") + + try: + orders = self.getStaticData("orders") + UserApi.logInfo("inserting data: %N", orders) + mapper.mapAll(orders) + except: + mapper.rollback() + raise + + mapper.commit() + + def validation(self): + omquser_ds = self.getDatasourcePool("omquser") + + orders_table = self.getSqlTable(omquser_ds, "exchange_orders") + + orders = self.getStaticData("orders") + for order in orders: + sh = {"status": "new", "id": order.id} + if orders_table.findSingle(sh): + return OMQ.StatComplete + + return OMQ.StatRetry diff --git a/03_basics_building_blocks/01_exchange_rates_app/14_workflow_validation_code/python/BB_BasicsExchangeRatesInsertToOrdersTableMapper-1.1.qstep.yaml b/03_basics_building_blocks/01_exchange_rates_app/14_workflow_validation_code/python/BB_BasicsExchangeRatesInsertToOrdersTableMapper-1.1.qstep.yaml new file mode 100644 index 0000000000000000000000000000000000000000..eac34ca7d3ebac63ef0fc1dd1bad7e909c9ff572 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/14_workflow_validation_code/python/BB_BasicsExchangeRatesInsertToOrdersTableMapper-1.1.qstep.yaml @@ -0,0 +1,12 @@ +# This is a generated file, don't edit! +type: step +name: BB_BasicsExchangeRatesInsertToOrdersTableMapper +desc: Inserts orders from static data into orders table +lang: python +base-class-name: QorusNormalStep +class-name: BB_BasicsExchangeRatesInsertToOrdersTableMapper +version: "1.1" +author: + - Qore Technologies, s.r.o. +steptype: NORMAL +code: BB_BasicsExchangeRatesInsertToOrdersTableMapper-1.1.qstep.py diff --git a/03_basics_building_blocks/01_exchange_rates_app/14_workflow_validation_code/python/create_order-post_reload.qscript b/03_basics_building_blocks/01_exchange_rates_app/14_workflow_validation_code/python/create_order-post_reload.qscript new file mode 100755 index 0000000000000000000000000000000000000000..c9a4592aeeb2ff44c89515a2c87fc260b70b86c8 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/14_workflow_validation_code/python/create_order-post_reload.qscript @@ -0,0 +1,25 @@ +#!/usr/bin/env qore +%new-style +%strict-args +%require-types +%enable-all-warnings + +%requires QorusClientCore + +QorusClient::initFast(); + +const WORKFLOW_NAME = "BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW-MAPPER-PYTHON"; + +const ORDER_DATA = { + "staticdata": { + "orders": { + "id": (22, 23), + "currency_to_buy": ("usd", "czk"), + "currency_to_sell": ("eur", "usd"), + "amount": (100, 600), + } + } +}; + +hash response = qrest.post("workflows/" + WORKFLOW_NAME + "?action=createOrder", ORDER_DATA); +printf("Response: %N\n", response); \ No newline at end of file diff --git a/03_basics_building_blocks/01_exchange_rates_app/14_workflow_validation_code/qore/BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW-1.0.yaml b/03_basics_building_blocks/01_exchange_rates_app/14_workflow_validation_code/qore/BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW-1.0.yaml new file mode 100644 index 0000000000000000000000000000000000000000..a5010fd683f877d885174482542f71c39c94c345 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/14_workflow_validation_code/qore/BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW-1.0.yaml @@ -0,0 +1,13 @@ +# This is a generated file, don't edit! +type: workflow +name: BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW +desc: "Exchange currency workflow" +author: + - Qore Technologies, s.r.o. +groups: + - BASIC-TRAINING-EXCHANGE-APP +steps: + [ + "BB_BasicsExchangeRatesInsertToOrdersTable:1.1" + ] +version: "1.0" diff --git a/03_basics_building_blocks/01_exchange_rates_app/14_workflow_validation_code/qore/BB_BasicsExchangeRatesInsertToOrdersTableMapper-1.1.qstep b/03_basics_building_blocks/01_exchange_rates_app/14_workflow_validation_code/qore/BB_BasicsExchangeRatesInsertToOrdersTableMapper-1.1.qstep new file mode 100644 index 0000000000000000000000000000000000000000..8b34c31dd4f8f413d3f848712d4854992a5bd745 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/14_workflow_validation_code/qore/BB_BasicsExchangeRatesInsertToOrdersTableMapper-1.1.qstep @@ -0,0 +1,33 @@ +%new-style +%strict-args +%require-types +%enable-all-warnings + +class BB_BasicsExchangeRatesInsertToOrdersTableMapper inherits QorusNormalStep { + primary() { + auto mapper = getMapper("basics-exchange-currency-orders"); + on_error mapper.rollback(); + on_success mapper.commit(); + + hash orders = WorkflowApi::getStaticData("orders"); + logDebug("inserting data: %N", orders); + mapper.mapAll(orders); + } + + string validation() { + DatasourcePool ds = getDatasourcePool("omquser"); + on_success ds.commit(); + on_error ds.rollback(); + + SqlUtil::AbstractTable orders_table = getSqlTable(ds, "exchange_orders"); + + list> orders = WorkflowApi::getStaticData("orders"); + foreach hash order in (\orders) { + hash sh = {"status": "new", "id": order.id}; + if (orders_table.findSingle(sh)) { + return OMQ::StatComplete; + } + } + return OMQ::StatRetry; + } +} diff --git a/03_basics_building_blocks/01_exchange_rates_app/14_workflow_validation_code/qore/BB_BasicsExchangeRatesInsertToOrdersTableMapper-1.1.qstep.yaml b/03_basics_building_blocks/01_exchange_rates_app/14_workflow_validation_code/qore/BB_BasicsExchangeRatesInsertToOrdersTableMapper-1.1.qstep.yaml new file mode 100644 index 0000000000000000000000000000000000000000..03d18d98a4ee5aed59650d108dbf0858061e726f --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/14_workflow_validation_code/qore/BB_BasicsExchangeRatesInsertToOrdersTableMapper-1.1.qstep.yaml @@ -0,0 +1,12 @@ +# This is a generated file, don't edit! +type: step +name: BB_BasicsExchangeRatesInsertToOrdersTableMapper +desc: Inserts orders from static data into orders table +lang: qore +author: + - Qore Technologies, s.r.o. +base-class-name: QorusNormalStep +class-name: BB_BasicsExchangeRatesInsertToOrdersTableMapper +version: "1.1" +steptype: NORMAL +code: BB_BasicsExchangeRatesInsertToOrdersTableMapper-1.1.qstep diff --git a/03_basics_building_blocks/01_exchange_rates_app/14_workflow_validation_code/qore/create_order-post_reload.qscript b/03_basics_building_blocks/01_exchange_rates_app/14_workflow_validation_code/qore/create_order-post_reload.qscript new file mode 100755 index 0000000000000000000000000000000000000000..c733f901f4d485444597970882e2f7e87f4f7512 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/14_workflow_validation_code/qore/create_order-post_reload.qscript @@ -0,0 +1,25 @@ +#!/usr/bin/env qore +%new-style +%strict-args +%require-types +%enable-all-warnings + +%requires QorusClientCore + +QorusClient::initFast(); + +const WORKFLOW_NAME = "BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW-MAPPER"; + +const ORDER_DATA = { + "staticdata": { + "orders": { + "id": (24, 25), + "currency_to_buy": ("usd", "czk"), + "currency_to_sell": ("eur", "usd"), + "amount": (100, 600), + } + } +}; + +hash response = qrest.post("workflows/" + WORKFLOW_NAME + "?action=createOrder", ORDER_DATA); +printf("Response: %N\n", response); \ No newline at end of file diff --git a/03_basics_building_blocks/01_exchange_rates_app/15_workflow_error_function/README.md b/03_basics_building_blocks/01_exchange_rates_app/15_workflow_error_function/README.md new file mode 100644 index 0000000000000000000000000000000000000000..f567f6a984391d31b2910acc905c1971d2082ffa --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/15_workflow_error_function/README.md @@ -0,0 +1,81 @@ +# Workflow Error Handling and Recovery + +Qorus includes a framework for defining error information and raising errors. If a workflow defines the `errors` tag (see the example below) in the workflow definition the error information is loaded from the file the tag is referencing to and the information is then loaded in the system database in the *GLOBAL_WORKFLOW_ERRORS* and *WORKFLOW_ERRORS* (see Global and Workflow-Specific Error Definitions for more information). The return value of this function is a hash describing how the system should act when certain errors are raised when processing workflow order data. + +```yaml +errors: bb-bb-bb-basics-process-orders-wf-errors.yaml +``` + +Workflows raise errors by calling the [stepError()](https://qoretechnologies.com/manual/qorus/latest/qorus/classOMQ_1_1UserApi_1_1Workflow_1_1WorkflowApi.html#a3c816730408849ce515958a44f503804) method or by throwing exceptions. In the case an exception is thrown, the exception name is used as the error name; the system will then use the error name as the +hash key to look up error information in the workflow error object. + +To allow a workflow to recover gracefully from an error, implement a [validation code](../workflow_validation_code) for each step. [Validation code](../workflow_validation_code) allow workflows to recover gracefully from errors such as lost request or response messages or temporary communication failures without requiring manual intervention. + +## Global and Workflow-Specific Error Definitions + +By default, error definitions are global. A global definition is a workflow error definition that applies to all workflows. Workflow-specific error definitions apply only to a particular workflow configuration. + +There are three ways to create workflow-specific error definitions: + ++ tag the error definition with *"level"* = [OMQ::ErrLevelWorkflow](https://www.qoretechnologies.com/manual/qorus/latest/qorus/group__errleveltypes.html#ga0f322173cd15b5e1083ef05238403427) in the [errorfunction's](#workflow-error-function) return value ++ create the workflow-specific error manually with an API call (ex: [omq.system.update-workflow-error()](https://www.qoretechnologies.com/manual/qorus/latest/qorus/QorusSystemAPI_8qc.html#adad109b9c87f9ba0d057bc59abe0a83f)) ++ leave the "level" key unassigned (or assigned to the default: [OMQ::ErrLevelAuto](https://www.qoretechnologies.com/manual/qorus/latest/qorus/group__errleveltypes.html#ga7849e84aaef7b8d2fbe961df7faedfc8)) in the [errorfunction's](#workflow-error-function) return value but give the error a different definition than an existing global error + +The last point above implies that if two or more workflows define the same error with different attributes (but leave the error's "level" option either unset or assigned to the default: [OMQ::ErrLevelAuto](https://www.qoretechnologies.com/manual/qorus/latest/qorus/group__errleveltypes.html#ga7849e84aaef7b8d2fbe961df7faedfc8)), the first error will be a global error, and each time the other workflows define the error with a different set of attributes, those new errors will be workflow-specific error definitions. + +Information on how to implement Qorus Objects using YAML format can be found [here](https://qoretechnologies.com/manual/qorus/latest/qorus/implementingqorusobjectsusingyaml.html). + +The YAML schema used for validation of the workflow errors can be downloaded [here](https://qoretechnologies.com/manual/qorus/latest/qorus/errors_schema.yaml). + +## Default Global Workflow Error Definitions +Qorus includes default error definitions for common technical errors that should normally result in a workflow order instance retry and also template errors that can be re-used in workflows as needed. + +The following table lists the default errors delivered with Qorus; note that workflows with a severity level of [OMQ::ES_Warning](https://www.qoretechnologies.com/manual/qorus/latest/qorus/group__ErrorSeverityCodes.html#gab6e9d95ef75ebcd814b8cafcb0b8c5f1) (*"WARNING"*) only result in a warning error record but do not affect the logic flow of the workflow. + +Default global workflow error definitions can be found [**here**](https://www.qoretechnologies.com/manual/qorus/latest/qorus/designimplworkflows.html#globalandworkflowerrors) + +# Workflow Error Definition + +**Expected Return Value** +A hash where each key is the error string code (ex: *"PAYMENT-MESSAGE-TIMEOUT"*), and each value is a hash with error information as given in the following description of the keys: + ++ desc: the description of the error ++ severity: the severity code; see Error Severity Codes, default: OMQ::ES_Major ++ status: the status code for the step due to this error; either OMQ::StatRetry or OMQ::StatError (default if not present) ++ business: is this a business error? default: False ++ retry-delay: the default retry time for the error as an integer giving seconds (ie 1200 for 20 minutes) or a relative date/time value (i.e. 2D + 12h or P2D12H, both meaning 2 days and 12 hours); the relative date/time value format is normally recommended as it's more readable ++ level: the error level value; see Error Level Type Constants for possible values; default: OMQ::ErrLevelAuto ("AUTO") + +Only the desc key is required. Note: + ++ if the "severity" key is not present, the error has a default severity of OMQ::ES_Major ("MAJOR") ++ if the "status" key is not present, the default is OMQ::StatError ("ERROR") ++ if the "level" key is not present, the default is OMQ::ErrLevelAuto ("AUTO") ++ the "retry-delay" key should only be given on errors with status set to OMQ::StatRetry ("RETRY") + +## Errors Definition Example + +```yaml +type: errors +errors: + - name: CONNECTION-ERROR + desc: The given connection is not known + status: RETRY + business: false + retry-delay: 30 +``` + +This error object defines one error that tells how the **"BASICS-EXCHANGE-CURRENCY-WORKFLOW"** workflow should react in case some of its steps throws the *"CONNECTION-ERROR"* exception. If the exception is thrown it tells Qorus to automatically retry to run a workflow instance in 30 seconds. This exception can be thrown by the *getUserConnection()* method in the **"BasicsSendEmailToUser"** step when **"smtp-email"** user connection is not available. + +# Testing + +To test the error function the **"smtp-email"** user connection can be simply removed from the Qorus user connections. Then after creation of a new workflow order it will stop in **"BasicsSendEmailToUser"** step with the above error. And the workflow instance will be retried in 30 seconds. By restoring the removed user connection the workflow order should get complete status. + +--- + + + + + + +
[← Previous: Workflow validation code](../14_workflow_validation_code)[Next: Testing →](../16_testing)
diff --git a/03_basics_building_blocks/01_exchange_rates_app/15_workflow_error_function/bb-basics-process-orders-wf-errors.yaml b/03_basics_building_blocks/01_exchange_rates_app/15_workflow_error_function/bb-basics-process-orders-wf-errors.yaml new file mode 100644 index 0000000000000000000000000000000000000000..5ab410291edf11863cd9d4909d1fda4940bf6ae5 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/15_workflow_error_function/bb-basics-process-orders-wf-errors.yaml @@ -0,0 +1,9 @@ +type: errors +name: bb-bb-bb-basics-process-orders-wf-errors +desc: custom error definitions +errors: + - name: CONNECTION-ERROR + desc: The given connection is not known + status: RETRY + business: false + retry-delay: 30 diff --git a/03_basics_building_blocks/01_exchange_rates_app/15_workflow_error_function/java/BB-BASICS-PROCESS-ORDERS-WORKFLOW-JAVA-1.0.qwf.yaml b/03_basics_building_blocks/01_exchange_rates_app/15_workflow_error_function/java/BB-BASICS-PROCESS-ORDERS-WORKFLOW-JAVA-1.0.qwf.yaml new file mode 100644 index 0000000000000000000000000000000000000000..9b5080f5107dc398ac28d8efcf56fd0b4bed79dc --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/15_workflow_error_function/java/BB-BASICS-PROCESS-ORDERS-WORKFLOW-JAVA-1.0.qwf.yaml @@ -0,0 +1,23 @@ +# This is a generated file, don't edit! +type: workflow +name: BB-BASICS-PROCESS-ORDERS-WORKFLOW-JAVA +desc: Processes given orders +author: + - Qore Technologies, s.r.o. +autostart: 1 +classes: + - BB_BasicsChangeOrderStatusJava +errors: ../bb-basics-process-orders-wf-errors.yaml +groups: + - BASIC-TRAINING-EXCHANGE-APP +keylist: + - ids +version: '1.0' +steps: + [ + "BB_BasicsChangeOrderStatusToInProgressJava:1.0", + "BB_BasicsSendMoneyToBankJava:1.0", + "BB_BasicsSendMoneyToUserJava:1.0", + "BB_BasicsSendEmailToUserJava:1.0", + "BB_BasicsChangeOrderStatusToCompletedJava:1.0" + ] diff --git a/03_basics_building_blocks/01_exchange_rates_app/15_workflow_error_function/python/BB-BASICS-PROCESS-ORDERS-WORKFLOW-PYTHON-1.0.qwf.yaml b/03_basics_building_blocks/01_exchange_rates_app/15_workflow_error_function/python/BB-BASICS-PROCESS-ORDERS-WORKFLOW-PYTHON-1.0.qwf.yaml new file mode 100644 index 0000000000000000000000000000000000000000..080abd53a663c6aedbf017bd5694505d041cdce3 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/15_workflow_error_function/python/BB-BASICS-PROCESS-ORDERS-WORKFLOW-PYTHON-1.0.qwf.yaml @@ -0,0 +1,22 @@ +# This is a generated file, don't edit! +type: workflow +name: BB-BASICS-PROCESS-ORDERS-WORKFLOW-PYTHON +desc: Processes given orders +author: + - Qore Technologies, s.r.o. +autostart: 1 +classes: + - BB_BasicsChangeOrderStatusPython +groups: + - BASIC-TRAINING-EXCHANGE-APP +keylist: + - ids +version: "1.0" +steps: + [ + "BB_BasicsChangeOrderStatusToInProgressPython:1.0", + "BB_BasicsSendMoneyToBankPython:1.0", + "BB_BasicsSendMoneyToUserPython:1.0", + "BB_BasicsSendEmailToUserPython:1.0", + "BB_BasicsChangeOrderStatusToCompletedPython:1.0" + ] diff --git a/03_basics_building_blocks/01_exchange_rates_app/15_workflow_error_function/qore/BB-BASICS-PROCESS-ORDERS-WORKFLOW-1.0.yaml b/03_basics_building_blocks/01_exchange_rates_app/15_workflow_error_function/qore/BB-BASICS-PROCESS-ORDERS-WORKFLOW-1.0.yaml new file mode 100644 index 0000000000000000000000000000000000000000..bf6779771e14b75dba5806919e76f83ae8f9b07f --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/15_workflow_error_function/qore/BB-BASICS-PROCESS-ORDERS-WORKFLOW-1.0.yaml @@ -0,0 +1,23 @@ +# This is a generated file, don't edit! +type: workflow +name: BB-BASICS-PROCESS-ORDERS-WORKFLOW +desc: "Processes given orders" +author: + - Qore Technologies, s.r.o. +classes: + - BB_BasicsChangeOrderStatus +groups: + - BASIC-TRAINING-EXCHANGE-APP +keylist: + - ids +steps: + [ + "BB_BasicsChangeOrderStatusToInProgress:1.0", + "BB_BasicsSendMoneyToBank:1.0", + "BB_BasicsSendMoneyToUser:1.0", + "BB_BasicsSendEmailToUser:1.0", + "BB_BasicsChangeOrderStatusToCompleted:1.0" + ] +version: "1.0" +autostart: 1 +errors: bb-basics-process-orders-wf-errors.yaml \ No newline at end of file diff --git a/03_basics_building_blocks/01_exchange_rates_app/16_testing/README.md b/03_basics_building_blocks/01_exchange_rates_app/16_testing/README.md new file mode 100644 index 0000000000000000000000000000000000000000..7593094f8f943eb967620fa25ae660d9f31cddf1 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/16_testing/README.md @@ -0,0 +1,172 @@ +# Testing + +## Introduction to the QUnit Module + +The [QUnit](https://qoretechnologies.com/manual/qorus/current/qore/modules/QUnit/html/index.html#qunitintro) module provides a framework for automated testing. + +It contains base classes for creating test cases and test suites. It also provides a dependency injection helper for mocking pre-existing classes without modifying their code. + +It also provides a number of pre-defined testing functions for use in assertions. + +Example: + +```php +#!/usr/bin/env qore +# -*- mode: qore; indent-tabs-mode: nil -*- +%new-style +%enable-all-warnings +%require-types +%strict-args +%requires ../../qlib/QUnit.qm +#%include ./_some_module_to_test +%exec-class QUnitTest +public class QUnitTest inherits QUnit::Test { + constructor() : Test("QUnitTest", "1.0") { + addTestCase("What this method is testing", \testMethod(), NOTHING); + addTestCase("Skipped test", \testSkipped(), NOTHING); + # Return for compatibility with test harness that checks return value. + set_return_value(main()); + } + testMethod() { + # Test against success + testAssertion("success", \equals(), (True, True)); + # Test against something else + testAssertion("failure", \equals(), (True, False), RESULT_FAILURE); + } + testSkipped() { + # Skip this test + testSkip("Because of the reason it skipped"); + } +} +``` + +## Introduction to the QorusInterfaceTest Module + +The [QorusInterfaceTest](https://qoretechnologies.com/manual/qorus/current/qorus/classQorusInterfaceTest_1_1QorusInterfaceTest.html) module provides functionality for automatic testing; it is based on the [QUnit module](https://qoretechnologies.com/manual/qorus/current/qore/modules/QUnit/html/index.html#qunitintro) from Qore. Currently the module provides the following classes: + +* [QorusInterfaceTest](https://qoretechnologies.com/manual/qorus/current/qorus/classQorusInterfaceTest_1_1QorusInterfaceTest.html): extension of the Qore [QUnit module](https://qoretechnologies.com/manual/qorus/current/qore/modules/QUnit/html/index.html#qunitintro) with functions testing Qorus-specific interfaces (abstract class) + * [QorusJobTest](https://qoretechnologies.com/manual/qorus/current/qorus/classQorusInterfaceTest_1_1QorusJobTest.html): base class for testing jobs + * [QorusPassiveWorkflowTest](https://qoretechnologies.com/manual/qorus/current/qorus/classQorusInterfaceTest_1_1QorusPassiveWorkflowTest.html): class for testing workflows without starting execution instances (for example, for testing synchronous workflow orders, etc) + * [QorusServiceTest](https://qoretechnologies.com/manual/qorus/current/qorus/classQorusInterfaceTest_1_1QorusServiceTest.html): base class for testing services + * [QorusWorkflowTest](https://qoretechnologies.com/manual/qorus/current/qorus/classQorusInterfaceTest_1_1QorusWorkflowTest.html): base class for testing workflows, will automatically start execution instances if not already running +* [Action](https://qoretechnologies.com/manual/qorus/current/qorus/classQorusInterfaceTest_1_1Action.html): abstract class representing some action + * [CreateFile](https://qoretechnologies.com/manual/qorus/current/qorus/classQorusInterfaceTest_1_1CreateFile.html): abstract class for input file + * [CreateFileText](https://qoretechnologies.com/manual/qorus/current/qorus/classQorusInterfaceTest_1_1CreateFileText.html): input text file + * [CreateFileCsv](https://qoretechnologies.com/manual/qorus/current/qorus/classQorusInterfaceTest_1_1CreateFileCsv.html): input csv file + * [InsertDbTableRows](https://qoretechnologies.com/manual/qorus/current/qorus/classQorusInterfaceTest_1_1InsertDbTableRows.html): inserts one or more rows into DB table + * [InsertDbTableRow](https://qoretechnologies.com/manual/qorus/current/qorus/classQorusInterfaceTest_1_1InsertDbTableRow.html): insert a row into DB table + * [DeleteDbTableData](https://qoretechnologies.com/manual/qorus/current/qorus/classQorusInterfaceTest_1_1DeleteDbTableData.html): delete data from DB table + * [TruncateTable](https://qoretechnologies.com/manual/qorus/current/qorus/classQorusInterfaceTest_1_1TruncateTable.html): truncate a DB table (delete all rows) + * [AbstractRemoteDbSqlUtilAction](https://qoretechnologies.com/manual/qorus/current/qorus/classQorusInterfaceTest_1_1AbstractRemoteDbSqlUtilAction.html): abstract base class for remote sqlutil streaming actions + * [AbstractRemoteDbSelectAction](https://qoretechnologies.com/manual/qorus/current/qorus/classQorusInterfaceTest_1_1AbstractRemoteDbSelectAction.html): abstract base class for streaming data from a remote DB + * [RemoteDbSelectAction](https://qoretechnologies.com/manual/qorus/current/qorus/classQorusInterfaceTest_1_1RemoteDbSelectAction.html): streams data from a remote DB and compares to expected row data + * [RemoteDbDeleteAction](https://qoretechnologies.com/manual/qorus/current/qorus/classQorusInterfaceTest_1_1RemoteDbDeleteAction.html): deletes data in a remote datasource + * [RemoteDbRowSqlUtilAction](https://qoretechnologies.com/manual/qorus/current/qorus/classQorusInterfaceTest_1_1RemoteDbRowSqlUtilAction.html): performs sqlutil-based streaming inserts or upserts of data in remote datasources in one or more tables in a single remote transaction + * [InsertRemoteDbTableRows](https://qoretechnologies.com/manual/qorus/current/qorus/classQorusInterfaceTest_1_1InsertRemoteDbTableRows.html): performs sqlutil-based streaming to insert rows in a single remote table + * [UpsertRemoteDbTableRows](https://qoretechnologies.com/manual/qorus/current/qorus/classQorusInterfaceTest_1_1UpsertRemoteDbTableRows.html): performs sqlutil-based streaming to upsert (or merge) rows in a single remote table + * [CallService](https://qoretechnologies.com/manual/qorus/current/qorus/classQorusInterfaceTest_1_1CallService.html): call any Qorus service + * [Sleep](https://qoretechnologies.com/manual/qorus/current/qorus/classQorusInterfaceTest_1_1Sleep.html): wait action + * [WaitForWfiid](https://qoretechnologies.com/manual/qorus/current/qorus/classQorusInterfaceTest_1_1WaitForWfiid.html): wait for WFIID to be finished + * [CreateOrder](https://qoretechnologies.com/manual/qorus/current/qorus/classQorusInterfaceTest_1_1CreateOrder.html): creates order + * [CheckFile](https://qoretechnologies.com/manual/qorus/current/qorus/classQorusInterfaceTest_1_1CheckFile.html): check whether some file exists + * [CheckFileCsv](https://qoretechnologies.com/manual/qorus/current/qorus/classQorusInterfaceTest_1_1CheckFileCsv.html): check csv file content + * [CheckFileNonexisting](https://qoretechnologies.com/manual/qorus/current/qorus/classQorusInterfaceTest_1_1CheckFileNonexisting.html): check whether some file doesn't exist + * [ExecSynchronousOrder](https://qoretechnologies.com/manual/qorus/current/qorus/classQorusInterfaceTest_1_1ExecSynchronousOrder.html): creates and executes a synchronous workflow order + * [StartLog](https://qoretechnologies.com/manual/qorus/current/qorus/classQorusInterfaceTest_1_1StartLog.html): starts log stopwatch (i. e. from this time we will check log files) + * [CheckLog](https://qoretechnologies.com/manual/qorus/current/qorus/classQorusInterfaceTest_1_1CheckLog.html): check whether log matches specified pattern + * [CheckDbTableRows](https://qoretechnologies.com/manual/qorus/current/qorus/classQorusInterfaceTest_1_1CheckDbTableRows.html): check rows in a database + * [CheckDbTableRow](https://qoretechnologies.com/manual/qorus/current/qorus/classQorusInterfaceTest_1_1CheckDbTableRow.html): check a row in a database + * [CallService](https://qoretechnologies.com/manual/qorus/current/qorus/classQorusInterfaceTest_1_1CallService.html): invokes given service + * [RunJob](https://qoretechnologies.com/manual/qorus/current/qorus/classQorusInterfaceTest_1_1RunJob.html): invokes the given job and retrieves the result + * [RunJobResult](https://qoretechnologies.com/manual/qorus/current/qorus/classQorusInterfaceTest_1_1RunJobResult.html): runs the given job and checks the result status and optionally the result job info hash To use this module, use "%requires [QorusInterfaceTest](https://qoretechnologies.com/manual/qorus/current/qorus/classQorusInterfaceTest_1_1QorusInterfaceTest.html)" in your code. Example: + ```java + class MyJobTest inherits QorusJobTest { + constructor() : QorusJobTest("my-job", "1.0", \ARGV) { + addTestCase("my-test-1", est1()); + } + test1() { + t.exec(new CreateFileText(input_filename, file_content)); + t.exec(new RunJobResult(OMQ::StatComplete)); + } + } + ``` + +### How to run tests + +Make sure your scripts have the executable bit set (on UNIX systems) and an appropriate hash-bang as the first line (ex: "#!/usr/bin/env qr" or "#!/usr/bin/env qore". If your script's name is test.qtest, then The following command should execute the script: + +test.qtest [OPTIONS] + +See [QorusInterfaceTest module](https://qoretechnologies.com/manual/qorus/current/qorus/namespaceQorusInterfaceTest.html) in [Qore](https://qoretechnologies.com/manual/qorus/current/qore/lang/html/namespace_qore.html) for options, output formats and further details. + +## Exchange rates application tests + +### BASICS-EXCHANGE-CURRENCY-WORKFLOW test + +This test can be run using the following command from the command line: + +``` +./BASICS-EXCHANGE-CURRENCY-WORKFLOW.qtest -i1 -bCZK -sEUR -a150 +``` + +This will create a workflow order with the following exchange order data: +```php +id : 1 +currency_to_buy : "CZK" +currency_to_sell : "EUR" +amount : 150 +``` + +The [options](https://qoretechnologies.com/manual/qorus/latest/qore/lang/html/class_qore_1_1_get_opt.html#a548ec0b81ec224ccb9498ca4a6e6e831) of the test are defined in the following hash: +```php +const OPTIONS = Opts + { + "id": "i,id=i", + "currency_to_buy": "b,buy=s", + "currency_to_sell": "s,sell=s", + "amount": "a,amount=f" +}; +``` + +The Qorus Visual Code extension currently doesn't support deploying **.qtest** files, so the test has to be run manually, in case your Qorus is running in docker you would need to connect to the container: + +``` +docker exec -it qorus bash +``` + +And set the environment variables: + +``` +. /opt/qorus/bin/env.sh +``` + +### BASICS-PROCESS-ORDERS-WORKFLOW test + +This test can be run using the following command from the command line: + +``` +./BASICS-PROCESS-ORDERS-WORKFLOW.qtest -i1 -eyour@email.com +``` + +This will create a workflow order to process the exchange order with id 1 from the database. + +### BasicsExchangeCurrencyApiService test + +This test can be run the same way the **BASICS-EXCHANGE-CURRENCY-WORKFLOW** is run: +``` +./BasicsExchangeCurrencyApiService.qtest -i1 -bCZK -sEUR -a150 +``` + +The test will call the exchange method of the **basics-exchange-currency-api-service** service. + + +### BasicsProcessOrdersJob test + +The BasicsProcessOrdersJob test just calls the run function of the **basics-process-orders-job** job. + +--- + + + + + +
[← Previous: Workflow error function](../15_workflow_error_function)
diff --git a/03_basics_building_blocks/01_exchange_rates_app/16_testing/java/BB_BasicsExchangeCurrencyApiServiceTest.java b/03_basics_building_blocks/01_exchange_rates_app/16_testing/java/BB_BasicsExchangeCurrencyApiServiceTest.java new file mode 100755 index 0000000000000000000000000000000000000000..af186eacf09eda761781dc6f482eb490e38bf37a --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/16_testing/java/BB_BasicsExchangeCurrencyApiServiceTest.java @@ -0,0 +1,32 @@ +import com.qoretechnologies.qorus.*; +import com.qoretechnologies.qorus.client.*; +import com.qoretechnologies.qorus.test.*; + +import org.qore.jni.Hash; + +import java.util.List; + +public class BB_BasicsExchangeCurrencyApiServiceTest { + private static final String ServiceName = "bb-basics-exchange-currency-api-service-java"; + private static Hash Order = new Hash(); + + public static void main(String[] args) throws Throwable { + Order.put("id", 12345); + Order.put("currency_to_buy", "czk"); + Order.put("currency_to_sell", "eur"); + Order.put("amount", 213); + Order.put("comment", "test"); + + QorusClientCore.init2(); + QorusServiceTest test = new QorusServiceTest(ServiceName); + + test.addTestCase("svc test", () -> mainTest(test)); + test.main(); + } + + private static void mainTest(QorusServiceTest test) throws Throwable { + CallService call = new CallService(ServiceName + ".exchange", Order); + test.exec(call); + System.out.print("Result: " + call.getResult()); + } +} diff --git a/03_basics_building_blocks/01_exchange_rates_app/16_testing/java/BB_BasicsProcessOrdersJobTest.java b/03_basics_building_blocks/01_exchange_rates_app/16_testing/java/BB_BasicsProcessOrdersJobTest.java new file mode 100755 index 0000000000000000000000000000000000000000..a7ccbd827df2986add5dfc229683c782974b8e39 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/16_testing/java/BB_BasicsProcessOrdersJobTest.java @@ -0,0 +1,23 @@ +import com.qoretechnologies.qorus.*; +import com.qoretechnologies.qorus.client.*; +import com.qoretechnologies.qorus.test.*; + +import java.util.HashMap; + +public class BB_BasicsProcessOrdersJobTest { + public static void main(String[] args) throws Throwable { + QorusClientCore.init2(); + QorusJobTest test = new QorusJobTest("bb-basics-process-orders-job"); + + test.addTestCase("job test", () -> testJob(test)); + test.main(); + } + + private static void testJob(QorusJobTest test) throws Throwable { + RunJob action = new RunJob(); + test.exec(action); + + action = new RunJobResult(OMQ.StatComplete); + test.exec(action); + } +} \ No newline at end of file diff --git a/03_basics_building_blocks/01_exchange_rates_app/16_testing/java/BB_ExchangeCurrencyWfTest.java b/03_basics_building_blocks/01_exchange_rates_app/16_testing/java/BB_ExchangeCurrencyWfTest.java new file mode 100755 index 0000000000000000000000000000000000000000..1d3e56f90d001435c167d1620d6469eb28d9ddbc --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/16_testing/java/BB_ExchangeCurrencyWfTest.java @@ -0,0 +1,52 @@ +import com.qoretechnologies.qorus.test.*; +import com.qoretechnologies.qorus.client.*; + +import org.qore.jni.Hash; +import org.qore.lang.qunit.*; + +import java.util.Map; +import java.util.HashMap; +import java.util.ArrayList; +import java.util.List; + +public class BB_ExchangeCurrencyWfTest { + private static final String WorkflowName = "BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW-JAVA"; + + private static Hash Orders = new Hash(); + + public static void main(String[] args) throws Throwable { + Orders.put("id", 212345); + Orders.put("currency_to_buy", "czk"); + Orders.put("currency_to_sell", "eur"); + Orders.put("amount", 31234); + + QorusClientCore.init2(); + QorusWorkflowTest test = new QorusWorkflowTest(WorkflowName, "1.0"); + + test.addTestCase(WorkflowName, () -> mainTest(test)); + test.main(); + } + + @SuppressWarnings("unchecked") + private static void mainTest(QorusWorkflowTest test) throws Throwable { + Hash staticData = new Hash(); + staticData.put("orders", Orders); + + int wfiid = test.createOrder(staticData); + test.exec(new WaitForWfiid(wfiid)); + + Hash select = new Hash(); + + Hash where = new Hash(); + where.put("id", 212345); + + select.put("where", where); + + Orders.put("status", "new"); + + Hash[] expected = new Hash[1]; + expected[0] = Orders; + + test.exec(new CheckDbTableRows("omquser", "exchange_orders", select, expected)); + } +} diff --git a/03_basics_building_blocks/01_exchange_rates_app/16_testing/python/BasicsProcessOrdersJobTest.py b/03_basics_building_blocks/01_exchange_rates_app/16_testing/python/BasicsProcessOrdersJobTest.py new file mode 100755 index 0000000000000000000000000000000000000000..886ddce8e028642b9d4042a3f98e775c97787184 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/16_testing/python/BasicsProcessOrdersJobTest.py @@ -0,0 +1,19 @@ +import sys +import qoreloader +from qore.QorusInterfaceTest import QorusJobTest, RunJob, RunJobResult +from qore.__root__ import OMQ + +class BasicsProcessOrdersJobTest(QorusJobTest): + def __init__(self): + super(QorusJobTest, self).__init__("basics-process-orders-job", "1.0") + self.addTestCase("job test", self.test1) + self.main() + + def test1(self): + action = RunJob() + self.exec(action) + + action = RunJobResult(OMQ.StatComplete) + self.exec(action) + +mytest = BasicsProcessOrdersJobTest() \ No newline at end of file diff --git a/03_basics_building_blocks/01_exchange_rates_app/16_testing/qore/BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW.qtest b/03_basics_building_blocks/01_exchange_rates_app/16_testing/qore/BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW.qtest new file mode 100755 index 0000000000000000000000000000000000000000..7fd74f5abda099147dadb364123bedefef4be96d --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/16_testing/qore/BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW.qtest @@ -0,0 +1,52 @@ +#! /usr/bin/env qore + +# -*- mode: qore; indent-tabs-mode: nil -*- +# test file: BASICS-EXCHANGE-CURRENCY-WORKFLOW +# author: Alzhan Turlybekov (Qore Technologies) + +%new-style +%strict-args +%require-types +%enable-all-warnings + +%requires QorusInterfaceTest + +%exec-class Test + +class Test inherits QorusWorkflowTest { + private { + const WORKFLOW_NAME = "BB-BASICS-EXCHANGE-CURRENCY-WORKFLOW"; + + const OPTIONS = Opts + { + "id": "i,id=i", + "currency_to_buy": "b,buy=s", + "currency_to_sell": "s,sell=s", + "amount": "a,amount=f", + "comment": "c,comment=s" + }; + } + + constructor() : QorusWorkflowTest(WORKFLOW_NAME, "1.0", \ARGV, OPTIONS) { + addTestCase(WORKFLOW_NAME, \mainTest()); + set_return_value(main()); + } + + mainTest() { + m_options = m_options + {"creation_date": date(format_date("YYYYMMDD", now())), "status": "new"}; + printf("%N\n", m_options ); + int wfiid = exec(new CreateOrder(WORKFLOW_NAME, {"orders": {m_options.id: m_options}})).wfiid(); + exec(new WaitForWfiid(wfiid)); + + hash select_hash = { + "where": { + "id": m_options.id + } + }; + + hash expected = m_options + { + "status": "new" + }; + + exec(new CheckDbTableRows("omquser", "exchange_orders", select_hash, expected)); + } +} \ No newline at end of file diff --git a/03_basics_building_blocks/01_exchange_rates_app/16_testing/qore/BB-BASICS-PROCESS-ORDERS-WORKFLOW.qtest b/03_basics_building_blocks/01_exchange_rates_app/16_testing/qore/BB-BASICS-PROCESS-ORDERS-WORKFLOW.qtest new file mode 100755 index 0000000000000000000000000000000000000000..0421f6fc5574ad321e35890c4327e6a501568094 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/16_testing/qore/BB-BASICS-PROCESS-ORDERS-WORKFLOW.qtest @@ -0,0 +1,67 @@ +#! /usr/bin/env qore + +# -*- mode: qore; indent-tabs-mode: nil -*- +# test file: BASICS-PROCESS-ORDERS-WORKFLOW +# author: Alzhan Turlybekov (Qore Technologies) + +%new-style +%strict-args +%require-types +%enable-all-warnings + +%requires QorusInterfaceTest +%requires SqlUtil + +%exec-class Test + +class Test inherits QorusWorkflowTest { + private { + const WORKFLOW_NAME = "BB-BASICS-PROCESS-ORDERS-WORKFLOW"; + const DATASOURCE_NAME = "omquser"; + const TABLE_NAME = "exchange_orders"; + + const OPTIONS = Opts + { + "id": "i,id=i", # id of the exchange order from the database + "user_email": "e,email=s" + }; + + const RATES = { + "exchange_rates": { + "eur": 1.0, + "czk": 25.0, + "usd": 22.0 + } + }; + } + + constructor() : QorusWorkflowTest(WORKFLOW_NAME, "1.0", \ARGV, OPTIONS) { + addTestCase(WORKFLOW_NAME, \mainTest()); + set_return_value(main()); + } + + mainTest() { + printf("%N\n", m_options); + + hash select_hash = { + "where": { + "id": m_options.id + } + }; + + # check if the order is in the database + exec(new CheckDbTableRows(DATASOURCE_NAME, TABLE_NAME, select_hash, {"status": "new"})); + + SqlUtil::AbstractTable exchange_orders_table = get_sql_table(DATASOURCE_NAME, TABLE_NAME); + hash result = exchange_orders_table.selectRow(select_hash); + result.user_email = m_options.user_email; + + printf("%N\n", m_options + {"orders": list(result,),} + RATES); + int wfiid = exec(new CreateOrder(WORKFLOW_NAME, + { + "orders": list(result,), + } + RATES)).wfiid(); + exec(new WaitForWfiid(wfiid)); + + exec(new CheckDbTableRows(DATASOURCE_NAME, TABLE_NAME, select_hash, {"status": "completed"})); + } +} \ No newline at end of file diff --git a/03_basics_building_blocks/01_exchange_rates_app/16_testing/qore/BB_BasicsExchangeCurrencyApiService.qtest b/03_basics_building_blocks/01_exchange_rates_app/16_testing/qore/BB_BasicsExchangeCurrencyApiService.qtest new file mode 100755 index 0000000000000000000000000000000000000000..ed7e317d471f4c34e284951a19578b0c1fe9ff13 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/16_testing/qore/BB_BasicsExchangeCurrencyApiService.qtest @@ -0,0 +1,39 @@ +#! /usr/bin/env qore + +# -*- mode: qore; indent-tabs-mode: nil -*- +# test file: BasicsExchangeCurrencyApiService +# author: Alzhan Turlybekov (Qore Technologies) + +%new-style +%strict-args +%require-types +%enable-all-warnings + +%requires QorusInterfaceTest + +%exec-class Test + +class Test inherits QorusServiceTest { + private { + const SERVICE_NAME = "bb-basics-exchange-currency-api-service"; + + const OPTIONS = Opts + { + "id": "i,id=i", + "currency_to_buy": "b,buy=s", + "currency_to_sell": "s,sell=s", + "amount": "a,amount=f", + "comment": "c,comment=s" + }; + } + + constructor() : QorusServiceTest(SERVICE_NAME) { + addTestCase("mainTest", \mainTest()); + set_return_value(main()); + } + + mainTest() { + printf("%N\n", m_options); + auto response = exec(new CallService("user." + SERVICE_NAME + ".exchange", m_options)).getResult(); + printf("worfklow instance id: %y\n", response); + } +} \ No newline at end of file diff --git a/03_basics_building_blocks/01_exchange_rates_app/16_testing/qore/BB_BasicsProcessOrdersJob.qtest b/03_basics_building_blocks/01_exchange_rates_app/16_testing/qore/BB_BasicsProcessOrdersJob.qtest new file mode 100755 index 0000000000000000000000000000000000000000..f7537f7ff0edd4a32f3af6a533dca52e0f2f8df7 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/16_testing/qore/BB_BasicsProcessOrdersJob.qtest @@ -0,0 +1,26 @@ +#! /usr/bin/env qore + +# -*- mode: qore; indent-tabs-mode: nil -*- +# test file: BasicsProcessOrdersJob +# author: Alzhan Turlybekov (Qore Technologies) + +%new-style +%strict-args +%require-types +%enable-all-warnings + +%requires QorusInterfaceTest + +%exec-class Test + +class Test inherits QorusJobTest { + constructor() : QorusJobTest("bb-basics-process-orders-job", "1.0") { + addTestCase("mainTest", \mainTest()); + set_return_value(main()); + } + + mainTest() { + auto result = run(); + printf("Result: %N\n", result); + } +} \ No newline at end of file diff --git a/03_basics_building_blocks/01_exchange_rates_app/README.md b/03_basics_building_blocks/01_exchange_rates_app/README.md new file mode 100644 index 0000000000000000000000000000000000000000..0522133ef2c18111c3a4c144914cea521d4103a9 --- /dev/null +++ b/03_basics_building_blocks/01_exchange_rates_app/README.md @@ -0,0 +1,38 @@ +# Exchange rates application +This application allows user to convert currencies and get information about actual exchange rates. +It touches upon fundamental Qorus objects such as workflows, services and jobs. Also it covers topics such as mappers, fail-safe processing using validation and error function and it shows how these objects can communicate with each other. + +List of tutorials: +1. [Implementing Qorus Jobs](01_job) +2. [Implementing Qorus Services](02_service) +3. [Implementing Qorus Workflows](03_workflow) +4. [Workflow Serial Steps](04_workflow_serial_steps) +5. [Workflow Parallel Steps](05_workflow_parallel_steps) +6. [Exchange Rates API User Connection](06_exchange_api_user_connection) +7. [Get Exchange Rates using Job](07_job_get_exchange_rates) +8. [Exchange Rates Datasource Connection](08_exchange_rates_datasource_connection) +9. [Exchange Currency Workflow](09_exchange_currency_workflow) +10. [Exchange Currency Workflow with Mapper](10_exchange_currency_workflow_with_mapper) +11. [Exchange Currency API Service](11_exchange_currency_api_service) +12. [Process Orders Workflow](12_process_orders_workflow) +13. [Process Orders Job](13_process_orders_job) +14. [Workflow Validation Code](14_workflow_validation_code) +15. [Workflow Error Function](15_workflow_error_function) +16. [Class-based step configuration items](16_workflow_step_configuration_items) +17. [Testing](17_testing) +--- + +## Design + + + +

This browser does not support PDFs. Please download the PDF to view it: Download PDF.

+ +
+ + + + + + +
[Go Back to: Basics of Qorus](../)[Start tutorial: Implementing Qorus jobs](01_job)
diff --git a/03_basics_building_blocks/01_exchange_rates_app/img/exchange_app_design.pdf b/03_basics_building_blocks/01_exchange_rates_app/img/exchange_app_design.pdf new file mode 100644 index 0000000000000000000000000000000000000000..4fece8af26ada1e97d55d0762eae8882b228f341 Binary files /dev/null and b/03_basics_building_blocks/01_exchange_rates_app/img/exchange_app_design.pdf differ diff --git a/03_basics_building_blocks/README.md b/03_basics_building_blocks/README.md new file mode 100644 index 0000000000000000000000000000000000000000..da1ae225927b1351feb8bfece822cfc16a777c1a --- /dev/null +++ b/03_basics_building_blocks/README.md @@ -0,0 +1,30 @@ +# Basics + +## What is Qorus? +Qorus Integration Engine® is an integration platorm designed for the rapid development and operations of fault-tolerant interfaces. The "fault-tolerant" part is important, as we believe that error handling must be included from the very first release of an interface project, and furthermore that error handling be consistent, configurable, and automatic regarding technical errors. Therefore every solution delivered in Qorus should be failsafe or tolerant of technical errors. + +The goal of Qorus is to provide very high business flexibility and low costs, meaning low development and also low operational costs. Qorus is particularly strong with the fault-tolerant execution of stateful integration solutions and is also suitable for solving a wide variety of other integration challenges. + +Read more in the documentation: [Introduction to the Qorus Integration Engine](https://qoretechnologies.com/manual/qorus/latest/qorus/sysrefintro.html) + +## Features of Qorus Integration Engine® + +The following are features of Qorus Integration Engine® supporting the goals mentioned above: + +* Dynamic loading and unloading of interface configurations and code +* Low-code/no-code focus on configuration over coding; coding should be minimized and building blocks should be used instead +* Focus on simplicity; the platform should handle the complexity, not the developer +* Complete operational traceability with full visibility of configuration and code in the operational UI resulting in an unrivaled DevOps platform +* Focus on quality and repeatable results with direct support for Continuous Integration and Continuous Delivery +* Consistent platform-defined automatic error handling of technical errors and error recovery for stateful interfaces (workflows) + +## Tutorials +1. [Exchange rates application](01_exchange_rates_app) + +--- + + + + + +
[Go Back to: Qorus training program](https://git.qoretechnologies.com/qorus/training/tree/master/03_basics)
diff --git a/building-blocks b/building-blocks new file mode 160000 index 0000000000000000000000000000000000000000..8fde1e62721af254671d42fc9d58dbf59d4849f7 --- /dev/null +++ b/building-blocks @@ -0,0 +1 @@ +Subproject commit 8fde1e62721af254671d42fc9d58dbf59d4849f7