feat(server): search user in workspace (#9870)

This commit is contained in:
forehalo
2025-01-23 08:09:16 +00:00
parent 2088b760bf
commit 85434fe309
4 changed files with 57 additions and 16 deletions

View File

@@ -250,6 +250,13 @@ export const USER_FRIENDLY_ERRORS = {
message: 'Resource not found.',
},
// Input errors
query_too_long: {
type: 'invalid_input',
args: { max: 'number' },
message: ({ max }) => `Query is too long, max length is ${max}.`,
},
// User Errors
user_not_found: {
type: 'resource_not_found',

View File

@@ -21,6 +21,16 @@ export class NotFound extends UserFriendlyError {
super('resource_not_found', 'not_found', message);
}
}
@ObjectType()
class QueryTooLongDataType {
@Field() max!: number
}
export class QueryTooLong extends UserFriendlyError {
constructor(args: QueryTooLongDataType, message?: string | ((args: QueryTooLongDataType) => string)) {
super('invalid_input', 'query_too_long', message, args);
}
}
export class UserNotFound extends UserFriendlyError {
constructor(message?: string) {
@@ -645,6 +655,7 @@ export enum ErrorNames {
INTERNAL_SERVER_ERROR,
TOO_MANY_REQUEST,
NOT_FOUND,
QUERY_TOO_LONG,
USER_NOT_FOUND,
USER_AVATAR_NOT_FOUND,
EMAIL_ALREADY_USED,
@@ -735,5 +746,5 @@ registerEnumType(ErrorNames, {
export const ErrorDataUnionType = createUnionType({
name: 'ErrorDataUnion',
types: () =>
[WrongSignInCredentialsDataType, UnknownOauthProviderDataType, MissingOauthQueryParameterDataType, InvalidEmailDataType, InvalidPasswordLengthDataType, SpaceNotFoundDataType, MemberNotFoundInSpaceDataType, NotInSpaceDataType, AlreadyInSpaceDataType, SpaceAccessDeniedDataType, SpaceOwnerNotFoundDataType, DocNotFoundDataType, DocAccessDeniedDataType, VersionRejectedDataType, InvalidHistoryTimestampDataType, DocHistoryNotFoundDataType, BlobNotFoundDataType, UnsupportedSubscriptionPlanDataType, SubscriptionAlreadyExistsDataType, SubscriptionNotExistsDataType, SameSubscriptionRecurringDataType, SubscriptionPlanNotFoundDataType, CopilotMessageNotFoundDataType, CopilotPromptNotFoundDataType, CopilotProviderSideErrorDataType, RuntimeConfigNotFoundDataType, InvalidRuntimeConfigTypeDataType, InvalidLicenseUpdateParamsDataType, WorkspaceMembersExceedLimitToDowngradeDataType] as const,
[QueryTooLongDataType, WrongSignInCredentialsDataType, UnknownOauthProviderDataType, MissingOauthQueryParameterDataType, InvalidEmailDataType, InvalidPasswordLengthDataType, SpaceNotFoundDataType, MemberNotFoundInSpaceDataType, NotInSpaceDataType, AlreadyInSpaceDataType, SpaceAccessDeniedDataType, SpaceOwnerNotFoundDataType, DocNotFoundDataType, DocAccessDeniedDataType, VersionRejectedDataType, InvalidHistoryTimestampDataType, DocHistoryNotFoundDataType, BlobNotFoundDataType, UnsupportedSubscriptionPlanDataType, SubscriptionAlreadyExistsDataType, SubscriptionNotExistsDataType, SameSubscriptionRecurringDataType, SubscriptionPlanNotFoundDataType, CopilotMessageNotFoundDataType, CopilotPromptNotFoundDataType, CopilotProviderSideErrorDataType, RuntimeConfigNotFoundDataType, InvalidRuntimeConfigTypeDataType, InvalidLicenseUpdateParamsDataType, WorkspaceMembersExceedLimitToDowngradeDataType] as const,
});

View File

@@ -10,7 +10,7 @@ import {
ResolveField,
Resolver,
} from '@nestjs/graphql';
import { PrismaClient, WorkspaceMemberStatus } from '@prisma/client';
import { Prisma, PrismaClient, WorkspaceMemberStatus } from '@prisma/client';
import GraphQLUpload from 'graphql-upload/GraphQLUpload.mjs';
import type { FileUpload } from '../../../base';
@@ -21,6 +21,7 @@ import {
EventEmitter,
InternalServerError,
MemberQuotaExceeded,
QueryTooLong,
RequestMutex,
SpaceAccessDenied,
SpaceNotFound,
@@ -148,25 +149,42 @@ export class WorkspaceResolver {
async members(
@Parent() workspace: WorkspaceType,
@Args('skip', { type: () => Int, nullable: true }) skip?: number,
@Args('take', { type: () => Int, nullable: true }) take?: number
@Args('take', { type: () => Int, nullable: true }) take?: number,
@Args('query', { type: () => String, nullable: true }) query?: string
) {
const data = await this.prisma.workspaceUserPermission.findMany({
const args: Prisma.WorkspaceUserPermissionFindManyArgs = {
where: { workspaceId: workspace.id },
skip,
take: take || 8,
orderBy: [{ createdAt: 'asc' }, { type: 'desc' }],
include: { user: true },
};
if (query) {
if (query.length > 255) {
throw new QueryTooLong({ max: 255 });
}
// @ts-expect-error not null
args.where.user = {
// TODO(@forehalo): case-insensitive search later
OR: [{ name: { contains: query } }, { email: { contains: query } }],
};
}
const data = await this.prisma.workspaceUserPermission.findMany({
...args,
include: {
user: true,
},
});
return data
.filter(({ user }) => !!user)
.map(({ id, accepted, status, type, user }) => ({
...user,
permission: type,
inviteId: id,
accepted,
status,
}));
return data.map(({ id, accepted, status, type, user }) => ({
...user,
permission: type,
inviteId: id,
accepted,
status,
}));
}
@ResolveField(() => WorkspacePageMeta, {

View File

@@ -209,7 +209,7 @@ type EditorType {
name: String!
}
union ErrorDataUnion = AlreadyInSpaceDataType | BlobNotFoundDataType | CopilotMessageNotFoundDataType | CopilotPromptNotFoundDataType | CopilotProviderSideErrorDataType | DocAccessDeniedDataType | DocHistoryNotFoundDataType | DocNotFoundDataType | InvalidEmailDataType | InvalidHistoryTimestampDataType | InvalidLicenseUpdateParamsDataType | InvalidPasswordLengthDataType | InvalidRuntimeConfigTypeDataType | MemberNotFoundInSpaceDataType | MissingOauthQueryParameterDataType | NotInSpaceDataType | RuntimeConfigNotFoundDataType | SameSubscriptionRecurringDataType | SpaceAccessDeniedDataType | SpaceNotFoundDataType | SpaceOwnerNotFoundDataType | SubscriptionAlreadyExistsDataType | SubscriptionNotExistsDataType | SubscriptionPlanNotFoundDataType | UnknownOauthProviderDataType | UnsupportedSubscriptionPlanDataType | VersionRejectedDataType | WorkspaceMembersExceedLimitToDowngradeDataType | WrongSignInCredentialsDataType
union ErrorDataUnion = AlreadyInSpaceDataType | BlobNotFoundDataType | CopilotMessageNotFoundDataType | CopilotPromptNotFoundDataType | CopilotProviderSideErrorDataType | DocAccessDeniedDataType | DocHistoryNotFoundDataType | DocNotFoundDataType | InvalidEmailDataType | InvalidHistoryTimestampDataType | InvalidLicenseUpdateParamsDataType | InvalidPasswordLengthDataType | InvalidRuntimeConfigTypeDataType | MemberNotFoundInSpaceDataType | MissingOauthQueryParameterDataType | NotInSpaceDataType | QueryTooLongDataType | RuntimeConfigNotFoundDataType | SameSubscriptionRecurringDataType | SpaceAccessDeniedDataType | SpaceNotFoundDataType | SpaceOwnerNotFoundDataType | SubscriptionAlreadyExistsDataType | SubscriptionNotExistsDataType | SubscriptionPlanNotFoundDataType | UnknownOauthProviderDataType | UnsupportedSubscriptionPlanDataType | VersionRejectedDataType | WorkspaceMembersExceedLimitToDowngradeDataType | WrongSignInCredentialsDataType
enum ErrorNames {
ACCESS_DENIED
@@ -271,6 +271,7 @@ enum ErrorNames {
OAUTH_STATE_EXPIRED
PAGE_IS_NOT_PUBLIC
PASSWORD_REQUIRED
QUERY_TOO_LONG
RUNTIME_CONFIG_NOT_FOUND
SAME_EMAIL_PROVIDED
SAME_SUBSCRIPTION_RECURRING
@@ -692,6 +693,10 @@ input QueryChatHistoriesInput {
skip: Int
}
type QueryTooLongDataType {
max: Int!
}
type QuotaQueryType {
blobLimit: SafeInt!
copilotActionLimit: SafeInt
@@ -1059,7 +1064,7 @@ type WorkspaceType {
memberCount: Int!
"""Members of workspace"""
members(skip: Int, take: Int): [InviteUserType!]!
members(query: String, skip: Int, take: Int): [InviteUserType!]!
"""Owner of workspace"""
owner: UserType!