Merge pull request #2314 from automatisch/create-folder

feat: Implement create folder API endpoint
This commit is contained in:
Ömer Faruk Aydın
2025-01-27 16:01:08 +01:00
committed by GitHub
12 changed files with 153 additions and 0 deletions

View File

@@ -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 });
};

View File

@@ -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);
});
});

View File

@@ -133,6 +133,10 @@ const authorizationList = {
action: 'create',
subject: 'Connection',
},
'POST /api/v1/folders/': {
action: 'create',
subject: 'Flow',
},
};
export const authorizeUser = async (request, response, next) => {

View File

@@ -20,6 +20,7 @@ import Permission from './permission.js';
import Role from './role.js';
import Step from './step.js';
import Subscription from './subscription.ee.js';
import Folder from './folder.js';
import UsageData from './usage-data.ee.js';
import Billing from '../helpers/billing/index.ee.js';
import NotAuthorizedError from '../errors/not-authorized.js';
@@ -178,6 +179,14 @@ class User extends Base {
to: 'users.id',
},
},
folders: {
relation: Base.HasManyRelation,
modelClass: Folder,
join: {
from: 'users.id',
to: 'folders.user_id',
},
},
});
static get virtualAttributes() {

View File

@@ -14,6 +14,7 @@ import Role from './role.js';
import Step from './step.js';
import Subscription from './subscription.ee.js';
import UsageData from './usage-data.ee.js';
import Folder from './folder.js';
import User from './user.js';
import deleteUserQueue from '../queues/delete-user.ee.js';
import emailQueue from '../queues/email.js';
@@ -153,6 +154,14 @@ describe('User model', () => {
to: 'users.id',
},
},
folders: {
relation: Base.HasManyRelation,
modelClass: Folder,
join: {
from: 'users.id',
to: 'folders.user_id',
},
},
};
expect(relationMappings).toStrictEqual(expectedRelations);

View 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;

View File

@@ -19,6 +19,7 @@ import rolesRouter from './api/v1/admin/roles.ee.js';
import permissionsRouter from './api/v1/admin/permissions.ee.js';
import adminUsersRouter from './api/v1/admin/users.ee.js';
import installationUsersRouter from './api/v1/installation/users.js';
import foldersRouter from './api/v1/folders.js';
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/saml-auth-providers', adminSamlAuthProvidersRouter);
router.use('/api/v1/installation/users', installationUsersRouter);
router.use('/api/v1/folders', foldersRouter);
export default router;

View 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;

View 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);
});
});

View File

@@ -19,6 +19,7 @@ import executionStepSerializer from './execution-step.js';
import subscriptionSerializer from './subscription.ee.js';
import adminUserSerializer from './admin/user.js';
import configSerializer from './config.js';
import folderSerializer from './folder.js';
const serializers = {
AdminUser: adminUserSerializer,
@@ -42,6 +43,7 @@ const serializers = {
ExecutionStep: executionStepSerializer,
Subscription: subscriptionSerializer,
Config: configSerializer,
Folder: folderSerializer,
};
export default serializers;

View 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;
};

View File

@@ -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;