feat: Implement create folder API endpoint
This commit is contained in:
@@ -0,0 +1,11 @@
|
|||||||
|
import { renderObject } from '../../../../helpers/renderer.js';
|
||||||
|
|
||||||
|
export default async (request, response) => {
|
||||||
|
const folder = await request.currentUser
|
||||||
|
.$relatedQuery('folders')
|
||||||
|
.insertAndFetch({
|
||||||
|
name: request.body.name,
|
||||||
|
});
|
||||||
|
|
||||||
|
renderObject(response, folder, { status: 201 });
|
||||||
|
};
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
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 createFolderMock from '../../../../../test/mocks/rest/api/v1/folders/create-folder.js';
|
||||||
|
import { createPermission } from '../../../../../test/factories/permission.js';
|
||||||
|
|
||||||
|
describe('POST /api/v1/folders', () => {
|
||||||
|
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/folders')
|
||||||
|
.set('Authorization', token)
|
||||||
|
.send({
|
||||||
|
name: 'Test Folder',
|
||||||
|
})
|
||||||
|
.expect(201);
|
||||||
|
|
||||||
|
const refetchedFolder = await currentUser
|
||||||
|
.$relatedQuery('folders')
|
||||||
|
.findById(response.body.data.id);
|
||||||
|
|
||||||
|
const expectedPayload = await createFolderMock(refetchedFolder);
|
||||||
|
|
||||||
|
expect(response.body).toMatchObject(expectedPayload);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -133,6 +133,10 @@ const authorizationList = {
|
|||||||
action: 'create',
|
action: 'create',
|
||||||
subject: 'Connection',
|
subject: 'Connection',
|
||||||
},
|
},
|
||||||
|
'POST /api/v1/folders/': {
|
||||||
|
action: 'create',
|
||||||
|
subject: 'Flow',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const authorizeUser = async (request, response, next) => {
|
export const authorizeUser = async (request, response, next) => {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import Permission from './permission.js';
|
|||||||
import Role from './role.js';
|
import Role from './role.js';
|
||||||
import Step from './step.js';
|
import Step from './step.js';
|
||||||
import Subscription from './subscription.ee.js';
|
import Subscription from './subscription.ee.js';
|
||||||
|
import Folder from './folder.js';
|
||||||
import UsageData from './usage-data.ee.js';
|
import UsageData from './usage-data.ee.js';
|
||||||
import Billing from '../helpers/billing/index.ee.js';
|
import Billing from '../helpers/billing/index.ee.js';
|
||||||
import NotAuthorizedError from '../errors/not-authorized.js';
|
import NotAuthorizedError from '../errors/not-authorized.js';
|
||||||
@@ -178,6 +179,14 @@ class User extends Base {
|
|||||||
to: 'users.id',
|
to: 'users.id',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
folders: {
|
||||||
|
relation: Base.HasManyRelation,
|
||||||
|
modelClass: Folder,
|
||||||
|
join: {
|
||||||
|
from: 'users.id',
|
||||||
|
to: 'folders.user_id',
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
static get virtualAttributes() {
|
static get virtualAttributes() {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import Role from './role.js';
|
|||||||
import Step from './step.js';
|
import Step from './step.js';
|
||||||
import Subscription from './subscription.ee.js';
|
import Subscription from './subscription.ee.js';
|
||||||
import UsageData from './usage-data.ee.js';
|
import UsageData from './usage-data.ee.js';
|
||||||
|
import Folder from './folder.js';
|
||||||
import User from './user.js';
|
import User from './user.js';
|
||||||
import deleteUserQueue from '../queues/delete-user.ee.js';
|
import deleteUserQueue from '../queues/delete-user.ee.js';
|
||||||
import emailQueue from '../queues/email.js';
|
import emailQueue from '../queues/email.js';
|
||||||
@@ -153,6 +154,14 @@ describe('User model', () => {
|
|||||||
to: 'users.id',
|
to: 'users.id',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
folders: {
|
||||||
|
relation: Base.HasManyRelation,
|
||||||
|
modelClass: Folder,
|
||||||
|
join: {
|
||||||
|
from: 'users.id',
|
||||||
|
to: 'folders.user_id',
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(relationMappings).toStrictEqual(expectedRelations);
|
expect(relationMappings).toStrictEqual(expectedRelations);
|
||||||
|
|||||||
10
packages/backend/src/routes/api/v1/folders.js
Normal file
10
packages/backend/src/routes/api/v1/folders.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { Router } from 'express';
|
||||||
|
import { authenticateUser } from '../../../helpers/authentication.js';
|
||||||
|
import { authorizeUser } from '../../../helpers/authorization.js';
|
||||||
|
import createFolderAction from '../../../controllers/api/v1/folders/create-folder.js';
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
router.post('/', authenticateUser, authorizeUser, createFolderAction);
|
||||||
|
|
||||||
|
export default router;
|
||||||
@@ -19,6 +19,7 @@ import rolesRouter from './api/v1/admin/roles.ee.js';
|
|||||||
import permissionsRouter from './api/v1/admin/permissions.ee.js';
|
import permissionsRouter from './api/v1/admin/permissions.ee.js';
|
||||||
import adminUsersRouter from './api/v1/admin/users.ee.js';
|
import adminUsersRouter from './api/v1/admin/users.ee.js';
|
||||||
import installationUsersRouter from './api/v1/installation/users.js';
|
import installationUsersRouter from './api/v1/installation/users.js';
|
||||||
|
import foldersRouter from './api/v1/folders.js';
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
@@ -42,5 +43,6 @@ router.use('/api/v1/admin/roles', rolesRouter);
|
|||||||
router.use('/api/v1/admin/permissions', permissionsRouter);
|
router.use('/api/v1/admin/permissions', permissionsRouter);
|
||||||
router.use('/api/v1/admin/saml-auth-providers', adminSamlAuthProvidersRouter);
|
router.use('/api/v1/admin/saml-auth-providers', adminSamlAuthProvidersRouter);
|
||||||
router.use('/api/v1/installation/users', installationUsersRouter);
|
router.use('/api/v1/installation/users', installationUsersRouter);
|
||||||
|
router.use('/api/v1/folders', foldersRouter);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|||||||
10
packages/backend/src/serializers/folder.js
Normal file
10
packages/backend/src/serializers/folder.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
const folderSerilializer = (folder) => {
|
||||||
|
return {
|
||||||
|
id: folder.id,
|
||||||
|
name: folder.name,
|
||||||
|
createdAt: folder.createdAt.getTime(),
|
||||||
|
updatedAt: folder.updatedAt.getTime(),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default folderSerilializer;
|
||||||
22
packages/backend/src/serializers/folder.test.js
Normal file
22
packages/backend/src/serializers/folder.test.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { describe, it, expect, beforeEach } from 'vitest';
|
||||||
|
import { createFolder } from '../../test/factories/folder';
|
||||||
|
import folderSerializer from './folder';
|
||||||
|
|
||||||
|
describe('folder serializer', () => {
|
||||||
|
let folder;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
folder = await createFolder();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return folder data', async () => {
|
||||||
|
const expectedPayload = {
|
||||||
|
id: folder.id,
|
||||||
|
name: folder.name,
|
||||||
|
createdAt: folder.createdAt.getTime(),
|
||||||
|
updatedAt: folder.updatedAt.getTime(),
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(folderSerializer(folder)).toStrictEqual(expectedPayload);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -19,6 +19,7 @@ import executionStepSerializer from './execution-step.js';
|
|||||||
import subscriptionSerializer from './subscription.ee.js';
|
import subscriptionSerializer from './subscription.ee.js';
|
||||||
import adminUserSerializer from './admin/user.js';
|
import adminUserSerializer from './admin/user.js';
|
||||||
import configSerializer from './config.js';
|
import configSerializer from './config.js';
|
||||||
|
import folderSerializer from './folder.js';
|
||||||
|
|
||||||
const serializers = {
|
const serializers = {
|
||||||
AdminUser: adminUserSerializer,
|
AdminUser: adminUserSerializer,
|
||||||
@@ -42,6 +43,7 @@ const serializers = {
|
|||||||
ExecutionStep: executionStepSerializer,
|
ExecutionStep: executionStepSerializer,
|
||||||
Subscription: subscriptionSerializer,
|
Subscription: subscriptionSerializer,
|
||||||
Config: configSerializer,
|
Config: configSerializer,
|
||||||
|
Folder: folderSerializer,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default serializers;
|
export default serializers;
|
||||||
|
|||||||
10
packages/backend/test/factories/folder.js
Normal file
10
packages/backend/test/factories/folder.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import Folder from '../../src/models/folder.js';
|
||||||
|
import { faker } from '@faker-js/faker';
|
||||||
|
|
||||||
|
export const createFolder = async (params = {}) => {
|
||||||
|
params.name = params?.name || faker.lorem.word();
|
||||||
|
|
||||||
|
const folder = await Folder.query().insertAndFetch(params);
|
||||||
|
|
||||||
|
return folder;
|
||||||
|
};
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
const createFolderMock = async (flow) => {
|
||||||
|
const data = {
|
||||||
|
id: flow.id,
|
||||||
|
name: flow.name,
|
||||||
|
createdAt: flow.createdAt.getTime(),
|
||||||
|
updatedAt: flow.updatedAt.getTime(),
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: data,
|
||||||
|
meta: {
|
||||||
|
count: 1,
|
||||||
|
currentPage: null,
|
||||||
|
isArray: false,
|
||||||
|
totalPages: null,
|
||||||
|
type: 'Folder',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default createFolderMock;
|
||||||
Reference in New Issue
Block a user