mirror of
https://github.com/logto-io/logto.git
synced 2025-03-31 22:51:25 -05:00
Merge pull request #6078 from logto-io/gao-reorg-org-rotues
refactor(core): reorg organization routes
This commit is contained in:
commit
6f06c418b2
19 changed files with 150 additions and 100 deletions
|
@ -55,7 +55,6 @@ jobs:
|
|||
- uses: logto-io/actions-package-logto-artifact@v2
|
||||
with:
|
||||
artifact-name: dev-feature-disabled-integration-test-${{ github.sha }}
|
||||
branch: ${{github.base_ref}}
|
||||
pnpm-version: 9
|
||||
|
||||
run-logto:
|
||||
|
|
|
@ -11,10 +11,9 @@ import koaGuard from '#src/middleware/koa-guard.js';
|
|||
import SchemaRouter from '#src/utils/SchemaRouter.js';
|
||||
import assertThat from '#src/utils/assert-that.js';
|
||||
|
||||
import { errorHandler } from '../organization/utils.js';
|
||||
import { type ManagementApiRouter, type RouterInitArgs } from '../types.js';
|
||||
|
||||
import { errorHandler } from './utils.js';
|
||||
|
||||
export default function organizationInvitationRoutes<T extends ManagementApiRouter>(
|
||||
...[
|
||||
originalRouter,
|
|
@ -16,14 +16,13 @@ import { organizationRoleSearchKeys } from '#src/queries/organization/index.js';
|
|||
import SchemaRouter from '#src/utils/SchemaRouter.js';
|
||||
import { parseSearchOptions } from '#src/utils/search.js';
|
||||
|
||||
import { errorHandler } from '../organization/utils.js';
|
||||
import {
|
||||
type ManagementApiRouter,
|
||||
type ManagementApiRouterContext,
|
||||
type RouterInitArgs,
|
||||
} from '../types.js';
|
||||
|
||||
import { errorHandler } from './utils.js';
|
||||
|
||||
export default function organizationRoleRoutes<T extends ManagementApiRouter>(
|
||||
...[
|
||||
originalRouter,
|
|
@ -3,10 +3,9 @@ import { OrganizationScopes } from '@logto/schemas';
|
|||
import koaQuotaGuard from '#src/middleware/koa-quota-guard.js';
|
||||
import SchemaRouter from '#src/utils/SchemaRouter.js';
|
||||
|
||||
import { errorHandler } from '../organization/utils.js';
|
||||
import { type ManagementApiRouter, type RouterInitArgs } from '../types.js';
|
||||
|
||||
import { errorHandler } from './utils.js';
|
||||
|
||||
export default function organizationScopeRoutes<T extends ManagementApiRouter>(
|
||||
...[
|
||||
originalRouter,
|
|
@ -8,6 +8,7 @@
|
|||
"paths": {
|
||||
"/api/organizations/{id}/applications": {
|
||||
"get": {
|
||||
"tags": ["Dev feature"],
|
||||
"summary": "Get organization applications",
|
||||
"description": "Get applications associated with the organization.",
|
||||
"responses": {
|
||||
|
@ -17,6 +18,7 @@
|
|||
}
|
||||
},
|
||||
"post": {
|
||||
"tags": ["Dev feature"],
|
||||
"summary": "Add organization application",
|
||||
"description": "Add an application to the organization.",
|
||||
"requestBody": {
|
||||
|
@ -42,6 +44,7 @@
|
|||
}
|
||||
},
|
||||
"put": {
|
||||
"tags": ["Dev feature"],
|
||||
"summary": "Replace organization applications",
|
||||
"description": "Replace all applications associated with the organization with the given data.",
|
||||
"requestBody": {
|
||||
|
@ -69,6 +72,7 @@
|
|||
},
|
||||
"/api/organizations/{id}/applications/{applicationId}": {
|
||||
"delete": {
|
||||
"tags": ["Dev feature"],
|
||||
"summary": "Remove organization application",
|
||||
"description": "Remove an application from the organization.",
|
||||
"responses": {
|
||||
|
@ -80,6 +84,7 @@
|
|||
},
|
||||
"/api/organizations/{id}/applications/{applicationId}/roles": {
|
||||
"get": {
|
||||
"tags": ["Dev feature"],
|
||||
"summary": "Get organization application roles",
|
||||
"description": "Get roles associated with the application in the organization.",
|
||||
"responses": {
|
||||
|
@ -89,6 +94,7 @@
|
|||
}
|
||||
},
|
||||
"post": {
|
||||
"tags": ["Dev feature"],
|
||||
"summary": "Add organization application role",
|
||||
"description": "Add a role to the application in the organization.",
|
||||
"requestBody": {
|
||||
|
@ -114,6 +120,7 @@
|
|||
}
|
||||
},
|
||||
"put": {
|
||||
"tags": ["Dev feature"],
|
||||
"summary": "Replace organization application roles",
|
||||
"description": "Replace all roles associated with the application in the organization with the given data.",
|
||||
"requestBody": {
|
||||
|
@ -141,6 +148,7 @@
|
|||
},
|
||||
"/api/organizations/{id}/applications/{applicationId}/roles/{organizationRoleId}": {
|
||||
"delete": {
|
||||
"tags": ["Dev feature"],
|
||||
"summary": "Remove organization application role",
|
||||
"description": "Remove a role from the application in the organization.",
|
||||
"responses": {
|
23
packages/core/src/routes/organization/application/index.ts
Normal file
23
packages/core/src/routes/organization/application/index.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { type OrganizationKeys, type CreateOrganization, type Organization } from '@logto/schemas';
|
||||
|
||||
import { EnvSet } from '#src/env-set/index.js';
|
||||
import type OrganizationQueries from '#src/queries/organization/index.js';
|
||||
import type SchemaRouter from '#src/utils/SchemaRouter.js';
|
||||
|
||||
import applicationRoleRelationRoutes from './role-relations.js';
|
||||
|
||||
/** Mounts the application-related routes on the organization router. */
|
||||
export default function applicationRoutes(
|
||||
router: SchemaRouter<OrganizationKeys, CreateOrganization, Organization>,
|
||||
organizations: OrganizationQueries
|
||||
) {
|
||||
if (EnvSet.values.isDevFeaturesEnabled) {
|
||||
// MARK: Organization - application relation routes
|
||||
router.addRelationRoutes(organizations.relations.apps, undefined, {
|
||||
hookEvent: 'Organization.Membership.Updated',
|
||||
});
|
||||
|
||||
// MARK: Organization - application role relation routes
|
||||
applicationRoleRelationRoutes(router, organizations);
|
||||
}
|
||||
}
|
|
@ -1,28 +1,21 @@
|
|||
import {
|
||||
type OrganizationWithFeatured,
|
||||
Organizations,
|
||||
featuredUserGuard,
|
||||
userWithOrganizationRolesGuard,
|
||||
} from '@logto/schemas';
|
||||
import { type OrganizationWithFeatured, Organizations, featuredUserGuard } from '@logto/schemas';
|
||||
import { yes } from '@silverhand/essentials';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { EnvSet } from '#src/env-set/index.js';
|
||||
import koaGuard from '#src/middleware/koa-guard.js';
|
||||
import koaPagination from '#src/middleware/koa-pagination.js';
|
||||
import koaQuotaGuard from '#src/middleware/koa-quota-guard.js';
|
||||
import { userSearchKeys } from '#src/queries/user.js';
|
||||
import SchemaRouter from '#src/utils/SchemaRouter.js';
|
||||
import { parseSearchOptions } from '#src/utils/search.js';
|
||||
|
||||
import organizationInvitationRoutes from '../organization-invitation/index.js';
|
||||
import organizationRoleRoutes from '../organization-role/index.js';
|
||||
import organizationScopeRoutes from '../organization-scope/index.js';
|
||||
import { type ManagementApiRouter, type RouterInitArgs } from '../types.js';
|
||||
|
||||
import applicationRoleRelationRoutes from './index.application-role-relations.js';
|
||||
import emailDomainRoutes from './index.jit.email-domains.js';
|
||||
import userRoleRelationRoutes from './index.user-role-relations.js';
|
||||
import organizationInvitationRoutes from './invitations.js';
|
||||
import organizationRoleRoutes from './roles.js';
|
||||
import organizationScopeRoutes from './scopes.js';
|
||||
import applicationRoutes from './application/index.js';
|
||||
import jitRoutes from './jit/index.js';
|
||||
import userRoutes from './user/index.js';
|
||||
import { errorHandler } from './utils.js';
|
||||
|
||||
export default function organizationRoutes<T extends ManagementApiRouter>(
|
||||
|
@ -83,81 +76,9 @@ export default function organizationRoutes<T extends ManagementApiRouter>(
|
|||
}
|
||||
);
|
||||
|
||||
// MARK: Organization - user relation routes
|
||||
router.addRelationRoutes(organizations.relations.users, undefined, {
|
||||
disabled: { get: true },
|
||||
hookEvent: 'Organization.Membership.Updated',
|
||||
});
|
||||
|
||||
router.get(
|
||||
'/:id/users',
|
||||
koaPagination(),
|
||||
koaGuard({
|
||||
query: z.object({ q: z.string().optional() }),
|
||||
params: z.object({ id: z.string().min(1) }),
|
||||
response: userWithOrganizationRolesGuard.array(),
|
||||
status: [200, 404],
|
||||
}),
|
||||
async (ctx, next) => {
|
||||
const search = parseSearchOptions(userSearchKeys, ctx.guard.query);
|
||||
|
||||
const [totalCount, entities] = await organizations.relations.users.getUsersByOrganizationId(
|
||||
ctx.guard.params.id,
|
||||
ctx.pagination,
|
||||
search
|
||||
);
|
||||
|
||||
ctx.pagination.totalCount = totalCount;
|
||||
ctx.body = entities;
|
||||
|
||||
return next();
|
||||
}
|
||||
);
|
||||
|
||||
// MARK: Organization - user role relation routes
|
||||
router.post(
|
||||
'/:id/users/roles',
|
||||
koaGuard({
|
||||
params: z.object({ id: z.string().min(1) }),
|
||||
body: z.object({
|
||||
userIds: z.string().min(1).array().nonempty(),
|
||||
organizationRoleIds: z.string().min(1).array().nonempty(),
|
||||
}),
|
||||
status: [201, 422],
|
||||
}),
|
||||
async (ctx, next) => {
|
||||
const { id } = ctx.guard.params;
|
||||
const { userIds, organizationRoleIds } = ctx.guard.body;
|
||||
|
||||
await organizations.relations.usersRoles.insert(
|
||||
...organizationRoleIds.flatMap((roleId) =>
|
||||
userIds.map((userId) => ({ organizationId: id, organizationRoleId: roleId, userId }))
|
||||
)
|
||||
);
|
||||
|
||||
ctx.status = 201;
|
||||
return next();
|
||||
}
|
||||
);
|
||||
|
||||
userRoleRelationRoutes(router, organizations);
|
||||
|
||||
if (EnvSet.values.isDevFeaturesEnabled) {
|
||||
// MARK: Organization - application relation routes
|
||||
router.addRelationRoutes(organizations.relations.apps, undefined, {
|
||||
hookEvent: 'Organization.Membership.Updated',
|
||||
});
|
||||
|
||||
// MARK: Organization - application role relation routes
|
||||
applicationRoleRelationRoutes(router, organizations);
|
||||
}
|
||||
|
||||
// MARK: Just-in-time provisioning
|
||||
emailDomainRoutes(router, organizations);
|
||||
router.addRelationRoutes(organizations.jit.roles, 'jit/roles', { isPaginationOptional: true });
|
||||
router.addRelationRoutes(organizations.jit.ssoConnectors, 'jit/sso-connectors', {
|
||||
isPaginationOptional: true,
|
||||
});
|
||||
userRoutes(router, organizations);
|
||||
applicationRoutes(router, organizations);
|
||||
jitRoutes(router, organizations);
|
||||
|
||||
// MARK: Mount sub-routes
|
||||
organizationRoleRoutes(...args);
|
||||
|
|
18
packages/core/src/routes/organization/jit/index.ts
Normal file
18
packages/core/src/routes/organization/jit/index.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { type OrganizationKeys, type CreateOrganization, type Organization } from '@logto/schemas';
|
||||
|
||||
import type OrganizationQueries from '#src/queries/organization/index.js';
|
||||
import type SchemaRouter from '#src/utils/SchemaRouter.js';
|
||||
|
||||
import emailDomainRoutes from './email-domains.js';
|
||||
|
||||
/** Mounts the jit-related routes on the organization router. */
|
||||
export default function jitRoutes(
|
||||
router: SchemaRouter<OrganizationKeys, CreateOrganization, Organization>,
|
||||
organizations: OrganizationQueries
|
||||
) {
|
||||
emailDomainRoutes(router, organizations);
|
||||
router.addRelationRoutes(organizations.jit.roles, 'jit/roles', { isPaginationOptional: true });
|
||||
router.addRelationRoutes(organizations.jit.ssoConnectors, 'jit/sso-connectors', {
|
||||
isPaginationOptional: true,
|
||||
});
|
||||
}
|
79
packages/core/src/routes/organization/user/index.ts
Normal file
79
packages/core/src/routes/organization/user/index.ts
Normal file
|
@ -0,0 +1,79 @@
|
|||
import {
|
||||
type OrganizationKeys,
|
||||
type CreateOrganization,
|
||||
type Organization,
|
||||
userWithOrganizationRolesGuard,
|
||||
} from '@logto/schemas';
|
||||
import { z } from 'zod';
|
||||
|
||||
import koaGuard from '#src/middleware/koa-guard.js';
|
||||
import koaPagination from '#src/middleware/koa-pagination.js';
|
||||
import type OrganizationQueries from '#src/queries/organization/index.js';
|
||||
import { userSearchKeys } from '#src/queries/user.js';
|
||||
import type SchemaRouter from '#src/utils/SchemaRouter.js';
|
||||
import { parseSearchOptions } from '#src/utils/search.js';
|
||||
|
||||
import userRoleRelationRoutes from './role-relations.js';
|
||||
|
||||
/** Mounts the user-related routes on the organization router. */
|
||||
export default function userRoutes(
|
||||
router: SchemaRouter<OrganizationKeys, CreateOrganization, Organization>,
|
||||
organizations: OrganizationQueries
|
||||
) {
|
||||
router.addRelationRoutes(organizations.relations.users, undefined, {
|
||||
disabled: { get: true },
|
||||
hookEvent: 'Organization.Membership.Updated',
|
||||
});
|
||||
|
||||
router.get(
|
||||
'/:id/users',
|
||||
koaPagination(),
|
||||
koaGuard({
|
||||
query: z.object({ q: z.string().optional() }),
|
||||
params: z.object({ id: z.string().min(1) }),
|
||||
response: userWithOrganizationRolesGuard.array(),
|
||||
status: [200, 404],
|
||||
}),
|
||||
async (ctx, next) => {
|
||||
const search = parseSearchOptions(userSearchKeys, ctx.guard.query);
|
||||
|
||||
const [totalCount, entities] = await organizations.relations.users.getUsersByOrganizationId(
|
||||
ctx.guard.params.id,
|
||||
ctx.pagination,
|
||||
search
|
||||
);
|
||||
|
||||
ctx.pagination.totalCount = totalCount;
|
||||
ctx.body = entities;
|
||||
|
||||
return next();
|
||||
}
|
||||
);
|
||||
|
||||
router.post(
|
||||
'/:id/users/roles',
|
||||
koaGuard({
|
||||
params: z.object({ id: z.string().min(1) }),
|
||||
body: z.object({
|
||||
userIds: z.string().min(1).array().nonempty(),
|
||||
organizationRoleIds: z.string().min(1).array().nonempty(),
|
||||
}),
|
||||
status: [201, 422],
|
||||
}),
|
||||
async (ctx, next) => {
|
||||
const { id } = ctx.guard.params;
|
||||
const { userIds, organizationRoleIds } = ctx.guard.body;
|
||||
|
||||
await organizations.relations.usersRoles.insert(
|
||||
...organizationRoleIds.flatMap((roleId) =>
|
||||
userIds.map((userId) => ({ organizationId: id, organizationRoleId: roleId, userId }))
|
||||
)
|
||||
);
|
||||
|
||||
ctx.status = 201;
|
||||
return next();
|
||||
}
|
||||
);
|
||||
|
||||
userRoleRelationRoutes(router, organizations);
|
||||
}
|
|
@ -1,18 +1,23 @@
|
|||
import { OrganizationRoles, OrganizationScopes } from '@logto/schemas';
|
||||
import type Router from 'koa-router';
|
||||
import {
|
||||
type CreateOrganization,
|
||||
type Organization,
|
||||
type OrganizationKeys,
|
||||
OrganizationRoles,
|
||||
OrganizationScopes,
|
||||
} from '@logto/schemas';
|
||||
import { z } from 'zod';
|
||||
|
||||
import RequestError from '#src/errors/RequestError/index.js';
|
||||
import koaGuard from '#src/middleware/koa-guard.js';
|
||||
import { type WithHookContext } from '#src/middleware/koa-management-api-hooks.js';
|
||||
import koaPagination from '#src/middleware/koa-pagination.js';
|
||||
import type OrganizationQueries from '#src/queries/organization/index.js';
|
||||
import type SchemaRouter from '#src/utils/SchemaRouter.js';
|
||||
|
||||
// Manually add these routes since I don't want to over-engineer the `SchemaRouter`.
|
||||
// Update: Now we also have "organization - organization role - application" relations. Consider
|
||||
// extracting the common logic to a class once we have one more relation like this.
|
||||
export default function userRoleRelationRoutes(
|
||||
router: Router<unknown, WithHookContext>,
|
||||
router: SchemaRouter<OrganizationKeys, CreateOrganization, Organization>,
|
||||
organizations: OrganizationQueries
|
||||
) {
|
||||
const params = Object.freeze({ id: z.string().min(1), userId: z.string().min(1) } as const);
|
Loading…
Add table
Reference in a new issue