Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d31309a92d |
4
.github/workflows/playwright.yml
vendored
4
.github/workflows/playwright.yml
vendored
@@ -12,6 +12,9 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
BULLMQ_DASHBOARD_USERNAME: root
|
||||
BULLMQ_DASHBOARD_PASSWORD: sample
|
||||
ENABLE_BULLMQ_DASHBOARD: true
|
||||
ENCRYPTION_KEY: sample_encryption_key
|
||||
WEBHOOK_SECRET_KEY: sample_webhook_secret_key
|
||||
APP_SECRET_KEY: sample_app_secret_key
|
||||
@@ -22,6 +25,7 @@ env:
|
||||
POSTGRES_PASSWORD: automatisch_password
|
||||
REDIS_HOST: localhost
|
||||
APP_ENV: production
|
||||
PORT: 3000
|
||||
LICENSE_KEY: dummy_license_key
|
||||
|
||||
jobs:
|
||||
|
@@ -32,7 +32,7 @@ describe('POST /api/v1/access-tokens', () => {
|
||||
})
|
||||
.expect(422);
|
||||
|
||||
expect(response.body.errors.general).toStrictEqual([
|
||||
expect(response.body.errors.general).toEqual([
|
||||
'Incorrect email or password.',
|
||||
]);
|
||||
});
|
||||
|
@@ -83,7 +83,7 @@ describe('POST /api/v1/admin/apps/:appKey/auth-clients', () => {
|
||||
.send(appAuthClient)
|
||||
.expect(422);
|
||||
|
||||
expect(response.body.meta.type).toStrictEqual('ModelValidation');
|
||||
expect(response.body.meta.type).toEqual('ModelValidation');
|
||||
expect(response.body.errors).toMatchObject({
|
||||
name: ["must have required property 'name'"],
|
||||
formattedAuthDefaults: [
|
||||
|
@@ -10,11 +10,11 @@ export default async (request, response) => {
|
||||
};
|
||||
|
||||
const appConfigParams = (request) => {
|
||||
const { customConnectionAllowed, shared, disabled } = request.body;
|
||||
const { allowCustomConnection, shared, disabled } = request.body;
|
||||
|
||||
return {
|
||||
key: request.params.appKey,
|
||||
customConnectionAllowed,
|
||||
allowCustomConnection,
|
||||
shared,
|
||||
disabled,
|
||||
};
|
||||
|
@@ -23,7 +23,7 @@ describe('POST /api/v1/admin/apps/:appKey/config', () => {
|
||||
|
||||
it('should return created app config', async () => {
|
||||
const appConfig = {
|
||||
customConnectionAllowed: true,
|
||||
allowCustomConnection: true,
|
||||
shared: true,
|
||||
disabled: false,
|
||||
};
|
||||
@@ -44,7 +44,7 @@ describe('POST /api/v1/admin/apps/:appKey/config', () => {
|
||||
it('should return HTTP 422 for already existing app config', async () => {
|
||||
const appConfig = {
|
||||
key: 'gitlab',
|
||||
customConnectionAllowed: true,
|
||||
allowCustomConnection: true,
|
||||
shared: true,
|
||||
disabled: false,
|
||||
};
|
||||
@@ -59,7 +59,7 @@ describe('POST /api/v1/admin/apps/:appKey/config', () => {
|
||||
})
|
||||
.expect(422);
|
||||
|
||||
expect(response.body.meta.type).toStrictEqual('UniqueViolationError');
|
||||
expect(response.body.meta.type).toEqual('UniqueViolationError');
|
||||
expect(response.body.errors).toMatchObject({
|
||||
key: ["'key' must be unique."],
|
||||
});
|
||||
|
@@ -32,7 +32,7 @@ describe('GET /api/v1/admin/apps/:appKey/auth-clients/:appAuthClientId', () => {
|
||||
.expect(200);
|
||||
|
||||
const expectedPayload = getAppAuthClientMock(currentAppAuthClient);
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return not found response for not existing app auth client ID', async () => {
|
||||
|
@@ -39,6 +39,6 @@ describe('GET /api/v1/admin/apps/:appKey/auth-clients', () => {
|
||||
appAuthClientOne,
|
||||
]);
|
||||
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
});
|
||||
|
@@ -8,19 +8,16 @@ export default async (request, response) => {
|
||||
})
|
||||
.throwIfNotFound();
|
||||
|
||||
await appConfig.$query().patchAndFetch({
|
||||
...appConfigParams(request),
|
||||
key: request.params.appKey,
|
||||
});
|
||||
await appConfig.$query().patchAndFetch(appConfigParams(request));
|
||||
|
||||
renderObject(response, appConfig);
|
||||
};
|
||||
|
||||
const appConfigParams = (request) => {
|
||||
const { customConnectionAllowed, shared, disabled } = request.body;
|
||||
const { allowCustomConnection, shared, disabled } = request.body;
|
||||
|
||||
return {
|
||||
customConnectionAllowed,
|
||||
allowCustomConnection,
|
||||
shared,
|
||||
disabled,
|
||||
};
|
||||
|
@@ -24,7 +24,7 @@ describe('PATCH /api/v1/admin/apps/:appKey/config', () => {
|
||||
it('should return updated app config', async () => {
|
||||
const appConfig = {
|
||||
key: 'gitlab',
|
||||
customConnectionAllowed: true,
|
||||
allowCustomConnection: true,
|
||||
shared: true,
|
||||
disabled: false,
|
||||
};
|
||||
@@ -34,7 +34,7 @@ describe('PATCH /api/v1/admin/apps/:appKey/config', () => {
|
||||
const newAppConfigValues = {
|
||||
shared: false,
|
||||
disabled: true,
|
||||
customConnectionAllowed: false,
|
||||
allowCustomConnection: false,
|
||||
};
|
||||
|
||||
const response = await request(app)
|
||||
@@ -55,7 +55,7 @@ describe('PATCH /api/v1/admin/apps/:appKey/config', () => {
|
||||
const appConfig = {
|
||||
shared: false,
|
||||
disabled: true,
|
||||
customConnectionAllowed: false,
|
||||
allowCustomConnection: false,
|
||||
};
|
||||
|
||||
await request(app)
|
||||
@@ -68,7 +68,7 @@ describe('PATCH /api/v1/admin/apps/:appKey/config', () => {
|
||||
it('should return HTTP 422 for invalid app config data', async () => {
|
||||
const appConfig = {
|
||||
key: 'gitlab',
|
||||
customConnectionAllowed: true,
|
||||
allowCustomConnection: true,
|
||||
shared: true,
|
||||
disabled: false,
|
||||
};
|
||||
@@ -83,7 +83,7 @@ describe('PATCH /api/v1/admin/apps/:appKey/config', () => {
|
||||
})
|
||||
.expect(422);
|
||||
|
||||
expect(response.body.meta.type).toStrictEqual('ModelValidation');
|
||||
expect(response.body.meta.type).toEqual('ModelValidation');
|
||||
expect(response.body.errors).toMatchObject({
|
||||
disabled: ['must be boolean'],
|
||||
});
|
||||
|
@@ -50,8 +50,8 @@ describe('PATCH /api/v1/admin/config', () => {
|
||||
.send(newConfigValues)
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.data.title).toStrictEqual(newTitle);
|
||||
expect(response.body.meta.type).toStrictEqual('Config');
|
||||
expect(response.body.data.title).toEqual(newTitle);
|
||||
expect(response.body.meta.type).toEqual('Config');
|
||||
});
|
||||
|
||||
it('should return created config for unexisting config', async () => {
|
||||
@@ -67,8 +67,8 @@ describe('PATCH /api/v1/admin/config', () => {
|
||||
.send(newConfigValues)
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.data.title).toStrictEqual(newTitle);
|
||||
expect(response.body.meta.type).toStrictEqual('Config');
|
||||
expect(response.body.data.title).toEqual(newTitle);
|
||||
expect(response.body.meta.type).toEqual('Config');
|
||||
});
|
||||
|
||||
it('should return null for deleted config entry', async () => {
|
||||
@@ -83,6 +83,6 @@ describe('PATCH /api/v1/admin/config', () => {
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.data.title).toBeNull();
|
||||
expect(response.body.meta.type).toStrictEqual('Config');
|
||||
expect(response.body.meta.type).toEqual('Config');
|
||||
});
|
||||
});
|
||||
|
@@ -27,6 +27,6 @@ describe('GET /api/v1/admin/permissions/catalog', () => {
|
||||
|
||||
const expectedPayload = await getPermissionsCatalogMock();
|
||||
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
});
|
||||
|
@@ -58,7 +58,7 @@ describe('POST /api/v1/admin/roles', () => {
|
||||
]
|
||||
);
|
||||
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return unprocessable entity response for invalid role data', async () => {
|
||||
|
@@ -92,4 +92,21 @@ describe('DELETE /api/v1/admin/roles/:roleId', () => {
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should not delete role and permissions on unsuccessful response', async () => {
|
||||
const role = await createRole();
|
||||
const permission = await createPermission({ roleId: role.id });
|
||||
await createUser({ roleId: role.id });
|
||||
|
||||
await request(app)
|
||||
.delete(`/api/v1/admin/roles/${role.id}`)
|
||||
.set('Authorization', token)
|
||||
.expect(422);
|
||||
|
||||
const refetchedRole = await role.$query();
|
||||
const refetchedPermission = await permission.$query();
|
||||
|
||||
expect(refetchedRole).toStrictEqual(role);
|
||||
expect(refetchedPermission).toStrictEqual(permission);
|
||||
});
|
||||
});
|
||||
|
@@ -34,7 +34,7 @@ describe('GET /api/v1/admin/roles/:roleId', () => {
|
||||
permissionTwo,
|
||||
]);
|
||||
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return not found response for not existing role UUID', async () => {
|
||||
|
@@ -28,6 +28,6 @@ describe('GET /api/v1/admin/roles', () => {
|
||||
|
||||
const expectedPayload = await getRolesMock([roleOne, roleTwo]);
|
||||
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
});
|
||||
|
@@ -46,6 +46,6 @@ describe('GET /api/v1/admin/saml-auth-providers/:samlAuthProviderId/role-mapping
|
||||
roleMappingTwo,
|
||||
]);
|
||||
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
});
|
||||
|
@@ -30,7 +30,7 @@ describe('GET /api/v1/admin/saml-auth-provider/:samlAuthProviderId', () => {
|
||||
|
||||
const expectedPayload = await getSamlAuthProviderMock(samlAuthProvider);
|
||||
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return not found response for not existing saml auth provider UUID', async () => {
|
||||
|
@@ -34,6 +34,6 @@ describe('GET /api/v1/admin/saml-auth-providers', () => {
|
||||
samlAuthProviderOne,
|
||||
]);
|
||||
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
});
|
||||
|
@@ -30,7 +30,7 @@ describe('GET /api/v1/admin/users/:userId', () => {
|
||||
.expect(200);
|
||||
|
||||
const expectedPayload = getUserMock(anotherUser, anotherUserRole);
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return not found response for not existing user UUID', async () => {
|
||||
|
@@ -40,6 +40,6 @@ describe('GET /api/v1/admin/users', () => {
|
||||
[anotherUserRole, currentUserRole]
|
||||
);
|
||||
|
||||
expect(response.body).toStrictEqual(expectedResponsePayload);
|
||||
expect(response.body).toEqual(expectedResponsePayload);
|
||||
});
|
||||
});
|
||||
|
@@ -61,8 +61,7 @@ describe('PATCH /api/v1/admin/users/:userId', () => {
|
||||
.send(anotherUserUpdatedData)
|
||||
.expect(422);
|
||||
|
||||
expect(response.body.meta.type).toStrictEqual('ModelValidation');
|
||||
|
||||
expect(response.body.meta.type).toEqual('ModelValidation');
|
||||
expect(response.body.errors).toMatchObject({
|
||||
email: ['must be string'],
|
||||
fullName: ['must be string'],
|
||||
|
@@ -155,7 +155,7 @@ describe('POST /api/v1/apps/:appKey/connections', () => {
|
||||
await createAppConfig({
|
||||
key: 'gitlab',
|
||||
disabled: false,
|
||||
customConnectionAllowed: true,
|
||||
allowCustomConnection: true,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -218,7 +218,7 @@ describe('POST /api/v1/apps/:appKey/connections', () => {
|
||||
await createAppConfig({
|
||||
key: 'gitlab',
|
||||
disabled: false,
|
||||
customConnectionAllowed: false,
|
||||
allowCustomConnection: false,
|
||||
});
|
||||
});
|
||||
|
||||
|
@@ -29,7 +29,7 @@ describe('GET /api/v1/apps/:appKey/actions/:actionKey/substeps', () => {
|
||||
.expect(200);
|
||||
|
||||
const expectedPayload = getActionSubstepsMock(exampleAction.substeps);
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return not found response for invalid app key', async () => {
|
||||
@@ -47,6 +47,6 @@ describe('GET /api/v1/apps/:appKey/actions/:actionKey/substeps', () => {
|
||||
.set('Authorization', token)
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.data).toStrictEqual([]);
|
||||
expect(response.body.data).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
@@ -23,7 +23,7 @@ describe('GET /api/v1/apps/:appKey/actions', () => {
|
||||
.expect(200);
|
||||
|
||||
const expectedPayload = getActionsMock(exampleApp.actions);
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return not found response for invalid app key', async () => {
|
||||
|
@@ -23,7 +23,7 @@ describe('GET /api/v1/apps/:appKey', () => {
|
||||
.expect(200);
|
||||
|
||||
const expectedPayload = getAppMock(exampleApp);
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return not found response for invalid app key', async () => {
|
||||
|
@@ -22,7 +22,7 @@ describe('GET /api/v1/apps', () => {
|
||||
.expect(200);
|
||||
|
||||
const expectedPayload = getAppsMock(apps);
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return all apps filtered by name', async () => {
|
||||
@@ -34,7 +34,7 @@ describe('GET /api/v1/apps', () => {
|
||||
.expect(200);
|
||||
|
||||
const expectedPayload = getAppsMock(appsWithNameGit);
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return only the apps with triggers', async () => {
|
||||
@@ -46,7 +46,7 @@ describe('GET /api/v1/apps', () => {
|
||||
.expect(200);
|
||||
|
||||
const expectedPayload = getAppsMock(appsWithTriggers);
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return only the apps with actions', async () => {
|
||||
@@ -58,6 +58,6 @@ describe('GET /api/v1/apps', () => {
|
||||
.expect(200);
|
||||
|
||||
const expectedPayload = getAppsMock(appsWithActions);
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
});
|
||||
|
@@ -29,7 +29,7 @@ describe('GET /api/v1/apps/:appKey/auth-clients/:appAuthClientId', () => {
|
||||
.expect(200);
|
||||
|
||||
const expectedPayload = getAppAuthClientMock(currentAppAuthClient);
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return not found response for not existing app auth client ID', async () => {
|
||||
|
@@ -37,6 +37,6 @@ describe('GET /api/v1/apps/:appKey/auth-clients', () => {
|
||||
appAuthClientOne,
|
||||
]);
|
||||
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
});
|
||||
|
@@ -23,7 +23,7 @@ describe('GET /api/v1/apps/:appKey/auth', () => {
|
||||
.expect(200);
|
||||
|
||||
const expectedPayload = getAuthMock(exampleApp.auth);
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return not found response for invalid app key', async () => {
|
||||
|
@@ -17,7 +17,7 @@ describe('GET /api/v1/apps/:appKey/config', () => {
|
||||
|
||||
appConfig = await createAppConfig({
|
||||
key: 'deepl',
|
||||
customConnectionAllowed: true,
|
||||
allowCustomConnection: true,
|
||||
shared: true,
|
||||
disabled: false,
|
||||
});
|
||||
@@ -32,7 +32,7 @@ describe('GET /api/v1/apps/:appKey/config', () => {
|
||||
.expect(200);
|
||||
|
||||
const expectedPayload = getAppConfigMock(appConfig);
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return not found response for not existing app key', async () => {
|
||||
|
@@ -47,7 +47,7 @@ describe('GET /api/v1/apps/:appKey/connections', () => {
|
||||
currentUserConnectionOne,
|
||||
]);
|
||||
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return the connections data of specified app for another user', async () => {
|
||||
@@ -82,7 +82,7 @@ describe('GET /api/v1/apps/:appKey/connections', () => {
|
||||
anotherUserConnectionOne,
|
||||
]);
|
||||
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return not found response for invalid connection UUID', async () => {
|
||||
|
@@ -62,7 +62,7 @@ describe('GET /api/v1/apps/:appKey/flows', () => {
|
||||
[triggerStepFlowOne, actionStepFlowOne]
|
||||
);
|
||||
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return the flows data of specified app for another user', async () => {
|
||||
@@ -110,7 +110,7 @@ describe('GET /api/v1/apps/:appKey/flows', () => {
|
||||
[triggerStepFlowOne, actionStepFlowOne]
|
||||
);
|
||||
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return not found response for invalid app key', async () => {
|
||||
|
@@ -29,7 +29,7 @@ describe('GET /api/v1/apps/:appKey/triggers/:triggerKey/substeps', () => {
|
||||
.expect(200);
|
||||
|
||||
const expectedPayload = getTriggerSubstepsMock(exampleTrigger.substeps);
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return not found response for invalid app key', async () => {
|
||||
@@ -47,6 +47,6 @@ describe('GET /api/v1/apps/:appKey/triggers/:triggerKey/substeps', () => {
|
||||
.set('Authorization', token)
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.data).toStrictEqual([]);
|
||||
expect(response.body.data).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
@@ -23,7 +23,7 @@ describe('GET /api/v1/apps/:appKey/triggers', () => {
|
||||
.expect(200);
|
||||
|
||||
const expectedPayload = getTriggersMock(exampleApp.triggers);
|
||||
expect(expectedPayload).toMatchObject(response.body);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return not found response for invalid app key', async () => {
|
||||
|
@@ -20,6 +20,6 @@ describe('GET /api/v1/automatisch/info', () => {
|
||||
|
||||
const expectedPayload = infoMock();
|
||||
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
});
|
||||
|
@@ -18,6 +18,6 @@ describe('GET /api/v1/automatisch/license', () => {
|
||||
|
||||
const expectedPayload = licenseMock();
|
||||
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
});
|
||||
|
@@ -21,6 +21,6 @@ describe('GET /api/v1/automatisch/version', () => {
|
||||
},
|
||||
};
|
||||
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
});
|
||||
|
@@ -69,7 +69,7 @@ describe('GET /api/v1/connections/:connectionId/flows', () => {
|
||||
[triggerStepFlowOne, actionStepFlowOne]
|
||||
);
|
||||
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return the flows data of specified connection for another user', async () => {
|
||||
@@ -123,6 +123,6 @@ describe('GET /api/v1/connections/:connectionId/flows', () => {
|
||||
[triggerStepFlowOne, actionStepFlowOne]
|
||||
);
|
||||
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
});
|
||||
|
@@ -43,7 +43,7 @@ describe('POST /api/v1/connections/:connectionId/test', () => {
|
||||
.set('Authorization', token)
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.data.verified).toStrictEqual(false);
|
||||
expect(response.body.data.verified).toEqual(false);
|
||||
});
|
||||
|
||||
it('should update the connection as not verified for another user', async () => {
|
||||
@@ -74,7 +74,7 @@ describe('POST /api/v1/connections/:connectionId/test', () => {
|
||||
.set('Authorization', token)
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.data.verified).toStrictEqual(false);
|
||||
expect(response.body.data.verified).toEqual(false);
|
||||
});
|
||||
|
||||
it('should return not found response for not existing connection UUID', async () => {
|
||||
|
@@ -8,7 +8,7 @@ export default async (request, response) => {
|
||||
})
|
||||
.throwIfNotFound();
|
||||
|
||||
connection = await connection.updateFormattedData(connectionParams(request));
|
||||
connection = await connection.update(connectionParams(request));
|
||||
|
||||
renderObject(response, connection);
|
||||
};
|
||||
|
@@ -47,7 +47,7 @@ describe('POST /api/v1/connections/:connectionId/verify', () => {
|
||||
.set('Authorization', token)
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.data.verified).toStrictEqual(true);
|
||||
expect(response.body.data.verified).toEqual(true);
|
||||
});
|
||||
|
||||
it('should return not found response for not existing connection UUID', async () => {
|
||||
|
@@ -69,7 +69,7 @@ describe('GET /api/v1/executions/:executionId/execution-steps', () => {
|
||||
[stepOne, stepTwo]
|
||||
);
|
||||
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return the execution steps of another user execution', async () => {
|
||||
@@ -118,7 +118,7 @@ describe('GET /api/v1/executions/:executionId/execution-steps', () => {
|
||||
[stepOne, stepTwo]
|
||||
);
|
||||
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return not found response for not existing execution step UUID', async () => {
|
||||
|
@@ -57,7 +57,7 @@ describe('GET /api/v1/executions/:executionId', () => {
|
||||
[stepOne, stepTwo]
|
||||
);
|
||||
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return the execution data of another user', async () => {
|
||||
@@ -99,7 +99,7 @@ describe('GET /api/v1/executions/:executionId', () => {
|
||||
[stepOne, stepTwo]
|
||||
);
|
||||
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return not found response for not existing execution UUID', async () => {
|
||||
|
@@ -66,7 +66,7 @@ describe('GET /api/v1/executions', () => {
|
||||
[stepOne, stepTwo]
|
||||
);
|
||||
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return the executions of another user', async () => {
|
||||
@@ -114,6 +114,6 @@ describe('GET /api/v1/executions', () => {
|
||||
[stepOne, stepTwo]
|
||||
);
|
||||
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
});
|
||||
|
@@ -41,7 +41,7 @@ describe('GET /api/v1/flows/:flowId', () => {
|
||||
actionStep,
|
||||
]);
|
||||
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return the flow data of another user', async () => {
|
||||
@@ -67,7 +67,7 @@ describe('GET /api/v1/flows/:flowId', () => {
|
||||
actionStep,
|
||||
]);
|
||||
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return not found response for not existing flow UUID', async () => {
|
||||
|
@@ -63,7 +63,7 @@ describe('GET /api/v1/flows', () => {
|
||||
]
|
||||
);
|
||||
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return the flows data of another user', async () => {
|
||||
@@ -113,6 +113,6 @@ describe('GET /api/v1/flows', () => {
|
||||
]
|
||||
);
|
||||
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
});
|
||||
|
@@ -53,7 +53,7 @@ describe('POST /api/v1/installation/users', () => {
|
||||
|
||||
const usersCountAfter = await User.query().resultSize();
|
||||
|
||||
expect(usersCountBefore).toStrictEqual(usersCountAfter);
|
||||
expect(usersCountBefore).toEqual(usersCountAfter);
|
||||
});
|
||||
});
|
||||
|
||||
|
@@ -28,6 +28,6 @@ describe('GET /api/v1/payment/paddle-info', () => {
|
||||
|
||||
const expectedResponsePayload = await getPaddleInfoMock();
|
||||
|
||||
expect(response.body).toStrictEqual(expectedResponsePayload);
|
||||
expect(response.body).toEqual(expectedResponsePayload);
|
||||
});
|
||||
});
|
||||
|
@@ -24,6 +24,6 @@ describe('GET /api/v1/payment/plans', () => {
|
||||
|
||||
const expectedResponsePayload = await getPaymentPlansMock();
|
||||
|
||||
expect(response.body).toStrictEqual(expectedResponsePayload);
|
||||
expect(response.body).toEqual(expectedResponsePayload);
|
||||
});
|
||||
});
|
||||
|
@@ -25,6 +25,6 @@ describe('GET /api/v1/saml-auth-providers', () => {
|
||||
samlAuthProviderOne,
|
||||
]);
|
||||
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
});
|
||||
|
@@ -78,7 +78,7 @@ describe('POST /api/v1/steps/:stepId/dynamic-data', () => {
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.data).toStrictEqual(repositories);
|
||||
expect(response.body.data).toEqual(repositories);
|
||||
});
|
||||
|
||||
it('of the another users step', async () => {
|
||||
@@ -117,7 +117,7 @@ describe('POST /api/v1/steps/:stepId/dynamic-data', () => {
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.data).toStrictEqual(repositories);
|
||||
expect(response.body.data).toEqual(repositories);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -171,7 +171,7 @@ describe('POST /api/v1/steps/:stepId/dynamic-data', () => {
|
||||
})
|
||||
.expect(422);
|
||||
|
||||
expect(response.body.errors).toStrictEqual(errors);
|
||||
expect(response.body.errors).toEqual(errors);
|
||||
});
|
||||
});
|
||||
|
||||
|
@@ -56,7 +56,7 @@ describe('POST /api/v1/steps/:stepId/dynamic-fields', () => {
|
||||
|
||||
const expectedPayload = await createDynamicFieldsMock();
|
||||
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return dynamically created fields of the another users step', async () => {
|
||||
@@ -97,7 +97,7 @@ describe('POST /api/v1/steps/:stepId/dynamic-fields', () => {
|
||||
|
||||
const expectedPayload = await createDynamicFieldsMock();
|
||||
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return not found response for not existing step UUID', async () => {
|
||||
|
@@ -43,7 +43,7 @@ describe('GET /api/v1/steps/:stepId/connection', () => {
|
||||
|
||||
const expectedPayload = await getConnectionMock(currentUserConnection);
|
||||
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return the current user connection data of specified step', async () => {
|
||||
@@ -70,7 +70,7 @@ describe('GET /api/v1/steps/:stepId/connection', () => {
|
||||
|
||||
const expectedPayload = await getConnectionMock(anotherUserConnection);
|
||||
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return not found response for not existing step without connection', async () => {
|
||||
|
@@ -70,7 +70,7 @@ describe('GET /api/v1/steps/:stepId/previous-steps', () => {
|
||||
[executionStepOne, executionStepTwo]
|
||||
);
|
||||
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return the previous steps of the specified step of another user', async () => {
|
||||
@@ -124,7 +124,7 @@ describe('GET /api/v1/steps/:stepId/previous-steps', () => {
|
||||
[executionStepOne, executionStepTwo]
|
||||
);
|
||||
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return not found response for not existing step UUID', async () => {
|
||||
|
@@ -79,7 +79,7 @@ describe('GET /api/v1/users/:userId/apps', () => {
|
||||
.expect(200);
|
||||
|
||||
const expectedPayload = getAppsMock();
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return all apps of the another user', async () => {
|
||||
@@ -143,7 +143,7 @@ describe('GET /api/v1/users/:userId/apps', () => {
|
||||
.expect(200);
|
||||
|
||||
const expectedPayload = getAppsMock();
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return specified app of the current user', async () => {
|
||||
@@ -204,7 +204,7 @@ describe('GET /api/v1/users/:userId/apps', () => {
|
||||
.set('Authorization', token)
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.data.length).toStrictEqual(1);
|
||||
expect(response.body.data[0].key).toStrictEqual('deepl');
|
||||
expect(response.body.data.length).toEqual(1);
|
||||
expect(response.body.data[0].key).toEqual('deepl');
|
||||
});
|
||||
});
|
||||
|
@@ -39,6 +39,6 @@ describe('GET /api/v1/users/me', () => {
|
||||
permissionTwo,
|
||||
]);
|
||||
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
});
|
||||
|
@@ -29,6 +29,6 @@ describe('GET /api/v1/user/invoices', () => {
|
||||
|
||||
const expectedPayload = await getInvoicesMock(invoices);
|
||||
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
});
|
||||
|
@@ -36,7 +36,7 @@ describe('GET /api/v1/users/:userId/plan-and-usage', () => {
|
||||
},
|
||||
};
|
||||
|
||||
expect(response.body.data).toStrictEqual(expectedResponseData);
|
||||
expect(response.body.data).toEqual(expectedResponseData);
|
||||
});
|
||||
|
||||
it('should return current plan and usage data', async () => {
|
||||
@@ -63,6 +63,6 @@ describe('GET /api/v1/users/:userId/plan-and-usage', () => {
|
||||
},
|
||||
};
|
||||
|
||||
expect(response.body.data).toStrictEqual(expectedResponseData);
|
||||
expect(response.body.data).toEqual(expectedResponseData);
|
||||
});
|
||||
});
|
||||
|
@@ -33,7 +33,7 @@ describe('GET /api/v1/users/:userId/subscription', () => {
|
||||
|
||||
const expectedPayload = getSubscriptionMock(subscription);
|
||||
|
||||
expect(response.body).toStrictEqual(expectedPayload);
|
||||
expect(response.body).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return not found response if there is no current subscription', async () => {
|
||||
|
@@ -32,7 +32,7 @@ describe('GET /api/v1/users/:userId/trial', () => {
|
||||
.expect(200);
|
||||
|
||||
const expectedResponsePayload = await getUserTrialMock(user);
|
||||
expect(response.body).toStrictEqual(expectedResponsePayload);
|
||||
expect(response.body).toEqual(expectedResponsePayload);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -43,7 +43,7 @@ describe('PATCH /api/v1/users/:userId/password', () => {
|
||||
.send(userData)
|
||||
.expect(422);
|
||||
|
||||
expect(response.body.meta.type).toStrictEqual('ValidationError');
|
||||
expect(response.body.meta.type).toEqual('ValidationError');
|
||||
expect(response.body.errors).toMatchObject({
|
||||
currentPassword: ['is incorrect.'],
|
||||
});
|
||||
|
@@ -47,8 +47,7 @@ describe('PATCH /api/v1/users/:userId', () => {
|
||||
.send(userData)
|
||||
.expect(422);
|
||||
|
||||
expect(response.body.meta.type).toStrictEqual('ModelValidation');
|
||||
|
||||
expect(response.body.meta.type).toEqual('ModelValidation');
|
||||
expect(response.body.errors).toMatchObject({
|
||||
email: ['must be string'],
|
||||
fullName: ['must be string'],
|
||||
|
@@ -1,37 +0,0 @@
|
||||
export async function up(knex) {
|
||||
await knex.schema.alterTable('app_configs', (table) => {
|
||||
table.boolean('connection_allowed').defaultTo(false);
|
||||
});
|
||||
|
||||
const appConfigs = await knex('app_configs').select('*');
|
||||
|
||||
for (const appConfig of appConfigs) {
|
||||
const appAuthClients = await knex('app_auth_clients').where(
|
||||
'app_key',
|
||||
appConfig.key
|
||||
);
|
||||
|
||||
const hasSomeActiveAppAuthClients = !!appAuthClients?.some(
|
||||
(appAuthClient) => appAuthClient.active
|
||||
);
|
||||
const shared = appConfig.shared;
|
||||
const active = appConfig.disabled === false;
|
||||
|
||||
const connectionAllowedConditions = [
|
||||
hasSomeActiveAppAuthClients,
|
||||
shared,
|
||||
active,
|
||||
];
|
||||
const connectionAllowed = connectionAllowedConditions.every(Boolean);
|
||||
|
||||
await knex('app_configs')
|
||||
.where('id', appConfig.id)
|
||||
.update({ connection_allowed: connectionAllowed });
|
||||
}
|
||||
}
|
||||
|
||||
export async function down(knex) {
|
||||
await knex.schema.alterTable('app_configs', (table) => {
|
||||
table.dropColumn('connection_allowed');
|
||||
});
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
export async function up(knex) {
|
||||
return knex.schema.alterTable('app_configs', (table) => {
|
||||
table.renameColumn('allow_custom_connection', 'custom_connection_allowed');
|
||||
});
|
||||
}
|
||||
|
||||
export async function down(knex) {
|
||||
return knex.schema.alterTable('app_configs', (table) => {
|
||||
table.renameColumn('custom_connection_allowed', 'allow_custom_connection');
|
||||
});
|
||||
}
|
@@ -1,13 +0,0 @@
|
||||
export async function up(knex) {
|
||||
return knex.schema.alterTable('app_configs', function (table) {
|
||||
table.dropPrimary();
|
||||
table.primary('key');
|
||||
});
|
||||
}
|
||||
|
||||
export async function down(knex) {
|
||||
return knex.schema.alterTable('app_configs', function (table) {
|
||||
table.dropPrimary();
|
||||
table.primary('id');
|
||||
});
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
export async function up(knex) {
|
||||
return knex.schema.alterTable('app_configs', function (table) {
|
||||
table.dropColumn('id');
|
||||
});
|
||||
}
|
||||
|
||||
export async function down(knex) {
|
||||
return knex.schema.alterTable('app_configs', function (table) {
|
||||
table.uuid('id').defaultTo(knex.raw('gen_random_uuid()'));
|
||||
});
|
||||
}
|
@@ -1,41 +0,0 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`AppConfig model > jsonSchema should have correct validations 1`] = `
|
||||
{
|
||||
"properties": {
|
||||
"connectionAllowed": {
|
||||
"default": false,
|
||||
"type": "boolean",
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "string",
|
||||
},
|
||||
"customConnectionAllowed": {
|
||||
"default": false,
|
||||
"type": "boolean",
|
||||
},
|
||||
"disabled": {
|
||||
"default": false,
|
||||
"type": "boolean",
|
||||
},
|
||||
"id": {
|
||||
"format": "uuid",
|
||||
"type": "string",
|
||||
},
|
||||
"key": {
|
||||
"type": "string",
|
||||
},
|
||||
"shared": {
|
||||
"default": false,
|
||||
"type": "boolean",
|
||||
},
|
||||
"updatedAt": {
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
"required": [
|
||||
"key",
|
||||
],
|
||||
"type": "object",
|
||||
}
|
||||
`;
|
@@ -1,42 +0,0 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`Permission model > jsonSchema should have correct validations 1`] = `
|
||||
{
|
||||
"properties": {
|
||||
"action": {
|
||||
"minLength": 1,
|
||||
"type": "string",
|
||||
},
|
||||
"conditions": {
|
||||
"items": {
|
||||
"type": "string",
|
||||
},
|
||||
"type": "array",
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "string",
|
||||
},
|
||||
"id": {
|
||||
"format": "uuid",
|
||||
"type": "string",
|
||||
},
|
||||
"roleId": {
|
||||
"format": "uuid",
|
||||
"type": "string",
|
||||
},
|
||||
"subject": {
|
||||
"minLength": 1,
|
||||
"type": "string",
|
||||
},
|
||||
"updatedAt": {
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
"required": [
|
||||
"roleId",
|
||||
"action",
|
||||
"subject",
|
||||
],
|
||||
"type": "object",
|
||||
}
|
||||
`;
|
@@ -1,33 +0,0 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`Role model > jsonSchema should have correct validations 1`] = `
|
||||
{
|
||||
"properties": {
|
||||
"createdAt": {
|
||||
"type": "string",
|
||||
},
|
||||
"description": {
|
||||
"maxLength": 255,
|
||||
"type": [
|
||||
"string",
|
||||
"null",
|
||||
],
|
||||
},
|
||||
"id": {
|
||||
"format": "uuid",
|
||||
"type": "string",
|
||||
},
|
||||
"name": {
|
||||
"minLength": 1,
|
||||
"type": "string",
|
||||
},
|
||||
"updatedAt": {
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
],
|
||||
"type": "object",
|
||||
}
|
||||
`;
|
@@ -2,7 +2,6 @@ import AES from 'crypto-js/aes.js';
|
||||
import enc from 'crypto-js/enc-utf8.js';
|
||||
import appConfig from '../config/app.js';
|
||||
import Base from './base.js';
|
||||
import AppConfig from './app-config.js';
|
||||
|
||||
class AppAuthClient extends Base {
|
||||
static tableName = 'app_auth_clients';
|
||||
@@ -22,17 +21,6 @@ class AppAuthClient extends Base {
|
||||
},
|
||||
};
|
||||
|
||||
static relationMappings = () => ({
|
||||
appConfig: {
|
||||
relation: Base.BelongsToOneRelation,
|
||||
modelClass: AppConfig,
|
||||
join: {
|
||||
from: 'app_auth_clients.app_key',
|
||||
to: 'app_configs.key',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
encryptData() {
|
||||
if (!this.eligibleForEncryption()) return;
|
||||
|
||||
@@ -60,17 +48,6 @@ class AppAuthClient extends Base {
|
||||
return this.authDefaults ? true : false;
|
||||
}
|
||||
|
||||
async triggerAppConfigUpdate() {
|
||||
const appConfig = await this.$relatedQuery('appConfig');
|
||||
|
||||
// This is a workaround to update connection allowed column for AppConfig
|
||||
await appConfig?.$query().patch({
|
||||
key: appConfig.key,
|
||||
shared: appConfig.shared,
|
||||
disabled: appConfig.disabled,
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: Make another abstraction like beforeSave instead of using
|
||||
// beforeInsert and beforeUpdate separately for the same operation.
|
||||
async $beforeInsert(queryContext) {
|
||||
@@ -78,23 +55,11 @@ class AppAuthClient extends Base {
|
||||
this.encryptData();
|
||||
}
|
||||
|
||||
async $afterInsert(queryContext) {
|
||||
await super.$afterInsert(queryContext);
|
||||
|
||||
await this.triggerAppConfigUpdate();
|
||||
}
|
||||
|
||||
async $beforeUpdate(opt, queryContext) {
|
||||
await super.$beforeUpdate(opt, queryContext);
|
||||
this.encryptData();
|
||||
}
|
||||
|
||||
async $afterUpdate(opt, queryContext) {
|
||||
await super.$afterUpdate(opt, queryContext);
|
||||
|
||||
await this.triggerAppConfigUpdate();
|
||||
}
|
||||
|
||||
async $afterFind() {
|
||||
this.decryptData();
|
||||
}
|
||||
|
@@ -2,12 +2,9 @@ import { describe, it, expect, vi } from 'vitest';
|
||||
import AES from 'crypto-js/aes.js';
|
||||
import enc from 'crypto-js/enc-utf8.js';
|
||||
|
||||
import AppConfig from './app-config.js';
|
||||
import AppAuthClient from './app-auth-client.js';
|
||||
import Base from './base.js';
|
||||
import appConfig from '../config/app.js';
|
||||
import { createAppAuthClient } from '../../test/factories/app-auth-client.js';
|
||||
import { createAppConfig } from '../../test/factories/app-config.js';
|
||||
|
||||
describe('AppAuthClient model', () => {
|
||||
it('tableName should return correct name', () => {
|
||||
@@ -18,23 +15,6 @@ describe('AppAuthClient model', () => {
|
||||
expect(AppAuthClient.jsonSchema).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('relationMappings should return correct associations', () => {
|
||||
const relationMappings = AppAuthClient.relationMappings();
|
||||
|
||||
const expectedRelations = {
|
||||
appConfig: {
|
||||
relation: Base.BelongsToOneRelation,
|
||||
modelClass: AppConfig,
|
||||
join: {
|
||||
from: 'app_auth_clients.app_key',
|
||||
to: 'app_configs.key',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(relationMappings).toStrictEqual(expectedRelations);
|
||||
});
|
||||
|
||||
describe('encryptData', () => {
|
||||
it('should return undefined if eligibleForEncryption is not true', async () => {
|
||||
vi.spyOn(
|
||||
@@ -69,9 +49,7 @@ describe('AppAuthClient model', () => {
|
||||
);
|
||||
|
||||
expect(formattedAuthDefaults).toStrictEqual(expectedDecryptedValue);
|
||||
expect(appAuthClient.authDefaults).not.toStrictEqual(
|
||||
formattedAuthDefaults
|
||||
);
|
||||
expect(appAuthClient.authDefaults).not.toEqual(formattedAuthDefaults);
|
||||
});
|
||||
|
||||
it('should encrypt formattedAuthDefaults and remove formattedAuthDefaults', async () => {
|
||||
@@ -126,9 +104,7 @@ describe('AppAuthClient model', () => {
|
||||
expect(appAuthClient.formattedAuthDefaults).toStrictEqual(
|
||||
formattedAuthDefaults
|
||||
);
|
||||
expect(appAuthClient.authDefaults).not.toStrictEqual(
|
||||
formattedAuthDefaults
|
||||
);
|
||||
expect(appAuthClient.authDefaults).not.toEqual(formattedAuthDefaults);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -164,63 +140,6 @@ describe('AppAuthClient model', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('triggerAppConfigUpdate', () => {
|
||||
it('should trigger an update in related app config', async () => {
|
||||
await createAppConfig({ key: 'gitlab' });
|
||||
|
||||
const appAuthClient = await createAppAuthClient({
|
||||
appKey: 'gitlab',
|
||||
});
|
||||
|
||||
const appConfigBeforeUpdateSpy = vi.spyOn(
|
||||
AppConfig.prototype,
|
||||
'$beforeUpdate'
|
||||
);
|
||||
|
||||
await appAuthClient.triggerAppConfigUpdate();
|
||||
|
||||
expect(appConfigBeforeUpdateSpy).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('should update related AppConfig after creating an instance', async () => {
|
||||
const appConfig = await createAppConfig({
|
||||
key: 'gitlab',
|
||||
disabled: false,
|
||||
shared: true,
|
||||
});
|
||||
|
||||
await createAppAuthClient({
|
||||
appKey: 'gitlab',
|
||||
active: true,
|
||||
});
|
||||
|
||||
const refetchedAppConfig = await appConfig.$query();
|
||||
|
||||
expect(refetchedAppConfig.connectionAllowed).toBe(true);
|
||||
});
|
||||
|
||||
it('should update related AppConfig after updating an instance', async () => {
|
||||
const appConfig = await createAppConfig({
|
||||
key: 'gitlab',
|
||||
disabled: false,
|
||||
shared: true,
|
||||
});
|
||||
|
||||
const appAuthClient = await createAppAuthClient({
|
||||
appKey: 'gitlab',
|
||||
active: false,
|
||||
});
|
||||
|
||||
let refetchedAppConfig = await appConfig.$query();
|
||||
expect(refetchedAppConfig.connectionAllowed).toBe(false);
|
||||
|
||||
await appAuthClient.$query().patchAndFetch({ active: true });
|
||||
|
||||
refetchedAppConfig = await appConfig.$query();
|
||||
expect(refetchedAppConfig.connectionAllowed).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('$beforeInsert should call AppAuthClient.encryptData', async () => {
|
||||
const appAuthClientBeforeInsertSpy = vi.spyOn(
|
||||
AppAuthClient.prototype,
|
||||
@@ -232,17 +151,6 @@ describe('AppAuthClient model', () => {
|
||||
expect(appAuthClientBeforeInsertSpy).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('$afterInsert should call AppAuthClient.triggerAppConfigUpdate', async () => {
|
||||
const appAuthClientAfterInsertSpy = vi.spyOn(
|
||||
AppAuthClient.prototype,
|
||||
'triggerAppConfigUpdate'
|
||||
);
|
||||
|
||||
await createAppAuthClient();
|
||||
|
||||
expect(appAuthClientAfterInsertSpy).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('$beforeUpdate should call AppAuthClient.encryptData', async () => {
|
||||
const appAuthClient = await createAppAuthClient();
|
||||
|
||||
@@ -256,19 +164,6 @@ describe('AppAuthClient model', () => {
|
||||
expect(appAuthClientBeforeUpdateSpy).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('$afterUpdate should call AppAuthClient.triggerAppConfigUpdate', async () => {
|
||||
const appAuthClient = await createAppAuthClient();
|
||||
|
||||
const appAuthClientAfterUpdateSpy = vi.spyOn(
|
||||
AppAuthClient.prototype,
|
||||
'triggerAppConfigUpdate'
|
||||
);
|
||||
|
||||
await appAuthClient.$query().patchAndFetch({ name: 'sample' });
|
||||
|
||||
expect(appAuthClientAfterUpdateSpy).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('$afterFind should call AppAuthClient.decryptData', async () => {
|
||||
const appAuthClient = await createAppAuthClient();
|
||||
|
||||
|
@@ -5,10 +5,6 @@ import Base from './base.js';
|
||||
class AppConfig extends Base {
|
||||
static tableName = 'app_configs';
|
||||
|
||||
static get idColumn() {
|
||||
return 'key';
|
||||
}
|
||||
|
||||
static jsonSchema = {
|
||||
type: 'object',
|
||||
required: ['key'],
|
||||
@@ -16,8 +12,7 @@ class AppConfig extends Base {
|
||||
properties: {
|
||||
id: { type: 'string', format: 'uuid' },
|
||||
key: { type: 'string' },
|
||||
connectionAllowed: { type: 'boolean', default: false },
|
||||
customConnectionAllowed: { type: 'boolean', default: false },
|
||||
allowCustomConnection: { type: 'boolean', default: false },
|
||||
shared: { type: 'boolean', default: false },
|
||||
disabled: { type: 'boolean', default: false },
|
||||
createdAt: { type: 'string' },
|
||||
@@ -36,44 +31,31 @@ class AppConfig extends Base {
|
||||
},
|
||||
});
|
||||
|
||||
static get virtualAttributes() {
|
||||
return ['canConnect', 'canCustomConnect'];
|
||||
}
|
||||
|
||||
get canCustomConnect() {
|
||||
return !this.disabled && this.allowCustomConnection;
|
||||
}
|
||||
|
||||
get canConnect() {
|
||||
const hasSomeActiveAppAuthClients = !!this.appAuthClients?.some(
|
||||
(appAuthClient) => appAuthClient.active
|
||||
);
|
||||
const shared = this.shared;
|
||||
const active = this.disabled === false;
|
||||
|
||||
const conditions = [hasSomeActiveAppAuthClients, shared, active];
|
||||
|
||||
return conditions.every(Boolean);
|
||||
}
|
||||
|
||||
async getApp() {
|
||||
if (!this.key) return null;
|
||||
|
||||
return await App.findOneByKey(this.key);
|
||||
}
|
||||
|
||||
async computeAndAssignConnectionAllowedProperty() {
|
||||
this.connectionAllowed = await this.computeConnectionAllowedProperty();
|
||||
}
|
||||
|
||||
async computeConnectionAllowedProperty() {
|
||||
const appAuthClients = await this.$relatedQuery('appAuthClients');
|
||||
|
||||
const hasSomeActiveAppAuthClients =
|
||||
appAuthClients?.some((appAuthClient) => appAuthClient.active) || false;
|
||||
|
||||
const conditions = [
|
||||
hasSomeActiveAppAuthClients,
|
||||
this.shared,
|
||||
!this.disabled,
|
||||
];
|
||||
|
||||
const connectionAllowed = conditions.every(Boolean);
|
||||
|
||||
return connectionAllowed;
|
||||
}
|
||||
|
||||
async $beforeInsert(queryContext) {
|
||||
await super.$beforeInsert(queryContext);
|
||||
|
||||
await this.computeAndAssignConnectionAllowedProperty();
|
||||
}
|
||||
|
||||
async $beforeUpdate(opt, queryContext) {
|
||||
await super.$beforeUpdate(opt, queryContext);
|
||||
|
||||
await this.computeAndAssignConnectionAllowedProperty();
|
||||
}
|
||||
}
|
||||
|
||||
export default AppConfig;
|
||||
|
@@ -1,180 +0,0 @@
|
||||
import { vi, describe, it, expect } from 'vitest';
|
||||
|
||||
import Base from './base.js';
|
||||
import AppConfig from './app-config.js';
|
||||
import App from './app.js';
|
||||
import AppAuthClient from './app-auth-client.js';
|
||||
import { createAppConfig } from '../../test/factories/app-config.js';
|
||||
import { createAppAuthClient } from '../../test/factories/app-auth-client.js';
|
||||
|
||||
describe('AppConfig model', () => {
|
||||
it('tableName should return correct name', () => {
|
||||
expect(AppConfig.tableName).toBe('app_configs');
|
||||
});
|
||||
|
||||
it('idColumn should return key field', () => {
|
||||
expect(AppConfig.idColumn).toBe('key');
|
||||
});
|
||||
|
||||
it('jsonSchema should have correct validations', () => {
|
||||
expect(AppConfig.jsonSchema).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('relationMappings should return correct associations', () => {
|
||||
const relationMappings = AppConfig.relationMappings();
|
||||
|
||||
const expectedRelations = {
|
||||
appAuthClients: {
|
||||
relation: Base.HasManyRelation,
|
||||
modelClass: AppAuthClient,
|
||||
join: {
|
||||
from: 'app_configs.key',
|
||||
to: 'app_auth_clients.app_key',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(relationMappings).toStrictEqual(expectedRelations);
|
||||
});
|
||||
|
||||
describe('getApp', () => {
|
||||
it('getApp should return null if there is no key', async () => {
|
||||
const appConfig = new AppConfig();
|
||||
const app = await appConfig.getApp();
|
||||
|
||||
expect(app).toBeNull();
|
||||
});
|
||||
|
||||
it('getApp should return app with provided key', async () => {
|
||||
const appConfig = new AppConfig();
|
||||
appConfig.key = 'deepl';
|
||||
|
||||
const app = await appConfig.getApp();
|
||||
const expectedApp = await App.findOneByKey(appConfig.key);
|
||||
|
||||
expect(app).toStrictEqual(expectedApp);
|
||||
});
|
||||
});
|
||||
|
||||
describe('computeAndAssignConnectionAllowedProperty', () => {
|
||||
it('should call computeConnectionAllowedProperty and assign the result', async () => {
|
||||
const appConfig = await createAppConfig();
|
||||
|
||||
const computeConnectionAllowedPropertySpy = vi
|
||||
.spyOn(appConfig, 'computeConnectionAllowedProperty')
|
||||
.mockResolvedValue(true);
|
||||
|
||||
await appConfig.computeAndAssignConnectionAllowedProperty();
|
||||
|
||||
expect(computeConnectionAllowedPropertySpy).toHaveBeenCalled();
|
||||
expect(appConfig.connectionAllowed).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('computeConnectionAllowedProperty', () => {
|
||||
it('should return true when app is enabled, shared and allows custom connection with an active app auth client', async () => {
|
||||
await createAppAuthClient({
|
||||
appKey: 'deepl',
|
||||
active: true,
|
||||
});
|
||||
|
||||
await createAppAuthClient({
|
||||
appKey: 'deepl',
|
||||
active: false,
|
||||
});
|
||||
|
||||
const appConfig = await createAppConfig({
|
||||
disabled: false,
|
||||
customConnectionAllowed: true,
|
||||
shared: true,
|
||||
key: 'deepl',
|
||||
});
|
||||
|
||||
const connectionAllowed =
|
||||
await appConfig.computeConnectionAllowedProperty();
|
||||
|
||||
expect(connectionAllowed).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false if there is no active app auth client', async () => {
|
||||
await createAppAuthClient({
|
||||
appKey: 'deepl',
|
||||
active: false,
|
||||
});
|
||||
|
||||
const appConfig = await createAppConfig({
|
||||
disabled: false,
|
||||
customConnectionAllowed: true,
|
||||
shared: true,
|
||||
key: 'deepl',
|
||||
});
|
||||
|
||||
const connectionAllowed =
|
||||
await appConfig.computeConnectionAllowedProperty();
|
||||
|
||||
expect(connectionAllowed).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false if there is no app auth clients', async () => {
|
||||
const appConfig = await createAppConfig({
|
||||
disabled: false,
|
||||
customConnectionAllowed: true,
|
||||
shared: true,
|
||||
key: 'deepl',
|
||||
});
|
||||
|
||||
const connectionAllowed =
|
||||
await appConfig.computeConnectionAllowedProperty();
|
||||
|
||||
expect(connectionAllowed).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false when app is disabled', async () => {
|
||||
const appConfig = await createAppConfig({
|
||||
disabled: true,
|
||||
customConnectionAllowed: true,
|
||||
});
|
||||
|
||||
const connectionAllowed =
|
||||
await appConfig.computeConnectionAllowedProperty();
|
||||
|
||||
expect(connectionAllowed).toBe(false);
|
||||
});
|
||||
|
||||
it(`should return false when app doesn't allow custom connection`, async () => {
|
||||
const appConfig = await createAppConfig({
|
||||
disabled: false,
|
||||
customConnectionAllowed: false,
|
||||
});
|
||||
|
||||
const connectionAllowed =
|
||||
await appConfig.computeConnectionAllowedProperty();
|
||||
|
||||
expect(connectionAllowed).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it('$beforeInsert should call computeAndAssignConnectionAllowedProperty', async () => {
|
||||
const computeAndAssignConnectionAllowedPropertySpy = vi
|
||||
.spyOn(AppConfig.prototype, 'computeAndAssignConnectionAllowedProperty')
|
||||
.mockResolvedValue(true);
|
||||
|
||||
await createAppConfig();
|
||||
|
||||
expect(computeAndAssignConnectionAllowedPropertySpy).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('$beforeUpdate should call computeAndAssignConnectionAllowedProperty', async () => {
|
||||
const appConfig = await createAppConfig();
|
||||
|
||||
const computeAndAssignConnectionAllowedPropertySpy = vi
|
||||
.spyOn(AppConfig.prototype, 'computeAndAssignConnectionAllowedProperty')
|
||||
.mockResolvedValue(true);
|
||||
|
||||
await appConfig.$query().patch({
|
||||
key: 'deepl',
|
||||
});
|
||||
|
||||
expect(computeAndAssignConnectionAllowedPropertySpy).toHaveBeenCalledOnce();
|
||||
});
|
||||
});
|
@@ -89,7 +89,7 @@ class Connection extends Base {
|
||||
}
|
||||
|
||||
if (this.appConfig) {
|
||||
return !this.appConfig.disabled && this.appConfig.customConnectionAllowed;
|
||||
return !this.appConfig.disabled && this.appConfig.allowCustomConnection;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -122,20 +122,10 @@ class Connection extends Base {
|
||||
return this.data ? true : false;
|
||||
}
|
||||
|
||||
async getApp() {
|
||||
if (!this.key) return null;
|
||||
|
||||
return await App.findOneByKey(this.key);
|
||||
}
|
||||
|
||||
async getAppConfig() {
|
||||
return await AppConfig.query().findOne({ key: this.key });
|
||||
}
|
||||
|
||||
async checkEligibilityForCreation() {
|
||||
const app = await this.getApp();
|
||||
const app = await App.findOneByKey(this.key);
|
||||
|
||||
const appConfig = await this.getAppConfig();
|
||||
const appConfig = await AppConfig.query().findOne({ key: this.key });
|
||||
|
||||
if (appConfig) {
|
||||
if (appConfig.disabled) {
|
||||
@@ -144,7 +134,7 @@ class Connection extends Base {
|
||||
);
|
||||
}
|
||||
|
||||
if (!appConfig.customConnectionAllowed && this.formattedData) {
|
||||
if (!appConfig.allowCustomConnection && this.formattedData) {
|
||||
throw new NotAuthorizedError(
|
||||
`New custom connections have been disabled for ${app.name}!`
|
||||
);
|
||||
@@ -170,6 +160,12 @@ class Connection extends Base {
|
||||
return this;
|
||||
}
|
||||
|
||||
async getApp() {
|
||||
if (!this.key) return null;
|
||||
|
||||
return await App.findOneByKey(this.key);
|
||||
}
|
||||
|
||||
async testAndUpdateConnection() {
|
||||
const app = await this.getApp();
|
||||
const $ = await globalVariable({ connection: this, app });
|
||||
@@ -228,7 +224,7 @@ class Connection extends Base {
|
||||
async reset() {
|
||||
const formattedData = this?.formattedData?.screenName
|
||||
? { screenName: this.formattedData.screenName }
|
||||
: {};
|
||||
: null;
|
||||
|
||||
const updatedConnection = await this.$query().patchAndFetch({
|
||||
formattedData,
|
||||
@@ -237,7 +233,7 @@ class Connection extends Base {
|
||||
return updatedConnection;
|
||||
}
|
||||
|
||||
async updateFormattedData({ formattedData, appAuthClientId }) {
|
||||
async update({ formattedData, appAuthClientId }) {
|
||||
if (appAuthClientId) {
|
||||
const appAuthClient = await AppAuthClient.query()
|
||||
.findById(appAuthClientId)
|
||||
|
@@ -3,16 +3,11 @@ import AES from 'crypto-js/aes.js';
|
||||
import enc from 'crypto-js/enc-utf8.js';
|
||||
import appConfig from '../config/app.js';
|
||||
import AppAuthClient from './app-auth-client.js';
|
||||
import App from './app.js';
|
||||
import AppConfig from './app-config.js';
|
||||
import Base from './base.js';
|
||||
import Connection from './connection';
|
||||
import Step from './step.js';
|
||||
import User from './user.js';
|
||||
import Telemetry from '../helpers/telemetry/index.js';
|
||||
import { createConnection } from '../../test/factories/connection.js';
|
||||
import { createAppConfig } from '../../test/factories/app-config.js';
|
||||
import { createAppAuthClient } from '../../test/factories/app-auth-client.js';
|
||||
|
||||
describe('Connection model', () => {
|
||||
it('tableName should return correct name', () => {
|
||||
@@ -31,138 +26,57 @@ describe('Connection model', () => {
|
||||
expect(virtualAttributes).toStrictEqual(expectedAttributes);
|
||||
});
|
||||
|
||||
describe('relationMappings', () => {
|
||||
it('should return correct associations', () => {
|
||||
const relationMappings = Connection.relationMappings();
|
||||
it('relationMappings should return correct associations', () => {
|
||||
const relationMappings = Connection.relationMappings();
|
||||
|
||||
const expectedRelations = {
|
||||
user: {
|
||||
relation: Base.BelongsToOneRelation,
|
||||
modelClass: User,
|
||||
join: {
|
||||
from: 'connections.user_id',
|
||||
to: 'users.id',
|
||||
},
|
||||
const expectedRelations = {
|
||||
user: {
|
||||
relation: Base.BelongsToOneRelation,
|
||||
modelClass: User,
|
||||
join: {
|
||||
from: 'connections.user_id',
|
||||
to: 'users.id',
|
||||
},
|
||||
steps: {
|
||||
relation: Base.HasManyRelation,
|
||||
modelClass: Step,
|
||||
join: {
|
||||
from: 'connections.id',
|
||||
to: 'steps.connection_id',
|
||||
},
|
||||
},
|
||||
steps: {
|
||||
relation: Base.HasManyRelation,
|
||||
modelClass: Step,
|
||||
join: {
|
||||
from: 'connections.id',
|
||||
to: 'steps.connection_id',
|
||||
},
|
||||
triggerSteps: {
|
||||
relation: Base.HasManyRelation,
|
||||
modelClass: Step,
|
||||
join: {
|
||||
from: 'connections.id',
|
||||
to: 'steps.connection_id',
|
||||
},
|
||||
filter: expect.any(Function),
|
||||
},
|
||||
triggerSteps: {
|
||||
relation: Base.HasManyRelation,
|
||||
modelClass: Step,
|
||||
join: {
|
||||
from: 'connections.id',
|
||||
to: 'steps.connection_id',
|
||||
},
|
||||
appConfig: {
|
||||
relation: Base.BelongsToOneRelation,
|
||||
modelClass: AppConfig,
|
||||
join: {
|
||||
from: 'connections.key',
|
||||
to: 'app_configs.key',
|
||||
},
|
||||
filter: expect.any(Function),
|
||||
},
|
||||
appConfig: {
|
||||
relation: Base.BelongsToOneRelation,
|
||||
modelClass: AppConfig,
|
||||
join: {
|
||||
from: 'connections.key',
|
||||
to: 'app_configs.key',
|
||||
},
|
||||
appAuthClient: {
|
||||
relation: Base.BelongsToOneRelation,
|
||||
modelClass: AppAuthClient,
|
||||
join: {
|
||||
from: 'connections.app_auth_client_id',
|
||||
to: 'app_auth_clients.id',
|
||||
},
|
||||
},
|
||||
appAuthClient: {
|
||||
relation: Base.BelongsToOneRelation,
|
||||
modelClass: AppAuthClient,
|
||||
join: {
|
||||
from: 'connections.app_auth_client_id',
|
||||
to: 'app_auth_clients.id',
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
expect(relationMappings).toStrictEqual(expectedRelations);
|
||||
});
|
||||
|
||||
it('triggerSteps should return only trigger typed steps', () => {
|
||||
const relations = Connection.relationMappings();
|
||||
const whereSpy = vi.fn();
|
||||
|
||||
relations.triggerSteps.filter({ where: whereSpy });
|
||||
|
||||
expect(whereSpy).toHaveBeenCalledWith('type', '=', 'trigger');
|
||||
});
|
||||
expect(relationMappings).toStrictEqual(expectedRelations);
|
||||
});
|
||||
|
||||
describe('reconnectable', () => {
|
||||
it('should return active status of app auth client when created via app auth client', async () => {
|
||||
const appAuthClient = await createAppAuthClient({
|
||||
active: true,
|
||||
formattedAuthDefaults: {
|
||||
clientId: 'sample-id',
|
||||
},
|
||||
});
|
||||
|
||||
const connection = await createConnection({
|
||||
appAuthClientId: appAuthClient.id,
|
||||
formattedData: {
|
||||
token: 'sample-token',
|
||||
},
|
||||
});
|
||||
|
||||
const connectionWithAppAuthClient = await connection
|
||||
.$query()
|
||||
.withGraphFetched({
|
||||
appAuthClient: true,
|
||||
});
|
||||
|
||||
expect(connectionWithAppAuthClient.reconnectable).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true when app config is not disabled and allows custom connection', async () => {
|
||||
const appConfig = await createAppConfig({
|
||||
key: 'gitlab',
|
||||
disabled: false,
|
||||
customConnectionAllowed: true,
|
||||
});
|
||||
|
||||
const connection = await createConnection({
|
||||
key: appConfig.key,
|
||||
formattedData: {
|
||||
token: 'sample-token',
|
||||
},
|
||||
});
|
||||
|
||||
const connectionWithAppAuthClient = await connection
|
||||
.$query()
|
||||
.withGraphFetched({
|
||||
appConfig: true,
|
||||
});
|
||||
|
||||
expect(connectionWithAppAuthClient.reconnectable).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false when app config is disabled or does not allow custom connection', async () => {
|
||||
const connection = await createConnection({
|
||||
key: 'gitlab',
|
||||
formattedData: {
|
||||
token: 'sample-token',
|
||||
},
|
||||
});
|
||||
|
||||
await createAppConfig({
|
||||
key: 'gitlab',
|
||||
disabled: true,
|
||||
customConnectionAllowed: false,
|
||||
});
|
||||
|
||||
const connectionWithAppAuthClient = await connection
|
||||
.$query()
|
||||
.withGraphFetched({
|
||||
appConfig: true,
|
||||
});
|
||||
|
||||
expect(connectionWithAppAuthClient.reconnectable).toBe(false);
|
||||
});
|
||||
});
|
||||
describe.todo('reconnectable');
|
||||
|
||||
describe('encryptData', () => {
|
||||
it('should return undefined if eligibleForEncryption is not true', async () => {
|
||||
@@ -193,7 +107,7 @@ describe('Connection model', () => {
|
||||
);
|
||||
|
||||
expect(formattedData).toStrictEqual(expectedDecryptedValue);
|
||||
expect(connection.data).not.toStrictEqual(formattedData);
|
||||
expect(connection.data).not.toEqual(formattedData);
|
||||
});
|
||||
|
||||
it('should encrypt formattedData and remove formattedData', async () => {
|
||||
@@ -243,561 +157,7 @@ describe('Connection model', () => {
|
||||
connection.decryptData();
|
||||
|
||||
expect(connection.formattedData).toStrictEqual(formattedData);
|
||||
expect(connection.data).not.toStrictEqual(formattedData);
|
||||
});
|
||||
});
|
||||
|
||||
describe('eligibleForEncryption', () => {
|
||||
it('should return true when formattedData property exists', async () => {
|
||||
const connection = new Connection();
|
||||
connection.formattedData = { clientId: 'sample-id' };
|
||||
|
||||
expect(connection.eligibleForEncryption()).toBe(true);
|
||||
});
|
||||
|
||||
it("should return false when formattedData property doesn't exist", async () => {
|
||||
const connection = new Connection();
|
||||
connection.formattedData = undefined;
|
||||
|
||||
expect(connection.eligibleForEncryption()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('eligibleForDecryption', () => {
|
||||
it('should return true when data property exists', async () => {
|
||||
const connection = new Connection();
|
||||
connection.data = 'encrypted-data';
|
||||
|
||||
expect(connection.eligibleForDecryption()).toBe(true);
|
||||
});
|
||||
|
||||
it("should return false when data property doesn't exist", async () => {
|
||||
const connection = new Connection();
|
||||
connection.data = undefined;
|
||||
|
||||
expect(connection.eligibleForDecryption()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getApp', () => {
|
||||
it('should return connection app when valid key exists', async () => {
|
||||
const connection = new Connection();
|
||||
connection.key = 'gitlab';
|
||||
|
||||
const connectionApp = await connection.getApp();
|
||||
const app = await App.findOneByKey('gitlab');
|
||||
|
||||
expect(connectionApp).toStrictEqual(app);
|
||||
});
|
||||
|
||||
it('should throw an error when invalid key exists', async () => {
|
||||
const connection = new Connection();
|
||||
connection.key = 'invalid-key';
|
||||
|
||||
await expect(() => connection.getApp()).rejects.toThrowError(
|
||||
`An application with the "invalid-key" key couldn't be found.`
|
||||
);
|
||||
});
|
||||
|
||||
it('should return null when no key exists', async () => {
|
||||
const connection = new Connection();
|
||||
|
||||
await expect(connection.getApp()).resolves.toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
it('getAppConfig should return connection app config', async () => {
|
||||
const connection = new Connection();
|
||||
connection.key = 'gitlab';
|
||||
|
||||
const appConfig = await createAppConfig({ key: 'gitlab' });
|
||||
|
||||
const connectionAppConfig = await connection.getAppConfig();
|
||||
|
||||
expect(connectionAppConfig).toStrictEqual(appConfig);
|
||||
});
|
||||
|
||||
describe('checkEligibilityForCreation', () => {
|
||||
it('should return connection if no app config exists', async () => {
|
||||
vi.spyOn(Connection.prototype, 'getApp').mockResolvedValue({
|
||||
name: 'gitlab',
|
||||
});
|
||||
|
||||
vi.spyOn(Connection.prototype, 'getAppConfig').mockResolvedValue();
|
||||
|
||||
const connection = new Connection();
|
||||
|
||||
expect(await connection.checkEligibilityForCreation()).toBe(connection);
|
||||
});
|
||||
|
||||
it('should throw an error when app does not exist', async () => {
|
||||
vi.spyOn(Connection.prototype, 'getApp').mockRejectedValue(
|
||||
new Error(
|
||||
`An application with the "unexisting-app" key couldn't be found.`
|
||||
)
|
||||
);
|
||||
|
||||
vi.spyOn(Connection.prototype, 'getAppConfig').mockResolvedValue();
|
||||
|
||||
const connection = new Connection();
|
||||
|
||||
await expect(() =>
|
||||
connection.checkEligibilityForCreation()
|
||||
).rejects.toThrow(
|
||||
`An application with the "unexisting-app" key couldn't be found.`
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw an error when app config is disabled', async () => {
|
||||
vi.spyOn(Connection.prototype, 'getApp').mockResolvedValue({
|
||||
name: 'gitlab',
|
||||
});
|
||||
|
||||
vi.spyOn(Connection.prototype, 'getAppConfig').mockResolvedValue({
|
||||
disabled: true,
|
||||
});
|
||||
|
||||
const connection = new Connection();
|
||||
|
||||
await expect(() =>
|
||||
connection.checkEligibilityForCreation()
|
||||
).rejects.toThrow(
|
||||
'The application has been disabled for new connections!'
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw an error when app config does not allow custom connection with formatted data', async () => {
|
||||
vi.spyOn(Connection.prototype, 'getApp').mockResolvedValue({
|
||||
name: 'gitlab',
|
||||
});
|
||||
|
||||
vi.spyOn(Connection.prototype, 'getAppConfig').mockResolvedValue({
|
||||
disabled: false,
|
||||
customConnectionAllowed: false,
|
||||
});
|
||||
|
||||
const connection = new Connection();
|
||||
connection.formattedData = {};
|
||||
|
||||
await expect(() =>
|
||||
connection.checkEligibilityForCreation()
|
||||
).rejects.toThrow(
|
||||
'New custom connections have been disabled for gitlab!'
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw an error when app config is not shared with app auth client', async () => {
|
||||
vi.spyOn(Connection.prototype, 'getApp').mockResolvedValue({
|
||||
name: 'gitlab',
|
||||
});
|
||||
|
||||
vi.spyOn(Connection.prototype, 'getAppConfig').mockResolvedValue({
|
||||
disabled: false,
|
||||
shared: false,
|
||||
});
|
||||
|
||||
const connection = new Connection();
|
||||
connection.appAuthClientId = 'sample-id';
|
||||
|
||||
await expect(() =>
|
||||
connection.checkEligibilityForCreation()
|
||||
).rejects.toThrow(
|
||||
'The connection with the given app auth client is not allowed!'
|
||||
);
|
||||
});
|
||||
|
||||
it('should apply app auth client auth defaults when creating with shared app auth client', async () => {
|
||||
await createAppConfig({
|
||||
key: 'gitlab',
|
||||
disabled: false,
|
||||
customConnectionAllowed: true,
|
||||
shared: true,
|
||||
});
|
||||
|
||||
const appAuthClient = await createAppAuthClient({
|
||||
appKey: 'gitlab',
|
||||
active: true,
|
||||
formattedAuthDefaults: {
|
||||
clientId: 'sample-id',
|
||||
},
|
||||
});
|
||||
|
||||
const connection = await createConnection({
|
||||
key: 'gitlab',
|
||||
appAuthClientId: appAuthClient.id,
|
||||
formattedData: null,
|
||||
});
|
||||
|
||||
await connection.checkEligibilityForCreation();
|
||||
|
||||
expect(connection.formattedData).toStrictEqual({
|
||||
clientId: 'sample-id',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('testAndUpdateConnection', () => {
|
||||
it('should verify connection and persist it', async () => {
|
||||
const connection = await createConnection({ verified: false });
|
||||
|
||||
const isStillVerifiedSpy = vi.fn().mockReturnValue(true);
|
||||
|
||||
const originalApp = await connection.getApp();
|
||||
|
||||
const getAppSpy = vi
|
||||
.spyOn(connection, 'getApp')
|
||||
.mockImplementation(() => {
|
||||
return {
|
||||
...originalApp,
|
||||
auth: {
|
||||
...originalApp.auth,
|
||||
isStillVerified: isStillVerifiedSpy,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const updatedConnection = await connection.testAndUpdateConnection();
|
||||
|
||||
expect(getAppSpy).toHaveBeenCalledOnce();
|
||||
expect(isStillVerifiedSpy).toHaveBeenCalledOnce();
|
||||
expect(updatedConnection.verified).toBe(true);
|
||||
});
|
||||
|
||||
it('should unverify connection and persist it', async () => {
|
||||
const connection = await createConnection({ verified: true });
|
||||
|
||||
const isStillVerifiedSpy = vi
|
||||
.fn()
|
||||
.mockRejectedValue(new Error('Wrong credentials!'));
|
||||
|
||||
const originalApp = await connection.getApp();
|
||||
|
||||
const getAppSpy = vi
|
||||
.spyOn(connection, 'getApp')
|
||||
.mockImplementation(() => {
|
||||
return {
|
||||
...originalApp,
|
||||
auth: {
|
||||
...originalApp.auth,
|
||||
isStillVerified: isStillVerifiedSpy,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const updatedConnection = await connection.testAndUpdateConnection();
|
||||
|
||||
expect(getAppSpy).toHaveBeenCalledOnce();
|
||||
expect(isStillVerifiedSpy).toHaveBeenCalledOnce();
|
||||
expect(updatedConnection.verified).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('verifyAndUpdateConnection', () => {
|
||||
it('should verify connection with valid token', async () => {
|
||||
const connection = await createConnection({
|
||||
verified: false,
|
||||
draft: true,
|
||||
});
|
||||
|
||||
const verifyCredentialsSpy = vi.fn().mockResolvedValue(true);
|
||||
|
||||
const originalApp = await connection.getApp();
|
||||
|
||||
vi.spyOn(connection, 'getApp').mockImplementation(() => {
|
||||
return {
|
||||
...originalApp,
|
||||
auth: {
|
||||
...originalApp.auth,
|
||||
verifyCredentials: verifyCredentialsSpy,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const updatedConnection = await connection.verifyAndUpdateConnection();
|
||||
|
||||
expect(verifyCredentialsSpy).toHaveBeenCalledOnce();
|
||||
expect(updatedConnection.verified).toBe(true);
|
||||
expect(updatedConnection.draft).toBe(false);
|
||||
});
|
||||
|
||||
it('should throw an error with invalid token', async () => {
|
||||
const connection = await createConnection({
|
||||
verified: false,
|
||||
draft: true,
|
||||
});
|
||||
|
||||
const verifyCredentialsSpy = vi
|
||||
.fn()
|
||||
.mockRejectedValue(new Error('Invalid token!'));
|
||||
|
||||
const originalApp = await connection.getApp();
|
||||
|
||||
vi.spyOn(connection, 'getApp').mockImplementation(() => {
|
||||
return {
|
||||
...originalApp,
|
||||
auth: {
|
||||
...originalApp.auth,
|
||||
verifyCredentials: verifyCredentialsSpy,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
await expect(() =>
|
||||
connection.verifyAndUpdateConnection()
|
||||
).rejects.toThrowError('Invalid token!');
|
||||
expect(verifyCredentialsSpy).toHaveBeenCalledOnce();
|
||||
});
|
||||
});
|
||||
|
||||
describe('verifyWebhook', () => {
|
||||
it('should verify webhook on remote', async () => {
|
||||
const connection = await createConnection({ key: 'typeform' });
|
||||
|
||||
const verifyWebhookSpy = vi.fn().mockResolvedValue('verified-webhook');
|
||||
|
||||
const originalApp = await connection.getApp();
|
||||
|
||||
vi.spyOn(connection, 'getApp').mockImplementation(() => {
|
||||
return {
|
||||
...originalApp,
|
||||
auth: {
|
||||
...originalApp.auth,
|
||||
verifyWebhook: verifyWebhookSpy,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
expect(await connection.verifyWebhook()).toBe('verified-webhook');
|
||||
});
|
||||
|
||||
it('should return true if connection does not have value in key property', async () => {
|
||||
const connection = await createConnection({ key: null });
|
||||
|
||||
expect(await connection.verifyWebhook()).toBe(true);
|
||||
});
|
||||
|
||||
it('should throw an error at failed webhook verification', async () => {
|
||||
const connection = await createConnection({ key: 'typeform' });
|
||||
|
||||
const verifyWebhookSpy = vi.fn().mockRejectedValue('unverified-webhook');
|
||||
|
||||
const originalApp = await connection.getApp();
|
||||
|
||||
vi.spyOn(connection, 'getApp').mockImplementation(() => {
|
||||
return {
|
||||
...originalApp,
|
||||
auth: {
|
||||
...originalApp.auth,
|
||||
verifyWebhook: verifyWebhookSpy,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
await expect(() => connection.verifyWebhook()).rejects.toThrowError(
|
||||
'unverified-webhook'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('generateAuthUrl should return authentication url', async () => {
|
||||
const connection = await createConnection({
|
||||
key: 'typeform',
|
||||
formattedData: {
|
||||
url: 'https://automatisch.io/authentication-url',
|
||||
},
|
||||
});
|
||||
|
||||
const generateAuthUrlSpy = vi.fn();
|
||||
|
||||
const originalApp = await connection.getApp();
|
||||
|
||||
vi.spyOn(connection, 'getApp').mockImplementation(() => {
|
||||
return {
|
||||
...originalApp,
|
||||
auth: {
|
||||
...originalApp.auth,
|
||||
generateAuthUrl: generateAuthUrlSpy,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
expect(await connection.generateAuthUrl()).toStrictEqual({
|
||||
url: 'https://automatisch.io/authentication-url',
|
||||
});
|
||||
});
|
||||
|
||||
describe('reset', () => {
|
||||
it('should keep screen name when exists and reset the rest of the formatted data', async () => {
|
||||
const connection = await createConnection({
|
||||
formattedData: {
|
||||
screenName: 'Sample connection',
|
||||
token: 'sample-token',
|
||||
},
|
||||
});
|
||||
|
||||
await connection.reset();
|
||||
|
||||
const refetchedConnection = await connection.$query();
|
||||
|
||||
expect(refetchedConnection.formattedData).toStrictEqual({
|
||||
screenName: 'Sample connection',
|
||||
});
|
||||
});
|
||||
|
||||
it('should empty formatted data object when screen name does not exist', async () => {
|
||||
const connection = await createConnection({
|
||||
formattedData: {
|
||||
token: 'sample-token',
|
||||
},
|
||||
});
|
||||
|
||||
await connection.reset();
|
||||
|
||||
const refetchedConnection = await connection.$query();
|
||||
|
||||
expect(refetchedConnection.formattedData).toStrictEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateFormattedData', () => {
|
||||
it('should extend connection data with app auth client auth defaults', async () => {
|
||||
const appAuthClient = await createAppAuthClient({
|
||||
formattedAuthDefaults: {
|
||||
clientId: 'sample-id',
|
||||
},
|
||||
});
|
||||
|
||||
const connection = await createConnection({
|
||||
appAuthClientId: appAuthClient.id,
|
||||
formattedData: {
|
||||
token: 'sample-token',
|
||||
},
|
||||
});
|
||||
|
||||
const updatedConnection = await connection.updateFormattedData({
|
||||
appAuthClientId: appAuthClient.id,
|
||||
});
|
||||
|
||||
expect(updatedConnection.formattedData).toStrictEqual({
|
||||
clientId: 'sample-id',
|
||||
token: 'sample-token',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('$beforeInsert', () => {
|
||||
it('should call super.$beforeInsert', async () => {
|
||||
const superBeforeInsertSpy = vi
|
||||
.spyOn(Base.prototype, '$beforeInsert')
|
||||
.mockResolvedValue();
|
||||
|
||||
await createConnection();
|
||||
|
||||
expect(superBeforeInsertSpy).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('should call checkEligibilityForCreation', async () => {
|
||||
const checkEligibilityForCreationSpy = vi
|
||||
.spyOn(Connection.prototype, 'checkEligibilityForCreation')
|
||||
.mockResolvedValue();
|
||||
|
||||
await createConnection();
|
||||
|
||||
expect(checkEligibilityForCreationSpy).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('should call encryptData', async () => {
|
||||
const encryptDataSpy = vi
|
||||
.spyOn(Connection.prototype, 'encryptData')
|
||||
.mockResolvedValue();
|
||||
|
||||
await createConnection();
|
||||
|
||||
expect(encryptDataSpy).toHaveBeenCalledOnce();
|
||||
});
|
||||
});
|
||||
|
||||
describe('$beforeUpdate', () => {
|
||||
it('should call super.$beforeUpdate', async () => {
|
||||
const superBeforeUpdateSpy = vi
|
||||
.spyOn(Base.prototype, '$beforeUpdate')
|
||||
.mockResolvedValue();
|
||||
|
||||
const connection = await createConnection();
|
||||
|
||||
await connection.$query().patch({ verified: false });
|
||||
|
||||
expect(superBeforeUpdateSpy).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('should call encryptData', async () => {
|
||||
const connection = await createConnection();
|
||||
|
||||
const encryptDataSpy = vi
|
||||
.spyOn(Connection.prototype, 'encryptData')
|
||||
.mockResolvedValue();
|
||||
|
||||
await connection.$query().patch({ verified: false });
|
||||
|
||||
expect(encryptDataSpy).toHaveBeenCalledOnce();
|
||||
});
|
||||
});
|
||||
|
||||
describe('$afterFind', () => {
|
||||
it('should call decryptData', async () => {
|
||||
const connection = await createConnection();
|
||||
|
||||
const decryptDataSpy = vi
|
||||
.spyOn(Connection.prototype, 'decryptData')
|
||||
.mockResolvedValue();
|
||||
|
||||
await connection.$query();
|
||||
|
||||
expect(decryptDataSpy).toHaveBeenCalledOnce();
|
||||
});
|
||||
});
|
||||
|
||||
describe('$afterInsert', () => {
|
||||
it('should call super.$afterInsert', async () => {
|
||||
const superAfterInsertSpy = vi.spyOn(Base.prototype, '$afterInsert');
|
||||
|
||||
await createConnection();
|
||||
|
||||
expect(superAfterInsertSpy).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('should call Telemetry.connectionCreated', async () => {
|
||||
const telemetryConnectionCreatedSpy = vi
|
||||
.spyOn(Telemetry, 'connectionCreated')
|
||||
.mockImplementation(() => {});
|
||||
|
||||
const connection = await createConnection();
|
||||
|
||||
expect(telemetryConnectionCreatedSpy).toHaveBeenCalledWith(connection);
|
||||
});
|
||||
});
|
||||
|
||||
describe('$afterUpdate', () => {
|
||||
it('should call super.$afterUpdate', async () => {
|
||||
const superAfterInsertSpy = vi.spyOn(Base.prototype, '$afterUpdate');
|
||||
|
||||
const connection = await createConnection();
|
||||
|
||||
await connection.$query().patch({ verified: false });
|
||||
|
||||
expect(superAfterInsertSpy).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('should call Telemetry.connectionUpdated', async () => {
|
||||
const telemetryconnectionUpdatedSpy = vi
|
||||
.spyOn(Telemetry, 'connectionCreated')
|
||||
.mockImplementation(() => {});
|
||||
|
||||
const connection = await createConnection();
|
||||
|
||||
await connection.$query().patch({ verified: false });
|
||||
|
||||
expect(telemetryconnectionUpdatedSpy).toHaveBeenCalledWith(connection);
|
||||
expect(connection.data).not.toEqual(formattedData);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -19,39 +19,25 @@ class Permission extends Base {
|
||||
},
|
||||
};
|
||||
|
||||
static filter(permissions) {
|
||||
static sanitize(permissions) {
|
||||
const sanitizedPermissions = permissions.filter((permission) => {
|
||||
const { action, subject, conditions } = permission;
|
||||
|
||||
const relevantAction = this.findAction(action);
|
||||
const validSubject = this.isSubjectValid(subject, relevantAction);
|
||||
const validConditions = this.areConditionsValid(conditions);
|
||||
const relevantAction = permissionCatalog.actions.find(
|
||||
(actionCatalogItem) => actionCatalogItem.key === action
|
||||
);
|
||||
const validSubject = relevantAction.subjects.includes(subject);
|
||||
const validConditions = conditions.every((condition) => {
|
||||
return !!permissionCatalog.conditions.find(
|
||||
(conditionCatalogItem) => conditionCatalogItem.key === condition
|
||||
);
|
||||
});
|
||||
|
||||
return relevantAction && validSubject && validConditions;
|
||||
return validSubject && validConditions;
|
||||
});
|
||||
|
||||
return sanitizedPermissions;
|
||||
}
|
||||
|
||||
static findAction(action) {
|
||||
return permissionCatalog.actions.find(
|
||||
(actionCatalogItem) => actionCatalogItem.key === action
|
||||
);
|
||||
}
|
||||
|
||||
static isSubjectValid(subject, action) {
|
||||
return action && action.subjects.includes(subject);
|
||||
}
|
||||
|
||||
static areConditionsValid(conditions) {
|
||||
return conditions.every((condition) => this.isConditionValid(condition));
|
||||
}
|
||||
|
||||
static isConditionValid(condition) {
|
||||
return !!permissionCatalog.conditions.find(
|
||||
(conditionCatalogItem) => conditionCatalogItem.key === condition
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Permission;
|
||||
|
@@ -1,95 +0,0 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import Permission from './permission';
|
||||
import permissionCatalog from '../helpers/permission-catalog.ee.js';
|
||||
|
||||
describe('Permission model', () => {
|
||||
it('tableName should return correct name', () => {
|
||||
expect(Permission.tableName).toBe('permissions');
|
||||
});
|
||||
|
||||
it('jsonSchema should have correct validations', () => {
|
||||
expect(Permission.jsonSchema).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('filter should return only valid permissions based on permission catalog', () => {
|
||||
const permissions = [
|
||||
{ action: 'read', subject: 'Flow', conditions: ['isCreator'] },
|
||||
{ action: 'delete', subject: 'Connection', conditions: [] },
|
||||
{ action: 'publish', subject: 'Flow', conditions: ['isCreator'] },
|
||||
{ action: 'update', subject: 'Execution', conditions: [] }, // Invalid subject
|
||||
{ action: 'read', subject: 'Execution', conditions: ['invalid'] }, // Invalid condition
|
||||
{ action: 'invalid', subject: 'Execution', conditions: [] }, // Invalid action
|
||||
];
|
||||
|
||||
const result = Permission.filter(permissions);
|
||||
|
||||
expect(result).toStrictEqual([
|
||||
{ action: 'read', subject: 'Flow', conditions: ['isCreator'] },
|
||||
{ action: 'delete', subject: 'Connection', conditions: [] },
|
||||
{ action: 'publish', subject: 'Flow', conditions: ['isCreator'] },
|
||||
]);
|
||||
});
|
||||
|
||||
describe('findAction', () => {
|
||||
it('should return action from permission catalog', () => {
|
||||
const action = Permission.findAction('create');
|
||||
expect(action.key).toStrictEqual('create');
|
||||
});
|
||||
|
||||
it('should return undefined for invalid actions', () => {
|
||||
const invalidAction = Permission.findAction('invalidAction');
|
||||
expect(invalidAction).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('isSubjectValid', () => {
|
||||
it('should return true for valid subjects', () => {
|
||||
const validAction = permissionCatalog.actions.find(
|
||||
(action) => action.key === 'create'
|
||||
);
|
||||
|
||||
const validSubject = Permission.isSubjectValid('Connection', validAction);
|
||||
expect(validSubject).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false for invalid subjects', () => {
|
||||
const validAction = permissionCatalog.actions.find(
|
||||
(action) => action.key === 'create'
|
||||
);
|
||||
|
||||
const invalidSubject = Permission.isSubjectValid(
|
||||
'Execution',
|
||||
validAction
|
||||
);
|
||||
|
||||
expect(invalidSubject).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('areConditionsValid', () => {
|
||||
it('should return true for valid conditions', () => {
|
||||
const validConditions = Permission.areConditionsValid(['isCreator']);
|
||||
expect(validConditions).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false for invalid conditions', () => {
|
||||
const invalidConditions = Permission.areConditionsValid([
|
||||
'invalidCondition',
|
||||
]);
|
||||
|
||||
expect(invalidConditions).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isConditionValid', () => {
|
||||
it('should return true for valid conditions', () => {
|
||||
const validCondition = Permission.isConditionValid('isCreator');
|
||||
expect(validCondition).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false for invalid conditions', () => {
|
||||
const invalidCondition = Permission.isConditionValid('invalidCondition');
|
||||
expect(invalidCondition).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
@@ -52,64 +52,57 @@ class Role extends Base {
|
||||
return await this.query().findOne({ name: 'Admin' });
|
||||
}
|
||||
|
||||
async preventAlteringAdmin() {
|
||||
const currentRole = await Role.query().findById(this.id);
|
||||
|
||||
if (currentRole.isAdmin) {
|
||||
async updateWithPermissions(data) {
|
||||
if (this.isAdmin) {
|
||||
throw new NotAuthorizedError('The admin role cannot be altered!');
|
||||
}
|
||||
}
|
||||
|
||||
async deletePermissions() {
|
||||
return await this.$relatedQuery('permissions').delete();
|
||||
}
|
||||
|
||||
async createPermissions(permissions) {
|
||||
if (permissions?.length) {
|
||||
const validPermissions = Permission.filter(permissions).map(
|
||||
(permission) => ({
|
||||
...permission,
|
||||
roleId: this.id,
|
||||
})
|
||||
);
|
||||
|
||||
await Permission.query().insert(validPermissions);
|
||||
}
|
||||
}
|
||||
|
||||
async updatePermissions(permissions) {
|
||||
await this.deletePermissions();
|
||||
|
||||
await this.createPermissions(permissions);
|
||||
}
|
||||
|
||||
async updateWithPermissions(data) {
|
||||
const { name, description, permissions } = data;
|
||||
|
||||
await this.updatePermissions(permissions);
|
||||
return await Role.transaction(async (trx) => {
|
||||
await this.$relatedQuery('permissions', trx).delete();
|
||||
|
||||
await this.$query().patchAndFetch({
|
||||
id: this.id,
|
||||
name,
|
||||
description,
|
||||
});
|
||||
if (permissions?.length) {
|
||||
const sanitizedPermissions = Permission.sanitize(permissions).map(
|
||||
(permission) => ({
|
||||
...permission,
|
||||
roleId: this.id,
|
||||
})
|
||||
);
|
||||
|
||||
return await this.$query()
|
||||
.leftJoinRelated({
|
||||
permissions: true,
|
||||
})
|
||||
.withGraphFetched({
|
||||
permissions: true,
|
||||
await Permission.query().insert(sanitizedPermissions);
|
||||
}
|
||||
|
||||
await this.$query(trx).patch({
|
||||
name,
|
||||
description,
|
||||
});
|
||||
|
||||
return await this.$query(trx)
|
||||
.leftJoinRelated({
|
||||
permissions: true,
|
||||
})
|
||||
.withGraphFetched({
|
||||
permissions: true,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async deleteWithPermissions() {
|
||||
await this.deletePermissions();
|
||||
return await Role.transaction(async (trx) => {
|
||||
await this.$relatedQuery('permissions', trx).delete();
|
||||
|
||||
return await this.$query().delete();
|
||||
return await this.$query(trx).delete();
|
||||
});
|
||||
}
|
||||
|
||||
async assertNoRoleUserExists() {
|
||||
async $beforeDelete(queryContext) {
|
||||
await super.$beforeDelete(queryContext);
|
||||
|
||||
if (this.isAdmin) {
|
||||
throw new NotAuthorizedError('The admin role cannot be deleted!');
|
||||
}
|
||||
|
||||
const userCount = await this.$relatedQuery('users').limit(1).resultSize();
|
||||
const hasUsers = userCount > 0;
|
||||
|
||||
@@ -125,9 +118,7 @@ class Role extends Base {
|
||||
type: 'ValidationError',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async assertNoConfigurationUsage() {
|
||||
const samlAuthProviderUsingDefaultRole = await SamlAuthProvider.query()
|
||||
.where({
|
||||
default_role_id: this.id,
|
||||
@@ -149,26 +140,6 @@ class Role extends Base {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async assertRoleIsNotUsed() {
|
||||
await this.assertNoRoleUserExists();
|
||||
|
||||
await this.assertNoConfigurationUsage();
|
||||
}
|
||||
|
||||
async $beforeUpdate(opt, queryContext) {
|
||||
await super.$beforeUpdate(opt, queryContext);
|
||||
|
||||
await this.preventAlteringAdmin();
|
||||
}
|
||||
|
||||
async $beforeDelete(queryContext) {
|
||||
await super.$beforeDelete(queryContext);
|
||||
|
||||
await this.preventAlteringAdmin();
|
||||
|
||||
await this.assertRoleIsNotUsed();
|
||||
}
|
||||
}
|
||||
|
||||
export default Role;
|
||||
|
@@ -1,287 +0,0 @@
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import Role from './role';
|
||||
import Base from './base.js';
|
||||
import Permission from './permission.js';
|
||||
import User from './user.js';
|
||||
import { createRole } from '../../test/factories/role.js';
|
||||
import { createPermission } from '../../test/factories/permission.js';
|
||||
import { createUser } from '../../test/factories/user.js';
|
||||
import { createSamlAuthProvider } from '../../test/factories/saml-auth-provider.ee.js';
|
||||
|
||||
describe('Role model', () => {
|
||||
it('tableName should return correct name', () => {
|
||||
expect(Role.tableName).toBe('roles');
|
||||
});
|
||||
|
||||
it('jsonSchema should have correct validations', () => {
|
||||
expect(Role.jsonSchema).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('relationMappingsshould return correct associations', () => {
|
||||
const relationMappings = Role.relationMappings();
|
||||
|
||||
const expectedRelations = {
|
||||
users: {
|
||||
relation: Base.HasManyRelation,
|
||||
modelClass: User,
|
||||
join: {
|
||||
from: 'roles.id',
|
||||
to: 'users.role_id',
|
||||
},
|
||||
},
|
||||
permissions: {
|
||||
relation: Base.HasManyRelation,
|
||||
modelClass: Permission,
|
||||
join: {
|
||||
from: 'roles.id',
|
||||
to: 'permissions.role_id',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(relationMappings).toStrictEqual(expectedRelations);
|
||||
});
|
||||
|
||||
it('virtualAttributes should return correct attributes', () => {
|
||||
expect(Role.virtualAttributes).toStrictEqual(['isAdmin']);
|
||||
});
|
||||
|
||||
describe('isAdmin', () => {
|
||||
it('should return true for admin named role', () => {
|
||||
const role = new Role();
|
||||
role.name = 'Admin';
|
||||
|
||||
expect(role.isAdmin).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false for not admin named roles', () => {
|
||||
const role = new Role();
|
||||
role.name = 'User';
|
||||
|
||||
expect(role.isAdmin).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it('findAdmin should return admin role', async () => {
|
||||
const createdAdminRole = await createRole({ name: 'Admin' });
|
||||
|
||||
const adminRole = await Role.findAdmin();
|
||||
|
||||
expect(createdAdminRole).toStrictEqual(adminRole);
|
||||
});
|
||||
|
||||
describe('preventAlteringAdmin', () => {
|
||||
it('preventAlteringAdmin should throw an error when altering admin role', async () => {
|
||||
const role = await createRole({ name: 'Admin' });
|
||||
|
||||
await expect(() => role.preventAlteringAdmin()).rejects.toThrowError(
|
||||
'The admin role cannot be altered!'
|
||||
);
|
||||
});
|
||||
|
||||
it('preventAlteringAdmin should not throw an error when altering non-admin roles', async () => {
|
||||
const role = await createRole({ name: 'User' });
|
||||
|
||||
expect(await role.preventAlteringAdmin()).toBe(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
it("deletePermissions should delete role's permissions", async () => {
|
||||
const role = await createRole({ name: 'User' });
|
||||
await createPermission({ roleId: role.id });
|
||||
|
||||
await role.deletePermissions();
|
||||
|
||||
expect(await role.$relatedQuery('permissions')).toStrictEqual([]);
|
||||
});
|
||||
|
||||
describe('createPermissions', () => {
|
||||
it('should create permissions', async () => {
|
||||
const role = await createRole({ name: 'User' });
|
||||
|
||||
await role.createPermissions([
|
||||
{ action: 'read', subject: 'Flow', conditions: [] },
|
||||
]);
|
||||
|
||||
expect(await role.$relatedQuery('permissions')).toMatchObject([
|
||||
{
|
||||
action: 'read',
|
||||
subject: 'Flow',
|
||||
conditions: [],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should call Permission.filter', async () => {
|
||||
const role = await createRole({ name: 'User' });
|
||||
|
||||
const permissions = [{ action: 'read', subject: 'Flow', conditions: [] }];
|
||||
|
||||
const permissionFilterSpy = vi
|
||||
.spyOn(Permission, 'filter')
|
||||
.mockReturnValue(permissions);
|
||||
|
||||
await role.createPermissions(permissions);
|
||||
|
||||
expect(permissionFilterSpy).toHaveBeenCalledWith(permissions);
|
||||
});
|
||||
});
|
||||
|
||||
it('updatePermissions should delete existing permissions and create new permissions', async () => {
|
||||
const permissionsData = [
|
||||
{ action: 'read', subject: 'Flow', conditions: [] },
|
||||
];
|
||||
|
||||
const deletePermissionsSpy = vi
|
||||
.spyOn(Role.prototype, 'deletePermissions')
|
||||
.mockResolvedValueOnce();
|
||||
const createPermissionsSpy = vi
|
||||
.spyOn(Role.prototype, 'createPermissions')
|
||||
.mockResolvedValueOnce();
|
||||
|
||||
const role = await createRole({ name: 'User' });
|
||||
|
||||
await role.updatePermissions(permissionsData);
|
||||
|
||||
expect(deletePermissionsSpy.mock.invocationCallOrder[0]).toBeLessThan(
|
||||
createPermissionsSpy.mock.invocationCallOrder[0]
|
||||
);
|
||||
|
||||
expect(deletePermissionsSpy).toHaveBeenNthCalledWith(1);
|
||||
expect(createPermissionsSpy).toHaveBeenNthCalledWith(1, permissionsData);
|
||||
});
|
||||
|
||||
describe('updateWithPermissions', () => {
|
||||
it('should update role along with given permissions', async () => {
|
||||
const role = await createRole({ name: 'User' });
|
||||
await createPermission({
|
||||
roleId: role.id,
|
||||
subject: 'Flow',
|
||||
action: 'read',
|
||||
conditions: [],
|
||||
});
|
||||
|
||||
const newRoleData = {
|
||||
name: 'Updated user',
|
||||
description: 'Updated description',
|
||||
permissions: [
|
||||
{
|
||||
action: 'update',
|
||||
subject: 'Flow',
|
||||
conditions: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
await role.updateWithPermissions(newRoleData);
|
||||
|
||||
const roleWithPermissions = await role
|
||||
.$query()
|
||||
.leftJoinRelated({ permissions: true })
|
||||
.withGraphFetched({ permissions: true });
|
||||
|
||||
expect(roleWithPermissions).toMatchObject(newRoleData);
|
||||
});
|
||||
});
|
||||
|
||||
describe('deleteWithPermissions', () => {
|
||||
it('should delete role along with given permissions', async () => {
|
||||
const role = await createRole({ name: 'User' });
|
||||
await createPermission({
|
||||
roleId: role.id,
|
||||
subject: 'Flow',
|
||||
action: 'read',
|
||||
conditions: [],
|
||||
});
|
||||
|
||||
await role.deleteWithPermissions();
|
||||
|
||||
const refetchedRole = await role.$query();
|
||||
const rolePermissions = await Permission.query().where({
|
||||
roleId: role.id,
|
||||
});
|
||||
|
||||
expect(refetchedRole).toBe(undefined);
|
||||
expect(rolePermissions).toStrictEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('assertNoRoleUserExists', () => {
|
||||
it('should reject with an error when the role has users', async () => {
|
||||
const role = await createRole({ name: 'User' });
|
||||
await createUser({ roleId: role.id });
|
||||
|
||||
await expect(() => role.assertNoRoleUserExists()).rejects.toThrowError(
|
||||
`All users must be migrated away from the "User" role.`
|
||||
);
|
||||
});
|
||||
|
||||
it('should resolve when the role does not have any users', async () => {
|
||||
const role = await createRole();
|
||||
|
||||
expect(await role.assertNoRoleUserExists()).toBe(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe('assertNoConfigurationUsage', () => {
|
||||
it('should reject with an error when the role is used in configuration', async () => {
|
||||
const role = await createRole();
|
||||
await createSamlAuthProvider({ defaultRoleId: role.id });
|
||||
|
||||
await expect(() =>
|
||||
role.assertNoConfigurationUsage()
|
||||
).rejects.toThrowError(
|
||||
'samlAuthProvider: You need to change the default role in the SAML configuration before deleting this role.'
|
||||
);
|
||||
});
|
||||
|
||||
it('should resolve when the role does not have any users', async () => {
|
||||
const role = await createRole();
|
||||
|
||||
expect(await role.assertNoConfigurationUsage()).toBe(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
it('assertRoleIsNotUsed should call assertNoRoleUserExists and assertNoConfigurationUsage', async () => {
|
||||
const role = new Role();
|
||||
|
||||
const assertNoRoleUserExistsSpy = vi
|
||||
.spyOn(role, 'assertNoRoleUserExists')
|
||||
.mockResolvedValue();
|
||||
|
||||
const assertNoConfigurationUsageSpy = vi
|
||||
.spyOn(role, 'assertNoConfigurationUsage')
|
||||
.mockResolvedValue();
|
||||
|
||||
await role.assertRoleIsNotUsed();
|
||||
|
||||
expect(assertNoRoleUserExistsSpy).toHaveBeenCalledOnce();
|
||||
expect(assertNoConfigurationUsageSpy).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
describe('$beforeDelete', () => {
|
||||
it('should call preventAlteringAdmin', async () => {
|
||||
const role = await createRole({ name: 'User' });
|
||||
|
||||
const preventAlteringAdminSpy = vi
|
||||
.spyOn(role, 'preventAlteringAdmin')
|
||||
.mockResolvedValue();
|
||||
|
||||
await role.$query().delete();
|
||||
|
||||
expect(preventAlteringAdminSpy).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('should call assertRoleIsNotUsed', async () => {
|
||||
const role = await createRole({ name: 'User' });
|
||||
|
||||
const assertRoleIsNotUsedSpy = vi
|
||||
.spyOn(role, 'assertRoleIsNotUsed')
|
||||
.mockResolvedValue();
|
||||
|
||||
await role.$query().delete();
|
||||
|
||||
expect(assertRoleIsNotUsedSpy).toHaveBeenCalledOnce();
|
||||
});
|
||||
});
|
||||
});
|
@@ -16,6 +16,6 @@ describe('actionSerializer', () => {
|
||||
type: action.type,
|
||||
};
|
||||
|
||||
expect(expectedPayload).toMatchObject(actionSerializer(action));
|
||||
expect(actionSerializer(action)).toEqual(expectedPayload);
|
||||
});
|
||||
});
|
||||
|
@@ -25,7 +25,7 @@ describe('adminSamlAuthProviderSerializer', () => {
|
||||
defaultRoleId: samlAuthProvider.defaultRoleId,
|
||||
};
|
||||
|
||||
expect(adminSamlAuthProviderSerializer(samlAuthProvider)).toStrictEqual(
|
||||
expect(adminSamlAuthProviderSerializer(samlAuthProvider)).toEqual(
|
||||
expectedPayload
|
||||
);
|
||||
});
|
||||
|
@@ -12,7 +12,7 @@ describe('adminUserSerializer', () => {
|
||||
it('should return user data with accept invitation url', async () => {
|
||||
const serializedUser = adminUserSerializer(user);
|
||||
|
||||
expect(serializedUser.acceptInvitationUrl).toStrictEqual(
|
||||
expect(serializedUser.acceptInvitationUrl).toEqual(
|
||||
user.acceptInvitationUrl
|
||||
);
|
||||
});
|
||||
|
@@ -17,8 +17,6 @@ describe('appAuthClient serializer', () => {
|
||||
active: appAuthClient.active,
|
||||
};
|
||||
|
||||
expect(appAuthClientSerializer(appAuthClient)).toStrictEqual(
|
||||
expectedPayload
|
||||
);
|
||||
expect(appAuthClientSerializer(appAuthClient)).toEqual(expectedPayload);
|
||||
});
|
||||
});
|
||||
|
@@ -1,10 +1,12 @@
|
||||
const appConfigSerializer = (appConfig) => {
|
||||
return {
|
||||
id: appConfig.id,
|
||||
key: appConfig.key,
|
||||
customConnectionAllowed: appConfig.customConnectionAllowed,
|
||||
allowCustomConnection: appConfig.allowCustomConnection,
|
||||
shared: appConfig.shared,
|
||||
disabled: appConfig.disabled,
|
||||
connectionAllowed: appConfig.connectionAllowed,
|
||||
canConnect: appConfig.canConnect,
|
||||
canCustomConnect: appConfig.canCustomConnect,
|
||||
createdAt: appConfig.createdAt.getTime(),
|
||||
updatedAt: appConfig.updatedAt.getTime(),
|
||||
};
|
||||
|
@@ -11,15 +11,17 @@ describe('appConfig serializer', () => {
|
||||
|
||||
it('should return app config data', async () => {
|
||||
const expectedPayload = {
|
||||
id: appConfig.id,
|
||||
key: appConfig.key,
|
||||
customConnectionAllowed: appConfig.customConnectionAllowed,
|
||||
allowCustomConnection: appConfig.allowCustomConnection,
|
||||
shared: appConfig.shared,
|
||||
disabled: appConfig.disabled,
|
||||
connectionAllowed: appConfig.connectionAllowed,
|
||||
canConnect: appConfig.canConnect,
|
||||
canCustomConnect: appConfig.canCustomConnect,
|
||||
createdAt: appConfig.createdAt.getTime(),
|
||||
updatedAt: appConfig.updatedAt.getTime(),
|
||||
};
|
||||
|
||||
expect(appConfigSerializer(appConfig)).toStrictEqual(expectedPayload);
|
||||
expect(appConfigSerializer(appConfig)).toEqual(expectedPayload);
|
||||
});
|
||||
});
|
||||
|
@@ -15,6 +15,6 @@ describe('appSerializer', () => {
|
||||
primaryColor: app.primaryColor,
|
||||
};
|
||||
|
||||
expect(appSerializer(app)).toStrictEqual(expectedPayload);
|
||||
expect(appSerializer(app)).toEqual(expectedPayload);
|
||||
});
|
||||
});
|
||||
|
@@ -12,6 +12,6 @@ describe('authSerializer', () => {
|
||||
reconnectionSteps: auth.reconnectionSteps,
|
||||
};
|
||||
|
||||
expect(authSerializer(auth)).toStrictEqual(expectedPayload);
|
||||
expect(authSerializer(auth)).toEqual(expectedPayload);
|
||||
});
|
||||
});
|
||||
|
@@ -27,6 +27,6 @@ describe('configSerializer', () => {
|
||||
updatedAt: config.updatedAt.getTime(),
|
||||
};
|
||||
|
||||
expect(configSerializer(config)).toStrictEqual(expectedPayload);
|
||||
expect(configSerializer(config)).toEqual(expectedPayload);
|
||||
});
|
||||
});
|
||||
|
@@ -23,6 +23,6 @@ describe('connectionSerializer', () => {
|
||||
updatedAt: connection.updatedAt.getTime(),
|
||||
};
|
||||
|
||||
expect(connectionSerializer(connection)).toStrictEqual(expectedPayload);
|
||||
expect(connectionSerializer(connection)).toEqual(expectedPayload);
|
||||
});
|
||||
});
|
||||
|
@@ -26,9 +26,7 @@ describe('executionStepSerializer', () => {
|
||||
updatedAt: executionStep.updatedAt.getTime(),
|
||||
};
|
||||
|
||||
expect(executionStepSerializer(executionStep)).toStrictEqual(
|
||||
expectedPayload
|
||||
);
|
||||
expect(executionStepSerializer(executionStep)).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return the execution step data with the step', async () => {
|
||||
|
@@ -23,7 +23,7 @@ describe('executionSerializer', () => {
|
||||
updatedAt: execution.updatedAt.getTime(),
|
||||
};
|
||||
|
||||
expect(executionSerializer(execution)).toStrictEqual(expectedPayload);
|
||||
expect(executionSerializer(execution)).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return the execution data with status', async () => {
|
||||
@@ -37,7 +37,7 @@ describe('executionSerializer', () => {
|
||||
status: 'success',
|
||||
};
|
||||
|
||||
expect(executionSerializer(execution)).toStrictEqual(expectedPayload);
|
||||
expect(executionSerializer(execution)).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return the execution data with the flow', async () => {
|
||||
|
@@ -31,7 +31,7 @@ describe('flowSerializer', () => {
|
||||
updatedAt: flow.updatedAt.getTime(),
|
||||
};
|
||||
|
||||
expect(flowSerializer(flow)).toStrictEqual(expectedPayload);
|
||||
expect(flowSerializer(flow)).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return flow data with the steps', async () => {
|
||||
|
@@ -20,6 +20,6 @@ describe('permissionSerializer', () => {
|
||||
updatedAt: permission.updatedAt.getTime(),
|
||||
};
|
||||
|
||||
expect(permissionSerializer(permission)).toStrictEqual(expectedPayload);
|
||||
expect(permissionSerializer(permission)).toEqual(expectedPayload);
|
||||
});
|
||||
});
|
||||
|
@@ -34,7 +34,7 @@ describe('roleSerializer', () => {
|
||||
isAdmin: role.isAdmin,
|
||||
};
|
||||
|
||||
expect(roleSerializer(role)).toStrictEqual(expectedPayload);
|
||||
expect(roleSerializer(role)).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return role data with the permissions', async () => {
|
||||
|
@@ -17,7 +17,7 @@ describe('samlAuthProviderSerializer', () => {
|
||||
issuer: samlAuthProvider.issuer,
|
||||
};
|
||||
|
||||
expect(samlAuthProviderSerializer(samlAuthProvider)).toStrictEqual(
|
||||
expect(samlAuthProviderSerializer(samlAuthProvider)).toEqual(
|
||||
expectedPayload
|
||||
);
|
||||
});
|
||||
|
@@ -24,7 +24,7 @@ describe('stepSerializer', () => {
|
||||
parameters: step.parameters,
|
||||
};
|
||||
|
||||
expect(stepSerializer(step)).toStrictEqual(expectedPayload);
|
||||
expect(stepSerializer(step)).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return step data with the last execution step', async () => {
|
||||
|
@@ -30,6 +30,6 @@ describe('subscriptionSerializer', () => {
|
||||
cancellationEffectiveDate: subscription.cancellationEffectiveDate,
|
||||
};
|
||||
|
||||
expect(subscriptionSerializer(subscription)).toStrictEqual(expectedPayload);
|
||||
expect(subscriptionSerializer(subscription)).toEqual(expectedPayload);
|
||||
});
|
||||
});
|
||||
|
@@ -16,6 +16,6 @@ describe('triggerSerializer', () => {
|
||||
type: trigger.type,
|
||||
};
|
||||
|
||||
expect(triggerSerializer(trigger)).toStrictEqual(expectedPayload);
|
||||
expect(triggerSerializer(trigger)).toEqual(expectedPayload);
|
||||
});
|
||||
});
|
||||
|
@@ -39,7 +39,7 @@ describe('userSerializer', () => {
|
||||
updatedAt: user.updatedAt.getTime(),
|
||||
};
|
||||
|
||||
expect(userSerializer(user)).toStrictEqual(expectedPayload);
|
||||
expect(userSerializer(user)).toEqual(expectedPayload);
|
||||
});
|
||||
|
||||
it('should return user data with the role', async () => {
|
||||
|
@@ -10,6 +10,7 @@ const formattedAuthDefaults = {
|
||||
|
||||
export const createAppAuthClient = async (params = {}) => {
|
||||
params.name = params?.name || faker.person.fullName();
|
||||
params.id = params?.id || faker.string.uuid();
|
||||
params.appKey = params?.appKey || 'deepl';
|
||||
params.active = params?.active ?? true;
|
||||
params.formattedAuthDefaults =
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user