Fix message channel processing (#12021)
Several users have complained about not being able to read their emails anymore. This is because the find-messages post query hook is expecting ObjectRecord[] as an input but is actually getting a graphql Connection Typing was wrong. This PR fixes the typing and make sure the post query hook always get an ObjectRecord[]
This commit is contained in:
@ -24,4 +24,5 @@ export enum GraphqlQueryRunnerExceptionCode {
|
||||
RELATION_TARGET_OBJECT_METADATA_NOT_FOUND = 'RELATION_TARGET_OBJECT_METADATA_NOT_FOUND',
|
||||
NOT_IMPLEMENTED = 'NOT_IMPLEMENTED',
|
||||
OBJECT_METADATA_COLLECTION_NOT_FOUND = 'OBJECT_METADATA_COLLECTION_NOT_FOUND',
|
||||
INVALID_POST_HOOK_PAYLOAD = 'INVALID_POST_HOOK_PAYLOAD',
|
||||
}
|
||||
|
||||
@ -171,20 +171,14 @@ export abstract class GraphqlQueryBaseResolverService<
|
||||
options.objectMetadataMaps,
|
||||
);
|
||||
|
||||
const resultWithGettersArray = Array.isArray(resultWithGetters)
|
||||
? resultWithGetters
|
||||
: [resultWithGetters];
|
||||
|
||||
await this.workspaceQueryHookService.executePostQueryHooks(
|
||||
authContext,
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
operationName,
|
||||
resultWithGettersArray,
|
||||
resultWithGetters,
|
||||
);
|
||||
|
||||
return Array.isArray(resultWithGetters)
|
||||
? resultWithGettersArray
|
||||
: resultWithGettersArray[0];
|
||||
return resultWithGetters;
|
||||
} catch (error) {
|
||||
workspaceQueryRunnerGraphqlApiExceptionHandler(error, options);
|
||||
}
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import { QueryResultFieldValue } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/interfaces/query-result-field-value';
|
||||
import { ResolverArgs } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
|
||||
|
||||
import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
|
||||
export interface WorkspaceQueryHookInstance {
|
||||
export interface WorkspacePreQueryHookInstance {
|
||||
execute(
|
||||
authContext: AuthContext,
|
||||
objectName: string,
|
||||
@ -10,10 +11,10 @@ export interface WorkspaceQueryHookInstance {
|
||||
): Promise<ResolverArgs>;
|
||||
}
|
||||
|
||||
export interface WorkspaceQueryPostHookInstance {
|
||||
export interface WorkspacePostQueryHookInstance {
|
||||
execute(
|
||||
authContext: AuthContext,
|
||||
objectName: string,
|
||||
payload: unknown[],
|
||||
payload: QueryResultFieldValue,
|
||||
): Promise<void>;
|
||||
}
|
||||
|
||||
@ -4,7 +4,10 @@ import { Module } from '@nestjs/core/injector/module';
|
||||
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { WorkspaceQueryHookInstance } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/interfaces/workspace-query-hook.interface';
|
||||
import {
|
||||
WorkspacePostQueryHookInstance,
|
||||
WorkspacePreQueryHookInstance,
|
||||
} from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/interfaces/workspace-query-hook.interface';
|
||||
import { WorkspaceResolverBuilderMethodNames } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
|
||||
|
||||
import { WorkspaceQueryHookKey } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/decorators/workspace-query-hook.decorator';
|
||||
@ -19,16 +22,16 @@ interface WorkspaceQueryHookData<T> {
|
||||
export class WorkspaceQueryHookStorage {
|
||||
private preHookInstances = new Map<
|
||||
WorkspaceQueryHookKey,
|
||||
WorkspaceQueryHookData<WorkspaceQueryHookInstance>[]
|
||||
WorkspaceQueryHookData<WorkspacePreQueryHookInstance>[]
|
||||
>();
|
||||
private postHookInstances = new Map<
|
||||
WorkspaceQueryHookKey,
|
||||
WorkspaceQueryHookData<WorkspaceQueryHookInstance>[]
|
||||
WorkspaceQueryHookData<WorkspacePostQueryHookInstance>[]
|
||||
>();
|
||||
|
||||
registerWorkspaceQueryPreHookInstance(
|
||||
key: WorkspaceQueryHookKey,
|
||||
data: WorkspaceQueryHookData<WorkspaceQueryHookInstance>,
|
||||
data: WorkspaceQueryHookData<WorkspacePreQueryHookInstance>,
|
||||
) {
|
||||
if (!this.preHookInstances.has(key)) {
|
||||
this.preHookInstances.set(key, []);
|
||||
@ -39,11 +42,11 @@ export class WorkspaceQueryHookStorage {
|
||||
|
||||
getWorkspaceQueryPreHookInstances(
|
||||
key: WorkspaceQueryHookKey,
|
||||
): WorkspaceQueryHookData<WorkspaceQueryHookInstance>[] {
|
||||
): WorkspaceQueryHookData<WorkspacePreQueryHookInstance>[] {
|
||||
const methodName = key.split('.')?.[1] as
|
||||
| WorkspaceResolverBuilderMethodNames
|
||||
| undefined;
|
||||
let wildcardInstances: WorkspaceQueryHookData<WorkspaceQueryHookInstance>[] =
|
||||
let wildcardInstances: WorkspaceQueryHookData<WorkspacePreQueryHookInstance>[] =
|
||||
[];
|
||||
|
||||
if (!methodName) {
|
||||
@ -62,9 +65,9 @@ export class WorkspaceQueryHookStorage {
|
||||
return [...wildcardInstances, ...(this.preHookInstances.get(key) ?? [])];
|
||||
}
|
||||
|
||||
registerWorkspaceQueryPostHookInstance(
|
||||
registerWorkspacePostQueryHookInstance(
|
||||
key: WorkspaceQueryHookKey,
|
||||
data: WorkspaceQueryHookData<WorkspaceQueryHookInstance>,
|
||||
data: WorkspaceQueryHookData<WorkspacePostQueryHookInstance>,
|
||||
) {
|
||||
if (!this.postHookInstances.has(key)) {
|
||||
this.postHookInstances.set(key, []);
|
||||
@ -73,13 +76,13 @@ export class WorkspaceQueryHookStorage {
|
||||
this.postHookInstances.get(key)?.push(data);
|
||||
}
|
||||
|
||||
getWorkspaceQueryPostHookInstances(
|
||||
getWorkspacePostQueryHookInstances(
|
||||
key: WorkspaceQueryHookKey,
|
||||
): WorkspaceQueryHookData<WorkspaceQueryHookInstance>[] {
|
||||
): WorkspaceQueryHookData<WorkspacePostQueryHookInstance>[] {
|
||||
const methodName = key.split('.')?.[1] as
|
||||
| WorkspaceResolverBuilderMethodNames
|
||||
| undefined;
|
||||
let wildcardInstances: WorkspaceQueryHookData<WorkspaceQueryHookInstance>[] =
|
||||
let wildcardInstances: WorkspaceQueryHookData<WorkspacePostQueryHookInstance>[] =
|
||||
[];
|
||||
|
||||
if (!methodName) {
|
||||
|
||||
@ -3,8 +3,21 @@ import { DiscoveryService, ModuleRef, createContextId } from '@nestjs/core';
|
||||
import { Injector } from '@nestjs/core/injector/injector';
|
||||
import { Module } from '@nestjs/core/injector/module';
|
||||
|
||||
import { WorkspaceQueryHookInstance } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/interfaces/workspace-query-hook.interface';
|
||||
import { ObjectRecord } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface';
|
||||
import { QueryResultFieldValue } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/interfaces/query-result-field-value';
|
||||
import {
|
||||
WorkspacePostQueryHookInstance,
|
||||
WorkspacePreQueryHookInstance,
|
||||
} from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/interfaces/workspace-query-hook.interface';
|
||||
|
||||
import {
|
||||
GraphqlQueryRunnerException,
|
||||
GraphqlQueryRunnerExceptionCode,
|
||||
} from 'src/engine/api/graphql/graphql-query-runner/errors/graphql-query-runner.exception';
|
||||
import { isQueryResultFieldValueAConnection } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/guards/is-query-result-field-value-a-connection.guard';
|
||||
import { isQueryResultFieldValueANestedRecordArray } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/guards/is-query-result-field-value-a-nested-record-array.guard';
|
||||
import { isQueryResultFieldValueARecordArray } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/guards/is-query-result-field-value-a-record-array.guard';
|
||||
import { isQueryResultFieldValueARecord } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/guards/is-query-result-field-value-a-record.guard';
|
||||
import { WorkspaceQueryHookKey } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/decorators/workspace-query-hook.decorator';
|
||||
import { WorkspaceQueryHookStorage } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/storage/workspace-query-hook.storage';
|
||||
import { WorkspaceQueryHookType } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/types/workspace-query-hook.type';
|
||||
@ -68,12 +81,12 @@ export class WorkspaceQueryHookExplorer implements OnModuleInit {
|
||||
}
|
||||
}
|
||||
|
||||
async handleHook(
|
||||
payload: Parameters<WorkspaceQueryHookInstance['execute']>,
|
||||
async handlePreHook(
|
||||
executeParams: Parameters<WorkspacePreQueryHookInstance['execute']>,
|
||||
instance: object,
|
||||
host: Module,
|
||||
isRequestScoped: boolean,
|
||||
): Promise<ReturnType<WorkspaceQueryHookInstance['execute']>> {
|
||||
): Promise<ReturnType<WorkspacePreQueryHookInstance['execute']>> {
|
||||
const methodName = 'execute';
|
||||
|
||||
if (isRequestScoped) {
|
||||
@ -83,7 +96,7 @@ export class WorkspaceQueryHookExplorer implements OnModuleInit {
|
||||
this.moduleRef.registerRequestByContextId(
|
||||
{
|
||||
req: {
|
||||
workspaceId: payload?.[0].workspace.id,
|
||||
workspaceId: executeParams?.[0].workspace.id,
|
||||
},
|
||||
},
|
||||
contextId,
|
||||
@ -97,9 +110,82 @@ export class WorkspaceQueryHookExplorer implements OnModuleInit {
|
||||
contextId,
|
||||
);
|
||||
|
||||
return contextInstance[methodName].call(contextInstance, ...payload);
|
||||
return contextInstance[methodName].call(
|
||||
contextInstance,
|
||||
...executeParams,
|
||||
);
|
||||
} else {
|
||||
return instance[methodName].call(instance, ...payload);
|
||||
return instance[methodName].call(instance, ...executeParams);
|
||||
}
|
||||
}
|
||||
|
||||
private transformPayload(payload: QueryResultFieldValue): ObjectRecord[] {
|
||||
if (isQueryResultFieldValueAConnection(payload)) {
|
||||
return payload.edges.map((edge) => edge.node);
|
||||
}
|
||||
|
||||
if (isQueryResultFieldValueANestedRecordArray(payload)) {
|
||||
return payload.records;
|
||||
}
|
||||
|
||||
if (isQueryResultFieldValueARecordArray(payload)) {
|
||||
return payload;
|
||||
}
|
||||
|
||||
if (isQueryResultFieldValueARecord(payload)) {
|
||||
return [payload];
|
||||
}
|
||||
|
||||
throw new GraphqlQueryRunnerException(
|
||||
`Unsupported payload type: ${payload}`,
|
||||
GraphqlQueryRunnerExceptionCode.INVALID_POST_HOOK_PAYLOAD,
|
||||
);
|
||||
}
|
||||
|
||||
async handlePostHook(
|
||||
executeParams: Parameters<WorkspacePostQueryHookInstance['execute']>,
|
||||
instance: object,
|
||||
host: Module,
|
||||
isRequestScoped: boolean,
|
||||
): Promise<ReturnType<WorkspacePostQueryHookInstance['execute']>> {
|
||||
const methodName = 'execute';
|
||||
|
||||
const transformedPayload = this.transformPayload(executeParams[2]);
|
||||
|
||||
if (isRequestScoped) {
|
||||
const contextId = createContextId();
|
||||
|
||||
if (this.moduleRef.registerRequestByContextId) {
|
||||
this.moduleRef.registerRequestByContextId(
|
||||
{
|
||||
req: {
|
||||
workspaceId: executeParams?.[0].workspace.id,
|
||||
},
|
||||
},
|
||||
contextId,
|
||||
);
|
||||
}
|
||||
|
||||
const contextInstance = await this.injector.loadPerContext(
|
||||
instance,
|
||||
host,
|
||||
host.providers,
|
||||
contextId,
|
||||
);
|
||||
|
||||
return contextInstance[methodName].call(
|
||||
contextInstance,
|
||||
executeParams[0],
|
||||
executeParams[1],
|
||||
transformedPayload,
|
||||
);
|
||||
} else {
|
||||
return instance[methodName].call(
|
||||
instance,
|
||||
executeParams[0],
|
||||
executeParams[1],
|
||||
transformedPayload,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -115,17 +201,17 @@ export class WorkspaceQueryHookExplorer implements OnModuleInit {
|
||||
this.workspaceQueryHookStorage.registerWorkspaceQueryPreHookInstance(
|
||||
key,
|
||||
{
|
||||
instance: instance as WorkspaceQueryHookInstance,
|
||||
instance: instance as WorkspacePreQueryHookInstance,
|
||||
host,
|
||||
isRequestScoped,
|
||||
},
|
||||
);
|
||||
break;
|
||||
case WorkspaceQueryHookType.PostHook:
|
||||
this.workspaceQueryHookStorage.registerWorkspaceQueryPostHookInstance(
|
||||
this.workspaceQueryHookStorage.registerWorkspacePostQueryHookInstance(
|
||||
key,
|
||||
{
|
||||
instance: instance as WorkspaceQueryHookInstance,
|
||||
instance: instance as WorkspacePostQueryHookInstance,
|
||||
host,
|
||||
isRequestScoped,
|
||||
},
|
||||
|
||||
@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common';
|
||||
|
||||
import merge from 'lodash.merge';
|
||||
|
||||
import { ObjectRecord } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface';
|
||||
import { QueryResultFieldValue } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/interfaces/query-result-field-value';
|
||||
import { WorkspaceResolverBuilderMethodNames } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
|
||||
|
||||
import { WorkspaceQueryHookKey } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/decorators/workspace-query-hook.decorator';
|
||||
@ -37,7 +37,7 @@ export class WorkspaceQueryHookService {
|
||||
|
||||
for (const preHookInstance of preHookInstances) {
|
||||
// Deep merge all return of handleHook into payload before returning it
|
||||
const hookPayload = await this.workspaceQueryHookExplorer.handleHook(
|
||||
const hookPayload = await this.workspaceQueryHookExplorer.handlePreHook(
|
||||
[authContext, objectName, payload],
|
||||
preHookInstance.instance,
|
||||
preHookInstance.host,
|
||||
@ -53,24 +53,23 @@ export class WorkspaceQueryHookService {
|
||||
|
||||
public async executePostQueryHooks<
|
||||
T extends WorkspaceResolverBuilderMethodNames,
|
||||
U extends ObjectRecord = ObjectRecord,
|
||||
>(
|
||||
authContext: AuthContext,
|
||||
// TODO: We should allow wildcard for object name
|
||||
objectName: string,
|
||||
methodName: T,
|
||||
payload: U[],
|
||||
payload: QueryResultFieldValue,
|
||||
): Promise<void> {
|
||||
const key: WorkspaceQueryHookKey = `${objectName}.${methodName}`;
|
||||
const postHookInstances =
|
||||
this.workspaceQueryHookStorage.getWorkspaceQueryPostHookInstances(key);
|
||||
this.workspaceQueryHookStorage.getWorkspacePostQueryHookInstances(key);
|
||||
|
||||
if (!postHookInstances) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const postHookInstance of postHookInstances) {
|
||||
await this.workspaceQueryHookExplorer.handleHook(
|
||||
await this.workspaceQueryHookExplorer.handlePostHook(
|
||||
[authContext, objectName, payload],
|
||||
postHookInstance.instance,
|
||||
postHookInstance.host,
|
||||
|
||||
Reference in New Issue
Block a user