diff --git a/.circleci/config.yml b/.circleci/config.yml index a84644b..a04f749 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,28 +1,127 @@ -version: 2 +version: 2.1 +parameters: + node-version: + type: string + default: "18.13.0" +orbs: + node: circleci/node@5.0.0 + slack: circleci/slack@4.5.3 +commands: + notify_on_failure: + steps: + - slack/notify: + event: fail + custom: | + { + "blocks": [ + { + "type": "section", + "fields": [ + { + "type": "mrkdwn", + "text": ":red_circle: *$CIRCLE_PROJECT_REPONAME*:*$CIRCLE_TAG* build failed" + } + ] + }, + { + "type": "actions", + "elements": [ + { + "type": "button", + "text": { + "type": "plain_text", + "text": "View Job" + }, + "url": "${CIRCLE_BUILD_URL}" + } + ] + } + ] + } + notify_on_pass: + steps: + - slack/notify: + event: pass + custom: | + { + "blocks": [ + { + "type": "section", + "fields": [ + { + "type": "mrkdwn", + "text": ":tada: *$CIRCLE_PROJECT_REPONAME*:*$CIRCLE_TAG* was successfully built and published" + } + ] + }, + { + "type": "actions", + "elements": [ + { + "type": "button", + "text": { + "type": "plain_text", + "text": "View Job" + }, + "url": "${CIRCLE_BUILD_URL}" + } + ] + } + ] + } jobs: test: docker: - - image: circleci/node:14-stretch + - image: cimg/node:18.13.0 steps: - checkout - - restore_cache: - key: dependency-cache-{{ checksum "package.json" }} - - run: - name: Installing Dependencies - command: npm install + - node/install: + node-version: << pipeline.parameters.node-version >> + - node/install-packages: + cache-path: ./node_modules + override-ci-command: npm install - run: name: Audit Dependencies command: npm run audit - - save_cache: - key: dependency-cache-{{ checksum "package.json" }} - paths: - - node_modules - run: - name: Running Integration Tests - command: npm run integration-test - + name: Running Mocha Tests + command: npm test + build: + docker: + - image: cimg/node:18.13.0 + user: root + steps: + - checkout + - node/install: + node-version: << pipeline.parameters.node-version >> + - setup_remote_docker: + version: 19.03.13 + docker_layer_caching: true + # build and push Docker image + - run: + name: Install component-build-helper lib + command: npm install -g @elastic.io/component-build-helper + - run: + name: Build and publish docker image + command: build_component_docker + - notify_on_failure + - notify_on_pass workflows: - version: 2 - build_and_test: + test: + jobs: + - test: + name: "Running tests" + filters: + tags: + ignore: /.*/ + publish_release: jobs: - - test + - build: + name: "Build and publish docker image" + context: + - componentspusher + filters: + branches: + ignore: /.*/ + tags: + only: /^([0-9]+)\.([0-9]+)\.([0-9]+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+)?$/ \ No newline at end of file diff --git a/.grype-ignore.yaml b/.grype-ignore.yaml new file mode 100644 index 0000000..db25770 --- /dev/null +++ b/.grype-ignore.yaml @@ -0,0 +1,10 @@ +ignore: + - vulnerability: CVE-2022-3996 + package: + name: libssl3 + version: 3.0.7-r0 + + - vulnerability: CVE-2022-3996 + package: + name: libcrypto3 + version: 3.0.7-r0 diff --git a/component.json b/component.json index 5397756..dc6add9 100644 --- a/component.json +++ b/component.json @@ -1,8 +1,7 @@ { - "title": "AMQP", - "buildType": "docker", - "description": "Pub/Sub Component for async communication with queues and topics", - "version": "1.4.0", + "title": "AMQP component", + "description": "AMQP Component for async communication with queues and topics", + "version": "1.4.0-dev.50", "credentials": { "fields": { "amqpURI": { diff --git a/package-lock.json b/package-lock.json index becae1d..c81ceb4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "pub-sub", + "name": "amqp-component", "version": "1.3.3", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "pub-sub", + "name": "amqp-component", "license": "Apache-2.0", "dependencies": { "@elastic.io/component-logger": "0.0.1", @@ -27,7 +27,7 @@ "sinon": "15.0.2" }, "engines": { - "node": "14" + "node": "18.x" } }, "node_modules/@acuminous/bitsyntax": { diff --git a/package.json b/package.json index 67d84b4..e350574 100644 --- a/package.json +++ b/package.json @@ -1,25 +1,20 @@ { - "name": "pub-sub", - "description": "Pub/Sub Component for async communication with queues and topics", + "name": "amqp-component", "homepage": "http://www.elastic.io", "author": { "name": "elastic.io GmbH", "email": "info@elastic.io", "url": "https://elastic.io" }, - "files": [ - "lib" - ], "engines": { - "node": "14" + "node": "18.x" }, "scripts": { "audit": "better-npm-audit audit --level high --production", "pretest": "eslint spec-integration lib verifyCredentials.js --fix", - "test": "exit", + "test": "mocha \"spec/**/*spec.js\"", "integration-test": "NODE_ENV=test mocha spec-integration/* --timeout 10000" }, - "main": "lib/index.js", "keywords": [ "elasticio", "component", @@ -48,6 +43,6 @@ "mocha": "10.2.0", "sinon": "15.0.2" }, - "repository": "elasticio/pub-sub", + "repository": "elasticio/amqp-component", "license": "Apache-2.0" } diff --git a/spec/actions/publush.spec.js b/spec/actions/publush.spec.js new file mode 100644 index 0000000..7db42d0 --- /dev/null +++ b/spec/actions/publush.spec.js @@ -0,0 +1,63 @@ +/* eslint-disable no-unused-vars */ +const sinon = require('sinon'); +const { expect } = require('chai'); +const { getContext } = require('../common'); +const { AMQPClient } = require('../../lib/amqp'); +const { process } = require('../../lib/actions/publish'); + +describe('processAction', () => { + beforeEach(() => { + }); + afterEach(() => { + sinon.restore(); + }); + + it('should send unencrypted message if doNotEncrypt is truthy', async () => { + const message = { + id: '123', + body: { + payload: { + name: 'John', + age: 30, + }, + routingKey: 'test', + }, + }; + const configuration = { + doNotEncrypt: true, + }; + const publishStub = sinon.stub(AMQPClient.prototype, 'publish').callsFake(async () => { }); + const result = await process.call(getContext(), message, configuration); + + expect(result).to.deep.equal(message); + expect(publishStub.getCall(0).args[0]).to.be.deep.equal(message.body.routingKey); + expect(publishStub.getCall(0).args[1]).to.be.deep.equal(Buffer.from(JSON.stringify(message.body.payload))); + expect(publishStub.getCall(0).args[2]).to.be.deep.equal({ + contentType: 'application/octet-stream', + messageId: message.id, + }); + }); + + it('should send encrypted message if doNotEncrypt is falsy', async () => { + const message = { + id: '123', + body: { + payload: { + name: 'John', + age: 30, + }, + routingKey: 'test', + }, + }; + const publishStub = sinon.stub(AMQPClient.prototype, 'publish').callsFake(async () => { }); + const result = await process.call(getContext(), message, {}); + + expect(result).to.deep.equal(message); + expect(publishStub.getCall(0).args[0]).to.be.deep.equal(message.body.routingKey); + expect(publishStub.getCall(0).args[1].toString('utf8')).to.be.deep.equal('Z�.�g�{.\u0018�Cƚ\n�95�Ì(��6�-rN*��h(]�=qEa���cu�<\u0013'); + expect(publishStub.getCall(0).args[2]).to.be.deep.equal({ + contentType: 'application/octet-stream', + messageId: message.id, + }); + }); +}); diff --git a/spec/common.js b/spec/common.js new file mode 100644 index 0000000..9b6e411 --- /dev/null +++ b/spec/common.js @@ -0,0 +1,14 @@ +/* eslint-disable import/first */ +process.env.LOG_OUTPUT_MODE = 'short'; +process.env.API_RETRY_DELAY = '0'; +process.env.ELASTICIO_MESSAGE_CRYPTO_PASSWORD = '1'; +process.env.ELASTICIO_MESSAGE_CRYPTO_IV = 'yaku5ooSh3iedaem'; +process.env.ELASTICIO_EXEC_ID = '1a'; +process.env.ELASTICIO_FLOW_ID = '2b'; +const sinon = require('sinon'); +const getLogger = require('@elastic.io/component-logger'); + +exports.getContext = () => ({ + logger: getLogger(), + emit: sinon.spy(), +}); diff --git a/spec/triggers/consume.spec.js b/spec/triggers/consume.spec.js new file mode 100644 index 0000000..11b7f9d --- /dev/null +++ b/spec/triggers/consume.spec.js @@ -0,0 +1,27 @@ +/* eslint-disable no-unused-vars */ +const sinon = require('sinon'); +const { expect } = require('chai'); +const { getContext } = require('../common'); +const { AMQPClient } = require('../../lib/amqp'); +const consume = require('../../lib/triggers/consume'); + +describe('processAction', () => { + beforeEach(() => { + }); + afterEach(() => { + sinon.restore(); + }); + + it('should start consume messages', async () => { + const configuration = { + doNotEncrypt: true, + }; + sinon.stub(AMQPClient.prototype, 'init').callsFake(async () => { }); + const consumeStub = sinon.stub(AMQPClient.prototype, 'consume').callsFake(async () => { }); + await consume.process.call(getContext(), {}, configuration); + expect(consumeStub.getCall(0).args[1]).to.be.deep.equal({ + noAck: true, + consumerTag: `consumer_${process.env.ELASTICIO_EXEC_ID}_${process.env.ELASTICIO_FLOW_ID}`, + }); + }); +});