Add getters factory for attachements (#4567)
* Add getter factory for attachements * Override guard in test * Add secret in env variables * Return custom message on expiration * Rename to signPayload --------- Co-authored-by: Thomas Trompette <thomast@twenty.com>
This commit is contained in:
@ -1,7 +1,9 @@
|
||||
import { QueryResultGettersFactory } from './query-result-getters.factory';
|
||||
import { RecordPositionFactory } from './record-position.factory';
|
||||
import { QueryRunnerArgsFactory } from './query-runner-args.factory';
|
||||
|
||||
export const workspaceQueryRunnerFactories = [
|
||||
QueryRunnerArgsFactory,
|
||||
RecordPositionFactory,
|
||||
QueryResultGettersFactory,
|
||||
];
|
||||
|
||||
@ -0,0 +1,75 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { addMilliseconds } from 'date-fns';
|
||||
import ms from 'ms';
|
||||
|
||||
import { ObjectMetadataInterface } from 'src/engine-metadata/field-metadata/interfaces/object-metadata.interface';
|
||||
|
||||
import { EnvironmentService } from 'src/engine/integrations/environment/environment.service';
|
||||
import { TokenService } from 'src/engine/modules/auth/services/token.service';
|
||||
|
||||
@Injectable()
|
||||
export class QueryResultGettersFactory {
|
||||
constructor(
|
||||
private readonly tokenService: TokenService,
|
||||
private readonly environmentService: EnvironmentService,
|
||||
) {}
|
||||
|
||||
async create<Result>(
|
||||
result: Result,
|
||||
objectMetadataItem: ObjectMetadataInterface,
|
||||
): Promise<Result> {
|
||||
// TODO: look for file type once implemented
|
||||
switch (objectMetadataItem.nameSingular) {
|
||||
case 'attachment':
|
||||
return this.applyAttachmentGetters(result);
|
||||
default:
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private async applyAttachmentGetters<Result>(
|
||||
attachments: any,
|
||||
): Promise<Result> {
|
||||
if (!attachments || !attachments.edges) {
|
||||
return attachments;
|
||||
}
|
||||
|
||||
const fileTokenExpiresIn = this.environmentService.get(
|
||||
'FILE_TOKEN_EXPIRES_IN',
|
||||
);
|
||||
const secret = this.environmentService.get('FILE_TOKEN_SECRET');
|
||||
|
||||
const mappedEdges = await Promise.all(
|
||||
attachments.edges.map(async (attachment: any) => {
|
||||
if (!attachment.node.id || !attachment?.node?.fullPath) {
|
||||
return attachment;
|
||||
}
|
||||
|
||||
const expirationDate = addMilliseconds(
|
||||
new Date(),
|
||||
ms(fileTokenExpiresIn),
|
||||
);
|
||||
|
||||
const signedPayload = await this.tokenService.encodePayload(
|
||||
{
|
||||
expiration_date: expirationDate,
|
||||
attachment_id: attachment.node.id,
|
||||
},
|
||||
{
|
||||
secret,
|
||||
},
|
||||
);
|
||||
|
||||
attachment.node.fullPath = `${attachment.node.fullPath}?token=${signedPayload}`;
|
||||
|
||||
return attachment;
|
||||
}),
|
||||
);
|
||||
|
||||
return {
|
||||
...attachments,
|
||||
edges: mappedEdges,
|
||||
} as Result;
|
||||
}
|
||||
}
|
||||
@ -5,11 +5,13 @@ import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/works
|
||||
import { WorkspacePreQueryHookModule } from 'src/engine/api/graphql/workspace-query-runner/workspace-pre-query-hook/workspace-pre-query-hook.module';
|
||||
import { workspaceQueryRunnerFactories } from 'src/engine/api/graphql/workspace-query-runner/factories';
|
||||
import { RecordPositionListener } from 'src/engine/api/graphql/workspace-query-runner/listeners/record-position.listener';
|
||||
import { AuthModule } from 'src/engine/modules/auth/auth.module';
|
||||
|
||||
import { WorkspaceQueryRunnerService } from './workspace-query-runner.service';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
AuthModule,
|
||||
WorkspaceQueryBuilderModule,
|
||||
WorkspaceDataSourceModule,
|
||||
WorkspacePreQueryHookModule,
|
||||
|
||||
@ -45,6 +45,7 @@ import { WorkspacePreQueryHookService } from 'src/engine/api/graphql/workspace-q
|
||||
import { EnvironmentService } from 'src/engine/integrations/environment/environment.service';
|
||||
import { NotFoundError } from 'src/engine/filters/utils/graphql-errors.util';
|
||||
import { QueryRunnerArgsFactory } from 'src/engine/api/graphql/workspace-query-runner/factories/query-runner-args.factory';
|
||||
import { QueryResultGettersFactory } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters.factory';
|
||||
|
||||
import { WorkspaceQueryRunnerOptions } from './interfaces/query-runner-option.interface';
|
||||
import {
|
||||
@ -61,6 +62,7 @@ export class WorkspaceQueryRunnerService {
|
||||
private readonly workspaceQueryBuilderFactory: WorkspaceQueryBuilderFactory,
|
||||
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
|
||||
private readonly queryRunnerArgsFactory: QueryRunnerArgsFactory,
|
||||
private readonly queryResultGettersFactory: QueryResultGettersFactory,
|
||||
@Inject(MessageQueue.webhookQueue)
|
||||
private readonly messageQueueService: MessageQueueService,
|
||||
private readonly eventEmitter: EventEmitter2,
|
||||
@ -133,7 +135,7 @@ export class WorkspaceQueryRunnerService {
|
||||
);
|
||||
|
||||
const result = await this.execute(query, workspaceId);
|
||||
const parsedResult = this.parseResult<IConnection<Record>>(
|
||||
const parsedResult = await this.parseResult<IConnection<Record>>(
|
||||
result,
|
||||
objectMetadataItem,
|
||||
'',
|
||||
@ -174,7 +176,7 @@ export class WorkspaceQueryRunnerService {
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
const parsedResult = this.parseResult<Record<string, unknown>>(
|
||||
const parsedResult = await this.parseResult<Record<string, unknown>>(
|
||||
existingRecordResult,
|
||||
objectMetadataItem,
|
||||
'',
|
||||
@ -227,10 +229,12 @@ export class WorkspaceQueryRunnerService {
|
||||
|
||||
const result = await this.execute(query, workspaceId);
|
||||
|
||||
const parsedResults = this.parseResult<PGGraphQLMutation<Record>>(
|
||||
result,
|
||||
objectMetadataItem,
|
||||
'insertInto',
|
||||
const parsedResults = (
|
||||
await this.parseResult<PGGraphQLMutation<Record>>(
|
||||
result,
|
||||
objectMetadataItem,
|
||||
'insertInto',
|
||||
)
|
||||
)?.records;
|
||||
|
||||
await this.triggerWebhooks<Record>(
|
||||
@ -280,10 +284,12 @@ export class WorkspaceQueryRunnerService {
|
||||
|
||||
const result = await this.execute(query, workspaceId);
|
||||
|
||||
const parsedResults = this.parseResult<PGGraphQLMutation<Record>>(
|
||||
result,
|
||||
objectMetadataItem,
|
||||
'update',
|
||||
const parsedResults = (
|
||||
await this.parseResult<PGGraphQLMutation<Record>>(
|
||||
result,
|
||||
objectMetadataItem,
|
||||
'update',
|
||||
)
|
||||
)?.records;
|
||||
|
||||
await this.triggerWebhooks<Record>(
|
||||
@ -316,10 +322,12 @@ export class WorkspaceQueryRunnerService {
|
||||
|
||||
const result = await this.execute(query, workspaceId);
|
||||
|
||||
const parsedResults = this.parseResult<PGGraphQLMutation<Record>>(
|
||||
result,
|
||||
objectMetadataItem,
|
||||
'update',
|
||||
const parsedResults = (
|
||||
await this.parseResult<PGGraphQLMutation<Record>>(
|
||||
result,
|
||||
objectMetadataItem,
|
||||
'update',
|
||||
)
|
||||
)?.records;
|
||||
|
||||
await this.triggerWebhooks<Record>(
|
||||
@ -349,10 +357,12 @@ export class WorkspaceQueryRunnerService {
|
||||
|
||||
const result = await this.execute(query, workspaceId);
|
||||
|
||||
const parsedResults = this.parseResult<PGGraphQLMutation<Record>>(
|
||||
result,
|
||||
objectMetadataItem,
|
||||
'deleteFrom',
|
||||
const parsedResults = (
|
||||
await this.parseResult<PGGraphQLMutation<Record>>(
|
||||
result,
|
||||
objectMetadataItem,
|
||||
'deleteFrom',
|
||||
)
|
||||
)?.records;
|
||||
|
||||
await this.triggerWebhooks<Record>(
|
||||
@ -382,10 +392,12 @@ export class WorkspaceQueryRunnerService {
|
||||
);
|
||||
const result = await this.execute(query, workspaceId);
|
||||
|
||||
const parsedResults = this.parseResult<PGGraphQLMutation<Record>>(
|
||||
result,
|
||||
objectMetadataItem,
|
||||
'deleteFrom',
|
||||
const parsedResults = (
|
||||
await this.parseResult<PGGraphQLMutation<Record>>(
|
||||
result,
|
||||
objectMetadataItem,
|
||||
'deleteFrom',
|
||||
)
|
||||
)?.records;
|
||||
|
||||
await this.triggerWebhooks<Record>(
|
||||
@ -445,11 +457,11 @@ export class WorkspaceQueryRunnerService {
|
||||
return results;
|
||||
}
|
||||
|
||||
private parseResult<Result>(
|
||||
private async parseResult<Result>(
|
||||
graphqlResult: PGGraphQLResult | undefined,
|
||||
objectMetadataItem: ObjectMetadataInterface,
|
||||
command: string,
|
||||
): Result {
|
||||
): Promise<Result> {
|
||||
const entityKey = `${command}${computeObjectTargetTable(
|
||||
objectMetadataItem,
|
||||
)}Collection`;
|
||||
@ -481,7 +493,12 @@ export class WorkspaceQueryRunnerService {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return parseResult(result);
|
||||
const resultWithGetters = await this.queryResultGettersFactory.create(
|
||||
result,
|
||||
objectMetadataItem,
|
||||
);
|
||||
|
||||
return parseResult(resultWithGetters);
|
||||
}
|
||||
|
||||
async executeAndParse<Result>(
|
||||
|
||||
Reference in New Issue
Block a user