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:
@@ -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
|
||||
? {
|
||||
|
||||
+8
-25
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+12
-6
@@ -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,
|
||||
}),
|
||||
|
||||
+12
-6
@@ -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,
|
||||
});
|
||||
|
||||
+12
-6
@@ -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,
|
||||
}),
|
||||
|
||||
+12
-6
@@ -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,
|
||||
});
|
||||
|
||||
+12
-6
@@ -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,
|
||||
}),
|
||||
|
||||
+12
-6
@@ -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,
|
||||
});
|
||||
|
||||
+12
-6
@@ -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 }],
|
||||
|
||||
+12
-6
@@ -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,
|
||||
|
||||
+12
-6
@@ -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,
|
||||
});
|
||||
|
||||
+12
-6
@@ -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,
|
||||
|
||||
+12
-6
@@ -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,
|
||||
});
|
||||
|
||||
+12
-6
@@ -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,
|
||||
}),
|
||||
|
||||
+12
-6
@@ -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,
|
||||
});
|
||||
|
||||
+12
-6
@@ -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,
|
||||
}),
|
||||
|
||||
+12
-6
@@ -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,
|
||||
});
|
||||
|
||||
+37
@@ -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,
|
||||
},
|
||||
};
|
||||
};
|
||||
-17
@@ -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,
|
||||
|
||||
-2
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user