mirror of
https://github.com/logto-io/logto.git
synced 2025-03-24 22:41:28 -05:00
refactor(core): fix org apis (#4890)
This commit is contained in:
parent
d200ca56c8
commit
bfea0b0fdd
3 changed files with 97 additions and 6 deletions
|
@ -1,5 +1,13 @@
|
|||
import { OrganizationRoles } from '@logto/schemas';
|
||||
import {
|
||||
type CreateOrganizationRole,
|
||||
OrganizationRoles,
|
||||
organizationRoleWithScopesGuard,
|
||||
} from '@logto/schemas';
|
||||
import { generateStandardId } from '@logto/shared';
|
||||
import { z } from 'zod';
|
||||
|
||||
import koaGuard from '#src/middleware/koa-guard.js';
|
||||
import koaPagination from '#src/middleware/koa-pagination.js';
|
||||
import SchemaRouter from '#src/utils/SchemaRouter.js';
|
||||
|
||||
import { type AuthedRouter, type RouterInitArgs } from '../types.js';
|
||||
|
@ -20,9 +28,63 @@ export default function organizationRoleRoutes<T extends AuthedRouter>(
|
|||
]: RouterInitArgs<T>
|
||||
) {
|
||||
const router = new SchemaRouter(OrganizationRoles, roles, {
|
||||
disabled: { get: true, post: true },
|
||||
errorHandler,
|
||||
searchFields: ['name'],
|
||||
});
|
||||
|
||||
router.get(
|
||||
'/',
|
||||
koaPagination(),
|
||||
koaGuard({
|
||||
response: organizationRoleWithScopesGuard.array(),
|
||||
status: [200],
|
||||
}),
|
||||
async (ctx, next) => {
|
||||
const { limit, offset } = ctx.pagination;
|
||||
const [count, entities] = await roles.findAll(limit, offset);
|
||||
|
||||
ctx.pagination.totalCount = count;
|
||||
ctx.body = entities;
|
||||
return next();
|
||||
}
|
||||
);
|
||||
|
||||
/** Allows to carry an initial set of scopes for creating a new organization role. */
|
||||
type CreateOrganizationRolePayload = Omit<CreateOrganizationRole, 'id'> & {
|
||||
organizationScopeIds: string[];
|
||||
};
|
||||
|
||||
const createGuard: z.ZodType<CreateOrganizationRolePayload, z.ZodTypeDef, unknown> =
|
||||
OrganizationRoles.createGuard
|
||||
.omit({
|
||||
id: true,
|
||||
})
|
||||
.extend({
|
||||
organizationScopeIds: z.array(z.string()).default([]),
|
||||
});
|
||||
|
||||
router.post(
|
||||
'/',
|
||||
koaGuard({
|
||||
body: createGuard,
|
||||
response: OrganizationRoles.guard,
|
||||
status: [201, 422],
|
||||
}),
|
||||
async (ctx, next) => {
|
||||
const { organizationScopeIds: scopeIds, ...data } = ctx.guard.body;
|
||||
const role = await roles.insert({ id: generateStandardId(), ...data });
|
||||
|
||||
if (scopeIds.length > 0) {
|
||||
await rolesScopes.insert(...scopeIds.map<[string, string]>((id) => [role.id, id]));
|
||||
}
|
||||
|
||||
ctx.body = role;
|
||||
ctx.status = 201;
|
||||
return next();
|
||||
}
|
||||
);
|
||||
|
||||
router.addRelationRoutes(rolesScopes, 'scopes');
|
||||
|
||||
originalRouter.use(router.routes());
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
import { type OrganizationScope, type OrganizationRole, type Organization } from '@logto/schemas';
|
||||
import {
|
||||
type OrganizationScope,
|
||||
type OrganizationRole,
|
||||
type Organization,
|
||||
type OrganizationRoleWithScopes,
|
||||
} from '@logto/schemas';
|
||||
import { trySafe } from '@silverhand/essentials';
|
||||
|
||||
import {
|
||||
|
@ -20,10 +25,11 @@ export class OrganizationRoleApiTest extends OrganizationRoleApi {
|
|||
return this.#roles;
|
||||
}
|
||||
|
||||
override async create(data: CreateOrganizationRolePostData): Promise<OrganizationRole> {
|
||||
override async create(data: CreateOrganizationRolePostData): Promise<OrganizationRoleWithScopes> {
|
||||
const created = await super.create(data);
|
||||
this.roles.push(created);
|
||||
return created;
|
||||
// eslint-disable-next-line no-restricted-syntax -- to override the type
|
||||
return created as OrganizationRoleWithScopes;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import assert from 'node:assert';
|
||||
|
||||
import { generateStandardId } from '@logto/shared';
|
||||
import { isKeyInObject } from '@silverhand/essentials';
|
||||
import { isKeyInObject, pick } from '@silverhand/essentials';
|
||||
import { HTTPError } from 'got';
|
||||
|
||||
import { OrganizationRoleApiTest, OrganizationScopeApiTest } from '#src/helpers/organization.js';
|
||||
|
@ -12,9 +12,10 @@ const randomId = () => generateStandardId(4);
|
|||
describe('organization role APIs', () => {
|
||||
describe('organization roles', () => {
|
||||
const roleApi = new OrganizationRoleApiTest();
|
||||
const scopeApi = new OrganizationScopeApiTest();
|
||||
|
||||
afterEach(async () => {
|
||||
await roleApi.cleanUp();
|
||||
await Promise.all([roleApi.cleanUp(), scopeApi.cleanUp()]);
|
||||
});
|
||||
|
||||
it('should fail if the name of the new organization role already exists', async () => {
|
||||
|
@ -71,6 +72,28 @@ describe('organization role APIs', () => {
|
|||
expect(role).toStrictEqual(createdRole);
|
||||
});
|
||||
|
||||
it('should be able to create a new organization with initial scopes', async () => {
|
||||
const [scope1, scope2] = await Promise.all([
|
||||
scopeApi.create({ name: 'test' + randomId() }),
|
||||
scopeApi.create({ name: 'test' + randomId() }),
|
||||
]);
|
||||
const createdRole = await roleApi.create({
|
||||
name: 'test' + randomId(),
|
||||
description: 'test description.',
|
||||
organizationScopeIds: [scope1.id, scope2.id],
|
||||
});
|
||||
const scopes = await roleApi.getScopes(createdRole.id);
|
||||
const roles = await roleApi.getList();
|
||||
const roleWithScopes = roles.find((role) => role.id === createdRole.id);
|
||||
|
||||
for (const scope of [scope1, scope2]) {
|
||||
expect(roleWithScopes?.scopes).toContainEqual(
|
||||
expect.objectContaining(pick(scope, 'id', 'name'))
|
||||
);
|
||||
expect(scopes).toContainEqual(expect.objectContaining(pick(scope, 'id', 'name')));
|
||||
}
|
||||
});
|
||||
|
||||
it('should fail when try to get an organization role that does not exist', async () => {
|
||||
const response = await roleApi.get('0').catch((error: unknown) => error);
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue