Solution Framework Components
The local scope of the implementation class is accessed by using a reserved word ‘this’.
From here it is possible to access other components that are need for the implementation such as input, output, repository, factory, util …etc.
For example:
// create ns1 entity
const entity1 = this.factory.entity.nsacrnm1.entityIdentifier();
// create ns2 entity
const entity2 = this.factory.entity.nsacrnm2.entityIdentifier();
These components are the following:
Input
// Access the value of the input property
const val = this.input.propertyName
Output
// Initialize output via factory and assign value to output properties
this.output = this.factory.entity.nsacrnm.serviceIdentifier_Output();
this.output.property1 = ‘Some property value’;
Instance
// Edit instance property value
this.instance.property1 = ‘New property value’;
Entity Factory
// Create an Entity using entity factory
const entity1 = this.factory.entity.nsacrnm.Entityidentifier();
// Access entity1 properties
entity1.propIdentifier = ‘some property value’;
Error Factory
// Create an Error using error factory
const error1 = this.factory.error.nsacrnm.ErrorIdentifier();
// Access error1 properties
error1.errorDescription = ‘Some Error Description’;
error1.errorMessage = ‘Some Error Message;
Reference Factory
// Create an Entity Reference for root entity using reference factory
const ref1 = this.factory.reference.nsacrnm.RootEntityIdentifier(‘someEntityId);
// Load referenced entity
const rootEntity = await ref1.load();
// create a reference to an external entity that takes an input
// Create input object via factory
const input = this.factory.entity.cptest.ExternalEntityIdentifier_ConstInput();
input.propertyIdentifier = 'Some property value';
External Factory
This calls the constructor method of the external entity. It returns the loacal insatnce of the external Entity from which it is posssible to access the Associated Properties as well as call the Load and Validade methods.
// Create external entity reference
const extEntity = await this.factory.external.cptest.ExternalEntityIdentifier(input);
// Access the associated properties
extEntity.assProp1;
// Load external entity - no input needed
extEntity.load();
// Validate external or update local - no input needed
extEntity.validate();
Event Factory
// Create an Event using Event factory
const event1 = this.factory.event.nsacrnm.EventIdentifier();
// create event payload using entity factory
const payoad = this.factory.entity.nsacrnm.EntityIdentifier();
// set payload properties
payload.propIdentifier = ‘some property value’;
// Set event payload
event1.payload = payoad;
// publish event
Await event1.publish();
Schema Factory
This is used to create schema objects. It returns the properties that belong to the
schema (i.e. the entire schema object/array/oneOf). The properties returned are
editable. This is accessible from operation scripts and .test.ts
files of operations.
// Create a Schema using schema factory
const schema1 = this.factory.schema.nsacrnm.SchemaIdentifier();
// Access schema1 properties
schema1.propIdentifier = ‘some property value’;
Repository
This is used to access the datastore of a solution and retrieve an existing instance of a root entity given an id or a filter. It is also possible to create a completely new root entity through this component by accessing the factory command. It returns the entity object (or an array of entity objects when searched by filter). From each entity object all the properties and instance commands linked to this entity are available. The returned values are read-only.
// Access the repository of the specific root entity and choose the find
// Find takes an object argument and a filter argument
// In the Find Configuration Object,
// The first one is a Boolean indicated whether all children should be included in the filtering (true) or not (false).
// The second is the limit (optional field) that takes string of a pair of numbers as a value where the first number is the <offset> and the second is the <amount> of entries that will be returned
// The third is the sortBy argument that takes a string of a pair where the first element is the local identifier of a property and the second is either ASC or DESC
const rootEntityInstance = this.repo.nsacrnm.RootEntityIdentifier.find(FindConfigurationObj, Filter);
//Example
const rootEntityInstance = this.repo.nsacrnm.RootEntityIdentifier.find({includeSubentities: true, limit: “2,20”, sortBy: “customerID,DESC”}, `name == "${name}"`);
// Find entity by Id
const rootEntityInstance = this.repo.nsacrnm.RootEntityIdentifier. findById(`EntityId`);
Filter expressions
Using filter supported by datastore, one can do filtering on first level as well as filtering on sub-structures of the aggregate.
This includes:
- And/ OR oeprators to combine diffrent filter criteria
- Use of operators.
Operators and Their Alias
Name | Symbol | Alias |
EQ | == | =eq= |
NEQ | != | =neq= |
TEXT_STARTS_WITH | ^* | =tsw= |
TEXT_ENDS_WITH | *$ | =tew= |
TEXT_CONTAINS | ** | =tco= |
LT | < | =lt= |
LTE | <= | =lte= |
GTE | >= | =gte= |
GT | > | =gt= |
SUBCLASS | =* | =sc= |
IN | =in= | |
CONTAINS | =co= | |
NEARBY | =nb= |
Uncommon operators
^* | Starts-With-OperatorStarts-with text search, always not case sensitive.
*$ | Ends-With-Operator
Ends-with text search, always not case sensitive.
** | Text-Contains-Operator
Contains text search, always not case sensitive.
=in= | In-Operator
[„a“, „b“, „c“]
Value must be a list of strings (without wildcard-support) in array-notation separated by ,
=nb= | Nearby-Operator
Nearby operator for GeoPoint, The value to compare must always be a combination of the decimals latitude, longitude and range and will be accepted in the following order:
[<lat>,<lon>;<range>]
All three elements must be given. The range is the radius in meter around the given geoPoint in which the result must be.
location=nb=[49.011370,12.095336;5000] // all elements witin a range of 5km of the point
location=nb=[700.43,-9999;3] // would be invalid
location=nb=[49.011370,12.095336;-300] // results into a $minDistance 300m in underlying mongoDB
=co= | Contains-Operator
Contains operator for list-operations. It returns true, if the exists one object in the list, that fits to the given filterExpression.
For example
entityList=co=((entityString==”knowis”)and(entityNumber>=200))
will only result into true, if the list contains an object which fits both of the comparisons joined. If only one comparison is needed, the == operator can be used instead.
Services
// Initialize service input via factory and assign value to input properties
const input = this.factory.entity.nsacrnm.serviceIdentifier_Input();
input.property1 = ‘Some property value’;
// Create a service instance
const service1 = await this.services.nsacrnm.serviceIdentifier(input);
// Access service output
const val = service1.output.propIdentifier;
Solution metadata
It is possible to access the Solution Details for logging purposes by using the
this.solution
component.
const acrnm = this.solution.acronym;
const name = this.solution.name;
const category = this.solution.category;
The values in the this.solution
are read-only.
InstanceOf check
Entity Instance Check
In the implementation file of a service, command, agent and operation entity instance check can be done through isInstanceOf functionality, the entity instance check will also check for the entity hierarchy.
// check an object
if (this.isInstanceOf.entity.ns1.BlueCar(car)) {
// now one can access the special property that only blue car has
const prop = car.specialPropOfBlueCar;
}
// check BlueCar is a Car (Since BlueCar is a child of Car, the entity instance check will also check for the entity hierarchy)
if (this.isInstanceOf.entity.ns1.Car(blueCar)) {
// now one can access the special property that only car has
const prop = blueCar.specialPropOfCar;
}
Error Instance Check
In the implementation file of a service, command, agent and operation error instance check can be done by using isInstanceOf functionality, thus enable to code handling logic for different error instances
General Errors Checking
try {
// try some logic
} catch (e) {
// check for General error
if (this.isInstanceOf.error.GeneralError(e)) {
// now can handle this general error
}
// check whether the validation of an entity (used as input or instance) fails
if (this.isInstanceOf.error.ValidationError(e)) {
// now can handle this validation error
}
// check for a child of abstract BusinessError
if (this.isInstanceOf.error.BusinessError(e)) {
// now can handle this Business error
}
// check whether an action (e.g. a command) is not currently available
if (this.isInstanceOf.error.ActionNotAvailableError(e)) {
// now can handle this ActionNotAvailable error
}
// check whether an aggregate is found in the datastore
if (this.isInstanceOf.error.AggregateNotFound(e)) {
// now can handle this AggregateNotFound error
}
// check whether an external request (i.e. this.util.request) caused an error
if (this.isInstanceOf.error.ExternalRequestError(e)) {
// now can handle this ExternalRequest error
}
// check whether an internal request caused an error
if (this.isInstanceOf.error.InternalRequestError(e)) {
// now can handle this InternalRequest error
}
}
Business Errors Checking
try {
// try some logic
} catch (e) {
// check whether the error matches any of the errors modelled in the SOlution Designer
if (this.isInstanceOf.businessError.ns1.NoBalanceAvailable(e)) {
// now can handle this specific business error
}
if (this.isInstanceOf.error.ValidationError(e)) {
// now can handle this validation error
}
if (this.isInstanceOf.businessError.ns2.NoCustomerFound(e)) {
// now can handle this specific business error
}
}
API Dependencies
As a precondition the API needs to be defined as an API Dependency in an Integration Namespace and needs an Open API 3.0- or Swagger 2.0 specification. If this is given, the API could be called within the implementation of an Integration Service.
Call an operation of the API
// call operation "getPets" in integration API "petstore"
const result = await this.apis.petstore.getPets();
Call an operation with path parameters and query parameters
If path parameters or query parameters are defined, the function offers the possiblity to set these parameters which are identified by the name. Thevalues of the path parameters as well as the query parameters are expected to be Javascript objects where each key is the name of the parameter. The URL and ca_certs are used automatically according to the specification.
// findPets by owner (pathParameter) and filter by status and age (queryParameter)
const result = await this.apis.petstore.getPetsOfOwner({
owner: 'MyOwner'
}, {
status: 'alive',
age: 5
});
Call an operation with request body
If a request body is defined for the operation, the function offers the possiblity to set the request body as a Javascript object.
// add new pet using "pet data" (request body)
await this.apis.petstore.addPet({
owner: 'MyOwner',
status: 'alive',
age: 1
});
Read result of operation
The result of the operation always consists of the status code and the actual response data. Both are returned from the operation call.
try {
const result = await this.apis.petstore.getPets();
// read result depending on status code
if (result.status === 200) {
// everything is good --> read data
const pets = result.data;
console.log(pets[0].status);
}
} catch (e) {
// handle error
this.util.log.error(e.message);
throw e;
}
API Binding
The value of the API Bindings is provided under this.apiBindings.APIIdentifier()
// get binding of petstore API
const bindingValue = await this.apiBindings.getPetstore();
Logging
// the log.error() can take an array of argumnets
this.util.log.error(‘Error Message’, errorObject);
// the log.debug() can take an array of argumnets
this.util.log.debug(‘Debug Message’, errorObject);
// the log.info() can take an array of argumnets
this.util.log.info(‘Info Message’, object);
// the log.warn() can take an array of argumnets
this.util.log.warn(‘Warning Message’, object);
Audit Logging
// the revlog.publish() can take an array of argumnets
this.util.revlog.publish(‘Audit Message’, Object);
Global Kafka consumers can be implemented to consume the Audit log messages and persist them in a secure data store.
HTTP Requests
To make an HTTP request against external APIs.
The this.util.requestprovides functions for the get,post,del,patch, and put HTTP operations.
const response = await this.util.request.get(url: string, queryParams?: HTTPQueryParams, head-ers?: HTTPHeaders, sslConfig?: SSLConfig);
OR
const response = await this.util.request.del(url: string, queryParams?: HTTPQueryParams, head-ers?: HTTPHeaders, sslConfig?: SSLConfig);
OR
const response = await this.util.request.patch(url: string, queryParams?: HTTPQueryParams, body?: any, headers?: HTTPHeaders, sslConfig?: SSLConfig);
OR
const response = await this.util.request.post(url: string, queryParams?: HTTPQueryParams, body?: any, headers?: HTTPHeaders, sslConfig?: SSLConfig);
OR
const response = await this.util.request.put(url: string, queryParams?: HTTPQueryParams, body?: any, headers?: HTTPHeaders, sslConfig?: SSLConfig);
Request Context
// get request context via this._context
const requestContext = this._context.requestContext;
Request
This provides access to operation request it contains the below components.
Path
This is used to give access to the request path parameters of an operation.
// access operation request path parameters
const val = this.request.path.pathParamIdentifier;
Query
This is used to give access to the request query parameters of an operation.
// access operation request query parameters
const val = this.request.query.queryParamIdentifier;
Body
// access operation request body
const body = this.request.body;
const val = this.request.body.popertyIdentifier;
Response
This provides access to operation request it contains the below components
Status
// set response status to be returned
this.response.status = 200;
// access operation response status
const status = this.response.status;
Body
This is used to access the response body of an operation to the user. If the body is a primitive type schema, then a value can be directly accessed / assigned. Otherwise, the body must be initialized to its corresponding schema type (using the schema factory component) and then each property should be initialized accordingly.
// initialize response body via schema factory
const body = this.factory.nsacrnm.schema.schemaIdentifier();
// set operation response body
this.response.body = body;
const val = this.response.body.popertyIdentifier;
Headers
// access operation request body
const headers = this.response.headers;
Install new module dependencies
In the solution, you can install two kind of dependencies: devDependencies and dependencies.
DevDependencies
Modules that are only needed for local development
and testing.
Dependencies
Modules required by your application in production.
Install DevDependenices
In the solution directory, run the command npm install moduleName
-D
Install Dependencies
In the solution directory, run the command npm install
moduleName
Different scopes inside each implementation
Below is an Illustrative table for the diffrent scopes inside each implementation element.
Service | Command | Agent | Operation | External Entity construct | External Entity load | External Entity validate | |
this.input | √ | √ | √ | √ | |||
this.output | √ | √ | √ | √ | √ | √ | |
this.instance |
|
√ | √ | √ | √ | ||
this.factory.entity | √ | √ | √ | √ | √ | √ | |
this.factory.error | √ | √ | √ | ||||
this.factory.reference | √ | √ | √ | ||||
this.factory.event | √ | √ | √ | ||||
this.factory.schemas | √ | ||||||
this.repo | √ | √ | √ | √ | √ | ||
this.services | √ | √ | √ | √ | √ | ||
this.util.log | √ | √ | √ | √ | √ | √ | √ |
this.util.revlog | √ | √ | √ | √ | √ | √ | √ |
this.request.path | √ | ||||||
this.request.query | √ | ||||||
this.request.body | √ | ||||||
this.response.headers | √ | ||||||
this.response.status | √ | ||||||
this.response.body | √ | ||||||
this.requestContext | √ | √ | √ | √ |