mirror of
https://github.com/logto-io/logto.git
synced 2025-01-27 21:39:16 -05:00
refactor(core): reorg organization queries
This commit is contained in:
parent
ec6f1d39d8
commit
ce911309da
3 changed files with 121 additions and 113 deletions
|
@ -28,7 +28,8 @@ import { TwoRelationsQueries } from '#src/utils/RelationQueries.js';
|
||||||
import SchemaQueries from '#src/utils/SchemaQueries.js';
|
import SchemaQueries from '#src/utils/SchemaQueries.js';
|
||||||
import { conditionalSql, convertToIdentifiers } from '#src/utils/sql.js';
|
import { conditionalSql, convertToIdentifiers } from '#src/utils/sql.js';
|
||||||
|
|
||||||
import { RoleUserRelationQueries, UserRelationQueries } from './relations.js';
|
import { RoleUserRelationQueries } from './role-user-relations.js';
|
||||||
|
import { UserRelationQueries } from './user-relations.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The schema field keys that can be used for searching roles.
|
* The schema field keys that can be used for searching roles.
|
||||||
|
|
117
packages/core/src/queries/organization/role-user-relations.ts
Normal file
117
packages/core/src/queries/organization/role-user-relations.ts
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
import {
|
||||||
|
Organizations,
|
||||||
|
OrganizationRoles,
|
||||||
|
OrganizationScopes,
|
||||||
|
OrganizationRoleScopeRelations,
|
||||||
|
Users,
|
||||||
|
OrganizationRoleUserRelations,
|
||||||
|
type OrganizationScope,
|
||||||
|
type ResourceScopeEntity,
|
||||||
|
Scopes,
|
||||||
|
OrganizationRoleResourceScopeRelations,
|
||||||
|
Resources,
|
||||||
|
} from '@logto/schemas';
|
||||||
|
import { sql, type CommonQueryMethods } from '@silverhand/slonik';
|
||||||
|
|
||||||
|
import RelationQueries from '#src/utils/RelationQueries.js';
|
||||||
|
import { conditionalSql, convertToIdentifiers } from '#src/utils/sql.js';
|
||||||
|
|
||||||
|
export class RoleUserRelationQueries extends RelationQueries<
|
||||||
|
[typeof Organizations, typeof OrganizationRoles, typeof Users]
|
||||||
|
> {
|
||||||
|
constructor(pool: CommonQueryMethods) {
|
||||||
|
super(pool, OrganizationRoleUserRelations.table, Organizations, OrganizationRoles, Users);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get the available scopes of a user in an organization. */
|
||||||
|
async getUserScopes(
|
||||||
|
organizationId: string,
|
||||||
|
userId: string
|
||||||
|
): Promise<readonly OrganizationScope[]> {
|
||||||
|
const { fields } = convertToIdentifiers(OrganizationRoleUserRelations, true);
|
||||||
|
const roleScopeRelations = convertToIdentifiers(OrganizationRoleScopeRelations, true);
|
||||||
|
const scopes = convertToIdentifiers(OrganizationScopes, true);
|
||||||
|
|
||||||
|
return this.pool.any<OrganizationScope>(sql`
|
||||||
|
select distinct on (${scopes.fields.id})
|
||||||
|
${sql.join(Object.values(scopes.fields), sql`, `)}
|
||||||
|
from ${this.table}
|
||||||
|
join ${roleScopeRelations.table}
|
||||||
|
on ${roleScopeRelations.fields.organizationRoleId} = ${fields.organizationRoleId}
|
||||||
|
join ${scopes.table}
|
||||||
|
on ${scopes.fields.id} = ${roleScopeRelations.fields.organizationScopeId}
|
||||||
|
where ${fields.organizationId} = ${organizationId}
|
||||||
|
and ${fields.userId} = ${userId}
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the available resource scopes of a user in all organizations.
|
||||||
|
* If `organizationId` is provided, it will only search in that organization.
|
||||||
|
*/
|
||||||
|
async getUserResourceScopes(
|
||||||
|
userId: string,
|
||||||
|
resourceIndicator: string,
|
||||||
|
organizationId?: string
|
||||||
|
): Promise<readonly ResourceScopeEntity[]> {
|
||||||
|
const { fields } = convertToIdentifiers(OrganizationRoleUserRelations, true);
|
||||||
|
const roleScopeRelations = convertToIdentifiers(OrganizationRoleResourceScopeRelations, true);
|
||||||
|
const scopes = convertToIdentifiers(Scopes, true);
|
||||||
|
const resources = convertToIdentifiers(Resources, true);
|
||||||
|
|
||||||
|
return this.pool.any<ResourceScopeEntity>(sql`
|
||||||
|
select distinct on (${scopes.fields.id})
|
||||||
|
${scopes.fields.id}, ${scopes.fields.name}
|
||||||
|
from ${this.table}
|
||||||
|
join ${roleScopeRelations.table}
|
||||||
|
on ${roleScopeRelations.fields.organizationRoleId} = ${fields.organizationRoleId}
|
||||||
|
join ${scopes.table}
|
||||||
|
on ${scopes.fields.id} = ${roleScopeRelations.fields.scopeId}
|
||||||
|
join ${resources.table}
|
||||||
|
on ${resources.fields.id} = ${scopes.fields.resourceId}
|
||||||
|
where ${fields.userId} = ${userId}
|
||||||
|
and ${resources.fields.indicator} = ${resourceIndicator}
|
||||||
|
${conditionalSql(organizationId, (value) => sql`and ${fields.organizationId} = ${value}`)}
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Replace the roles of a user in an organization. */
|
||||||
|
async replace(organizationId: string, userId: string, roleIds: string[]) {
|
||||||
|
const users = convertToIdentifiers(Users);
|
||||||
|
const relations = convertToIdentifiers(OrganizationRoleUserRelations);
|
||||||
|
|
||||||
|
return this.pool.transaction(async (transaction) => {
|
||||||
|
// Lock user
|
||||||
|
await transaction.query(sql`
|
||||||
|
select id
|
||||||
|
from ${users.table}
|
||||||
|
where ${users.fields.id} = ${userId}
|
||||||
|
for update
|
||||||
|
`);
|
||||||
|
|
||||||
|
// Delete old relations
|
||||||
|
await transaction.query(sql`
|
||||||
|
delete from ${relations.table}
|
||||||
|
where ${relations.fields.userId} = ${userId}
|
||||||
|
and ${relations.fields.organizationId} = ${organizationId}
|
||||||
|
`);
|
||||||
|
|
||||||
|
// Insert new relations
|
||||||
|
if (roleIds.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await transaction.query(sql`
|
||||||
|
insert into ${relations.table} (
|
||||||
|
${relations.fields.userId},
|
||||||
|
${relations.fields.organizationId},
|
||||||
|
${relations.fields.organizationRoleId}
|
||||||
|
)
|
||||||
|
values ${sql.join(
|
||||||
|
roleIds.map((roleId) => sql`(${userId}, ${organizationId}, ${roleId})`),
|
||||||
|
sql`, `
|
||||||
|
)}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,28 +1,18 @@
|
||||||
import {
|
import {
|
||||||
Organizations,
|
Organizations,
|
||||||
OrganizationRoles,
|
OrganizationRoles,
|
||||||
OrganizationScopes,
|
|
||||||
OrganizationRoleScopeRelations,
|
|
||||||
Users,
|
Users,
|
||||||
OrganizationUserRelations,
|
OrganizationUserRelations,
|
||||||
OrganizationRoleUserRelations,
|
OrganizationRoleUserRelations,
|
||||||
type OrganizationWithRoles,
|
type OrganizationWithRoles,
|
||||||
type UserWithOrganizationRoles,
|
type UserWithOrganizationRoles,
|
||||||
type FeaturedUser,
|
type FeaturedUser,
|
||||||
type OrganizationScope,
|
|
||||||
type ResourceScopeEntity,
|
|
||||||
Scopes,
|
|
||||||
OrganizationRoleResourceScopeRelations,
|
|
||||||
Resources,
|
|
||||||
} from '@logto/schemas';
|
} from '@logto/schemas';
|
||||||
import { sql, type CommonQueryMethods } from '@silverhand/slonik';
|
import { sql, type CommonQueryMethods } from '@silverhand/slonik';
|
||||||
|
|
||||||
import { type SearchOptions, buildSearchSql, expandFields } from '#src/database/utils.js';
|
import { type SearchOptions, buildSearchSql, expandFields } from '#src/database/utils.js';
|
||||||
import RelationQueries, {
|
import { type GetEntitiesOptions, TwoRelationsQueries } from '#src/utils/RelationQueries.js';
|
||||||
type GetEntitiesOptions,
|
import { convertToIdentifiers } from '#src/utils/sql.js';
|
||||||
TwoRelationsQueries,
|
|
||||||
} from '#src/utils/RelationQueries.js';
|
|
||||||
import { conditionalSql, convertToIdentifiers } from '#src/utils/sql.js';
|
|
||||||
|
|
||||||
import { type userSearchKeys } from '../user.js';
|
import { type userSearchKeys } from '../user.js';
|
||||||
|
|
||||||
|
@ -170,103 +160,3 @@ export class UserRelationQueries extends TwoRelationsQueries<typeof Organization
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RoleUserRelationQueries extends RelationQueries<
|
|
||||||
[typeof Organizations, typeof OrganizationRoles, typeof Users]
|
|
||||||
> {
|
|
||||||
constructor(pool: CommonQueryMethods) {
|
|
||||||
super(pool, OrganizationRoleUserRelations.table, Organizations, OrganizationRoles, Users);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Get the available scopes of a user in an organization. */
|
|
||||||
async getUserScopes(
|
|
||||||
organizationId: string,
|
|
||||||
userId: string
|
|
||||||
): Promise<readonly OrganizationScope[]> {
|
|
||||||
const { fields } = convertToIdentifiers(OrganizationRoleUserRelations, true);
|
|
||||||
const roleScopeRelations = convertToIdentifiers(OrganizationRoleScopeRelations, true);
|
|
||||||
const scopes = convertToIdentifiers(OrganizationScopes, true);
|
|
||||||
|
|
||||||
return this.pool.any<OrganizationScope>(sql`
|
|
||||||
select distinct on (${scopes.fields.id})
|
|
||||||
${sql.join(Object.values(scopes.fields), sql`, `)}
|
|
||||||
from ${this.table}
|
|
||||||
join ${roleScopeRelations.table}
|
|
||||||
on ${roleScopeRelations.fields.organizationRoleId} = ${fields.organizationRoleId}
|
|
||||||
join ${scopes.table}
|
|
||||||
on ${scopes.fields.id} = ${roleScopeRelations.fields.organizationScopeId}
|
|
||||||
where ${fields.organizationId} = ${organizationId}
|
|
||||||
and ${fields.userId} = ${userId}
|
|
||||||
`);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the available resource scopes of a user in all organizations.
|
|
||||||
* If `organizationId` is provided, it will only search in that organization.
|
|
||||||
*/
|
|
||||||
async getUserResourceScopes(
|
|
||||||
userId: string,
|
|
||||||
resourceIndicator: string,
|
|
||||||
organizationId?: string
|
|
||||||
): Promise<readonly ResourceScopeEntity[]> {
|
|
||||||
const { fields } = convertToIdentifiers(OrganizationRoleUserRelations, true);
|
|
||||||
const roleScopeRelations = convertToIdentifiers(OrganizationRoleResourceScopeRelations, true);
|
|
||||||
const scopes = convertToIdentifiers(Scopes, true);
|
|
||||||
const resources = convertToIdentifiers(Resources, true);
|
|
||||||
|
|
||||||
return this.pool.any<ResourceScopeEntity>(sql`
|
|
||||||
select distinct on (${scopes.fields.id})
|
|
||||||
${scopes.fields.id}, ${scopes.fields.name}
|
|
||||||
from ${this.table}
|
|
||||||
join ${roleScopeRelations.table}
|
|
||||||
on ${roleScopeRelations.fields.organizationRoleId} = ${fields.organizationRoleId}
|
|
||||||
join ${scopes.table}
|
|
||||||
on ${scopes.fields.id} = ${roleScopeRelations.fields.scopeId}
|
|
||||||
join ${resources.table}
|
|
||||||
on ${resources.fields.id} = ${scopes.fields.resourceId}
|
|
||||||
where ${fields.userId} = ${userId}
|
|
||||||
and ${resources.fields.indicator} = ${resourceIndicator}
|
|
||||||
${conditionalSql(organizationId, (value) => sql`and ${fields.organizationId} = ${value}`)}
|
|
||||||
`);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Replace the roles of a user in an organization. */
|
|
||||||
async replace(organizationId: string, userId: string, roleIds: string[]) {
|
|
||||||
const users = convertToIdentifiers(Users);
|
|
||||||
const relations = convertToIdentifiers(OrganizationRoleUserRelations);
|
|
||||||
|
|
||||||
return this.pool.transaction(async (transaction) => {
|
|
||||||
// Lock user
|
|
||||||
await transaction.query(sql`
|
|
||||||
select id
|
|
||||||
from ${users.table}
|
|
||||||
where ${users.fields.id} = ${userId}
|
|
||||||
for update
|
|
||||||
`);
|
|
||||||
|
|
||||||
// Delete old relations
|
|
||||||
await transaction.query(sql`
|
|
||||||
delete from ${relations.table}
|
|
||||||
where ${relations.fields.userId} = ${userId}
|
|
||||||
and ${relations.fields.organizationId} = ${organizationId}
|
|
||||||
`);
|
|
||||||
|
|
||||||
// Insert new relations
|
|
||||||
if (roleIds.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await transaction.query(sql`
|
|
||||||
insert into ${relations.table} (
|
|
||||||
${relations.fields.userId},
|
|
||||||
${relations.fields.organizationId},
|
|
||||||
${relations.fields.organizationRoleId}
|
|
||||||
)
|
|
||||||
values ${sql.join(
|
|
||||||
roleIds.map((roleId) => sql`(${userId}, ${organizationId}, ${roleId})`),
|
|
||||||
sql`, `
|
|
||||||
)}
|
|
||||||
`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Add table
Reference in a new issue