diff --git a/packages/backend/src/controllers/api/v1/flows/create-flow.js b/packages/backend/src/controllers/api/v1/flows/create-flow.js new file mode 100644 index 00000000..0b049193 --- /dev/null +++ b/packages/backend/src/controllers/api/v1/flows/create-flow.js @@ -0,0 +1,11 @@ +import { renderObject } from '../../../../helpers/renderer.js'; + +export default async (request, response) => { + let flow = await request.currentUser.$relatedQuery('flows').insert({ + name: 'Name your flow', + }); + + flow = await flow.createInitialSteps(); + + renderObject(response, flow, { status: 201 }); +}; diff --git a/packages/backend/src/controllers/api/v1/flows/create-flow.test.js b/packages/backend/src/controllers/api/v1/flows/create-flow.test.js new file mode 100644 index 00000000..fb8f5635 --- /dev/null +++ b/packages/backend/src/controllers/api/v1/flows/create-flow.test.js @@ -0,0 +1,41 @@ +import { describe, it, expect, beforeEach } from 'vitest'; +import request from 'supertest'; + +import app from '../../../../app.js'; +import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id.js'; +import { createUser } from '../../../../../test/factories/user.js'; +import createFlowMock from '../../../../../test/mocks/rest/api/v1/flows/create-flow.js'; +import { createPermission } from '../../../../../test/factories/permission.js'; + +describe('POST /api/v1/flows', () => { + let currentUser, currentUserRole, token; + + beforeEach(async () => { + currentUser = await createUser(); + currentUserRole = await currentUser.$relatedQuery('role'); + + token = await createAuthTokenByUserId(currentUser.id); + }); + + it('should return created flow', async () => { + await createPermission({ + action: 'create', + subject: 'Flow', + roleId: currentUserRole.id, + conditions: ['isCreator'], + }); + + const response = await request(app) + .post('/api/v1/flows') + .set('Authorization', token) + .expect(201); + + const refetchedFlow = await currentUser + .$relatedQuery('flows') + .findById(response.body.data.id); + + const expectedPayload = await createFlowMock(refetchedFlow); + + expect(response.body).toMatchObject(expectedPayload); + }); +}); diff --git a/packages/backend/src/helpers/authorization.js b/packages/backend/src/helpers/authorization.js index 567d9351..41486316 100644 --- a/packages/backend/src/helpers/authorization.js +++ b/packages/backend/src/helpers/authorization.js @@ -21,6 +21,10 @@ const authorizationList = { action: 'read', subject: 'Flow', }, + 'POST /api/v1/flows/': { + action: 'create', + subject: 'Flow', + }, 'GET /api/v1/steps/:stepId/connection': { action: 'read', subject: 'Flow', diff --git a/packages/backend/src/models/flow.js b/packages/backend/src/models/flow.js index 30b0310e..7e657fbb 100644 --- a/packages/backend/src/models/flow.js +++ b/packages/backend/src/models/flow.js @@ -119,6 +119,22 @@ class Flow extends Base { }); } + async createInitialSteps() { + await Step.query().insert({ + flowId: this.id, + type: 'trigger', + position: 1, + }); + + await Step.query().insert({ + flowId: this.id, + type: 'action', + position: 2, + }); + + return this.$query().withGraphFetched('steps'); + } + async $beforeUpdate(opt, queryContext) { await super.$beforeUpdate(opt, queryContext); diff --git a/packages/backend/src/routes/api/v1/flows.js b/packages/backend/src/routes/api/v1/flows.js index 125ad23a..38ce3201 100644 --- a/packages/backend/src/routes/api/v1/flows.js +++ b/packages/backend/src/routes/api/v1/flows.js @@ -4,11 +4,13 @@ import { authorizeUser } from '../../../helpers/authorization.js'; import getFlowsAction from '../../../controllers/api/v1/flows/get-flows.js'; import getFlowAction from '../../../controllers/api/v1/flows/get-flow.js'; import updateFlowAction from '../../../controllers/api/v1/flows/update-flow.js'; +import createFlowAction from '../../../controllers/api/v1/flows/create-flow.js'; const router = Router(); router.get('/', authenticateUser, authorizeUser, getFlowsAction); router.get('/:flowId', authenticateUser, authorizeUser, getFlowAction); +router.post('/', authenticateUser, authorizeUser, createFlowAction); router.patch('/:flowId', authenticateUser, authorizeUser, updateFlowAction); export default router; diff --git a/packages/backend/test/mocks/rest/api/v1/flows/create-flow.js b/packages/backend/test/mocks/rest/api/v1/flows/create-flow.js new file mode 100644 index 00000000..e63d1b03 --- /dev/null +++ b/packages/backend/test/mocks/rest/api/v1/flows/create-flow.js @@ -0,0 +1,35 @@ +const createFlowMock = async (flow) => { + const data = { + id: flow.id, + active: flow.active, + name: flow.name, + status: flow.status, + createdAt: flow.createdAt.getTime(), + updatedAt: flow.updatedAt.getTime(), + steps: [ + { + position: 1, + status: 'incomplete', + type: 'trigger', + }, + { + position: 2, + status: 'incomplete', + type: 'action', + }, + ], + }; + + return { + data: data, + meta: { + count: 1, + currentPage: null, + isArray: false, + totalPages: null, + type: 'Flow', + }, + }; +}; + +export default createFlowMock;