mirror of
https://github.com/logto-io/logto.git
synced 2024-12-30 20:33:54 -05:00
feat(core,test,phrases): add application-role related APIs (#4425)
This commit is contained in:
parent
285aa745e7
commit
9251c2bb40
36 changed files with 584 additions and 0 deletions
|
@ -2,6 +2,7 @@ import { VerificationCodeType } from '@logto/connector-kit';
|
||||||
import type {
|
import type {
|
||||||
AdminConsoleData,
|
AdminConsoleData,
|
||||||
Application,
|
Application,
|
||||||
|
ApplicationsRole,
|
||||||
Passcode,
|
Passcode,
|
||||||
Resource,
|
Resource,
|
||||||
Role,
|
Role,
|
||||||
|
@ -120,3 +121,10 @@ export const mockUserRole: UsersRole = {
|
||||||
userId: 'foo',
|
userId: 'foo',
|
||||||
roleId: 'role_id',
|
roleId: 'role_id',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const mockApplicationRole: ApplicationsRole = {
|
||||||
|
tenantId: 'fake_tenant',
|
||||||
|
id: 'application_role_id',
|
||||||
|
applicationId: 'application_id',
|
||||||
|
roleId: 'role_id',
|
||||||
|
};
|
||||||
|
|
98
packages/core/src/routes/application-role.test.ts
Normal file
98
packages/core/src/routes/application-role.test.ts
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
import { ApplicationType } from '@logto/schemas';
|
||||||
|
import { pickDefault } from '@logto/shared/esm';
|
||||||
|
|
||||||
|
import {
|
||||||
|
mockAdminApplicationRole,
|
||||||
|
mockApplication,
|
||||||
|
mockAdminUserRole2,
|
||||||
|
mockApplicationRole,
|
||||||
|
} from '#src/__mocks__/index.js';
|
||||||
|
import { mockId, mockStandardId } from '#src/test-utils/nanoid.js';
|
||||||
|
import { MockTenant } from '#src/test-utils/tenant.js';
|
||||||
|
import { createRequester } from '#src/utils/test-utils.js';
|
||||||
|
|
||||||
|
const { jest } = import.meta;
|
||||||
|
|
||||||
|
await mockStandardId();
|
||||||
|
|
||||||
|
const mockM2mApplication = { ...mockApplication, type: ApplicationType.MachineToMachine };
|
||||||
|
|
||||||
|
const applications = { findApplicationById: jest.fn(async () => mockM2mApplication) };
|
||||||
|
|
||||||
|
const roles = {
|
||||||
|
findRolesByRoleIds: jest.fn(),
|
||||||
|
findRoleById: jest.fn(),
|
||||||
|
countRoles: jest.fn(async () => ({ count: 1 })),
|
||||||
|
findRoles: jest.fn(async () => [mockAdminApplicationRole]),
|
||||||
|
};
|
||||||
|
const { findRolesByRoleIds } = roles;
|
||||||
|
|
||||||
|
const applicationsRoles = {
|
||||||
|
findApplicationsRolesByApplicationId: jest.fn(),
|
||||||
|
insertApplicationsRoles: jest.fn(),
|
||||||
|
deleteApplicationRole: jest.fn(),
|
||||||
|
};
|
||||||
|
const { findApplicationsRolesByApplicationId, insertApplicationsRoles, deleteApplicationRole } =
|
||||||
|
applicationsRoles;
|
||||||
|
|
||||||
|
const tenantContext = new MockTenant(undefined, { applicationsRoles, applications, roles });
|
||||||
|
|
||||||
|
const applicationRoleRoutes = await pickDefault(import('./application-role.js'));
|
||||||
|
|
||||||
|
describe('application role routes', () => {
|
||||||
|
const applicationRoleRequester = createRequester({
|
||||||
|
authedRoutes: applicationRoleRoutes,
|
||||||
|
tenantContext,
|
||||||
|
});
|
||||||
|
|
||||||
|
it('GET /applications/:applicationId/roles', async () => {
|
||||||
|
findApplicationsRolesByApplicationId.mockResolvedValueOnce([]);
|
||||||
|
const response = await applicationRoleRequester.get(
|
||||||
|
`/applications/${mockM2mApplication.id}/roles`
|
||||||
|
);
|
||||||
|
expect(response.status).toEqual(200);
|
||||||
|
expect(response.body).toEqual([mockAdminApplicationRole]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('POST /applications/:applicationId/roles', async () => {
|
||||||
|
findApplicationsRolesByApplicationId.mockResolvedValueOnce([]);
|
||||||
|
findRolesByRoleIds.mockResolvedValueOnce([]);
|
||||||
|
const response = await applicationRoleRequester
|
||||||
|
.post(`/applications/${mockM2mApplication.id}/roles`)
|
||||||
|
.send({
|
||||||
|
roleIds: [mockAdminApplicationRole.id],
|
||||||
|
});
|
||||||
|
expect(response.status).toEqual(201);
|
||||||
|
expect(insertApplicationsRoles).toHaveBeenCalledWith([
|
||||||
|
{ id: mockId, applicationId: mockM2mApplication.id, roleId: mockAdminApplicationRole.id },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('PUT /applications/:applicationId/roles', async () => {
|
||||||
|
findApplicationsRolesByApplicationId.mockResolvedValueOnce([mockApplicationRole]);
|
||||||
|
const response = await applicationRoleRequester
|
||||||
|
.put(`/applications/${mockM2mApplication.id}/roles`)
|
||||||
|
.send({
|
||||||
|
roleIds: [mockAdminUserRole2.id],
|
||||||
|
});
|
||||||
|
expect(response.status).toEqual(200);
|
||||||
|
expect(deleteApplicationRole).toHaveBeenCalledWith(
|
||||||
|
mockM2mApplication.id,
|
||||||
|
mockAdminApplicationRole.id
|
||||||
|
);
|
||||||
|
expect(insertApplicationsRoles).toHaveBeenCalledWith([
|
||||||
|
{ id: mockId, applicationId: mockM2mApplication.id, roleId: mockAdminUserRole2.id },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('DELETE /applications/:applicationId/roles/:roleId', async () => {
|
||||||
|
const response = await applicationRoleRequester.delete(
|
||||||
|
`/applications/${mockM2mApplication.id}/roles/${mockAdminApplicationRole.id}`
|
||||||
|
);
|
||||||
|
expect(response.status).toEqual(204);
|
||||||
|
expect(deleteApplicationRole).toHaveBeenCalledWith(
|
||||||
|
mockM2mApplication.id,
|
||||||
|
mockAdminApplicationRole.id
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
206
packages/core/src/routes/application-role.ts
Normal file
206
packages/core/src/routes/application-role.ts
Normal file
|
@ -0,0 +1,206 @@
|
||||||
|
import { ApplicationType, RoleType, Roles } from '@logto/schemas';
|
||||||
|
import { generateStandardId } from '@logto/shared';
|
||||||
|
import { tryThat } from '@silverhand/essentials';
|
||||||
|
import { array, object, string } from 'zod';
|
||||||
|
|
||||||
|
import RequestError from '#src/errors/RequestError/index.js';
|
||||||
|
import koaGuard from '#src/middleware/koa-guard.js';
|
||||||
|
import koaPagination from '#src/middleware/koa-pagination.js';
|
||||||
|
import koaRoleRlsErrorHandler from '#src/middleware/koa-role-rls-error-handler.js';
|
||||||
|
import assertThat from '#src/utils/assert-that.js';
|
||||||
|
import { parseSearchParamsForSearch } from '#src/utils/search.js';
|
||||||
|
|
||||||
|
import type { AuthedRouter, RouterInitArgs } from './types.js';
|
||||||
|
|
||||||
|
export default function applicationRoleRoutes<T extends AuthedRouter>(
|
||||||
|
...[router, { queries }]: RouterInitArgs<T>
|
||||||
|
) {
|
||||||
|
const {
|
||||||
|
roles: { findRoleById, countRoles, findRoles, findRolesByRoleIds },
|
||||||
|
applications: { findApplicationById },
|
||||||
|
applicationsRoles: {
|
||||||
|
findApplicationsRolesByApplicationId,
|
||||||
|
insertApplicationsRoles,
|
||||||
|
deleteApplicationRole,
|
||||||
|
},
|
||||||
|
} = queries;
|
||||||
|
|
||||||
|
router.use('/applications/:applicationId/roles(/.*)?', koaRoleRlsErrorHandler());
|
||||||
|
|
||||||
|
router.get(
|
||||||
|
'/applications/:applicationId/roles',
|
||||||
|
koaPagination(),
|
||||||
|
koaGuard({
|
||||||
|
params: object({ applicationId: string() }),
|
||||||
|
response: array(Roles.guard),
|
||||||
|
status: [200, 400, 404, 422],
|
||||||
|
}),
|
||||||
|
async (ctx, next) => {
|
||||||
|
const { applicationId } = ctx.guard.params;
|
||||||
|
const { limit, offset } = ctx.pagination;
|
||||||
|
const { searchParams } = ctx.request.URL;
|
||||||
|
|
||||||
|
const application = await findApplicationById(applicationId);
|
||||||
|
assertThat(
|
||||||
|
application.type === ApplicationType.MachineToMachine,
|
||||||
|
new RequestError({
|
||||||
|
code: 'application.invalid_type',
|
||||||
|
status: 422,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return tryThat(
|
||||||
|
async () => {
|
||||||
|
const search = parseSearchParamsForSearch(searchParams);
|
||||||
|
const applicationRoles = await findApplicationsRolesByApplicationId(applicationId);
|
||||||
|
const roleIds = applicationRoles.map(({ roleId }) => roleId);
|
||||||
|
const [{ count }, roles] = await Promise.all([
|
||||||
|
countRoles(search, { roleIds }),
|
||||||
|
findRoles(search, limit, offset, { roleIds }),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Return totalCount to pagination middleware
|
||||||
|
ctx.pagination.totalCount = count;
|
||||||
|
ctx.body = roles;
|
||||||
|
|
||||||
|
return next();
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
if (error instanceof TypeError) {
|
||||||
|
throw new RequestError(
|
||||||
|
{ code: 'request.invalid_input', details: error.message },
|
||||||
|
error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
router.post(
|
||||||
|
'/applications/:applicationId/roles',
|
||||||
|
koaGuard({
|
||||||
|
params: object({ applicationId: string() }),
|
||||||
|
body: object({ roleIds: string().min(1).array() }),
|
||||||
|
status: [201, 404, 422],
|
||||||
|
}),
|
||||||
|
async (ctx, next) => {
|
||||||
|
const {
|
||||||
|
params: { applicationId },
|
||||||
|
body: { roleIds },
|
||||||
|
} = ctx.guard;
|
||||||
|
|
||||||
|
const application = await findApplicationById(applicationId);
|
||||||
|
assertThat(
|
||||||
|
application.type === ApplicationType.MachineToMachine,
|
||||||
|
new RequestError({
|
||||||
|
code: 'application.invalid_type',
|
||||||
|
status: 422,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
const applicationRoles = await findApplicationsRolesByApplicationId(applicationId);
|
||||||
|
|
||||||
|
const roles = await findRolesByRoleIds(roleIds);
|
||||||
|
|
||||||
|
for (const role of roles) {
|
||||||
|
assertThat(
|
||||||
|
!applicationRoles.some(({ roleId: _roleId }) => _roleId === role.id),
|
||||||
|
new RequestError({
|
||||||
|
code: 'application.role_exists',
|
||||||
|
status: 422,
|
||||||
|
roleId: role.id,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assertThat(
|
||||||
|
role.type === RoleType.MachineToMachine,
|
||||||
|
new RequestError({
|
||||||
|
code: 'application.invalid_role_type',
|
||||||
|
status: 422,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(roleIds.map(async (roleId) => findRoleById(roleId)));
|
||||||
|
await insertApplicationsRoles(
|
||||||
|
roleIds.map((roleId) => ({ id: generateStandardId(), applicationId, roleId }))
|
||||||
|
);
|
||||||
|
ctx.status = 201;
|
||||||
|
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
router.put(
|
||||||
|
'/applications/:applicationId/roles',
|
||||||
|
koaGuard({
|
||||||
|
params: object({ applicationId: string() }),
|
||||||
|
body: object({ roleIds: string().min(1).array() }),
|
||||||
|
status: [200, 404, 422],
|
||||||
|
}),
|
||||||
|
async (ctx, next) => {
|
||||||
|
const {
|
||||||
|
params: { applicationId },
|
||||||
|
body: { roleIds },
|
||||||
|
} = ctx.guard;
|
||||||
|
|
||||||
|
const application = await findApplicationById(applicationId);
|
||||||
|
assertThat(
|
||||||
|
application.type === ApplicationType.MachineToMachine,
|
||||||
|
new RequestError({
|
||||||
|
code: 'application.invalid_type',
|
||||||
|
status: 422,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
const applicationRoles = await findApplicationsRolesByApplicationId(applicationId);
|
||||||
|
|
||||||
|
// Only add the ones that doesn't exist
|
||||||
|
const roleIdsToAdd = roleIds.filter(
|
||||||
|
(roleId) => !applicationRoles.some(({ roleId: _roleId }) => _roleId === roleId)
|
||||||
|
);
|
||||||
|
// Remove existing roles that isn't wanted by app anymore
|
||||||
|
const roleIdsToRemove = applicationRoles
|
||||||
|
.filter(({ roleId }) => !roleIds.includes(roleId))
|
||||||
|
.map(({ roleId }) => roleId);
|
||||||
|
|
||||||
|
await Promise.all(roleIdsToAdd.map(async (roleId) => findRoleById(roleId)));
|
||||||
|
await Promise.all(
|
||||||
|
roleIdsToRemove.map(async (roleId) => deleteApplicationRole(applicationId, roleId))
|
||||||
|
);
|
||||||
|
await insertApplicationsRoles(
|
||||||
|
roleIdsToAdd.map((roleId) => ({ id: generateStandardId(), applicationId, roleId }))
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx.status = 200;
|
||||||
|
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
router.delete(
|
||||||
|
'/applications/:applicationId/roles/:roleId',
|
||||||
|
koaGuard({
|
||||||
|
params: object({ applicationId: string(), roleId: string() }),
|
||||||
|
status: [204, 404, 422],
|
||||||
|
}),
|
||||||
|
async (ctx, next) => {
|
||||||
|
const {
|
||||||
|
params: { applicationId, roleId },
|
||||||
|
} = ctx.guard;
|
||||||
|
|
||||||
|
const application = await findApplicationById(applicationId);
|
||||||
|
assertThat(
|
||||||
|
application.type === ApplicationType.MachineToMachine,
|
||||||
|
new RequestError({
|
||||||
|
code: 'application.invalid_type',
|
||||||
|
status: 422,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
await deleteApplicationRole(applicationId, roleId);
|
||||||
|
|
||||||
|
ctx.status = 204;
|
||||||
|
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
|
@ -14,6 +14,7 @@ import adminUserRoleRoutes from './admin-user-role.js';
|
||||||
import adminUserSearchRoutes from './admin-user-search.js';
|
import adminUserSearchRoutes from './admin-user-search.js';
|
||||||
import adminUserSocialRoutes from './admin-user-social.js';
|
import adminUserSocialRoutes from './admin-user-social.js';
|
||||||
import adminUserRoutes from './admin-user.js';
|
import adminUserRoutes from './admin-user.js';
|
||||||
|
import applicationRoleRoutes from './application-role.js';
|
||||||
import applicationRoutes from './application.js';
|
import applicationRoutes from './application.js';
|
||||||
import authnRoutes from './authn.js';
|
import authnRoutes from './authn.js';
|
||||||
import connectorRoutes from './connector/index.js';
|
import connectorRoutes from './connector/index.js';
|
||||||
|
@ -44,6 +45,7 @@ const createRouters = (tenant: TenantContext) => {
|
||||||
managementRouter.use(koaTenantGuard(tenant.cloudConnection));
|
managementRouter.use(koaTenantGuard(tenant.cloudConnection));
|
||||||
|
|
||||||
applicationRoutes(managementRouter, tenant);
|
applicationRoutes(managementRouter, tenant);
|
||||||
|
applicationRoleRoutes(managementRouter, tenant);
|
||||||
logtoConfigRoutes(managementRouter, tenant);
|
logtoConfigRoutes(managementRouter, tenant);
|
||||||
connectorRoutes(managementRouter, tenant);
|
connectorRoutes(managementRouter, tenant);
|
||||||
resourceRoutes(managementRouter, tenant);
|
resourceRoutes(managementRouter, tenant);
|
||||||
|
|
|
@ -3,6 +3,7 @@ import type {
|
||||||
CreateApplication,
|
CreateApplication,
|
||||||
ApplicationType,
|
ApplicationType,
|
||||||
OidcClientMetadata,
|
OidcClientMetadata,
|
||||||
|
Role,
|
||||||
} from '@logto/schemas';
|
} from '@logto/schemas';
|
||||||
|
|
||||||
import { authedAdminApi } from './api.js';
|
import { authedAdminApi } from './api.js';
|
||||||
|
@ -45,3 +46,19 @@ export const updateApplication = async (
|
||||||
|
|
||||||
export const deleteApplication = async (applicationId: string) =>
|
export const deleteApplication = async (applicationId: string) =>
|
||||||
authedAdminApi.delete(`applications/${applicationId}`);
|
authedAdminApi.delete(`applications/${applicationId}`);
|
||||||
|
|
||||||
|
export const getApplicationRoles = async (applicationId: string) =>
|
||||||
|
authedAdminApi.get(`applications/${applicationId}/roles`).json<Role[]>();
|
||||||
|
|
||||||
|
export const assignRolesToApplication = async (applicationId: string, roleIds: string[]) =>
|
||||||
|
authedAdminApi.post(`applications/${applicationId}/roles`, {
|
||||||
|
json: { roleIds },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const putRolesToApplication = async (applicationId: string, roleIds: string[]) =>
|
||||||
|
authedAdminApi.put(`applications/${applicationId}/roles`, {
|
||||||
|
json: { roleIds },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const deleteRoleFromApplication = async (applicationId: string, roleId: string) =>
|
||||||
|
authedAdminApi.delete(`applications/${applicationId}/roles/${roleId}`);
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
import { ApplicationType, RoleType } from '@logto/schemas';
|
||||||
|
import { generateStandardId } from '@logto/shared';
|
||||||
|
import { HTTPError } from 'got';
|
||||||
|
|
||||||
|
import {
|
||||||
|
createApplication,
|
||||||
|
getApplicationRoles,
|
||||||
|
assignRolesToApplication,
|
||||||
|
deleteRoleFromApplication,
|
||||||
|
putRolesToApplication,
|
||||||
|
} from '#src/api/index.js';
|
||||||
|
import { createRole } from '#src/api/role.js';
|
||||||
|
import { expectRejects } from '#src/helpers/index.js';
|
||||||
|
|
||||||
|
describe('admin console application management (roles)', () => {
|
||||||
|
it('should get empty list successfully', async () => {
|
||||||
|
const applicationType = ApplicationType.MachineToMachine;
|
||||||
|
const application = await createApplication(generateStandardId(), applicationType);
|
||||||
|
|
||||||
|
const applicationRoles = await getApplicationRoles(application.id);
|
||||||
|
expect(applicationRoles.length).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws when trying to get roles of non-m2m app', async () => {
|
||||||
|
const applicationType = ApplicationType.SPA;
|
||||||
|
const application = await createApplication(generateStandardId(), applicationType);
|
||||||
|
|
||||||
|
const response = await getApplicationRoles(application.id).catch((error: unknown) => error);
|
||||||
|
expect(response instanceof HTTPError && response.response.statusCode === 422).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should assign roles to app and get list successfully', async () => {
|
||||||
|
const applicationType = ApplicationType.MachineToMachine;
|
||||||
|
const application = await createApplication(generateStandardId(), applicationType);
|
||||||
|
const role1 = await createRole({ type: RoleType.MachineToMachine });
|
||||||
|
const role2 = await createRole({ type: RoleType.MachineToMachine });
|
||||||
|
|
||||||
|
await assignRolesToApplication(application.id, [role1.id, role2.id]);
|
||||||
|
const roles = await getApplicationRoles(application.id);
|
||||||
|
expect(roles.length).toBe(2);
|
||||||
|
expect(roles.find(({ id }) => id === role1.id)).toBeDefined();
|
||||||
|
expect(roles.find(({ id }) => id === role2.id)).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail when assign duplicated role to app', async () => {
|
||||||
|
const applicationType = ApplicationType.MachineToMachine;
|
||||||
|
const application = await createApplication(generateStandardId(), applicationType);
|
||||||
|
const role = await createRole({ type: RoleType.MachineToMachine });
|
||||||
|
|
||||||
|
await assignRolesToApplication(application.id, [role.id]);
|
||||||
|
await expectRejects(assignRolesToApplication(application.id, [role.id]), {
|
||||||
|
code: 'application.role_exists',
|
||||||
|
statusCode: 422,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail when assign role to non-m2m app', async () => {
|
||||||
|
const applicationType = ApplicationType.SPA;
|
||||||
|
const application = await createApplication(generateStandardId(), applicationType);
|
||||||
|
const role = await createRole({ type: RoleType.MachineToMachine });
|
||||||
|
|
||||||
|
await expectRejects(assignRolesToApplication(application.id, [role.id]), {
|
||||||
|
code: 'application.invalid_type',
|
||||||
|
statusCode: 422,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should put roles to app successfully', async () => {
|
||||||
|
const applicationType = ApplicationType.MachineToMachine;
|
||||||
|
const application = await createApplication(generateStandardId(), applicationType);
|
||||||
|
const role1 = await createRole({ type: RoleType.MachineToMachine });
|
||||||
|
const role2 = await createRole({ type: RoleType.MachineToMachine });
|
||||||
|
const role3 = await createRole({ type: RoleType.MachineToMachine });
|
||||||
|
|
||||||
|
await assignRolesToApplication(application.id, [role2.id]);
|
||||||
|
const roles = await getApplicationRoles(application.id);
|
||||||
|
expect(roles.length).toBe(1);
|
||||||
|
expect(roles.find(({ id }) => id === role2.id)).toBeDefined();
|
||||||
|
|
||||||
|
await putRolesToApplication(application.id, [role1.id, role3.id]);
|
||||||
|
const updatedRoles = await getApplicationRoles(application.id);
|
||||||
|
expect(updatedRoles.length).toBe(2);
|
||||||
|
expect(updatedRoles.find(({ id }) => id === role1.id)).toBeDefined();
|
||||||
|
expect(updatedRoles.find(({ id }) => id === role3.id)).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should delete role from app successfully', async () => {
|
||||||
|
const applicationType = ApplicationType.MachineToMachine;
|
||||||
|
const application = await createApplication(generateStandardId(), applicationType);
|
||||||
|
const role1 = await createRole({ type: RoleType.MachineToMachine });
|
||||||
|
const role2 = await createRole({ type: RoleType.MachineToMachine });
|
||||||
|
|
||||||
|
await assignRolesToApplication(application.id, [role1.id, role2.id]);
|
||||||
|
await deleteRoleFromApplication(application.id, role1.id);
|
||||||
|
|
||||||
|
const roles = await getApplicationRoles(application.id);
|
||||||
|
expect(roles.length).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should failed to delete non-existing-role from app', async () => {
|
||||||
|
const applicationType = ApplicationType.MachineToMachine;
|
||||||
|
const application = await createApplication(generateStandardId(), applicationType);
|
||||||
|
const role = await createRole({ type: RoleType.MachineToMachine });
|
||||||
|
|
||||||
|
const response = await deleteRoleFromApplication(application.id, role.id).catch(
|
||||||
|
(error: unknown) => error
|
||||||
|
);
|
||||||
|
expect(response instanceof HTTPError && response.response.statusCode === 404).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
8
packages/phrases/src/locales/de/errors/application.ts
Normal file
8
packages/phrases/src/locales/de/errors/application.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
const application = {
|
||||||
|
invalid_type: 'Nur Maschinen-zu-Maschinen-Anwendungen können Rollen haben.',
|
||||||
|
role_exists: 'Die Rolle mit der ID {{roleId}} wurde bereits dieser Anwendung hinzugefügt.',
|
||||||
|
invalid_role_type:
|
||||||
|
'Es ist nicht möglich, einer Maschinen-zu-Maschinen-Anwendung eine Benutzertyp-Rolle zuzuweisen.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Object.freeze(application);
|
|
@ -1,3 +1,4 @@
|
||||||
|
import application from './application.js';
|
||||||
import auth from './auth.js';
|
import auth from './auth.js';
|
||||||
import connector from './connector.js';
|
import connector from './connector.js';
|
||||||
import domain from './domain.js';
|
import domain from './domain.js';
|
||||||
|
@ -42,6 +43,7 @@ const errors = {
|
||||||
hook,
|
hook,
|
||||||
domain,
|
domain,
|
||||||
subscription,
|
subscription,
|
||||||
|
application,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Object.freeze(errors);
|
export default Object.freeze(errors);
|
||||||
|
|
7
packages/phrases/src/locales/en/errors/application.ts
Normal file
7
packages/phrases/src/locales/en/errors/application.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
const application = {
|
||||||
|
invalid_type: 'Only machine to machine applications can have associated roles.',
|
||||||
|
role_exists: 'The role id {{roleId}} is already been added to this application.',
|
||||||
|
invalid_role_type: 'Can not assign user type role to machine to machine application.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Object.freeze(application);
|
|
@ -1,3 +1,4 @@
|
||||||
|
import application from './application.js';
|
||||||
import auth from './auth.js';
|
import auth from './auth.js';
|
||||||
import connector from './connector.js';
|
import connector from './connector.js';
|
||||||
import domain from './domain.js';
|
import domain from './domain.js';
|
||||||
|
@ -42,6 +43,7 @@ const errors = {
|
||||||
hook,
|
hook,
|
||||||
domain,
|
domain,
|
||||||
subscription,
|
subscription,
|
||||||
|
application,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Object.freeze(errors);
|
export default Object.freeze(errors);
|
||||||
|
|
8
packages/phrases/src/locales/es/errors/application.ts
Normal file
8
packages/phrases/src/locales/es/errors/application.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
const application = {
|
||||||
|
invalid_type: 'Solo las aplicaciones de máquina a máquina pueden tener roles asociados.',
|
||||||
|
role_exists: 'La identificación del rol {{roleId}} ya se ha agregado a esta aplicación.',
|
||||||
|
invalid_role_type:
|
||||||
|
'No se puede asignar un rol de tipo usuario a una aplicación de máquina a máquina.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Object.freeze(application);
|
|
@ -1,3 +1,4 @@
|
||||||
|
import application from './application.js';
|
||||||
import auth from './auth.js';
|
import auth from './auth.js';
|
||||||
import connector from './connector.js';
|
import connector from './connector.js';
|
||||||
import domain from './domain.js';
|
import domain from './domain.js';
|
||||||
|
@ -42,6 +43,7 @@ const errors = {
|
||||||
hook,
|
hook,
|
||||||
domain,
|
domain,
|
||||||
subscription,
|
subscription,
|
||||||
|
application,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Object.freeze(errors);
|
export default Object.freeze(errors);
|
||||||
|
|
8
packages/phrases/src/locales/fr/errors/application.ts
Normal file
8
packages/phrases/src/locales/fr/errors/application.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
const application = {
|
||||||
|
invalid_type: 'Seules les applications machine à machine peuvent avoir des rôles associés.',
|
||||||
|
role_exists: "Le rôle d'identifiant {{roleId}} a déjà été ajouté à cette application.",
|
||||||
|
invalid_role_type:
|
||||||
|
"Impossible d'assigner un rôle de type utilisateur à une application machine à machine.",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Object.freeze(application);
|
|
@ -1,3 +1,4 @@
|
||||||
|
import application from './application.js';
|
||||||
import auth from './auth.js';
|
import auth from './auth.js';
|
||||||
import connector from './connector.js';
|
import connector from './connector.js';
|
||||||
import domain from './domain.js';
|
import domain from './domain.js';
|
||||||
|
@ -42,6 +43,7 @@ const errors = {
|
||||||
hook,
|
hook,
|
||||||
domain,
|
domain,
|
||||||
subscription,
|
subscription,
|
||||||
|
application,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Object.freeze(errors);
|
export default Object.freeze(errors);
|
||||||
|
|
8
packages/phrases/src/locales/it/errors/application.ts
Normal file
8
packages/phrases/src/locales/it/errors/application.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
const application = {
|
||||||
|
invalid_type: 'Solo le applicazioni da macchina a macchina possono avere ruoli associati.',
|
||||||
|
role_exists: "L'ID ruolo {{roleId}} è già stato aggiunto a questa applicazione.",
|
||||||
|
invalid_role_type:
|
||||||
|
"Impossibile assegnare un ruolo di tipo utente all'applicazione da macchina a macchina.",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Object.freeze(application);
|
|
@ -1,3 +1,4 @@
|
||||||
|
import application from './application.js';
|
||||||
import auth from './auth.js';
|
import auth from './auth.js';
|
||||||
import connector from './connector.js';
|
import connector from './connector.js';
|
||||||
import domain from './domain.js';
|
import domain from './domain.js';
|
||||||
|
@ -42,6 +43,7 @@ const errors = {
|
||||||
hook,
|
hook,
|
||||||
domain,
|
domain,
|
||||||
subscription,
|
subscription,
|
||||||
|
application,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Object.freeze(errors);
|
export default Object.freeze(errors);
|
||||||
|
|
8
packages/phrases/src/locales/ja/errors/application.ts
Normal file
8
packages/phrases/src/locales/ja/errors/application.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
const application = {
|
||||||
|
invalid_type: '関連するロールを持つことができるのは、マシン間アプリケーションのみです。',
|
||||||
|
role_exists: 'ロールID {{roleId}} は、すでにこのアプリケーションに追加されています。',
|
||||||
|
invalid_role_type:
|
||||||
|
'ユーザータイプのロールをマシン間アプリケーションに割り当てることはできません。',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Object.freeze(application);
|
|
@ -1,3 +1,4 @@
|
||||||
|
import application from './application.js';
|
||||||
import auth from './auth.js';
|
import auth from './auth.js';
|
||||||
import connector from './connector.js';
|
import connector from './connector.js';
|
||||||
import domain from './domain.js';
|
import domain from './domain.js';
|
||||||
|
@ -42,6 +43,7 @@ const errors = {
|
||||||
hook,
|
hook,
|
||||||
domain,
|
domain,
|
||||||
subscription,
|
subscription,
|
||||||
|
application,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Object.freeze(errors);
|
export default Object.freeze(errors);
|
||||||
|
|
7
packages/phrases/src/locales/ko/errors/application.ts
Normal file
7
packages/phrases/src/locales/ko/errors/application.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
const application = {
|
||||||
|
invalid_type: '관련 역할을 가질 수 있는 것은 기계 대 기계 응용 프로그램만 가능합니다.',
|
||||||
|
role_exists: '역할 ID {{roleId}} 가 이미이 응용 프로그램에 추가되었습니다.',
|
||||||
|
invalid_role_type: '사용자 유형 역할을 기계 대 기계 응용 프로그램에 할당할 수 없습니다.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Object.freeze(application);
|
|
@ -1,3 +1,4 @@
|
||||||
|
import application from './application.js';
|
||||||
import auth from './auth.js';
|
import auth from './auth.js';
|
||||||
import connector from './connector.js';
|
import connector from './connector.js';
|
||||||
import domain from './domain.js';
|
import domain from './domain.js';
|
||||||
|
@ -42,6 +43,7 @@ const errors = {
|
||||||
hook,
|
hook,
|
||||||
domain,
|
domain,
|
||||||
subscription,
|
subscription,
|
||||||
|
application,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Object.freeze(errors);
|
export default Object.freeze(errors);
|
||||||
|
|
7
packages/phrases/src/locales/pl-pl/errors/application.ts
Normal file
7
packages/phrases/src/locales/pl-pl/errors/application.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
const application = {
|
||||||
|
invalid_type: 'Tylko aplikacje maszyna-do-maszyny mogą mieć przypisane role.',
|
||||||
|
role_exists: 'Rola o identyfikatorze {{roleId}} została już dodana do tej aplikacji.',
|
||||||
|
invalid_role_type: 'Nie można przypisać roli typu użytkownika do aplikacji maszyna-do-maszyny.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Object.freeze(application);
|
|
@ -1,3 +1,4 @@
|
||||||
|
import application from './application.js';
|
||||||
import auth from './auth.js';
|
import auth from './auth.js';
|
||||||
import connector from './connector.js';
|
import connector from './connector.js';
|
||||||
import domain from './domain.js';
|
import domain from './domain.js';
|
||||||
|
@ -42,6 +43,7 @@ const errors = {
|
||||||
hook,
|
hook,
|
||||||
domain,
|
domain,
|
||||||
subscription,
|
subscription,
|
||||||
|
application,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Object.freeze(errors);
|
export default Object.freeze(errors);
|
||||||
|
|
8
packages/phrases/src/locales/pt-br/errors/application.ts
Normal file
8
packages/phrases/src/locales/pt-br/errors/application.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
const application = {
|
||||||
|
invalid_type: 'Apenas aplicações de máquina para máquina podem ter funções associadas.',
|
||||||
|
role_exists: 'O id da função {{roleId}} já foi adicionado a este aplicativo.',
|
||||||
|
invalid_role_type:
|
||||||
|
'Não é possível atribuir uma função de tipo de usuário a um aplicativo de máquina para máquina.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Object.freeze(application);
|
|
@ -1,3 +1,4 @@
|
||||||
|
import application from './application.js';
|
||||||
import auth from './auth.js';
|
import auth from './auth.js';
|
||||||
import connector from './connector.js';
|
import connector from './connector.js';
|
||||||
import domain from './domain.js';
|
import domain from './domain.js';
|
||||||
|
@ -42,6 +43,7 @@ const errors = {
|
||||||
hook,
|
hook,
|
||||||
domain,
|
domain,
|
||||||
subscription,
|
subscription,
|
||||||
|
application,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Object.freeze(errors);
|
export default Object.freeze(errors);
|
||||||
|
|
8
packages/phrases/src/locales/pt-pt/errors/application.ts
Normal file
8
packages/phrases/src/locales/pt-pt/errors/application.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
const application = {
|
||||||
|
invalid_type: 'Apenas aplicações máquina a máquina podem ter funções associadas.',
|
||||||
|
role_exists: 'O id da função {{roleId}} já foi adicionado a esta aplicação.',
|
||||||
|
invalid_role_type:
|
||||||
|
'Não é possível atribuir uma função de tipo de utilizador a uma aplicação máquina a máquina.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Object.freeze(application);
|
|
@ -1,3 +1,4 @@
|
||||||
|
import application from './application.js';
|
||||||
import auth from './auth.js';
|
import auth from './auth.js';
|
||||||
import connector from './connector.js';
|
import connector from './connector.js';
|
||||||
import domain from './domain.js';
|
import domain from './domain.js';
|
||||||
|
@ -42,6 +43,7 @@ const errors = {
|
||||||
hook,
|
hook,
|
||||||
domain,
|
domain,
|
||||||
subscription,
|
subscription,
|
||||||
|
application,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Object.freeze(errors);
|
export default Object.freeze(errors);
|
||||||
|
|
8
packages/phrases/src/locales/ru/errors/application.ts
Normal file
8
packages/phrases/src/locales/ru/errors/application.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
const application = {
|
||||||
|
invalid_type: 'Только приложения типа "от машины к машине" могут иметь связанные роли.',
|
||||||
|
role_exists: 'Роль с идентификатором {{roleId}} уже добавлена в это приложение.',
|
||||||
|
invalid_role_type:
|
||||||
|
'Невозможно назначить роль типа "пользователь" для приложения типа "от машины к машине".',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Object.freeze(application);
|
|
@ -1,3 +1,4 @@
|
||||||
|
import application from './application.js';
|
||||||
import auth from './auth.js';
|
import auth from './auth.js';
|
||||||
import connector from './connector.js';
|
import connector from './connector.js';
|
||||||
import domain from './domain.js';
|
import domain from './domain.js';
|
||||||
|
@ -42,6 +43,7 @@ const errors = {
|
||||||
hook,
|
hook,
|
||||||
domain,
|
domain,
|
||||||
subscription,
|
subscription,
|
||||||
|
application,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Object.freeze(errors);
|
export default Object.freeze(errors);
|
||||||
|
|
7
packages/phrases/src/locales/tr-tr/errors/application.ts
Normal file
7
packages/phrases/src/locales/tr-tr/errors/application.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
const application = {
|
||||||
|
invalid_type: 'Sadece makine ile makine uygulamaları rollerle ilişkilendirilebilir.',
|
||||||
|
role_exists: 'Bu uygulamaya zaten {{roleId}} kimlikli bir rol eklenmiş.',
|
||||||
|
invalid_role_type: 'Kullanıcı tipi rolü makine ile makine uygulamasına atayamaz.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Object.freeze(application);
|
|
@ -1,3 +1,4 @@
|
||||||
|
import application from './application.js';
|
||||||
import auth from './auth.js';
|
import auth from './auth.js';
|
||||||
import connector from './connector.js';
|
import connector from './connector.js';
|
||||||
import domain from './domain.js';
|
import domain from './domain.js';
|
||||||
|
@ -42,6 +43,7 @@ const errors = {
|
||||||
hook,
|
hook,
|
||||||
domain,
|
domain,
|
||||||
subscription,
|
subscription,
|
||||||
|
application,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Object.freeze(errors);
|
export default Object.freeze(errors);
|
||||||
|
|
7
packages/phrases/src/locales/zh-cn/errors/application.ts
Normal file
7
packages/phrases/src/locales/zh-cn/errors/application.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
const application = {
|
||||||
|
invalid_type: '只有机器对机器应用程序可以有关联角色。',
|
||||||
|
role_exists: '角色 ID {{roleId}} 已添加到此应用程序。',
|
||||||
|
invalid_role_type: '无法将用户类型角色分配给机器对机器应用程序。',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Object.freeze(application);
|
|
@ -1,3 +1,4 @@
|
||||||
|
import application from './application.js';
|
||||||
import auth from './auth.js';
|
import auth from './auth.js';
|
||||||
import connector from './connector.js';
|
import connector from './connector.js';
|
||||||
import domain from './domain.js';
|
import domain from './domain.js';
|
||||||
|
@ -42,6 +43,7 @@ const errors = {
|
||||||
hook,
|
hook,
|
||||||
domain,
|
domain,
|
||||||
subscription,
|
subscription,
|
||||||
|
application,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Object.freeze(errors);
|
export default Object.freeze(errors);
|
||||||
|
|
7
packages/phrases/src/locales/zh-hk/errors/application.ts
Normal file
7
packages/phrases/src/locales/zh-hk/errors/application.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
const application = {
|
||||||
|
invalid_type: '只有機器對機器應用程式才能有相關職能。',
|
||||||
|
role_exists: '角色 ID {{roleId}} 已經被添加到此應用程式中。',
|
||||||
|
invalid_role_type: '無法將使用者類型的角色分配給機器對機器應用程式。',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Object.freeze(application);
|
|
@ -1,3 +1,4 @@
|
||||||
|
import application from './application.js';
|
||||||
import auth from './auth.js';
|
import auth from './auth.js';
|
||||||
import connector from './connector.js';
|
import connector from './connector.js';
|
||||||
import domain from './domain.js';
|
import domain from './domain.js';
|
||||||
|
@ -42,6 +43,7 @@ const errors = {
|
||||||
hook,
|
hook,
|
||||||
domain,
|
domain,
|
||||||
subscription,
|
subscription,
|
||||||
|
application,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Object.freeze(errors);
|
export default Object.freeze(errors);
|
||||||
|
|
7
packages/phrases/src/locales/zh-tw/errors/application.ts
Normal file
7
packages/phrases/src/locales/zh-tw/errors/application.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
const application = {
|
||||||
|
invalid_type: '僅允許機器對機器應用程式附加角色。',
|
||||||
|
role_exists: '該角色 ID {{roleId}} 已被添加至此應用程式。',
|
||||||
|
invalid_role_type: '無法將使用者類型的角色指派給機器對機器應用程式。',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Object.freeze(application);
|
|
@ -1,3 +1,4 @@
|
||||||
|
import application from './application.js';
|
||||||
import auth from './auth.js';
|
import auth from './auth.js';
|
||||||
import connector from './connector.js';
|
import connector from './connector.js';
|
||||||
import domain from './domain.js';
|
import domain from './domain.js';
|
||||||
|
@ -42,6 +43,7 @@ const errors = {
|
||||||
hook,
|
hook,
|
||||||
domain,
|
domain,
|
||||||
subscription,
|
subscription,
|
||||||
|
application,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Object.freeze(errors);
|
export default Object.freeze(errors);
|
||||||
|
|
Loading…
Reference in a new issue