Skip to content

Commit

Permalink
Merge pull request #11 from flow-build/feature/ADM-196
Browse files Browse the repository at this point in the history
Feature/adm 196
  • Loading branch information
pedropereiraassis authored May 9, 2023
2 parents d1fea7b + 9aeed6f commit edc3d1b
Show file tree
Hide file tree
Showing 16 changed files with 156 additions and 30 deletions.
24 changes: 15 additions & 9 deletions .github/workflows/publish-packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,21 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

version:
release:
name: 'Release on GitHub'
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- name: Checkout repo
uses: actions/checkout@v3
with:
node-version: 16
- run: npm ci
persist-credentials: false
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: 18.12
- name: Install dependencies
run: npm ci
- name: Get version from package.json before release step
id: initversion
run: echo "version=$(npm run get-version --silent)" >> $GITHUB_OUTPUT
Expand Down Expand Up @@ -88,15 +94,15 @@ jobs:
body: 'Version bump in package.json and package-lock.json for release [${{ steps.extractver.outputs.version }}](https://github.com/${{github.repository}}/releases/tag/v${{ steps.extractver.outputs.version }})'
branch: version-bump/${{ steps.extractver.outputs.version }}
outputs:
version: ${{ steps.extractver.outputs.version }}
version: ${{ steps.release.outputs.tag }}

build:
name: 'Build image on DockerHub'
needs: version
needs: release
runs-on: ubuntu-latest
steps:
- name: Print Version
run: echo "Using version ${{ needs.version.outputs.version }}"
run: echo "Using tag version ${{ needs.release.outputs.version }}"
- uses: actions/checkout@v2
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
Expand All @@ -112,7 +118,7 @@ jobs:
with:
context: .
push: true
tags: 'flowbuild/diagrams:${{ needs.version.outputs.version }}'
tags: 'flowbuild/diagrams:${{ needs.release.outputs.version }}'
- name: Build and push latest
uses: docker/build-push-action@v2
with:
Expand Down
10 changes: 6 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
FROM node:16.15.0 as base
FROM node:18.14 AS base

RUN apt-get update && apt-get install -y bash curl

RUN mkdir /usr/app
WORKDIR /usr/app
COPY . /usr/app

RUN npm ci
RUN npm install

EXPOSE 3000
EXPOSE 5000

CMD ["npm", "run", "start"]
CMD ["node", "src/server.js"]
12 changes: 10 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ services:
- 5432:5432

app:
image: node:18.14
build:
context: .
dockerfile: ./Dockerfile
env_file:
- ./.env.docker
container_name: diagrams_app
Expand All @@ -24,4 +26,10 @@ services:
- .:/usr/app
- /usr/app/node_modules
working_dir: /usr/app
command: bash -c "npm install && npm run migrations && npm run seeds && npm start"
command: bash -c "npm run migrations && npm run seeds && npm start"
healthcheck:
test: ["CMD", "curl", "-f", "http://0.0.0.0:5000/healthcheck"]
interval: 60s
timeout: 10s
retries: 3
start_period: 60s
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"migrations:local": "knex --env test --knexfile knexfile.js migrate:latest",
"seeds:local": "knex --env test --knexfile knexfile.js seed:run",
"get-version": "echo $npm_package_version",
"release": "semantic-release"
"release": "semantic-release && echo '::set-output name=tag::$(git describe --abbrev=0 --tags)'"
},
"repository": {
"type": "git",
Expand Down
4 changes: 2 additions & 2 deletions src/controllers/diagrams.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const saveDiagram = async (ctx, next) => {

const diagram = await diagramCore.saveDiagram({ diagram_xml, name, user_id });

if (!!workflow_id) {
if (workflow_id) {
logger.info(`Check Alignment event called - Diagram_id: ${diagram.id}`);
emitter.emit('Check Alignment', { ...ctx.request.body, diagram_id: diagram.id });

Expand Down Expand Up @@ -214,7 +214,7 @@ const updateDiagram = async (ctx, next) => {

if (diagram.blueprint_id && diagram_xml) {
const { blueprint_spec } = await blueprintCore.getBlueprintById(diagram.blueprint_id);
if (!!blueprint_spec?.nodes) {
if (blueprint_spec?.nodes) {
const blueprint = {
name: 'Check_Alignment',
description: 'Check alignmen',
Expand Down
28 changes: 27 additions & 1 deletion src/controllers/workflow.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
require('dotenv').config();
const { logger } = require('../utils/logger');
const { buildXmlDiagram } = require('@flowbuild/nodejs-diagram-builder');
const { removeNodesByCategory } = require('../utils/workflowModifier');
const { removeNodesByCategory, pinNodesByTypeAndCategory } = require('../utils/workflowModifier');

const buildDiagram = async (ctx, next) => {
logger.debug('buildDiagram controller called');
Expand Down Expand Up @@ -49,7 +49,33 @@ const buildDiagramNoBags = async (ctx, next) => {
return next();
}

const buildDiagramUserTask = async (ctx, next) => {
logger.debug('buildDiagramUserTask controller called');

try {
let { blueprint_spec, name, description } = ctx.request.body;
const nodesToPin = ['start', 'usertask', 'flow', 'finish', 'timer', 'event'];
blueprint_spec = await pinNodesByTypeAndCategory(blueprint_spec, nodesToPin);

const blueprint = {
blueprint_spec,
name: name || 'Diagram UserTask',
description: description || 'Building Diagram UserTask',
}

const diagram = await buildXmlDiagram(blueprint);

ctx.status = 200;
ctx.body = diagram;
} catch(err) {
throw new Error(err);
}

return next();
}

module.exports = {
buildDiagram,
buildDiagramNoBags,
buildDiagramUserTask,
}
1 change: 1 addition & 0 deletions src/routers/freeRouter.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ module.exports = (opts = {}) => {

router.get('/token', tokenController.getToken);
router.get('/', healthController.healtchCheck);
router.get('/healthcheck', healthController.healtchCheck);
router.get('/swagger', (ctx) => {
ctx.type = 'text/html; charset=utf-8'
ctx.body = fs.createReadStream('public/swagger-ui/index.html')
Expand Down
5 changes: 3 additions & 2 deletions src/routers/mainRouter.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ module.exports = (opts = {}) => {

router.use(cors(opts.corsOptions));

const diagrams = Router();
const diagrams = new Router();
diagrams.prefix('/diagrams');
diagrams.get(
'/user/:user_id/workflow/:workflow_id',
Expand All @@ -43,10 +43,11 @@ module.exports = (opts = {}) => {
);
diagrams.del('/:id', baseValidator.validateUUID, diagramsController.deleteDiagram);

const workflow = Router();
const workflow = new Router();
workflow.prefix('/workflow');
workflow.post('/', workflowValidator.validateBuildDiagram, workflowController.buildDiagram);
workflow.post('/nobags', workflowValidator.validateBuildDiagram, workflowController.buildDiagramNoBags);
workflow.post('/usertask', workflowValidator.validateBuildDiagram, workflowController.buildDiagramUserTask);

router.use(diagrams.routes());
router.use(workflow.routes());
Expand Down
8 changes: 4 additions & 4 deletions src/services/eventListener.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ function startEventListener(event) {

const workflowFetched = await workflowCore.getWorkflowById(workflow_id);

if (!!workflowFetched) {
if (workflowFetched) {
await diagramToWorkflowCore.saveDiagramToWorkflow({ diagram_id, workflow_id });

const { blueprint_spec } = await blueprintCore.getBlueprintById(workflowFetched.blueprint_id);

if (!!blueprint_spec?.nodes) {
if (blueprint_spec?.nodes) {
const blueprint = {
name: 'Check_Alignment',
description: 'Check Alignment',
Expand All @@ -45,7 +45,7 @@ function startEventListener(event) {
server: `unavailable: ${process.env.FLOWBUILD_URL}`
});
return;
} else if (!!blueprint) {
} else if (blueprint) {
await blueprintCore.updateBlueprint(workflowFetched.blueprint_id, blueprint.blueprint_spec);
await workflowCore.updateWorkflow(workflow_id, {
server: process.env.FLOWBUILD_URL
Expand Down Expand Up @@ -82,7 +82,7 @@ function startEventListener(event) {
server: `unavailable: ${process.env.FLOWBUILD_URL}`
});
return;
} else if (!!blueprint) {
} else if (blueprint) {
await blueprintCore.updateBlueprint(blueprintSaved.id, blueprint.blueprint_spec);

const aligned = await checkAlignment(blueprint, diagram_xml);
Expand Down
32 changes: 31 additions & 1 deletion src/tests/diagrams.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ nock(process.env.FLOWBUILD_URL)
});

let server;
let request;

beforeAll(async () => {
server = startServer(5001);
Expand Down Expand Up @@ -226,7 +227,7 @@ describe('GET /diagrams/workflow/:id/latest', () => {
expect(validate(getResponse.body.id)).toBeTruthy();
expect(getResponse.body.name).toEqual('Save Diagram With Workflow_id');
expect(getResponse.body.workflow_id).toEqual('7be513f4-98dc-43e2-8f3a-66e68a61aca8');
expect(getResponse.body.aligned).toBeTruthy();
expect(getResponse.body.aligned).toBeDefined();
});

test('should return 404', async () => {
Expand Down Expand Up @@ -481,6 +482,35 @@ describe('POST /workflow/nobags', () => {
.set('Authorization', `Bearer ${jwtToken}`)
.send({})

expect(postResponse.status).toBe(400);
expect(postResponse.body.message).toEqual('Invalid Request Body');
expect(postResponse.body.errors[0].message).toEqual("must have required property 'blueprint_spec'");
});
});

describe('POST /workflow/usertask', () => {
test('should return 200', async () => {
const tokenResponse = await request.get('/token');
const { jwtToken } = tokenResponse.body;

const postResponse = await request.post('/workflow/usertask')
.set('Authorization', `Bearer ${jwtToken}`)
.send({
blueprint_spec: blueprintSample.blueprint_spec
})

expect(postResponse.status).toBe(200);
expect(postResponse.body).toBeDefined();
});

test('should return 400 if doesnt have blueprint_spec', async () => {
const tokenResponse = await request.get('/token');
const { jwtToken } = tokenResponse.body;

const postResponse = await request.post('/workflow/usertask')
.set('Authorization', `Bearer ${jwtToken}`)
.send({})

expect(postResponse.status).toBe(400);
expect(postResponse.body.message).toEqual('Invalid Request Body');
expect(postResponse.body.errors[0].message).toEqual("must have required property 'blueprint_spec'");
Expand Down
1 change: 1 addition & 0 deletions src/tests/healthCheck.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const supertest = require('supertest');
const { startServer } = require('../app');

let server;
let request;

beforeAll(async () => {
server = startServer(5001);
Expand Down
1 change: 1 addition & 0 deletions src/tests/token.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const supertest = require('supertest');
const { startServer } = require('../app');

let server;
let request;

beforeAll(async () => {
server = startServer(5001);
Expand Down
11 changes: 10 additions & 1 deletion src/tests/workflowModifier.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const blueprintSample = require('../samples/blueprint');
const { removeNodesByCategory } = require('../utils/workflowModifier');
const { removeNodesByCategory, pinNodesByTypeAndCategory } = require('../utils/workflowModifier');

describe('Test removeNodesByCategory setToBag', () => {
test('should return blueprint_spec without nodes of category setToBag', async () => {
Expand All @@ -9,4 +9,13 @@ describe('Test removeNodesByCategory setToBag', () => {
expect(blueprint_spec.nodes).toHaveLength(11);
expect(setToBagNodes).toBeUndefined();
});
});

describe('Test pinNodesByTypeAndCategory', () => {
test('should return blueprint_spec with nodes start, userTask, timer, flow and finish', async () => {
const nodesToPin = ['start', 'usertask', 'flow', 'finish', 'timer', 'event'];
const blueprint_spec = await pinNodesByTypeAndCategory(blueprintSample.blueprint_spec, nodesToPin);
expect(blueprint_spec).toBeDefined();
expect(blueprint_spec.nodes).toHaveLength(9);
});
});
3 changes: 2 additions & 1 deletion src/utils/eventEmitter.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
const EventEmitter = require('node:events');
const emitter = new EventEmitter();

module.exports = emitter = new EventEmitter();
module.exports = emitter;
42 changes: 41 additions & 1 deletion src/utils/workflowModifier.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,22 @@ function getNextExcludingCategory(nodes, next, category) {
return next;
}

function getNextOfPinnedNodes(nodes, next, pinnedNodes) {
nodes.map((myNode) => {
if ((next === myNode.id) && !shouldPinNode(myNode, pinnedNodes)) {
next = myNode.next;
}
});
return next;
}

function shouldPinNode(node, nodesToPin) {
return nodesToPin.includes(node?.type?.toLowerCase()) || nodesToPin.includes(node?.category?.toLowerCase());
}

async function removeNodesByCategory(blueprint_spec, category) {
let blueprint = _.cloneDeep(blueprint_spec);
const nodes = [...blueprint_spec.nodes];
const nodes = _.cloneDeep(blueprint_spec.nodes);
const filteredNodes = nodes.filter((node) => node?.category?.toLowerCase() !== category);

blueprint.nodes = filteredNodes.map((node) => {
Expand All @@ -35,6 +48,33 @@ async function removeNodesByCategory(blueprint_spec, category) {
return blueprint;
}

async function pinNodesByTypeAndCategory(blueprint_spec, nodesToPin) {
let blueprint = _.cloneDeep(blueprint_spec);
const nodes = _.cloneDeep(blueprint_spec.nodes);

const filteredNodes = nodes.filter((node) => shouldPinNode(node, nodesToPin));
blueprint.nodes = filteredNodes.map((node) => {
// early return finish nodes
if (!node.next) {
return node;
}
// check unique nexts of flow nodes and get new next
if (typeof node.next === 'object') {
_.uniq(Object.entries(node.next)).map(([nextKey, nextValue]) => {
node.next[nextKey] = getNextOfPinnedNodes(nodes, nextValue, nodesToPin);
if (node.id === node.next[nextKey]) {
delete node.next[nextKey];
}
});
return node;
}
node.next = getNextOfPinnedNodes(nodes, node.next, nodesToPin);
return node;
});
return blueprint;
}

module.exports = {
removeNodesByCategory,
pinNodesByTypeAndCategory,
}
Loading

0 comments on commit edc3d1b

Please sign in to comment.