0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-16 20:26:19 -05:00

fix(core): fix unpaginated scopes response (#3810)

This commit is contained in:
wangsijie 2023-05-05 11:43:33 +08:00 committed by GitHub
parent f20f65407f
commit e57fc80bd4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 45 additions and 19 deletions

View file

@ -66,6 +66,11 @@ export const mockScope: Scope = {
createdAt: 1_645_334_775_356,
};
export const mockScopeWithResource = {
...mockScope,
resource: mockResource,
};
export const mockRole: Role = {
tenantId: 'fake_tenant',
id: 'role_id',

View file

@ -1,7 +1,7 @@
import type { Role } from '@logto/schemas';
import { pickDefault } from '@logto/shared/esm';
import { mockRole, mockScope, mockResource } from '#src/__mocks__/index.js';
import { mockRole, mockScope, mockResource, mockScopeWithResource } 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';
@ -68,7 +68,16 @@ describe('role scope routes', () => {
findScopesByIds.mockResolvedValueOnce([mockScope]);
const response = await roleRequester.get(`/roles/${mockRole.id}/scopes`);
expect(response.status).toEqual(200);
expect(response.body).toEqual([mockScope]);
expect(response.body).toEqual([mockScopeWithResource]);
});
it('GET /roles/:id/scopes (with pagination)', async () => {
findRoleById.mockResolvedValueOnce(mockRole);
findRolesScopesByRoleId.mockResolvedValueOnce([]);
findScopesByIds.mockResolvedValueOnce([mockScope]);
const response = await roleRequester.get(`/roles/${mockRole.id}/scopes?page=1`);
expect(response.status).toEqual(200);
expect(response.body).toEqual([mockScopeWithResource]);
});
it('POST /roles/:id/scopes', async () => {

View file

@ -1,4 +1,5 @@
import type { ScopeResponse } from '@logto/schemas';
import type { Scope, ScopeResponse } from '@logto/schemas';
import { scopeResponseGuard } from '@logto/schemas';
import { generateStandardId } from '@logto/shared';
import { tryThat } from '@silverhand/essentials';
import { object, string } from 'zod';
@ -21,11 +22,26 @@ export default function roleScopeRoutes<T extends AuthedRouter>(
scopes: { findScopeById, findScopesByIds, countScopesByScopeIds, searchScopesByScopeIds },
} = queries;
const attachResourceToScopes = async (scopes: readonly Scope[]): Promise<ScopeResponse[]> => {
const resources = await findResourcesByIds(scopes.map(({ resourceId }) => resourceId));
return scopes.map((scope) => {
const resource = resources.find(({ id }) => id === scope.resourceId);
assertThat(resource, new Error(`Cannot find resource for id ${scope.resourceId}`));
return {
...scope,
resource,
};
});
};
router.get(
'/roles/:id/scopes',
koaPagination({ isOptional: true }),
koaGuard({
params: object({ id: string().min(1) }),
response: scopeResponseGuard.array(),
}),
async (ctx, next) => {
const {
@ -43,7 +59,9 @@ export default function roleScopeRoutes<T extends AuthedRouter>(
const scopeIds = rolesScopes.map(({ scopeId }) => scopeId);
if (disabled) {
ctx.body = await searchScopesByScopeIds(scopeIds, search);
const scopes = await searchScopesByScopeIds(scopeIds, search);
ctx.body = await attachResourceToScopes(scopes);
return next();
}
@ -53,21 +71,9 @@ export default function roleScopeRoutes<T extends AuthedRouter>(
searchScopesByScopeIds(scopeIds, search, limit, offset),
]);
const resources = await findResourcesByIds(scopes.map(({ resourceId }) => resourceId));
const result: ScopeResponse[] = scopes.map((scope) => {
const resource = resources.find(({ id }) => id === scope.resourceId);
assertThat(resource, new Error(`Cannot find resource for id ${scope.resourceId}`));
return {
...scope,
resource,
};
});
// Return totalCount to pagination middleware
ctx.pagination.totalCount = count;
ctx.body = result;
ctx.body = await attachResourceToScopes(scopes);
return next();
},

View file

@ -1,3 +1,9 @@
import type { Resource, Scope } from '../db-entries/index.js';
import { type z } from 'zod';
export type ScopeResponse = Scope & { resource: Resource };
import { Resources, Scopes } from '../db-entries/index.js';
export const scopeResponseGuard = Scopes.guard.extend({
resource: Resources.guard,
});
export type ScopeResponse = z.infer<typeof scopeResponseGuard>;