diff --git a/packages/backend/src/controllers/api/v1/flows/get-flow.ee.js b/packages/backend/src/controllers/api/v1/flows/get-flow.ee.js new file mode 100644 index 00000000..d448d705 --- /dev/null +++ b/packages/backend/src/controllers/api/v1/flows/get-flow.ee.js @@ -0,0 +1,12 @@ +import Flow from '../../../../models/flow.js'; +import { renderObject } from '../../../../helpers/renderer.js'; + +export default async (request, response) => { + const flow = await Flow.query() + .withGraphJoined({ steps: true }) + .orderBy('steps.position', 'asc') + .findOne({ 'flows.id': request.params.flowId }) + .throwIfNotFound(); + + renderObject(response, flow); +}; diff --git a/packages/backend/src/controllers/api/v1/flows/get-flow.ee.test.js b/packages/backend/src/controllers/api/v1/flows/get-flow.ee.test.js new file mode 100644 index 00000000..7144b901 --- /dev/null +++ b/packages/backend/src/controllers/api/v1/flows/get-flow.ee.test.js @@ -0,0 +1,50 @@ +import Crypto from 'node:crypto'; +import request from 'supertest'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { createApiToken } from '../../../../../test/factories/api-token.js'; +import { createFlow } from '../../../../../test/factories/flow.js'; +import { createStep } from '../../../../../test/factories/step.js'; +import getFlowMock from '../../../../../test/mocks/rest/api/v1/flows/get-flow.js'; +import app from '../../../../app.js'; +import * as license from '../../../../helpers/license.ee.js'; + +describe('GET /api/v1/flows/:flowId', () => { + let token; + + beforeEach(async () => { + vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true); + + token = (await createApiToken()).token; + }); + + it('should return the flow data', async () => { + const flow = await createFlow(); + const triggerStep = await createStep({ flowId: flow.id }); + const actionStep = await createStep({ flowId: flow.id }); + + const response = await request(app) + .get(`/api/v1/flows/${flow.id}`) + .set('x-api-token', token) + .expect(200); + + const expectedPayload = await getFlowMock(flow, [triggerStep, actionStep]); + + expect(response.body).toStrictEqual(expectedPayload); + }); + + it('should return not found response for not existing flow UUID', async () => { + const notExistingFlowUUID = Crypto.randomUUID(); + + await request(app) + .get(`/api/v1/flows/${notExistingFlowUUID}`) + .set('x-api-token', token) + .expect(404); + }); + + it('should return bad request response for invalid UUID', async () => { + await request(app) + .get('/api/v1/flows/invalidFlowUUID') + .set('x-api-token', token) + .expect(400); + }); +}); diff --git a/packages/backend/src/routes/api/index.js b/packages/backend/src/routes/api/index.js index c6fc2c45..60851284 100644 --- a/packages/backend/src/routes/api/index.js +++ b/packages/backend/src/routes/api/index.js @@ -1,12 +1,14 @@ import { Router } from 'express'; import appsRouter from './v1/apps.ee.js'; import executionsRouter from './v1/executions.ee.js'; +import flowsRouter from './v1/flows.ee.js'; import foldersRouter from './v1/folders.ee.js'; import usersRouter from './v1/users.ee.js'; const router = Router(); router.use('/v1/apps', appsRouter); +router.use('/v1/flows', flowsRouter); router.use('/v1/executions', executionsRouter); router.use('/v1/folders', foldersRouter); router.use('/v1/users', usersRouter); diff --git a/packages/backend/src/routes/api/v1/flows.ee.js b/packages/backend/src/routes/api/v1/flows.ee.js new file mode 100644 index 00000000..390d2855 --- /dev/null +++ b/packages/backend/src/routes/api/v1/flows.ee.js @@ -0,0 +1,8 @@ +import { Router } from 'express'; +import getFlowAction from '../../../controllers/api/v1/flows/get-flow.ee.js'; + +const router = Router(); + +router.get('/:flowId', getFlowAction); + +export default router; diff --git a/packages/backend/test/mocks/rest/api/v1/flows/get-flow.js b/packages/backend/test/mocks/rest/api/v1/flows/get-flow.js new file mode 100644 index 00000000..49efe83c --- /dev/null +++ b/packages/backend/test/mocks/rest/api/v1/flows/get-flow.js @@ -0,0 +1,38 @@ +const getFlowMock = async (flow, steps = []) => { + const data = { + active: flow.active, + id: flow.id, + name: flow.name, + status: flow.active ? 'published' : 'draft', + createdAt: flow.createdAt.getTime(), + updatedAt: flow.updatedAt.getTime(), + }; + + if (steps.length) { + data.steps = steps.map((step) => ({ + appKey: step.appKey, + iconUrl: step.iconUrl, + id: step.id, + key: step.key, + name: step.name, + parameters: step.parameters, + position: step.position, + status: step.status, + type: step.type, + webhookUrl: step.webhookUrl, + })); + } + + return { + data: data, + meta: { + count: 1, + currentPage: null, + isArray: false, + totalPages: null, + type: 'Flow', + }, + }; +}; + +export default getFlowMock;