Create query runner context from request context instead of schema builder internal context (#16858)

## Context
This PR refactors how authentication context is handled in the GraphQL
resolver layer. Previously, the auth context was baked into the schema
builder context at schema creation time. Now, the auth context is
extracted from the request at query execution time, enabling better
schema caching.

## Implementation
- Removed user-specific data from schema cache key: The schema cache key
no longer includes userId and apiKeyId, allowing the same schema to be
shared across all users in a workspace
- Cache key simplified from
${workspaceId}-${userId}-${apiKeyId}-${url}-${cacheVersion} to
${workspaceId}-${url}-${cacheVersion}
- Removed authContext from WorkspaceSchemaBuilderContext: The schema
builder context is now purely about object/field metadata, not about who
is accessing it
- Created createQueryRunnerContext utility: A new helper that combines
the schema builder context with the auth context from the actual request
- Updated all resolver factories: All 15 resolver factories now use
createQueryRunnerContext to build the full context needed by query
runners at execution time

## Benefits
- Improved cache efficiency: GraphQL schemas are now cached per
workspace rather than per user, significantly reducing memory usage and
schema regeneration
- Cleaner separation of concerns: Schema building (metadata-driven) is
now separate from query execution (auth-driven)
- Fresh auth context: Auth context is always retrieved from the current
request, ensuring it reflects the latest state
- Reduced complexity: Simplified the schema creation path by removing
unnecessary auth checks

## Next steps
- Introduce a hash in redis and expose it in the req object so the yoga
patch can access the hash and use it during its own cache key
computation. This way we will be able to invalidate the local cache in
yoga graphql schema generation without restarting pods.
This commit is contained in:
Weiko
2025-12-30 15:16:57 +01:00
committed by GitHub
parent e3ffdb0c2b
commit ab95437065
22 changed files with 264 additions and 199 deletions
@@ -1,5 +1,5 @@
diff --git a/dist/cjs/index.js b/dist/cjs/index.js
index 1684394..32602b3 100644
index 1684394..dd0773d 100644
--- a/dist/cjs/index.js
+++ b/dist/cjs/index.js
@@ -3,10 +3,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
@@ -26,18 +26,16 @@ index 1684394..32602b3 100644
const app = this.httpAdapterHost.httpAdapter.getInstance();
preStartHook?.(app);
// nest's logger doesnt have the info method
@@ -42,6 +46,47 @@ class AbstractYogaDriver extends graphql_2.AbstractGraphQLDriver {
@@ -42,6 +46,45 @@ class AbstractYogaDriver extends graphql_2.AbstractGraphQLDriver {
}
const yoga = (0, graphql_yoga_1.createYoga)({
...options,
+ schema: async (request) => {
+ const workspaceId = request.req.workspace?.id ?? 'anonymous'
+ const workspaceCacheVersion = request.req.workspaceMetadataVersion ?? '0'
+ const workspaceUserId = request.req.user?.id ?? 'anonymous'
+ const apiKeyId = request.req.apiKey?.id ?? 'no-api-key'
+ const url = request.req.baseUrl
+
+ const cacheKey = `${workspaceId}-${workspaceUserId}-${apiKeyId}-${url}-${workspaceCacheVersion}`
+ const cacheKey = `${workspaceId}-${url}-${workspaceCacheVersion}`
+
+ if(this.schemaCache.has(cacheKey)) {
+ return this.schemaCache.get(cacheKey)
@@ -62,7 +60,7 @@ index 1684394..32602b3 100644
+ });
+
+ for (const key of this.schemaCache.keys()) {
+ if (key.startsWith(`${workspaceId}-${workspaceUserId}-${url}-`)) {
+ if (key.startsWith(`${workspaceId}-${url}-`)) {
+ this.schemaCache.delete(key);
+ }
+ }
@@ -74,7 +72,7 @@ index 1684394..32602b3 100644
graphqlEndpoint: options.path,
// disable logging by default
// however, if `true` use nest logger
@@ -54,11 +98,52 @@ class AbstractYogaDriver extends graphql_2.AbstractGraphQLDriver {
@@ -54,11 +97,50 @@ class AbstractYogaDriver extends graphql_2.AbstractGraphQLDriver {
this.yoga = yoga;
app.use(yoga.graphqlEndpoint, (req, res) => yoga(req, res, { req, res }));
}
@@ -87,11 +85,9 @@ index 1684394..32602b3 100644
+ schema: async (request) => {
+ const workspaceId = request.req.workspace?.id ?? 'anonymous'
+ const workspaceCacheVersion = request.req.workspaceMetadataVersion ?? '0'
+ const workspaceUserId = request.req.user?.id ?? 'anonymous'
+ const apiKeyId = request.req.apiKey?.id ?? 'no-api-key'
+ const url = request.req.baseUrl
+
+ const cacheKey = `${workspaceId}-${workspaceUserId}-${apiKeyId}-${url}-${workspaceCacheVersion}`
+ const cacheKey = `${workspaceId}-${url}-${workspaceCacheVersion}`
+
+ if(this.schemaCache.has(cacheKey)) {
+ return this.schemaCache.get(cacheKey)
@@ -116,7 +112,7 @@ index 1684394..32602b3 100644
+ });
+
+ for (const key of this.schemaCache.keys()) {
+ if (key.startsWith(`${workspaceId}-${workspaceUserId}-${url}-`)) {
+ if (key.startsWith(`${workspaceId}-${url}-`)) {
+ this.schemaCache.delete(key);
+ }
+ }
@@ -129,7 +125,7 @@ index 1684394..32602b3 100644
// disable logging by default
// however, if `true` use fastify logger
diff --git a/dist/esm/index.js b/dist/esm/index.js
index 7068c51..b8cbf9e 100644
index 7068c51..8494b69 100644
--- a/dist/esm/index.js
+++ b/dist/esm/index.js
@@ -1,9 +1,13 @@
@@ -158,18 +154,16 @@ index 7068c51..b8cbf9e 100644
const app = this.httpAdapterHost.httpAdapter.getInstance();
preStartHook?.(app);
// nest's logger doesnt have the info method
@@ -39,6 +43,47 @@ export class AbstractYogaDriver extends AbstractGraphQLDriver {
@@ -39,6 +43,45 @@ export class AbstractYogaDriver extends AbstractGraphQLDriver {
}
const yoga = createYoga({
...options,
+ schema: async (request) => {
+ const workspaceId = request.req.workspace?.id ?? 'anonymous'
+ const workspaceCacheVersion = request.req.workspaceMetadataVersion ?? '0'
+ const workspaceUserId = request.req.user?.id ?? 'anonymous'
+ const apiKeyId = request.req.apiKey?.id ?? 'no-api-key'
+ const url = request.req.baseUrl
+
+ const cacheKey = `${workspaceId}-${workspaceUserId}-${apiKeyId}-${url}-${workspaceCacheVersion}`
+ const cacheKey = `${workspaceId}-${url}-${workspaceCacheVersion}`
+
+ if (this.schemaCache.has(cacheKey)) {
+ return this.schemaCache.get(cacheKey)
@@ -194,7 +188,7 @@ index 7068c51..b8cbf9e 100644
+ });
+
+ for (const key of this.schemaCache.keys()) {
+ if (key.startsWith(`${workspaceId}-${workspaceUserId}-${url}-`)) {
+ if (key.startsWith(`${workspaceId}-${url}-`)) {
+ this.schemaCache.delete(key);
+ }
+ }
@@ -206,7 +200,7 @@ index 7068c51..b8cbf9e 100644
graphqlEndpoint: options.path,
// disable logging by default
// however, if `true` use nest logger
@@ -51,11 +95,52 @@ export class AbstractYogaDriver extends AbstractGraphQLDriver {
@@ -51,11 +94,50 @@ export class AbstractYogaDriver extends AbstractGraphQLDriver {
this.yoga = yoga;
app.use(yoga.graphqlEndpoint, (req, res) => yoga(req, res, { req, res }));
}
@@ -219,11 +213,9 @@ index 7068c51..b8cbf9e 100644
+ schema: async (request) => {
+ const workspaceId = request.req.workspace?.id ?? 'anonymous'
+ const workspaceCacheVersion = request.req.workspaceMetadataVersion ?? '0'
+ const workspaceUserId = request.req.user?.id ?? 'anonymous'
+ const apiKeyId = request.req.apiKey?.id ?? 'no-api-key'
+ const url = request.req.baseUrl
+
+ const cacheKey = `${workspaceId}-${workspaceUserId}-${apiKeyId}-${url}-${workspaceCacheVersion}`
+ const cacheKey = `${workspaceId}-${url}-${workspaceCacheVersion}`
+
+ if (this.schemaCache.has(cacheKey)) {
+ return this.schemaCache.get(cacheKey)
@@ -248,7 +240,7 @@ index 7068c51..b8cbf9e 100644
+ });
+
+ for (const key of this.schemaCache.keys()) {
+ if (key.startsWith(`${workspaceId}-${workspaceUserId}-${url}-`)) {
+ if (key.startsWith(`${workspaceId}-${url}-`)) {
+ this.schemaCache.delete(key);
+ }
+ }
@@ -338,7 +330,7 @@ index 2c6a965..fd86dac 100644
}): void;
subscriptionWithFilter<TPayload, TVariables, TContext>(instanceRef: unknown, filterFn: (payload: TPayload, variables: TVariables, context: TContext) => boolean | Promise<boolean>, createSubscribeContext: Function): (args_0: TPayload, args_1: TVariables, args_2: TContext) => Promise<import("graphql-yoga").Repeater<TPayload, void, unknown>>;
diff --git a/src/index.ts b/src/index.ts
index ce142f6..10e17d2 100644
index ce142f6..d3e7bab 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,9 +1,10 @@
@@ -413,18 +405,16 @@ index ce142f6..10e17d2 100644
{ preStartHook }: { preStartHook?: (app: Express) => void } = {},
) {
const app: Express = this.httpAdapterHost.httpAdapter.getInstance();
@@ -98,6 +109,41 @@ export abstract class AbstractYogaDriver<
@@ -98,6 +109,39 @@ export abstract class AbstractYogaDriver<
const yoga = createYoga<YogaDriverServerContext<'express'>>({
...options,
+ schema: async request => {
+ const workspaceId = request.req.workspace.id
+ const workspaceCacheVersion = request.req.workspaceMetadataVersion
+ const workspaceUserId = request.req.user?.id ?? 'anonymous'
+ const apiKeyId = request.req.apiKey?.id ?? 'no-api-key'
+ const url = request.req.baseUrl
+
+ const cacheKey = `${workspaceId}-${workspaceUserId}-${apiKeyId}-${url}-${workspaceCacheVersion}`
+ const cacheKey = `${workspaceId}-${url}-${workspaceCacheVersion}`
+
+ if (this.schemaCache.has(cacheKey)) {
+ return this.schemaCache.get(cacheKey)
@@ -455,7 +445,7 @@ index ce142f6..10e17d2 100644
graphqlEndpoint: options.path,
// disable logging by default
// however, if `true` use nest logger
@@ -105,8 +150,8 @@ export abstract class AbstractYogaDriver<
@@ -105,8 +149,8 @@ export abstract class AbstractYogaDriver<
options.logging == null
? false
: options.logging
@@ -466,7 +456,7 @@ index ce142f6..10e17d2 100644
});
this.yoga = yoga as YogaDriverServerInstance<Platform>;
@@ -115,7 +160,7 @@ export abstract class AbstractYogaDriver<
@@ -115,7 +159,7 @@ export abstract class AbstractYogaDriver<
}
protected registerFastify(
@@ -475,18 +465,16 @@ index ce142f6..10e17d2 100644
{ preStartHook }: { preStartHook?: (app: FastifyInstance) => void } = {},
) {
const app: FastifyInstance = this.httpAdapterHost.httpAdapter.getInstance();
@@ -124,6 +169,41 @@ export abstract class AbstractYogaDriver<
@@ -124,6 +168,39 @@ export abstract class AbstractYogaDriver<
const yoga = createYoga<YogaDriverServerContext<'fastify'>>({
...options,
+ schema: async request => {
+ const workspaceId = request.req.workspace.id
+ const workspaceCacheVersion = request.req.workspaceMetadataVersion
+ const workspaceUserId = request.req.user?.id ?? 'anonymous'
+ const apiKeyId = request.req.apiKey?.id ?? 'no-api-key'
+ const url = request.req.baseUrl
+
+ const cacheKey = `${workspaceId}-${workspaceUserId}-${apiKeyId}-${url}-${workspaceCacheVersion}`
+ const cacheKey = `${workspaceId}-${url}-${workspaceCacheVersion}`
+
+ if (this.schemaCache.has(cacheKey)) {
+ return this.schemaCache.get(cacheKey)
@@ -517,7 +505,7 @@ index ce142f6..10e17d2 100644
graphqlEndpoint: options.path,
// disable logging by default
// however, if `true` use fastify logger
@@ -191,8 +270,8 @@ export class YogaDriver<
@@ -191,8 +268,8 @@ export class YogaDriver<
const config: SubscriptionConfig =
options.subscriptions === true
? {
@@ -14,11 +14,11 @@ import {
type YogaInitialContext,
} from 'graphql-yoga';
import { JsonWebTokenError, TokenExpiredError } from 'jsonwebtoken';
import { isDefined } from 'twenty-shared/utils';
import { NodeEnvironment } from 'src/engine/core-modules/twenty-config/interfaces/node-environment.interface';
import { WorkspaceSchemaFactory } from 'src/engine/api/graphql/workspace-schema.factory';
import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
import {
ApiConfig,
Billing,
@@ -107,31 +107,14 @@ export class GraphQLConfigService
],
},
conditionalSchema: async (context) => {
let user: UserEntity | null | undefined;
let workspace: WorkspaceEntity | undefined;
const { workspace, user } = context.req;
try {
const {
user,
workspace,
apiKey,
application,
workspaceMemberId,
userWorkspaceId,
} = context.req;
if (!workspace) {
if (!isDefined(workspace)) {
return new GraphQLSchema({});
}
return await this.createSchema(context, {
user,
workspace,
apiKey,
application,
workspaceMemberId,
userWorkspaceId,
});
return await this.createSchema(context, workspace);
} catch (error) {
if (error instanceof UnauthorizedException) {
throw new GraphQLError('Unauthenticated', {
@@ -161,7 +144,7 @@ export class GraphQLConfigService
throw handleExceptionAndConvertToGraphQLError(
error,
this.exceptionHandlerService,
user
isDefined(user)
? {
id: user.id,
email: user.email,
@@ -169,7 +152,7 @@ export class GraphQLConfigService
lastName: user.lastName,
}
: undefined,
workspace
isDefined(workspace)
? {
id: workspace.id,
displayName: workspace.displayName,
@@ -197,7 +180,7 @@ export class GraphQLConfigService
async createSchema(
context: YogaDriverServerContext<'express'> & YogaInitialContext,
data: AuthContext,
workspace: WorkspaceEntity,
): Promise<GraphQLSchemaWithContext<YogaDriverServerContext<'express'>>> {
// Create a new contextId for each request
const contextId = ContextIdFactory.create();
@@ -216,6 +199,6 @@ export class GraphQLConfigService
},
);
return await workspaceFactory.createGraphQLSchema(data);
return await workspaceFactory.createGraphQLSchema(workspace);
}
}
@@ -14,6 +14,7 @@ import { CommonCreateManyQueryRunnerService } from 'src/engine/api/common/common
import { ObjectRecordsToGraphqlConnectionHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/object-records-to-graphql-connection.helper';
import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util';
import { RESOLVER_METHOD_NAMES } from 'src/engine/api/graphql/workspace-resolver-builder/constants/resolver-method-names';
import { createQueryRunnerContext } from 'src/engine/api/graphql/workspace-resolver-builder/utils/create-query-runner-context.util';
@Injectable()
export class CreateManyResolverFactory
@@ -30,26 +31,31 @@ export class CreateManyResolverFactory
): Resolver<CreateManyResolverArgs> {
const internalContext = context;
return async (_source, args, _context, info) => {
return async (_source, args, requestContext, info) => {
const selectedFields = graphqlFields(info);
const resolverContext = createQueryRunnerContext({
workspaceSchemaBuilderContext: internalContext,
request: requestContext.req,
});
try {
const records = await this.commonCreateManyQueryRunnerService.execute(
{ ...args, selectedFields },
internalContext,
resolverContext,
);
const typeORMObjectRecordsParser =
new ObjectRecordsToGraphqlConnectionHelper(
internalContext.flatObjectMetadataMaps,
internalContext.flatFieldMetadataMaps,
internalContext.objectIdByNameSingular,
resolverContext.flatObjectMetadataMaps,
resolverContext.flatFieldMetadataMaps,
resolverContext.objectIdByNameSingular,
);
return records.map((record: ObjectRecord) =>
typeORMObjectRecordsParser.processRecord({
objectRecord: record,
objectName: internalContext.flatObjectMetadata.nameSingular,
objectName: resolverContext.flatObjectMetadata.nameSingular,
take: 1,
totalCount: 1,
}),
@@ -13,6 +13,7 @@ import { CommonCreateOneQueryRunnerService } from 'src/engine/api/common/common-
import { ObjectRecordsToGraphqlConnectionHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/object-records-to-graphql-connection.helper';
import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util';
import { RESOLVER_METHOD_NAMES } from 'src/engine/api/graphql/workspace-resolver-builder/constants/resolver-method-names';
import { createQueryRunnerContext } from 'src/engine/api/graphql/workspace-resolver-builder/utils/create-query-runner-context.util';
@Injectable()
export class CreateOneResolverFactory
@@ -29,25 +30,30 @@ export class CreateOneResolverFactory
): Resolver<CreateOneResolverArgs> {
const internalContext = context;
return async (_source, args, _context, info) => {
return async (_source, args, requestContext, info) => {
const selectedFields = graphqlFields(info);
const resolverContext = createQueryRunnerContext({
workspaceSchemaBuilderContext: internalContext,
request: requestContext.req,
});
try {
const record = await this.commonCreateOneQueryRunnerService.execute(
{ ...args, selectedFields },
internalContext,
resolverContext,
);
const typeORMObjectRecordsParser =
new ObjectRecordsToGraphqlConnectionHelper(
internalContext.flatObjectMetadataMaps,
internalContext.flatFieldMetadataMaps,
internalContext.objectIdByNameSingular,
resolverContext.flatObjectMetadataMaps,
resolverContext.flatFieldMetadataMaps,
resolverContext.objectIdByNameSingular,
);
return typeORMObjectRecordsParser.processRecord({
objectRecord: record,
objectName: internalContext.flatObjectMetadata.nameSingular,
objectName: resolverContext.flatObjectMetadata.nameSingular,
take: 1,
totalCount: 1,
});
@@ -14,6 +14,7 @@ import { CommonDeleteManyQueryRunnerService } from 'src/engine/api/common/common
import { ObjectRecordsToGraphqlConnectionHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/object-records-to-graphql-connection.helper';
import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util';
import { RESOLVER_METHOD_NAMES } from 'src/engine/api/graphql/workspace-resolver-builder/constants/resolver-method-names';
import { createQueryRunnerContext } from 'src/engine/api/graphql/workspace-resolver-builder/utils/create-query-runner-context.util';
@Injectable()
export class DeleteManyResolverFactory
@@ -30,26 +31,31 @@ export class DeleteManyResolverFactory
): Resolver<DeleteManyResolverArgs> {
const internalContext = context;
return async (_source, args, _context, info) => {
return async (_source, args, requestContext, info) => {
const selectedFields = graphqlFields(info);
const resolverContext = createQueryRunnerContext({
workspaceSchemaBuilderContext: internalContext,
request: requestContext.req,
});
try {
const records = await this.commonDeleteManyQueryRunnerService.execute(
{ ...args, selectedFields },
internalContext,
resolverContext,
);
const typeORMObjectRecordsParser =
new ObjectRecordsToGraphqlConnectionHelper(
internalContext.flatObjectMetadataMaps,
internalContext.flatFieldMetadataMaps,
internalContext.objectIdByNameSingular,
resolverContext.flatObjectMetadataMaps,
resolverContext.flatFieldMetadataMaps,
resolverContext.objectIdByNameSingular,
);
return records.map((record: ObjectRecord) =>
typeORMObjectRecordsParser.processRecord({
objectRecord: record,
objectName: internalContext.flatObjectMetadata.nameSingular,
objectName: resolverContext.flatObjectMetadata.nameSingular,
take: 1,
totalCount: 1,
}),
@@ -13,6 +13,7 @@ import { CommonDeleteOneQueryRunnerService } from 'src/engine/api/common/common-
import { ObjectRecordsToGraphqlConnectionHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/object-records-to-graphql-connection.helper';
import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util';
import { RESOLVER_METHOD_NAMES } from 'src/engine/api/graphql/workspace-resolver-builder/constants/resolver-method-names';
import { createQueryRunnerContext } from 'src/engine/api/graphql/workspace-resolver-builder/utils/create-query-runner-context.util';
@Injectable()
export class DeleteOneResolverFactory
@@ -29,25 +30,30 @@ export class DeleteOneResolverFactory
): Resolver<DeleteOneResolverArgs> {
const internalContext = context;
return async (_source, args, _context, info) => {
return async (_source, args, requestContext, info) => {
const selectedFields = graphqlFields(info);
const resolverContext = createQueryRunnerContext({
workspaceSchemaBuilderContext: internalContext,
request: requestContext.req,
});
try {
const record = await this.commonDeleteOneQueryRunnerService.execute(
{ ...args, selectedFields },
internalContext,
resolverContext,
);
const typeORMObjectRecordsParser =
new ObjectRecordsToGraphqlConnectionHelper(
internalContext.flatObjectMetadataMaps,
internalContext.flatFieldMetadataMaps,
internalContext.objectIdByNameSingular,
resolverContext.flatObjectMetadataMaps,
resolverContext.flatFieldMetadataMaps,
resolverContext.objectIdByNameSingular,
);
return typeORMObjectRecordsParser.processRecord({
objectRecord: record,
objectName: internalContext.flatObjectMetadata.nameSingular,
objectName: resolverContext.flatObjectMetadata.nameSingular,
take: 1,
totalCount: 1,
});
@@ -14,6 +14,7 @@ import { CommonDestroyManyQueryRunnerService } from 'src/engine/api/common/commo
import { ObjectRecordsToGraphqlConnectionHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/object-records-to-graphql-connection.helper';
import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util';
import { RESOLVER_METHOD_NAMES } from 'src/engine/api/graphql/workspace-resolver-builder/constants/resolver-method-names';
import { createQueryRunnerContext } from 'src/engine/api/graphql/workspace-resolver-builder/utils/create-query-runner-context.util';
@Injectable()
export class DestroyManyResolverFactory
@@ -30,26 +31,31 @@ export class DestroyManyResolverFactory
): Resolver<DestroyManyResolverArgs> {
const internalContext = context;
return async (_source, args, _context, info) => {
return async (_source, args, requestContext, info) => {
const selectedFields = graphqlFields(info);
const resolverContext = createQueryRunnerContext({
workspaceSchemaBuilderContext: internalContext,
request: requestContext.req,
});
try {
const records = await this.commonDestroyManyQueryRunnerService.execute(
{ ...args, selectedFields },
internalContext,
resolverContext,
);
const typeORMObjectRecordsParser =
new ObjectRecordsToGraphqlConnectionHelper(
internalContext.flatObjectMetadataMaps,
internalContext.flatFieldMetadataMaps,
internalContext.objectIdByNameSingular,
resolverContext.flatObjectMetadataMaps,
resolverContext.flatFieldMetadataMaps,
resolverContext.objectIdByNameSingular,
);
return records.map((record: ObjectRecord) =>
typeORMObjectRecordsParser.processRecord({
objectRecord: record,
objectName: internalContext.flatObjectMetadata.nameSingular,
objectName: resolverContext.flatObjectMetadata.nameSingular,
take: 1,
totalCount: 1,
}),
@@ -13,6 +13,7 @@ import { CommonDestroyOneQueryRunnerService } from 'src/engine/api/common/common
import { ObjectRecordsToGraphqlConnectionHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/object-records-to-graphql-connection.helper';
import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util';
import { RESOLVER_METHOD_NAMES } from 'src/engine/api/graphql/workspace-resolver-builder/constants/resolver-method-names';
import { createQueryRunnerContext } from 'src/engine/api/graphql/workspace-resolver-builder/utils/create-query-runner-context.util';
@Injectable()
export class DestroyOneResolverFactory
@@ -29,25 +30,30 @@ export class DestroyOneResolverFactory
): Resolver<DestroyOneResolverArgs> {
const internalContext = context;
return async (_source, args, _context, info) => {
return async (_source, args, requestContext, info) => {
const selectedFields = graphqlFields(info);
const resolverContext = createQueryRunnerContext({
workspaceSchemaBuilderContext: internalContext,
request: requestContext.req,
});
try {
const record = await this.commonDestroyOneQueryRunnerService.execute(
{ ...args, selectedFields },
internalContext,
resolverContext,
);
const typeORMObjectRecordsParser =
new ObjectRecordsToGraphqlConnectionHelper(
internalContext.flatObjectMetadataMaps,
internalContext.flatFieldMetadataMaps,
internalContext.objectIdByNameSingular,
resolverContext.flatObjectMetadataMaps,
resolverContext.flatFieldMetadataMaps,
resolverContext.objectIdByNameSingular,
);
return typeORMObjectRecordsParser.processRecord({
objectRecord: record,
objectName: internalContext.flatObjectMetadata.nameSingular,
objectName: resolverContext.flatObjectMetadata.nameSingular,
take: 1,
totalCount: 1,
});
@@ -14,6 +14,7 @@ import { CommonFindDuplicatesQueryRunnerService } from 'src/engine/api/common/co
import { ObjectRecordsToGraphqlConnectionHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/object-records-to-graphql-connection.helper';
import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util';
import { RESOLVER_METHOD_NAMES } from 'src/engine/api/graphql/workspace-resolver-builder/constants/resolver-method-names';
import { createQueryRunnerContext } from 'src/engine/api/graphql/workspace-resolver-builder/utils/create-query-runner-context.util';
@Injectable()
export class FindDuplicatesResolverFactory
@@ -30,27 +31,32 @@ export class FindDuplicatesResolverFactory
): Resolver<FindDuplicatesResolverArgs> {
const internalContext = context;
return async (_source, args, _context, info) => {
return async (_source, args, requestContext, info) => {
const selectedFields = graphqlFields(info);
const resolverContext = createQueryRunnerContext({
workspaceSchemaBuilderContext: internalContext,
request: requestContext.req,
});
try {
const paginatedDuplicates =
await this.commonFindDuplicatesQueryRunnerService.execute(
{ ...args, selectedFields },
internalContext,
resolverContext,
);
const typeORMObjectRecordsParser =
new ObjectRecordsToGraphqlConnectionHelper(
internalContext.flatObjectMetadataMaps,
internalContext.flatFieldMetadataMaps,
internalContext.objectIdByNameSingular,
resolverContext.flatObjectMetadataMaps,
resolverContext.flatFieldMetadataMaps,
resolverContext.objectIdByNameSingular,
);
return paginatedDuplicates.map((duplicate) =>
typeORMObjectRecordsParser.createConnection({
objectRecords: duplicate.records,
objectName: internalContext.flatObjectMetadata.nameSingular,
objectName: resolverContext.flatObjectMetadata.nameSingular,
take: duplicate.records.length,
totalCount: duplicate.totalCount,
order: [{ id: OrderByDirection.AscNullsFirst }],
@@ -14,6 +14,7 @@ import { CommonFindManyQueryRunnerService } from 'src/engine/api/common/common-q
import { ObjectRecordsToGraphqlConnectionHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/object-records-to-graphql-connection.helper';
import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util';
import { RESOLVER_METHOD_NAMES } from 'src/engine/api/graphql/workspace-resolver-builder/constants/resolver-method-names';
import { createQueryRunnerContext } from 'src/engine/api/graphql/workspace-resolver-builder/utils/create-query-runner-context.util';
@Injectable()
export class FindManyResolverFactory
@@ -30,9 +31,14 @@ export class FindManyResolverFactory
): Resolver<FindManyResolverArgs> {
const internalContext = context;
return async (_source, args, _context, info) => {
return async (_source, args, requestContext, info) => {
const selectedFields = graphqlFields(info);
const resolverContext = createQueryRunnerContext({
workspaceSchemaBuilderContext: internalContext,
request: requestContext.req,
});
try {
const {
records,
@@ -42,21 +48,21 @@ export class FindManyResolverFactory
selectedFieldsResult,
} = await this.commonFindManyQueryRunnerService.execute(
{ ...args, selectedFields },
internalContext,
resolverContext,
);
const typeORMObjectRecordsParser =
new ObjectRecordsToGraphqlConnectionHelper(
internalContext.flatObjectMetadataMaps,
internalContext.flatFieldMetadataMaps,
internalContext.objectIdByNameSingular,
resolverContext.flatObjectMetadataMaps,
resolverContext.flatFieldMetadataMaps,
resolverContext.objectIdByNameSingular,
);
return typeORMObjectRecordsParser.createConnection({
objectRecords: records,
objectRecordsAggregatedValues: aggregatedValues,
selectedAggregatedFields: selectedFieldsResult.aggregate,
objectName: internalContext.flatObjectMetadata.nameSingular,
objectName: resolverContext.flatObjectMetadata.nameSingular,
take: args.first ?? args.last ?? QUERY_MAX_RECORDS,
totalCount,
order: args.orderBy,
@@ -13,6 +13,7 @@ import { CommonFindOneQueryRunnerService } from 'src/engine/api/common/common-qu
import { ObjectRecordsToGraphqlConnectionHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/object-records-to-graphql-connection.helper';
import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util';
import { RESOLVER_METHOD_NAMES } from 'src/engine/api/graphql/workspace-resolver-builder/constants/resolver-method-names';
import { createQueryRunnerContext } from 'src/engine/api/graphql/workspace-resolver-builder/utils/create-query-runner-context.util';
@Injectable()
export class FindOneResolverFactory
@@ -29,25 +30,30 @@ export class FindOneResolverFactory
): Resolver<FindOneResolverArgs> {
const internalContext = context;
return async (_source, args, _context, info) => {
return async (_source, args, requestContext, info) => {
try {
const selectedFields = graphqlFields(info);
const resolverContext = createQueryRunnerContext({
workspaceSchemaBuilderContext: internalContext,
request: requestContext.req,
});
const record = await this.commonFindOneQueryRunnerService.execute(
{ ...args, selectedFields },
internalContext,
resolverContext,
);
const typeORMObjectRecordsParser =
new ObjectRecordsToGraphqlConnectionHelper(
internalContext.flatObjectMetadataMaps,
internalContext.flatFieldMetadataMaps,
internalContext.objectIdByNameSingular,
resolverContext.flatObjectMetadataMaps,
resolverContext.flatFieldMetadataMaps,
resolverContext.objectIdByNameSingular,
);
return typeORMObjectRecordsParser.processRecord({
objectRecord: record,
objectName: internalContext.flatObjectMetadata.nameSingular,
objectName: resolverContext.flatObjectMetadata.nameSingular,
take: 1,
totalCount: 1,
});
@@ -14,6 +14,7 @@ import { CommonGroupByQueryRunnerService } from 'src/engine/api/common/common-qu
import { ObjectRecordsToGraphqlConnectionHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/object-records-to-graphql-connection.helper';
import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util';
import { RESOLVER_METHOD_NAMES } from 'src/engine/api/graphql/workspace-resolver-builder/constants/resolver-method-names';
import { createQueryRunnerContext } from 'src/engine/api/graphql/workspace-resolver-builder/utils/create-query-runner-context.util';
@Injectable()
export class GroupByResolverFactory
@@ -30,9 +31,14 @@ export class GroupByResolverFactory
): Resolver<GroupByResolverArgs> {
const internalContext = context;
return async (_source, args, _context, info) => {
return async (_source, args, requestContext, info) => {
const selectedFields = graphqlFields(info);
const resolverContext = createQueryRunnerContext({
workspaceSchemaBuilderContext: internalContext,
request: requestContext.req,
});
const shouldIncludeRecords =
isDefined(selectedFields.edges?.node) &&
Object.keys(selectedFields.edges?.node).length > 0;
@@ -40,20 +46,20 @@ export class GroupByResolverFactory
try {
const typeORMObjectRecordsParser =
new ObjectRecordsToGraphqlConnectionHelper(
internalContext.flatObjectMetadataMaps,
internalContext.flatFieldMetadataMaps,
internalContext.objectIdByNameSingular,
resolverContext.flatObjectMetadataMaps,
resolverContext.flatFieldMetadataMaps,
resolverContext.objectIdByNameSingular,
);
const results = await this.commonGroupByQueryRunnerService.execute(
{ ...args, selectedFields, includeRecords: shouldIncludeRecords },
internalContext,
resolverContext,
);
const formattedResults = results.map((group) => {
const formattedRecords = typeORMObjectRecordsParser.createConnection({
objectRecords: group.records ?? [],
objectName: internalContext.flatObjectMetadata.nameSingular,
objectName: resolverContext.flatObjectMetadata.nameSingular,
objectRecordsAggregatedValues: {},
selectedAggregatedFields: {},
take: group.records?.length || 0,
@@ -13,6 +13,7 @@ import { CommonMergeManyQueryRunnerService } from 'src/engine/api/common/common-
import { ObjectRecordsToGraphqlConnectionHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/object-records-to-graphql-connection.helper';
import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util';
import { RESOLVER_METHOD_NAMES } from 'src/engine/api/graphql/workspace-resolver-builder/constants/resolver-method-names';
import { createQueryRunnerContext } from 'src/engine/api/graphql/workspace-resolver-builder/utils/create-query-runner-context.util';
@Injectable()
export class MergeManyResolverFactory
@@ -29,25 +30,30 @@ export class MergeManyResolverFactory
): Resolver<MergeManyResolverArgs> {
const internalContext = context;
return async (_source, args, _context, info) => {
return async (_source, args, requestContext, info) => {
const selectedFields = graphqlFields(info);
const resolverContext = createQueryRunnerContext({
workspaceSchemaBuilderContext: internalContext,
request: requestContext.req,
});
try {
const record = await this.commonMergeManyQueryRunnerService.execute(
{ ...args, selectedFields },
internalContext,
resolverContext,
);
const typeORMObjectRecordsParser =
new ObjectRecordsToGraphqlConnectionHelper(
internalContext.flatObjectMetadataMaps,
internalContext.flatFieldMetadataMaps,
internalContext.objectIdByNameSingular,
resolverContext.flatObjectMetadataMaps,
resolverContext.flatFieldMetadataMaps,
resolverContext.objectIdByNameSingular,
);
return typeORMObjectRecordsParser.processRecord({
objectRecord: record,
objectName: internalContext.flatObjectMetadata.nameSingular,
objectName: resolverContext.flatObjectMetadata.nameSingular,
take: 1,
totalCount: 1,
});
@@ -14,6 +14,7 @@ import { CommonRestoreManyQueryRunnerService } from 'src/engine/api/common/commo
import { ObjectRecordsToGraphqlConnectionHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/object-records-to-graphql-connection.helper';
import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util';
import { RESOLVER_METHOD_NAMES } from 'src/engine/api/graphql/workspace-resolver-builder/constants/resolver-method-names';
import { createQueryRunnerContext } from 'src/engine/api/graphql/workspace-resolver-builder/utils/create-query-runner-context.util';
@Injectable()
export class RestoreManyResolverFactory
@@ -30,26 +31,31 @@ export class RestoreManyResolverFactory
): Resolver<RestoreManyResolverArgs> {
const internalContext = context;
return async (_source, args, _context, info) => {
return async (_source, args, requestContext, info) => {
const selectedFields = graphqlFields(info);
const resolverContext = createQueryRunnerContext({
workspaceSchemaBuilderContext: internalContext,
request: requestContext.req,
});
try {
const records = await this.commonRestoreManyQueryRunnerService.execute(
{ ...args, selectedFields },
internalContext,
resolverContext,
);
const typeORMObjectRecordsParser =
new ObjectRecordsToGraphqlConnectionHelper(
internalContext.flatObjectMetadataMaps,
internalContext.flatFieldMetadataMaps,
internalContext.objectIdByNameSingular,
resolverContext.flatObjectMetadataMaps,
resolverContext.flatFieldMetadataMaps,
resolverContext.objectIdByNameSingular,
);
return records.map((record: ObjectRecord) =>
typeORMObjectRecordsParser.processRecord({
objectRecord: record,
objectName: internalContext.flatObjectMetadata.nameSingular,
objectName: resolverContext.flatObjectMetadata.nameSingular,
take: 1,
totalCount: 1,
}),
@@ -13,6 +13,7 @@ import { CommonRestoreOneQueryRunnerService } from 'src/engine/api/common/common
import { ObjectRecordsToGraphqlConnectionHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/object-records-to-graphql-connection.helper';
import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util';
import { RESOLVER_METHOD_NAMES } from 'src/engine/api/graphql/workspace-resolver-builder/constants/resolver-method-names';
import { createQueryRunnerContext } from 'src/engine/api/graphql/workspace-resolver-builder/utils/create-query-runner-context.util';
@Injectable()
export class RestoreOneResolverFactory
@@ -29,25 +30,30 @@ export class RestoreOneResolverFactory
): Resolver<RestoreOneResolverArgs> {
const internalContext = context;
return async (_source, args, _context, info) => {
return async (_source, args, requestContext, info) => {
const selectedFields = graphqlFields(info);
const resolverContext = createQueryRunnerContext({
workspaceSchemaBuilderContext: internalContext,
request: requestContext.req,
});
try {
const record = await this.commonRestoreOneQueryRunnerService.execute(
{ ...args, selectedFields },
internalContext,
resolverContext,
);
const typeORMObjectRecordsParser =
new ObjectRecordsToGraphqlConnectionHelper(
internalContext.flatObjectMetadataMaps,
internalContext.flatFieldMetadataMaps,
internalContext.objectIdByNameSingular,
resolverContext.flatObjectMetadataMaps,
resolverContext.flatFieldMetadataMaps,
resolverContext.objectIdByNameSingular,
);
return typeORMObjectRecordsParser.processRecord({
objectRecord: record,
objectName: internalContext.flatObjectMetadata.nameSingular,
objectName: resolverContext.flatObjectMetadata.nameSingular,
take: 1,
totalCount: 1,
});
@@ -14,6 +14,7 @@ import { CommonUpdateManyQueryRunnerService } from 'src/engine/api/common/common
import { ObjectRecordsToGraphqlConnectionHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/object-records-to-graphql-connection.helper';
import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util';
import { RESOLVER_METHOD_NAMES } from 'src/engine/api/graphql/workspace-resolver-builder/constants/resolver-method-names';
import { createQueryRunnerContext } from 'src/engine/api/graphql/workspace-resolver-builder/utils/create-query-runner-context.util';
@Injectable()
export class UpdateManyResolverFactory
@@ -30,26 +31,31 @@ export class UpdateManyResolverFactory
): Resolver<UpdateManyResolverArgs> {
const internalContext = context;
return async (_source, args, _context, info) => {
return async (_source, args, requestContext, info) => {
const selectedFields = graphqlFields(info);
const resolverContext = createQueryRunnerContext({
workspaceSchemaBuilderContext: internalContext,
request: requestContext.req,
});
try {
const records = await this.commonUpdateManyQueryRunnerService.execute(
{ ...args, selectedFields },
internalContext,
resolverContext,
);
const typeORMObjectRecordsParser =
new ObjectRecordsToGraphqlConnectionHelper(
internalContext.flatObjectMetadataMaps,
internalContext.flatFieldMetadataMaps,
internalContext.objectIdByNameSingular,
resolverContext.flatObjectMetadataMaps,
resolverContext.flatFieldMetadataMaps,
resolverContext.objectIdByNameSingular,
);
return records.map((record: ObjectRecord) =>
typeORMObjectRecordsParser.processRecord({
objectRecord: record,
objectName: internalContext.flatObjectMetadata.nameSingular,
objectName: resolverContext.flatObjectMetadata.nameSingular,
take: 1,
totalCount: 1,
}),
@@ -13,6 +13,7 @@ import { CommonUpdateOneQueryRunnerService } from 'src/engine/api/common/common-
import { ObjectRecordsToGraphqlConnectionHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/object-records-to-graphql-connection.helper';
import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util';
import { RESOLVER_METHOD_NAMES } from 'src/engine/api/graphql/workspace-resolver-builder/constants/resolver-method-names';
import { createQueryRunnerContext } from 'src/engine/api/graphql/workspace-resolver-builder/utils/create-query-runner-context.util';
@Injectable()
export class UpdateOneResolverFactory
@@ -29,25 +30,30 @@ export class UpdateOneResolverFactory
): Resolver<UpdateOneResolverArgs> {
const internalContext = context;
return async (_source, args, _context, info) => {
return async (_source, args, requestContext, info) => {
const selectedFields = graphqlFields(info);
const resolverContext = createQueryRunnerContext({
workspaceSchemaBuilderContext: internalContext,
request: requestContext.req,
});
try {
const record = await this.commonUpdateOneQueryRunnerService.execute(
{ ...args, selectedFields },
internalContext,
resolverContext,
);
const typeORMObjectRecordsParser =
new ObjectRecordsToGraphqlConnectionHelper(
internalContext.flatObjectMetadataMaps,
internalContext.flatFieldMetadataMaps,
internalContext.objectIdByNameSingular,
resolverContext.flatObjectMetadataMaps,
resolverContext.flatFieldMetadataMaps,
resolverContext.objectIdByNameSingular,
);
return typeORMObjectRecordsParser.processRecord({
objectRecord: record,
objectName: internalContext.flatObjectMetadata.nameSingular,
objectName: resolverContext.flatObjectMetadata.nameSingular,
take: 1,
totalCount: 1,
});
@@ -0,0 +1,37 @@
import { type WorkspaceSchemaBuilderContext } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-schema-builder-context.interface';
import { type CommonBaseQueryRunnerContext } from 'src/engine/api/common/types/common-base-query-runner-context.type';
import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
export const createQueryRunnerContext = ({
workspaceSchemaBuilderContext,
request,
}: {
workspaceSchemaBuilderContext: WorkspaceSchemaBuilderContext;
request: AuthContext;
}): CommonBaseQueryRunnerContext => {
const {
user,
workspace,
apiKey,
workspaceMemberId,
userWorkspaceId,
userWorkspace,
authProvider,
impersonationContext,
} = request;
return {
...workspaceSchemaBuilderContext,
authContext: {
user,
workspace,
apiKey,
workspaceMemberId,
userWorkspaceId,
userWorkspace,
authProvider,
impersonationContext,
},
};
};
@@ -12,11 +12,6 @@ import { RestoreManyResolverFactory } from 'src/engine/api/graphql/workspace-res
import { RestoreOneResolverFactory } from 'src/engine/api/graphql/workspace-resolver-builder/factories/restore-one-resolver.factory';
import { UpdateManyResolverFactory } from 'src/engine/api/graphql/workspace-resolver-builder/factories/update-many-resolver.factory';
import { WorkspaceResolverBuilderService } from 'src/engine/api/graphql/workspace-resolver-builder/workspace-resolver-builder.service';
import {
AuthException,
AuthExceptionCode,
} from 'src/engine/core-modules/auth/auth.exception';
import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
@@ -59,7 +54,6 @@ export class WorkspaceResolverFactory {
) {}
async create(
authContext: AuthContext,
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>,
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
objectIdByNameSingular: Record<string, string>,
@@ -90,15 +84,6 @@ export class WorkspaceResolverFactory {
Mutation: {},
};
const workspaceId = authContext.workspace?.id;
if (!workspaceId) {
throw new AuthException(
'Unauthenticated',
AuthExceptionCode.UNAUTHENTICATED,
);
}
for (const flatObjectMetadata of Object.values(
flatObjectMetadataMaps.byId,
).filter(isDefined)) {
@@ -125,7 +110,6 @@ export class WorkspaceResolverFactory {
) {
// @ts-expect-error legacy noImplicitAny
resolvers.Query[resolverName] = resolverFactory.create({
authContext,
flatObjectMetadata,
flatObjectMetadataMaps,
flatFieldMetadataMaps,
@@ -157,7 +141,6 @@ export class WorkspaceResolverFactory {
) {
// @ts-expect-error legacy noImplicitAny
resolvers.Mutation[resolverName] = resolverFactory.create({
authContext,
flatObjectMetadata,
flatObjectMetadataMaps,
flatFieldMetadataMaps,
@@ -1,10 +1,8 @@
import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
export interface WorkspaceSchemaBuilderContext {
authContext: AuthContext;
flatObjectMetadata: FlatObjectMetadata;
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>;
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
@@ -9,11 +9,7 @@ import { ScalarsExplorerService } from 'src/engine/api/graphql/services/scalars-
import { workspaceResolverBuilderMethodNames } from 'src/engine/api/graphql/workspace-resolver-builder/factories/factories';
import { WorkspaceResolverFactory } from 'src/engine/api/graphql/workspace-resolver-builder/workspace-resolver.factory';
import { WorkspaceGraphQLSchemaGenerator } from 'src/engine/api/graphql/workspace-schema-builder/workspace-graphql-schema.factory';
import {
AuthException,
AuthExceptionCode,
} from 'src/engine/core-modules/auth/auth.exception';
import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
import {
FlatEntityMapsException,
@@ -34,33 +30,22 @@ export class WorkspaceSchemaFactory {
private readonly workspaceManyOrAllFlatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
) {}
async createGraphQLSchema(authContext: AuthContext): Promise<GraphQLSchema> {
if (!authContext.workspace?.id) {
return new GraphQLSchema({});
}
async createGraphQLSchema(
workspace: WorkspaceEntity,
): Promise<GraphQLSchema> {
const dataSourcesMetadata =
await this.dataSourceService.getDataSourcesMetadataFromWorkspaceId(
authContext.workspace.id,
workspace.id,
);
if (!dataSourcesMetadata || dataSourcesMetadata.length === 0) {
return new GraphQLSchema({});
}
const workspaceId = authContext.workspace.id;
if (!workspaceId) {
throw new AuthException(
'Unauthenticated',
AuthExceptionCode.UNAUTHENTICATED,
);
}
const { flatObjectMetadataMaps, flatFieldMetadataMaps, flatIndexMaps } =
await this.workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
{
workspaceId,
workspaceId: workspace.id,
flatMapsKeys: [
'flatObjectMetadataMaps',
'flatFieldMetadataMaps',
@@ -84,12 +69,14 @@ export class WorkspaceSchemaFactory {
}
let metadataVersion =
await this.workspaceCacheStorageService.getMetadataVersion(workspaceId);
await this.workspaceCacheStorageService.getMetadataVersion(workspace.id);
if (!isDefined(metadataVersion)) {
metadataVersion = authContext.workspace.metadataVersion ?? 0;
metadataVersion = isDefined(workspace.metadataVersion)
? workspace.metadataVersion
: 0;
await this.workspaceCacheStorageService.setMetadataVersion(
workspaceId,
workspace.id,
metadataVersion,
);
}
@@ -99,12 +86,12 @@ export class WorkspaceSchemaFactory {
);
let typeDefs = await this.workspaceCacheStorageService.getGraphQLTypeDefs(
authContext.workspace.id,
workspace.id,
metadataVersion,
);
let usedScalarNames =
await this.workspaceCacheStorageService.getGraphQLUsedScalarNames(
authContext.workspace.id,
workspace.id,
metadataVersion,
);
@@ -121,19 +108,18 @@ export class WorkspaceSchemaFactory {
typeDefs = printSchema(autoGeneratedSchema);
await this.workspaceCacheStorageService.setGraphQLTypeDefs(
authContext.workspace.id,
workspace.id,
metadataVersion,
typeDefs,
);
await this.workspaceCacheStorageService.setGraphQLUsedScalarNames(
authContext.workspace.id,
workspace.id,
metadataVersion,
usedScalarNames,
);
}
const autoGeneratedResolvers = await this.workspaceResolverFactory.create(
authContext,
flatObjectMetadataMaps,
flatFieldMetadataMaps,
idByNameSingular,
+2 -2
View File
@@ -7659,14 +7659,14 @@ __metadata:
"@graphql-yoga/nestjs@patch:@graphql-yoga/nestjs@2.1.0#./patches/@graphql-yoga+nestjs+2.1.0.patch::locator=twenty-server%40workspace%3Apackages%2Ftwenty-server":
version: 2.1.0
resolution: "@graphql-yoga/nestjs@patch:@graphql-yoga/nestjs@npm%3A2.1.0#./patches/@graphql-yoga+nestjs+2.1.0.patch::version=2.1.0&hash=971f26&locator=twenty-server%40workspace%3Apackages%2Ftwenty-server"
resolution: "@graphql-yoga/nestjs@patch:@graphql-yoga/nestjs@npm%3A2.1.0#./patches/@graphql-yoga+nestjs+2.1.0.patch::version=2.1.0&hash=d6801e&locator=twenty-server%40workspace%3Apackages%2Ftwenty-server"
peerDependencies:
"@nestjs/common": ^10.0.0
"@nestjs/core": ^10.0.0
"@nestjs/graphql": ^12.0.0
graphql: ^15.0.0 || ^16.0.0
graphql-yoga: ^4.0.4
checksum: 10c0/68ebaf195c93a6d31e22f91e5474cfb51675eac44a6c1a2ef7c856539e704dd9e7f8ea7cfd930a0ad37894edbcab6502b642f202d9f31f8f38b36c7d4d5add43
checksum: 10c0/929da1f7265003cb5f43291d6db96e752bd2ac5d81e21fb3af9b245e54e732f5862789d7e5941e893d85255919b4c6a5a37adb16ef6594fab606e8eec742891c
languageName: node
linkType: hard