@elastic.io/oih-standard-library
v2.0.3
Published
Library for OIH standards implementation
Downloads
86
Readme
OIH Standard Library
Table of Contents
Actions
Upsert Object
For implementing UpsertObject action in most cases you need to extend from UpsertObjectById
or UpsertObjectByUniqueCriteria
classes and following methods:
lookupObject(criteria: any, objectType: any, cfg?: any, msg?: any)
createObject(object, cfg, msg)
updateObject(criteria: any, objectType: any, object: any, cfg?: any, msg?: any)
If you need completely custom behaviour you can extend fromUpsert
class and overrideprocess
, in this case following OIH standard is your responsibility.
Note:
- Its your responsibility to ensure that
lookupObject
returns only 1 object. lookupObject
method must returnnull
to indicate that object hasn't been found.
Upsert Object By Id
- Create Action
- Create class that extends
UpsertObjectById
class - Implement
lookupObject()
,createObject()
,updateObject()
methods. Example below. - Optional override methods:
getType
- default implementation expectsobjectType
to be present in input configuration,getCriteria
- by default, returnsid
value from body in input message or null,getObjectFromMessage
- by default, returnsbody
object from input message.
- Create instance of your class and pass
logger
to constructor.const myUpsertObjectByIDImpl = new MyUpsertObjectByIdImpl(logger);
- Export
myUpsertObjectByIDImpl.process(msg, cfg, snapshot)
as process method. Example below.
Example of Usage
Works in sailor > 2.4.2 Upsert Object Action
const { UpsertObjectById } = require('@elastic.io/oih-standard-library/lib/actions/upsert');
class MyUpsertObjectByIdImpl extends UpsertObjectById {
async lookupObject(criteria, objectType, cfg, msg) {
const existObject = await getObjectInAPI(criteria, objectType, cfg, msg);
if (existObject) { return existObject; }
return null;
}
async createObject(object, cfg, msg) {
return await createObjectInAPI(object, cfg, msg);
}
async updateObject(criteria, objectType, object, cfg, msg) {
return await updateObjectInAPI(criteria, objectType, object, cfg, msg);
}
// You can also override getType, getCriteria and getObjectFromMessage methods if needed
}
async function process(msg, cfg, snapshot = {}) {
const upsertAction = new MyUpsertObjectByIdImpl(this.logger); // Sailor version > 2.4.2
return upsertAction.process(msg, cfg, snapshot);
}
module.exports.process = process;
Upsert By Unique Criteria
The same as Upsert Object By Id but getCriteria
method by default returns value of field uniqueCriteria
that is defined in input configuration, from the body in the input message.
Create Object
Similar to upsert object but needed for the following cases:
- Objects that can be created but can not be updated after creation (e.g. Invoices)
- Cases where you want to create an object and its children
- Cases where the id of the object includes information in the object (e.g. The ID of a sales line is the sales order ID + SKU).
Example Use Case
See above.
Lookup Object (at most 1)
For implementing LookupObject action in most cases you need to extend from LookupObjectById
or LookupObjectByUniqueCriteria
classes.
If you need completely custom behaviour you can extend from LookupObject
class and override process and lookupObject method,
in this case following OIH standard is your responsibility.
Lookup Object By Id
Note:
- Its your responsibility to ensure that
lookupObject
returns only 1 object. This implementation assume thatid
is unique forobjectType
. lookupObject
method must returnnull
to indicate that object hasn't been found.
- Create Action
- Create class that extends LookupObjectById class
- Override
lookupObject()
methods. Example below. - Optional override
getType
andgetId
methods. Default implementation expectsobjectType
to be present in input configuration andid
in input message. - Create instance of your class and pass
logger
to constructor.const myLookupByIDImpl = new MyLookupByIdImpl(logger);
- Export
myLookupByIDImpl.process(msg, cfg, snapshot)
as process method. Example below.
Example of Usage
Works in sailor > 2.4.2 Lookup Action
const { LookupById } = require('@elastic.io/oih-standard-library/lib/actions/lookupObject');
class MyLookupByIDImpl extends LookupById {
async lookupObject(id, objectType, cfg, msg) { // In most cases you need just id, type and cfg
const result = await lookupObjectInAPI(id, objectType, cfg, msg); // Lookup object
return result; // Return found object. If you return null, it will indicate no object found.
}
// You can also override getType and getId methods if needed
}
async function process(msg, cfg, snapshot = {}) {
const lookupAction = new MyLookupByIDImpl(this.logger); // Sailor version > 2.4.2
return lookupAction.process(msg, cfg, snapshot);
}
module.exports.process = process;
Lookup Object By Unique Criteria
Note:
- In case when
lookupObject
method has found more than 1 object it must return an array or throw error. lookupObject
method must returnnull
to indicate that object hasn't been found.
- Create Action
- Create class that extends LookupObjectByUniqueCriteria class
- Override
lookupObject()
methods. Example below. - Optional override
getType
andgetCriteria
methods. Default implementation expectsobjectType
to be present in input configuration andid
in input message. - Create instance of your class and pass
logger
to constructor.const myLookupObjectByUniqueCriteriaImpl = new MyLookupObjectByUniqueCriteriaImpl(logger);
- Export
myLookupObjectByUniqueCriteriaImpl.process(msg, cfg, snapshot)
as process method. Example below.
Example of Usage
Works in sailor > 2.4.2 Lookup Action
const { LookupByUniqueCriteria } = require('@elastic.io/oih-standard-library/lib/actions/lookupObject');
class MyLookupObjectByUniqueCriteriaImpl extends LookupByUniqueCriteria {
async lookupObject(criteria, objectType, cfg, msg) { // In most cases you need just criteria, type and cfg
const result = await lookupObjectInAPI(id, objectType, cfg, msg); // Lookup object
if (result instanceof Array) {
// return it or throw error. If result.length > 1 error will be thrown by action
}
return result; // Return found object. If you return null, it will indicate no object found.
}
// You can also override getType and getCriteria methods if needed
}
async function process(msg, cfg, snapshot = {}) {
const lookupAction = new MyLookupObjectByUniqueCriteriaImpl(this.logger); // Sailor version > 2.4.2
return lookupAction.process(msg, cfg, snapshot);
}
module.exports.process = process;
Lookup Objects (Plural)
For implementing Lookup Objects (plural), you will have to extend from LookupObjects
class.
You must provide the functions getObjectsByCriteria(objectType, criteria: Array, msg?, cfg?, pageSize?, firstPage?, orderBy?):
and getMetaModel(cfg)
Steps for implementation
- Create Action
- Create class that extends
LookupObjects (plural)
- Override
getObjectsByCriteria()
andgetMetaModel()
- Optionally override methods like
getCriteria()
. Default implementation assumes a specific format to the metadata fields as provided bygetInMetadata()
, so you may wish to also override this function - Create instance of your class and pass the
logger
method to the constructor. - Export
myLookupObjects.process(msg, cfg, snapshot)
as process method. Example below:
Example
const { LookupObjects } = require('@elastic.io/oih-standard-library/lib/actions/lookupObjects');
class MyLookupObjects extends LookupObjects {
async getObjectsByCriteria(objectType, criteria, msg, cfg) { // In most cases you need objectType, criteria
const results = await lookupObjects(objectType, criteria); // Perform lookup
return results; // Return lookup results
}
async getMetaModel(cfg) {
const metaModel = {};
const properties = {
fields: ['field1', 'field2'],
conditions: ['=', 'in'],
orderEnum: ['asc', 'desc'],
includeOrder: false,
additionalFields: 'additionalField',
criteriaLinkEnum: ['and', 'or'],
disableCriteriaLink: false
};
metaModel.in = this.getInMetadata(cfg, properties);
metaModel.out = {
type: object,
...
};
return metaModel;
}
}
async function process(msg, cfg, snapshot = {}) {
const deleteAction = new MyLookupObjects(this.logger, this.emit); // Sailor version > 2.4.2
return deleteAction.process(msg, cfg, snapshot);
}
module.exports.process = process;
Delete Object
For implementing Delete action in most cases you need to extend from DeleteById
or DeleteByUniqueCriteria
classes.
If you need completely custom behaviour you can extend from Delete
class and override process and deleteObject method,
in this case following OIH standard is your responsibility.
Delete By ID
Note:
- Its your responsibility to ensure that deleteObject deletes only 1 object. This implementation assume that
id
is unique forobjectType
. - If
deleteObject
method is returningnull
empty object would be emitted. You can indicate withnull
that object hasn`t been deleted or found.
- Create Action
- Create class that extends DeleteById class
- Override
deleteObject()
methods. Example below. - Optional override
getType
andgetId
methods. Default implementation expectsobjectType
to be present in input configuration. For unique criteria:uniqueCriteria
field name in input configuration and value in input message. - Create instance of your class and pass
logger
to constructor.const myDeleteByIDImpl = new MyDeleteByIdImpl(logger);
- Export
myDeleteByIDImpl.process(msg, cfg, snapshot)
as process method. Example below.
Example of Usage
Works in sailor > 2.4.2 Delete Action
const { DeleteById } = require('@elastic.io/oih-standard-library/lib/actions/delete');
class MyDeleteByIDImpl extends DeleteById {
async deleteObject(id, cfg, msg) { // In most cases you need just id, type and cfg
const deletedID = await deleteObjectInAPI(id, cfg, msg); // Delete object
return deletedID; // Return deleted object ID. If you return null, empty object would be emitted.
}
// You can also override getType and getId methods if needed
}
async function process(msg, cfg, snapshot = {}) {
const deleteAction = new MyDeleteByIDImpl(this.logger); // Sailor version > 2.4.2
return deleteAction.process(msg, cfg, snapshot);
}
module.exports.process = process;
Delete By Unique Criteria
Note:
- If more than 1 object was found with same uniqueCriteria, error would be thrown by this implementation
- If
deleteObject
method is returningnull
empty object would be emitted. You can indicate withnull
that object hasn`t been deleted or found.
- Create Action
- Create class that extends DeleteById class
- Override
findObjectByCriteria
anddeleteObject()
methods. Example below. - Optional override
getType
andgetCriteria
methods. Default implementation expectsobjectType
to be present in input configuration. For unique criteria:uniqueCriteria
field name in input configuration and value in input message. - Create instance of your class and pass
logger
to constructor.const myDeleteByCriteriaImpl = new MyDeleteByCriteriaImpl(logger);
- Export
myDeleteByCriteriaImpl.process(msg, cfg, snapshot)
as process method. Example below.
Example of Usage
Works in sailor > 2.4.2 Delete Action
const { DeleteByUniqueCriteria } = require('@elastic.io/oih-standard-library/lib/actions/delete');
class MyDeleteByCriteriaImpl extends DeleteByUniqueCriteria {
async findObjectByUniqueCriteria(criteria, type, cfg, msg) { // In most cases you need just criteria, type and cfg
// criteria sructure {
// uniqueCriteria: 'fieldName',
// value: 'fieldValue',
// }
const object = await findObjectInAPI(criteria, type, cfg, msg); // objects that match this criteria
const numberOfObjects = object.count; // You do not need to check or throw error in this case, action implementation will do this
return { // return structure must contain object and numberOfObjects found
object,
numberOfObjects
}
}
async deleteObject(object, cfg, msg) { // In most cases you need just object, type and cfg
const deletedID = await deleteObjectInAPI(object, cfg, msg); // Delete object
return deletedID; // Return deleted object ID if you return null, empty object would be emitted.
}
// You can also override getType and getId methods if needed
}
async function process(msg, cfg, snapshot = {}) {
const deleteAction = new MyDeleteByCriteriaImpl(this.logger); // Sailor version > 2.4.2
return deleteAction.process(msg, cfg, snapshot);
}
module.exports.process = process;
Lookup Set Of Objects
For implementing LookupObject action in most cases you need to extend from LookupSetOfObjectsSingleField
class or wait for LookupSetOfObjectsMultipleField
class to be implemented.
If you need completely custom behaviour you can extend from LookupSetOfObjects
class and override process and lookupSetOfObjects method,
in this case following OIH standard is your responsibility.
Notes:
- Lookup Set will handle the case when the set of items to lookup is empty.
- Lookup Set will handle casting the result into an array.
- Lookup Set will de-duplicate the incoming array and check size contraints.
- Lookup Set will make sure all the items in the set that should be there are there and then rebound or error as appropreate.
Methods that must be overridden:
async lookupSetOfObjects(criteriaFieldName, objectType, itemsToLookup, cfg, msg)
- Given an field name, object type and array of ids, find the objects in that set and return as an array
Methods that can be overridden (if the underlying functionality doesn't work):
async process(msg, cfg, snapshot)
- Overwrite the entire process functiongetType(cfg: any, msg: any)
- Identify type from configgetItemsToLookup(msg: any, cfg: any)
- Extract item info from the bodygetMaxItemsToLookup(msg:any, cfg: any)
- Provide max size of set: defaults to 100getCriteriaFieldName(msg: any, cfg: any): string
- Extract the criteria field from configdoesItemMatch(lookupCriteriaValue: any, item:any, msg:any, cfg:any): boolean
- Check if a given object has the id value described inlookupCriteriaValue
Triggers
Get New And Updated Objects Polling
For implementing et New And Updated Objects Polling(Polling Trigger) extend from class PollingTrigger
and override: getObjects
, getObjectsFromPage
methods.
Note:
getObjectsFromPage(options)
-options: { objectType, startTime, endTime, page, cfg, snapshot }
, wherepage
is structure with fields:pageNumber
,pageSize
.getObjectsFromPage
- must return following structure:{ 'objects': 'result of polling' 'nextPage' : 'number or object, represents next page to poll' }
.emitIndividually
behaviour expects array to be returned by method:getObjects
.
How to implement:
- Create Trigger
- Create class that extends
PollingTrigger
- Override
getObjects()
andgetObjectsFromPage()
methods - Optionally override methods any other methods, see class
PollingTrigger
- Create instance of your class and pass the
logger
nademitter
to the constructor. - Export
myPollingTrigger.process(cfg, snapshot)
as process method. Example below:
Example of Usage
const PollingTrigger = require('@elastic.io/oih-standard-library/lib/triggers/getNewAndUpdated');
class MyPollingTriggerImpl extends PollingTrigger {
async getObjects(options) {
// options: { objectType, startTime, endTime, cfg }
return api.poll(options); // Poll object from API, Note: emitIndividually expects array to be returned by this method
}
async getObjectsFromPage(options) {
// options: { objectType, startTime, endTime, page, cfg, snapshot }
// page.pageNumber - current page,
// page.pageSize - size of page to poll,
const result = await api.pollPage(objectType, page, startTime, endTime, cfg, snapshot);
return { nextPage: result.nextPage, objects: result.objects } // Must return structure like this
}
}
async function process(cfg, snapshot) {
const trigger = new MyPollingTriggerImpl(this.logger, this.emit); // Sailor version > 2.4.2
return trigger.process(cfg, snapshot);
}
module.exports.process = process;
Webhooks
Firstly, Webhook subscription handling strategy should be implemented.
For this purpose HandleWebhookSubscriptionByIdAbstractStrategy
must be extended and createWebhooks
& deleteWebhooks
methods must be implemented.
Also can be defined absolutely new strategy. For this purpose, just implement interface HandleSWebhookSubscriptionAbstractStrategy
.
Example of Usage
const { HandleWebhookSubscriptionByIdAbstractStrategy, Webhook, defaultProcessWebhook } = require('@elastic.io/oih-standard-library/lib/triggers/webhook');
// implementation of subscription create strategy
// in this class we must implement create/delete WebhookSubscription methods
class HandleWebhookSubscriptionByIdStrategy extends HandleWebhookSubscriptionByIdAbstractStrategy {
constructor(client, logger) {
super(logger);
this.client = client;
}
// According to superclass definition should return object with array of ids; Example: {ids: ['webhook1', 'webhook2']}
async createWebhooks(objectType, eventTypes) {
const webhooks = await Promise.all(eventTypes.map(eventType => this.client.create({
topic: eventType,
address: process.env.ELASTICIO_FLOW_WEBHOOK_URI,
format: 'json',
}, WEBHOOK_OBJECT_TYPE)));
return { ids: webhooks.map(webhook => webhook.id) };
}
async deleteWebhooks(input) {
await Promise.all(input.ids.map(id => this.client.delete({ id }, WEBHOOK_OBJECT_TYPE)));
}
}
// construct Webhook object using HandleWebhookSubscriptionByIdStrategy strategy
function configuredWebhook(cfg, logger) {
const credentials = {
apiKey: cfg.apiKey,
shopName: cfg.shopName,
password: cfg.password,
};
const client = new Client(credentials);
const handleWebhookSubscriptionByIdStrategy = new HandleWebhookSubscriptionByIdStrategy(client, logger);
const webhook = new Webhook(logger, handleWebhookSubscriptionByIdStrategy);
return webhook;
}
// Export startup and shutdown functions
async function startup(cfg) {
const webhook = await configuredWebhook(cfg, this.logger);
return webhook.startup(cfg);
}
async function shutdown(cfg, data) {
const webhook = await configuredWebhook(cfg, this.logger);
await webhook.shutdown(cfg, data);
}
// Export static process function
module.exports.process = defaultProcessWebhook;
module.exports.startup = startup;
module.exports.shutdown = shutdown;