mirror of
https://github.com/logto-io/logto.git
synced 2025-01-20 21:32:31 -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 { 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.
|
||||
|
|
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 {
|
||||
Organizations,
|
||||
OrganizationRoles,
|
||||
OrganizationScopes,
|
||||
OrganizationRoleScopeRelations,
|
||||
Users,
|
||||
OrganizationUserRelations,
|
||||
OrganizationRoleUserRelations,
|
||||
type OrganizationWithRoles,
|
||||
type UserWithOrganizationRoles,
|
||||
type FeaturedUser,
|
||||
type OrganizationScope,
|
||||
type ResourceScopeEntity,
|
||||
Scopes,
|
||||
OrganizationRoleResourceScopeRelations,
|
||||
Resources,
|
||||
} from '@logto/schemas';
|
||||
import { sql, type CommonQueryMethods } from '@silverhand/slonik';
|
||||
|
||||
import { type SearchOptions, buildSearchSql, expandFields } from '#src/database/utils.js';
|
||||
import RelationQueries, {
|
||||
type GetEntitiesOptions,
|
||||
TwoRelationsQueries,
|
||||
} from '#src/utils/RelationQueries.js';
|
||||
import { conditionalSql, convertToIdentifiers } from '#src/utils/sql.js';
|
||||
import { type GetEntitiesOptions, TwoRelationsQueries } from '#src/utils/RelationQueries.js';
|
||||
import { convertToIdentifiers } from '#src/utils/sql.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