@cumulus/cumulus-message-adapter-js
Advanced tools
Comparing version 0.0.1-beta.2 to 0.0.1-beta.3
52
index.js
@@ -25,19 +25,19 @@ 'use strict'; | ||
return new Promise((resolve, reject) => { | ||
const sled = cp.spawn('python', ['./cumulus-message-adapter.zip', command]); | ||
const cumulusMessageAdapter = cp.spawn('python', ['./cumulus-message-adapter', command]); | ||
// Collect STDOUT | ||
let sledStdout = ''; | ||
sled.stdout.on('data', (chunk) => { | ||
sledStdout += chunk; | ||
let cumulusMessageAdapterStdout = ''; | ||
cumulusMessageAdapter.stdout.on('data', (chunk) => { | ||
cumulusMessageAdapterStdout += chunk; | ||
}); | ||
// Collect STDERR | ||
let sledStderr = ''; | ||
sled.stderr.on('data', (chunk) => { | ||
sledStderr += chunk; | ||
let cumulusMessageAdapterStderr = ''; | ||
cumulusMessageAdapter.stderr.on('data', (chunk) => { | ||
cumulusMessageAdapterStderr += chunk; | ||
}); | ||
sled.on('close', (code) => { | ||
if (code === 0) resolve(JSON.parse(sledStdout)); | ||
else reject(new CumulusMessageAdapterExecutionError(sledStderr)); | ||
cumulusMessageAdapter.on('close', (code) => { | ||
if (code === 0) resolve(JSON.parse(cumulusMessageAdapterStdout)); | ||
else reject(new CumulusMessageAdapterExecutionError(cumulusMessageAdapterStderr)); | ||
}); | ||
@@ -49,5 +49,5 @@ | ||
// result in an unhandled "Error: write EPIPE". | ||
sled.stdin.on('error', () => {}); | ||
cumulusMessageAdapter.stdin.on('error', () => {}); | ||
sled.stdin.end(JSON.stringify(input)); | ||
cumulusMessageAdapter.stdin.end(JSON.stringify(input)); | ||
}); | ||
@@ -121,17 +121,2 @@ } | ||
/** | ||
* Call the callback with an error or a Cumulus message | ||
* | ||
* @param {Error} err - an error to be handled | ||
* @param {Object} cumulusMessage - a full Cumulus message | ||
* @param {Fuction} callback - the callback to be invoked with the parsed error | ||
* @returns {undefined} - undefined | ||
*/ | ||
function handleError(err, cumulusMessage, callback) { | ||
if (err.name && err.name.includes('WorkflowError')) { | ||
callback(null, Object.assign({}, cumulusMessage, { payload: null, exception: err.name })); | ||
} | ||
else callback(err); | ||
} | ||
/** | ||
* Build a nested Cumulus event and pass it to a tasks's business function | ||
@@ -144,3 +129,5 @@ * | ||
* has completed. This should be the callback passed to the Lambda handler. | ||
* @returns {Promise} - resolves when the task has completed | ||
* @returns {undefined} - there is no return value from this function, but | ||
* the callback function will be invoked with either an error or a full | ||
* Cumulus message containing the result of the business logic function. | ||
*/ | ||
@@ -173,6 +160,11 @@ function runCumulusTask(taskFunction, cumulusMessage, context, callback) { | ||
return promisedNextEvent | ||
promisedNextEvent | ||
.then((nextEvent) => callback(null, nextEvent)) | ||
.catch((err) => handleError(err, cumulusMessage, callback)); | ||
.catch((err) => { | ||
if (err.name && err.name.includes('WorkflowError')) { | ||
callback(null, Object.assign({}, cumulusMessage, { payload: null, exception: err.name })); | ||
} | ||
else callback(err); | ||
}); | ||
} | ||
exports.runCumulusTask = runCumulusTask; |
{ | ||
"name": "@cumulus/cumulus-message-adapter-js", | ||
"version": "0.0.1-beta.2", | ||
"version": "0.0.1-beta.3", | ||
"description": "Cumulus message adapter", | ||
"main": "index.js", | ||
"scripts": { | ||
"test": "cp test/cumulus-message-adapter.zip . && ava; rm cumulus-message-adapter.zip" | ||
"test": "rm -rf cumulus-message-adapter && cp -R test/cumulus-message-adapter . && ava; rm -rf cumulus-message-adapter" | ||
}, | ||
@@ -9,0 +9,0 @@ "ava": { |
126
README.md
@@ -1,1 +0,125 @@ | ||
# cumulus-message-adapter-js | ||
# @cumulus/cumulus-message-adapter-js | ||
[![CircleCI](https://circleci.com/gh/cumulus-nasa/cumulus-message-adapter-js.svg?style=svg)](https://circleci.com/gh/cumulus-nasa/cumulus-message-adapter-js) | ||
## What is Cumulus? | ||
Cumulus is a cloud-based data ingest, archive, distribution and management | ||
prototype for NASA's future Earth science data streams. | ||
Read the [Cumulus Documentation](https://cumulus-nasa.github.io/) | ||
## What is the Cumulus Message Adapter? | ||
The Cumulus Message Adapter is a library that adapts incoming messages in the | ||
Cumulus protocol to a format more easily consumable by Cumulus tasks, invokes | ||
the tasks, and then adapts their response back to the Cumulus message protocol | ||
to be sent to the next task. | ||
## Installation | ||
The cumulus-message-adapter-js can be installed via Node Package Manager (NPM) and the package is located [here](https://www.npmjs.com/package/@cumulus/cumulus-message-adapter-js). | ||
The package can be added to your project by running `npm install @cumulus/cumulus-message-adapter-js --save`. | ||
## Task definition | ||
In order to use the Cumulus Message Adapter, you will need to create two | ||
methods in your task module: a handler function and a business logic function. | ||
The handler function is a standard Lambda handler function which takes three | ||
parameters (as specified by AWS): `event`, `context`, and `callback`. | ||
The business logic function is where the actual work of your task occurs. It | ||
should take two parameters: `nestedEvent` and `context`. | ||
The `nestedEvent` object contains two keys: | ||
* `input` - the task's input, typically the `payload` of the message, | ||
produced at runtime | ||
* `config` - the task's configuration, with any templated variables | ||
resolved | ||
The `context` parameter is the standard Lambda context as passed by AWS. | ||
The return value of the business logic function will be placed in the | ||
`payload` of the resulting Cumulus message. | ||
Expectations for input, config, and return values are all defined by the task, | ||
and should be well documented. Tasks should thoughtfully consider their inputs | ||
and return values, as breaking changes may have cascading effects on tasks | ||
throughout a workflow. Configuration changes are slightly less impactful, but | ||
must be communicated to those using the task. | ||
## Cumulus Message Adapter interface | ||
The Cumulus Message adapter for Javascript provides one method: | ||
`runCumulusTask`. It takes four parameters: | ||
* `taskFunction` - the function containing your business logic (as described | ||
above) | ||
* `cumulusMessage` - the event passed by Lambda, and should be a Cumulus | ||
Message | ||
* `context` - the Lambda context | ||
* `callback` - the callback passed by Lambda | ||
## Example Cumulus task | ||
```javascript | ||
const cumulusMessageAdapter = require('@cumulus/cumulus-message-adapter-js'); | ||
function myBusinessLogic(nestedEvent, context) { | ||
console.log('Hello, example!'); | ||
return { answer: 42 }; | ||
} | ||
// The handler function should rarely, if ever, contain more than this line | ||
function handler(event, context, callback) { | ||
cumulusMessageAdapter.runCumulusTask(myBusinessLogic, event, callback); | ||
} | ||
exports.handler = handler; | ||
``` | ||
## Creating a deployment package | ||
Tasks that use this library are just standard AWS Lambda tasks. Information on | ||
creating release packages is available [here](https://docs.aws.amazon.com/lambda/latest/dg/deployment-package-v2.html). | ||
## Usage in Cumulus Deployments | ||
During deployment, Cumulus will automatically obtain and inject the [Cumulus Message Adapter](https://github.com/cumulus-nasa/cumulus-message-adapter) | ||
into the compiled code and create a zip file to be deployed to Lambda. | ||
A task using the message adapter would be configured in lambdas.yml as follows: | ||
```yaml | ||
NodeTest: | ||
handler: index.handler | ||
timeout: 300 | ||
memory: 256 | ||
source: 'node_modules/@cumulus/task-task/dist/' | ||
useMessageAdapter: true | ||
``` | ||
## Development | ||
### Running Tests | ||
To run the tests for this package, run `npm test` | ||
## Why? | ||
This approach has a few major advantages: | ||
1. It explicitly prevents tasks from making assumptions about data structures | ||
like `meta` and `cumulus_meta` that are owned internally and may therefore | ||
be broken in future updates. To gain access to fields in these structures, | ||
tasks must be passed the data explicitly in the workflow configuration. | ||
1. It provides clearer ownership of the various data structures. Operators own | ||
`meta`. Cumulus owns `cumulus_meta`. Tasks define their own `config`, | ||
`input`, and `output` formats. | ||
1. The Cumulus Message Adapter greatly simplifies running Lambda functions not | ||
explicitly created for Cumulus. | ||
1. The approach greatly simplifies testing for tasks, as tasks don't need to | ||
set up cumbersome structures to emulate the message protocol and can just | ||
test their business function. |
@@ -6,3 +6,3 @@ /* eslint-disable require-jsdoc */ | ||
test('CUMULUS_MESSAGE_ADAPTER_DISABLED="true" bypasses the message adapter', (t) => { | ||
test.cb('CUMULUS_MESSAGE_ADAPTER_DISABLED="true" bypasses the message adapter', (t) => { | ||
process.env.CUMULUS_MESSAGE_ADAPTER_DISABLED = 'true'; | ||
@@ -23,2 +23,3 @@ | ||
t.deepEqual(data, { result: 42 }); | ||
t.end(); | ||
} | ||
@@ -25,0 +26,0 @@ |
@@ -6,3 +6,3 @@ /* eslint-disable require-jsdoc */ | ||
test('The correct cumulus message is returned', (t) => { | ||
test.cb('The correct cumulus message is returned', (t) => { | ||
const businessLogicOutput = 42; | ||
@@ -24,2 +24,3 @@ const businessLogic = () => businessLogicOutput; | ||
t.deepEqual(data, expectedOutput); | ||
t.end(); | ||
} | ||
@@ -30,3 +31,3 @@ | ||
test('The businessLogic receives the correct arguments', (t) => { | ||
test.cb('The businessLogic receives the correct arguments', (t) => { | ||
const inputEvent = { a: 1 }; | ||
@@ -45,6 +46,6 @@ const context = { b: 2 }; | ||
return cumulusMessageAdapter.runCumulusTask(businessLogic, inputEvent, context, () => true); | ||
return cumulusMessageAdapter.runCumulusTask(businessLogic, inputEvent, context, t.end); | ||
}); | ||
test('A WorkflowError is returned properly', (t) => { | ||
test.cb('A WorkflowError is returned properly', (t) => { | ||
const inputEvent = { a: 1 }; | ||
@@ -67,2 +68,3 @@ | ||
t.deepEqual(data, expectedOutput); | ||
t.end(); | ||
} | ||
@@ -73,3 +75,3 @@ | ||
test('A non-WorkflowError is raised', (t) => { | ||
test.cb('A non-WorkflowError is raised', (t) => { | ||
function businessLogic() { | ||
@@ -83,2 +85,3 @@ throw new Error('oh snap'); | ||
t.is(data, undefined); | ||
t.end(); | ||
} | ||
@@ -85,0 +88,0 @@ |
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
211066
15
5940
126
1