mirror of
https://github.com/logto-io/logto.git
synced 2025-02-24 22:05:56 -05:00
fix(core): show all organization permissions (#7070)
* fix(core): show all organization permissions * chore: update changeset * test: update org scopes tests
This commit is contained in:
parent
31adfb6ac2
commit
f15602f198
9 changed files with 40 additions and 11 deletions
10
.changeset/strange-oranges-sort.md
Normal file
10
.changeset/strange-oranges-sort.md
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
---
|
||||||
|
"@logto/core": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix: incorrect pagination behavior in organization role scopes APIs
|
||||||
|
|
||||||
|
- Fix `/api/organization-roles/{id}/scopes` and `/api/organization-roles/{id}/resource-scopes` endpoints to:
|
||||||
|
- Return all scopes when no pagination parameters are provided
|
||||||
|
- Support optional pagination when query parameters are present
|
||||||
|
- Fix Console to properly display all organization role scopes on the organization template page
|
|
@ -102,7 +102,7 @@
|
||||||
"/api/organization-roles/{id}/scopes": {
|
"/api/organization-roles/{id}/scopes": {
|
||||||
"get": {
|
"get": {
|
||||||
"summary": "Get organization role scopes",
|
"summary": "Get organization role scopes",
|
||||||
"description": "Get all organization scopes that are assigned to the specified organization role.",
|
"description": "Get organization scopes that are assigned to the specified organization role with optional pagination.",
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "A list of organization scopes."
|
"description": "A list of organization scopes."
|
||||||
|
@ -174,7 +174,7 @@
|
||||||
"/api/organization-roles/{id}/resource-scopes": {
|
"/api/organization-roles/{id}/resource-scopes": {
|
||||||
"get": {
|
"get": {
|
||||||
"summary": "Get organization role resource scopes",
|
"summary": "Get organization role resource scopes",
|
||||||
"description": "Get all resource scopes that are assigned to the specified organization role.",
|
"description": "Get resource scopes that are assigned to the specified organization role with optional pagination.",
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "A list of resource scopes."
|
"description": "A list of resource scopes."
|
||||||
|
|
|
@ -127,8 +127,8 @@ export default function organizationRoleRoutes<T extends ManagementApiRouter>(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.addRelationRoutes(rolesScopes, 'scopes');
|
router.addRelationRoutes(rolesScopes, 'scopes', { isPaginationOptional: true });
|
||||||
router.addRelationRoutes(rolesResourceScopes, 'resource-scopes');
|
router.addRelationRoutes(rolesResourceScopes, 'resource-scopes', { isPaginationOptional: true });
|
||||||
|
|
||||||
originalRouter.use(router.routes());
|
originalRouter.use(router.routes());
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
"/api/organization-scopes": {
|
"/api/organization-scopes": {
|
||||||
"get": {
|
"get": {
|
||||||
"summary": "Get organization scopes",
|
"summary": "Get organization scopes",
|
||||||
"description": "Get organization scopes that match with pagination.",
|
"description": "Get organization scopes that match with optional pagination.",
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "A list of organization scopes."
|
"description": "A list of organization scopes."
|
||||||
|
|
|
@ -19,6 +19,7 @@ export default function organizationScopeRoutes<T extends ManagementApiRouter>(
|
||||||
middlewares: [],
|
middlewares: [],
|
||||||
errorHandler,
|
errorHandler,
|
||||||
searchFields: ['name'],
|
searchFields: ['name'],
|
||||||
|
isPaginationOptional: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
originalRouter.use(router.routes());
|
originalRouter.use(router.routes());
|
||||||
|
|
|
@ -86,6 +86,11 @@ type SchemaRouterConfig<Key extends string> = {
|
||||||
* If not provided, the `schema.guard` will be used.
|
* If not provided, the `schema.guard` will be used.
|
||||||
*/
|
*/
|
||||||
entityGuard?: z.ZodTypeAny;
|
entityGuard?: z.ZodTypeAny;
|
||||||
|
/**
|
||||||
|
* If the GET route's pagination is optional.
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
isPaginationOptional?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type RelationRoutesConfig = {
|
type RelationRoutesConfig = {
|
||||||
|
@ -313,12 +318,12 @@ export default class SchemaRouter<
|
||||||
|
|
||||||
#addRoutes() {
|
#addRoutes() {
|
||||||
const { queries, schema, config } = this;
|
const { queries, schema, config } = this;
|
||||||
const { disabled, searchFields, idLength, entityGuard } = config;
|
const { disabled, searchFields, idLength, entityGuard, isPaginationOptional } = config;
|
||||||
|
|
||||||
if (!disabled.get) {
|
if (!disabled.get) {
|
||||||
this.get(
|
this.get(
|
||||||
'/',
|
'/',
|
||||||
koaPagination(),
|
koaPagination({ isOptional: isPaginationOptional }),
|
||||||
koaGuard({
|
koaGuard({
|
||||||
query: z.object({ q: z.string().optional() }),
|
query: z.object({ q: z.string().optional() }),
|
||||||
response: (entityGuard ?? schema.guard).array(),
|
response: (entityGuard ?? schema.guard).array(),
|
||||||
|
|
|
@ -299,6 +299,10 @@ describe('organization scope data hook events', () => {
|
||||||
expect(organizationScopeCreateHook?.payload.event).toBe('OrganizationScope.Created');
|
expect(organizationScopeCreateHook?.payload.event).toBe('OrganizationScope.Created');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await organizationScopeApi.cleanUp();
|
||||||
|
});
|
||||||
|
|
||||||
it.each(organizationScopeDataHookTestCases)(
|
it.each(organizationScopeDataHookTestCases)(
|
||||||
'test case %#: %p',
|
'test case %#: %p',
|
||||||
async ({ route, event, method, endpoint, payload }) => {
|
async ({ route, event, method, endpoint, payload }) => {
|
||||||
|
|
|
@ -39,15 +39,21 @@ describe('organization scope APIs', () => {
|
||||||
expect(scopes).toContainEqual(expect.objectContaining({ name: name2, description: null }));
|
expect(scopes).toContainEqual(expect.objectContaining({ name: name2, description: null }));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should get organization scopes with pagination', async () => {
|
it('should get organization scopes with optional pagination', async () => {
|
||||||
// Add 20 scopes to exceed the default page size
|
const pageSize = 20;
|
||||||
|
// Create scopes to exceed the default page size
|
||||||
|
const createCount = pageSize + 10;
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
Array.from({ length: 30 }).map(async () => scopeApi.create({ name: 'test' + randomId() }))
|
Array.from({ length: createCount }).map(async () =>
|
||||||
|
scopeApi.create({ name: 'test' + randomId() })
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Should return all scopes if no pagination is provided
|
||||||
const scopes = await scopeApi.getList();
|
const scopes = await scopeApi.getList();
|
||||||
expect(scopes).toHaveLength(20);
|
expect(scopes).toHaveLength(createCount);
|
||||||
|
|
||||||
|
// Should return paginated scopes based on specified page number and page size
|
||||||
const scopes2 = await scopeApi.getList(
|
const scopes2 = await scopeApi.getList(
|
||||||
new URLSearchParams({
|
new URLSearchParams({
|
||||||
page: '2',
|
page: '2',
|
||||||
|
|
|
@ -390,6 +390,9 @@ describe('organization user APIs', () => {
|
||||||
await organizationApi.addUserRoles(organization.id, user.id, [role2.id]);
|
await organizationApi.addUserRoles(organization.id, user.id, [role2.id]);
|
||||||
const newScopes = await organizationApi.getUserOrganizationScopes(organization.id, user.id);
|
const newScopes = await organizationApi.getUserOrganizationScopes(organization.id, user.id);
|
||||||
expect(newScopes.map(({ name }) => name)).toEqual([scope1.name]);
|
expect(newScopes.map(({ name }) => name)).toEqual([scope1.name]);
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
await organizationApi.cleanUp();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Reference in a new issue