Skip to main content

transform — Manipulate Data

Transform Flows are used to manipulate message data.

Interface

The interface TransformFlow can be defined as:

type TransformFunc = (msg: IMsg, context: IMessageContext) => IMsg
type TransformFlow =
| TransformFunc
| { kind: 'transform'; transform: TransformFunc }

Refer to the Message Interface for more information on the IMsg exposed methods and transforming the data in the message.

Refer to the Context Object for more information on the IMessageContext type.

The transformer functions of the class retuns back the class instance, so you can chain them together.

Use Case

The Ingestion and Route each allow for an array of flows. The transform flow, is one of the allowed flows in either one of these arrays. If a message is transformed in an ingestion flow, then all of the latter flows in the ingestion, and all of the routes will use the newly transformed data. If a message is transformed in a route, then only the latter flows in that route will use the newly transformed data. Routes are called asynchronously, The Ingestion flows are called synchronously.

const channel: ChannelConfig = {
// ...
ingestion: [
// transformers here
],
routes: [
{
kind: 'route',
name: 'Route 1',
flows: [
// transformers here
],
},
{
kind: 'route',
name: 'Route 2',
flows: [
// transformers here
],
}
]
};

Example

Here is an example of a transformer that takes the field PV1-3 and adds a prefix to it:

locationPrefix.ts
import gofer, { ChannelConfig, TransformFunc } from '@gofer-engine/engine';

const transform: TransformFunc = (msg) =>
msg.map('PV1-3[1].1', <T>(location: T) => `HOSP.${location}` as T);

// OOP style
gofer.listen('tcp', 'localhost', 5515)
.name('Hosp Location Channel with OOP')
.transform(transform)
.ack()

// Config style
const channelConfig: ChannelConfig = {
name: 'Hosp Location Channel with Configs',
source: {
kind: 'tcp',
tcp: {
host: 'localhost',
port: 5516,
},
},
ingestion: [{
kind: 'transform',
transform,
}],
};

gofer.configs([channelConfig]);

Advanced Example

Since Gofer Engine uses native TypeScript/JavaScript, you can use any of the recipes you are familiar with to pass variables and create advanced transformer logic. You could even keep your transformers in a common directory and import them where needed into your channels.

Let's refactor the previous example a little bit and make the transformer function reusable for other paths and allowing a passed through prefix/suffix.

addPrefix.ts
import { TransformFunc } from '@gofer-engine/engine';

export const addPrefix =
(path: string, prefix?: string): TransformFunc =>
(msg) =>
msg.map(
path,
<T>(value: T) => `${prefix ?? ''}${value}` as T,
);
prefixedChannel.ts
import gofer from '@gofer-engine/engine';
import { addPrefix } from './addPrefix';

gofer
.schedule(
'HL7v2',
{
second: 0,
minute: 15,
},
true,
)
.file({
directory: '//fileshare/your_directory',
filterOptions: {
filenameRegex: '*\\.hl7$',
fileAgeMinMS: 1000,
ignoreDotFiles: true,
}
})
.routes((route) => [
route()
.transform(addPrefix('PID-18.1', 'H'))
.send('tcp', 'emr.example.com', 5517),
route()
.transform(addPrefix('PID.18-1', 'L'))
.send('https', {
host: 'lab.example.com',
port: 443,
path: '/api/adt-feed',
method: 'POST',
basicAuth: {
username: 'gofer-service',
password: 'gofer-service-password',
}
})
]);```

Advanced Type Control

For advanced type control, you can pass through a generic to the ChannelConfig (the second generic option) which passes down to the FilterFlow generic to either:

  • 'F' = Only allows raw transform functions. E.G. ingestion: [(msg) => msg]
  • 'O' = Only allow transforming functions in objects. E.G. ingestion: [{ transform: (msg) => msg }]
  • 'B' = Allow Both raw transform functions or wrapped in objects. E.G. ingestion: [(msg) => msg, { transform: (msg) => msg }] The default is 'B'. E.G. const conf: ChannelConfig<'B', 'B'> = ...

OOP Style

The OOP style channel builder transform method aligns with the following types:

IngestionClass.transform = (transform: TransformFlow<'F'>) => IngestionClass
RouteClass.transform = (transform: TransformFlow<'F>) => RouteClass

This means that the transform method accepts a single argument which is a function.