0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-01-06 20:40:08 -05:00

feat(core): add organization scope APIs

This commit is contained in:
Gao Sun 2023-10-12 14:31:46 +08:00
parent fd7d5a4ab1
commit 996d550382
No known key found for this signature in database
GPG key ID: 13EBE123E4773688
6 changed files with 164 additions and 0 deletions

View file

@ -0,0 +1,20 @@
import {
type OrganizationScopeKeys,
OrganizationScopes,
type CreateOrganizationScope,
type OrganizationScope,
} from '@logto/schemas';
import { type CommonQueryMethods } from 'slonik';
import SchemaQueries from '#src/utils/SchemaQueries.js';
/** Class of queries for scopes in the organization template. */
export default class OrganizationScopeQueries extends SchemaQueries<
OrganizationScopeKeys,
CreateOrganizationScope,
OrganizationScope
> {
constructor(pool: CommonQueryMethods) {
super(pool, OrganizationScopes);
}
}

View file

@ -25,6 +25,7 @@ import hookRoutes from './hook.js';
import interactionRoutes from './interaction/index.js';
import logRoutes from './log.js';
import logtoConfigRoutes from './logto-config.js';
import organizationScopeRoutes from './organization-scopes.js';
import organizationRoutes from './organizations.js';
import resourceRoutes from './resource.js';
import roleRoutes from './role.js';
@ -65,6 +66,7 @@ const createRouters = (tenant: TenantContext) => {
userAssetsRoutes(managementRouter, tenant);
domainRoutes(managementRouter, tenant);
organizationRoutes(managementRouter, tenant);
organizationScopeRoutes(managementRouter, tenant);
const anonymousRouter: AnonymousRouter = new Router();
wellKnownRoutes(anonymousRouter, tenant);

View file

@ -0,0 +1,18 @@
import { OrganizationScopes } from '@logto/schemas';
import SchemaRouter, { SchemaActions } from '#src/utils/SchemaRouter.js';
import { type AuthedRouter, type RouterInitArgs } from './types.js';
export default function organizationScopeRoutes<T extends AuthedRouter>(
...[
originalRouter,
{
queries: { organizationScopes },
},
]: RouterInitArgs<T>
) {
const router = new SchemaRouter(OrganizationScopes, new SchemaActions(organizationScopes));
originalRouter.use(router.routes());
}

View file

@ -11,6 +11,7 @@ import { createHooksQueries } from '#src/queries/hooks.js';
import { createLogQueries } from '#src/queries/log.js';
import { createLogtoConfigQueries } from '#src/queries/logto-config.js';
import { createOidcModelInstanceQueries } from '#src/queries/oidc-model-instance.js';
import OrganizationScopeQueries from '#src/queries/organization-scopes.js';
import OrganizationQueries from '#src/queries/organizations.js';
import { createPasscodeQueries } from '#src/queries/passcode.js';
import { createResourceQueries } from '#src/queries/resource.js';
@ -43,6 +44,8 @@ export default class Queries {
domains = createDomainsQueries(this.pool);
dailyActiveUsers = createDailyActiveUsersQueries(this.pool);
organizations = new OrganizationQueries(this.pool);
/** Organization template scope queries. */
organizationScopes = new OrganizationScopeQueries(this.pool);
constructor(
public readonly pool: CommonQueryMethods,

View file

@ -0,0 +1,39 @@
import { type OrganizationScope } from '@logto/schemas';
import { authedAdminApi } from './api.js';
export const createOrganizationScope = async (name: string, description?: string) => {
return authedAdminApi
.post('organization-scopes', {
json: {
name,
description,
},
})
.json<OrganizationScope>();
};
export const getOrganizationScopes = async (params?: URLSearchParams) => {
return authedAdminApi
.get('organization-scopes?' + (params?.toString() ?? ''))
.json<OrganizationScope[]>();
};
export const getOrganizationScope = async (id: string) => {
return authedAdminApi.get('organization-scopes/' + id).json<OrganizationScope>();
};
export const updateOrganizationScope = async (id: string, name: string, description?: string) => {
return authedAdminApi
.patch('organization-scopes/' + id, {
json: {
name,
description,
},
})
.json<OrganizationScope>();
};
export const deleteOrganizationScope = async (id: string) => {
return authedAdminApi.delete('organization-scopes/' + id);
};

View file

@ -0,0 +1,82 @@
import { generateStandardId } from '@logto/shared';
import { HTTPError } from 'got';
import {
createOrganizationScope,
getOrganizationScopes,
getOrganizationScope,
updateOrganizationScope,
deleteOrganizationScope,
} from '#src/api/organization-scope.js';
const randomId = () => generateStandardId(4);
describe('organization scopes', () => {
it('should get organization scopes successfully', async () => {
const [name1, name2] = ['test' + randomId(), 'test' + randomId()];
await createOrganizationScope(name1, 'A test organization scope.');
await createOrganizationScope(name2);
const scopes = await getOrganizationScopes();
expect(scopes).toContainEqual(
expect.objectContaining({ name: name1, description: 'A test organization scope.' })
);
expect(scopes).toContainEqual(expect.objectContaining({ name: name2, description: null }));
});
it('should get organization scopes with pagination', async () => {
// Add 20 scopes to exceed the default page size
await Promise.all(
Array.from({ length: 30 }).map(async () => createOrganizationScope('test' + randomId()))
);
const scopes = await getOrganizationScopes();
expect(scopes).toHaveLength(20);
const scopes2 = await getOrganizationScopes(
new URLSearchParams({
page: '2',
page_size: '10',
})
);
expect(scopes2.length).toBeGreaterThanOrEqual(10);
expect(scopes2[0]?.id).not.toBeFalsy();
expect(scopes2[0]?.id).toBe(scopes[10]?.id);
});
it('should be able to create and get organization scopes by id', async () => {
const createdScope = await createOrganizationScope('test' + randomId());
const scope = await getOrganizationScope(createdScope.id);
expect(scope).toStrictEqual(createdScope);
});
it('should fail when try to get an organization scope that does not exist', async () => {
const response = await getOrganizationScope('0').catch((error: unknown) => error);
expect(response instanceof HTTPError && response.response.statusCode).toBe(404);
});
it('should be able to update organization scope', async () => {
const createdScope = await createOrganizationScope('test' + randomId());
const newName = 'test' + randomId();
const scope = await updateOrganizationScope(createdScope.id, newName, 'test description.');
expect(scope).toStrictEqual({
...createdScope,
name: newName,
description: 'test description.',
});
});
it('should be able to delete organization scope', async () => {
const createdScope = await createOrganizationScope('test' + randomId());
await deleteOrganizationScope(createdScope.id);
const response = await getOrganizationScope(createdScope.id).catch((error: unknown) => error);
expect(response instanceof HTTPError && response.response.statusCode).toBe(404);
});
it('should fail when try to delete an organization scope that does not exist', async () => {
const response = await deleteOrganizationScope('0').catch((error: unknown) => error);
expect(response instanceof HTTPError && response.response.statusCode).toBe(404);
});
});