Skip to content

Commit

Permalink
Fix record creation (twentyhq#8664)
Browse files Browse the repository at this point in the history
## Context
Now that each operation has its own resolver, we need to make sure they
all map to query arg getters. CreateOne was not properly mapped to the
position getter which made record creation fail because "position:
first" was not properly converted to a float.
Also fixing queries with custom object where we were wrongly using the
table name instead of entity name
  • Loading branch information
Weiko authored Nov 21, 2024
1 parent 1c04b2b commit 04c359a
Show file tree
Hide file tree
Showing 13 changed files with 150 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import { ProcessNestedRelationsHelper } from 'src/engine/api/graphql/graphql-que
import { assertIsValidUuid } from 'src/engine/api/graphql/workspace-query-runner/utils/assert-is-valid-uuid.util';
import { assertMutationNotOnRemoteObject } from 'src/engine/metadata-modules/object-metadata/utils/assert-mutation-not-on-remote-object.util';
import { formatResult } from 'src/engine/twenty-orm/utils/format-result.util';
import { computeTableName } from 'src/engine/utils/compute-table-name.util';

@Injectable()
export class GraphqlQueryDeleteManyResolverService extends GraphqlQueryBaseResolverService<
Expand All @@ -31,14 +30,9 @@ export class GraphqlQueryDeleteManyResolverService extends GraphqlQueryBaseResol
objectMetadataItemWithFieldMaps.nameSingular,
);

const tableName = computeTableName(
objectMetadataItemWithFieldMaps.nameSingular,
objectMetadataItemWithFieldMaps.isCustom,
);

executionArgs.graphqlQueryParser.applyFilterToBuilder(
queryBuilder,
tableName,
objectMetadataItemWithFieldMaps.nameSingular,
executionArgs.args.filter,
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import { QUERY_MAX_RECORDS } from 'src/engine/api/graphql/graphql-query-runner/c
import { ObjectRecordsToGraphqlConnectionHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/object-records-to-graphql-connection.helper';
import { ProcessNestedRelationsHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/process-nested-relations.helper';
import { formatResult } from 'src/engine/twenty-orm/utils/format-result.util';
import { computeTableName } from 'src/engine/utils/compute-table-name.util';

@Injectable()
export class GraphqlQueryDestroyManyResolverService extends GraphqlQueryBaseResolverService<
Expand All @@ -29,14 +28,9 @@ export class GraphqlQueryDestroyManyResolverService extends GraphqlQueryBaseReso
objectMetadataItemWithFieldMaps.nameSingular,
);

const tableName = computeTableName(
objectMetadataItemWithFieldMaps.nameSingular,
objectMetadataItemWithFieldMaps.isCustom,
);

executionArgs.graphqlQueryParser.applyFilterToBuilder(
queryBuilder,
tableName,
objectMetadataItemWithFieldMaps.nameSingular,
executionArgs.args.filter,
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/typ
import { getObjectMetadataMapItemByNameSingular } from 'src/engine/metadata-modules/utils/get-object-metadata-map-item-by-name-singular.util';
import { formatData } from 'src/engine/twenty-orm/utils/format-data.util';
import { formatResult } from 'src/engine/twenty-orm/utils/format-result.util';
import { computeTableName } from 'src/engine/utils/compute-table-name.util';

@Injectable()
export class GraphqlQueryFindDuplicatesResolverService extends GraphqlQueryBaseResolverService<
Expand Down Expand Up @@ -111,14 +110,9 @@ export class GraphqlQueryFindDuplicatesResolverService extends GraphqlQueryBaseR
objectMetadataItemWithFieldMaps.nameSingular,
);

const tableName = computeTableName(
objectMetadataItemWithFieldMaps.nameSingular,
objectMetadataItemWithFieldMaps.isCustom,
);

graphqlQueryParser.applyFilterToBuilder(
duplicateRecordsQueryBuilder,
tableName,
objectMetadataItemWithFieldMaps.nameSingular,
duplicateConditions,
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import {
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
import { formatResult } from 'src/engine/twenty-orm/utils/format-result.util';
import { computeTableName } from 'src/engine/utils/compute-table-name.util';
import { isDefined } from 'src/utils/is-defined';

@Injectable()
Expand All @@ -57,14 +56,9 @@ export class GraphqlQueryFindManyResolverService extends GraphqlQueryBaseResolve
let appliedFilters =
executionArgs.args.filter ?? ({} as ObjectRecordFilter);

const tableName = computeTableName(
objectMetadataItemWithFieldMaps.nameSingular,
objectMetadataItemWithFieldMaps.isCustom,
);

executionArgs.graphqlQueryParser.applyFilterToBuilder(
aggregateQueryBuilder,
tableName,
objectMetadataItemWithFieldMaps.nameSingular,
appliedFilters,
);

Expand Down Expand Up @@ -99,14 +93,14 @@ export class GraphqlQueryFindManyResolverService extends GraphqlQueryBaseResolve

executionArgs.graphqlQueryParser.applyFilterToBuilder(
queryBuilder,
tableName,
objectMetadataItemWithFieldMaps.nameSingular,
appliedFilters,
);

executionArgs.graphqlQueryParser.applyOrderToBuilder(
queryBuilder,
orderByWithIdCondition,
tableName,
objectMetadataItemWithFieldMaps.nameSingular,
isForwardPagination,
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import {
WorkspaceQueryRunnerExceptionCode,
} from 'src/engine/api/graphql/workspace-query-runner/workspace-query-runner.exception';
import { formatResult } from 'src/engine/twenty-orm/utils/format-result.util';
import { computeTableName } from 'src/engine/utils/compute-table-name.util';

@Injectable()
export class GraphqlQueryFindOneResolverService extends GraphqlQueryBaseResolverService<
Expand All @@ -40,14 +39,9 @@ export class GraphqlQueryFindOneResolverService extends GraphqlQueryBaseResolver
objectMetadataItemWithFieldMaps.nameSingular,
);

const tableName = computeTableName(
objectMetadataItemWithFieldMaps.nameSingular,
objectMetadataItemWithFieldMaps.isCustom,
);

executionArgs.graphqlQueryParser.applyFilterToBuilder(
queryBuilder,
tableName,
objectMetadataItemWithFieldMaps.nameSingular,
executionArgs.args.filter ?? ({} as ObjectRecordFilter),
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import { ProcessNestedRelationsHelper } from 'src/engine/api/graphql/graphql-que
import { assertIsValidUuid } from 'src/engine/api/graphql/workspace-query-runner/utils/assert-is-valid-uuid.util';
import { assertMutationNotOnRemoteObject } from 'src/engine/metadata-modules/object-metadata/utils/assert-mutation-not-on-remote-object.util';
import { formatResult } from 'src/engine/twenty-orm/utils/format-result.util';
import { computeTableName } from 'src/engine/utils/compute-table-name.util';

@Injectable()
export class GraphqlQueryRestoreManyResolverService extends GraphqlQueryBaseResolverService<
Expand All @@ -31,14 +30,9 @@ export class GraphqlQueryRestoreManyResolverService extends GraphqlQueryBaseReso
objectMetadataItemWithFieldMaps.nameSingular,
);

const tableName = computeTableName(
objectMetadataItemWithFieldMaps.nameSingular,
objectMetadataItemWithFieldMaps.isCustom,
);

executionArgs.graphqlQueryParser.applyFilterToBuilder(
queryBuilder,
tableName,
objectMetadataItemWithFieldMaps.nameSingular,
executionArgs.args.filter,
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import { ObjectRecordsToGraphqlConnectionHelper } from 'src/engine/api/graphql/g
import { ProcessNestedRelationsHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/process-nested-relations.helper';
import { SEARCH_VECTOR_FIELD } from 'src/engine/metadata-modules/constants/search-vector-field.constants';
import { formatResult } from 'src/engine/twenty-orm/utils/format-result.util';
import { computeTableName } from 'src/engine/utils/compute-table-name.util';
import { isDefined } from 'src/utils/is-defined';

@Injectable()
Expand Down Expand Up @@ -64,14 +63,9 @@ export class GraphqlQuerySearchResolverService extends GraphqlQueryBaseResolverS
objectMetadataItemWithFieldMaps.nameSingular,
);

const tableName = computeTableName(
objectMetadataItemWithFieldMaps.nameSingular,
objectMetadataItemWithFieldMaps.isCustom,
);

executionArgs.graphqlQueryParser.applyFilterToBuilder(
queryBuilder,
tableName,
objectMetadataItemWithFieldMaps.nameSingular,
executionArgs.args.filter ?? ({} as ObjectRecordFilter),
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import { assertIsValidUuid } from 'src/engine/api/graphql/workspace-query-runner
import { assertMutationNotOnRemoteObject } from 'src/engine/metadata-modules/object-metadata/utils/assert-mutation-not-on-remote-object.util';
import { formatData } from 'src/engine/twenty-orm/utils/format-data.util';
import { formatResult } from 'src/engine/twenty-orm/utils/format-result.util';
import { computeTableName } from 'src/engine/utils/compute-table-name.util';

@Injectable()
export class GraphqlQueryUpdateManyResolverService extends GraphqlQueryBaseResolverService<
Expand All @@ -32,14 +31,9 @@ export class GraphqlQueryUpdateManyResolverService extends GraphqlQueryBaseResol
objectMetadataItemWithFieldMaps.nameSingular,
);

const tableName = computeTableName(
objectMetadataItemWithFieldMaps.nameSingular,
objectMetadataItemWithFieldMaps.isCustom,
);

executionArgs.graphqlQueryParser.applyFilterToBuilder(
queryBuilder,
tableName,
objectMetadataItemWithFieldMaps.nameSingular,
executionArgs.args.filter,
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
import { WorkspaceQueryRunnerOptions } from 'src/engine/api/graphql/workspace-query-runner/interfaces/query-runner-option.interface';
import {
CreateManyResolverArgs,
CreateOneResolverArgs,
FindDuplicatesResolverArgs,
FindManyResolverArgs,
FindOneResolverArgs,
Expand Down Expand Up @@ -43,6 +44,19 @@ export class QueryRunnerArgsFactory {
);

switch (resolverArgsType) {
case ResolverArgsType.CreateOne:
return {
...args,
data: await this.overrideDataByFieldMetadata(
(args as CreateOneResolverArgs).data,
options,
fieldMetadataMapByNameByName,
{
argIndex: 0,
shouldBackfillPosition,
},
),
} satisfies CreateOneResolverArgs;
case ResolverArgsType.CreateMany:
return {
...args,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { SCOPE_OPTIONS_METADATA } from '@nestjs/common/constants';

import { WorkspaceResolverBuilderMethodNames } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';

import { WORKSPACE_QUERY_HOOK_METADATA } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/workspace-query-hook.constants';
import { WorkspaceQueryHookType } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/types/workspace-query-hook.type';
import { WORKSPACE_QUERY_HOOK_METADATA } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/workspace-query-hook.constants';

export type WorkspaceQueryHookKey =
`${string}.${WorkspaceResolverBuilderMethodNames}`;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';

import { CreatedByPreQueryHook } from 'src/engine/core-modules/actor/query-hooks/created-by.pre-query-hook';
import { CreatedByCreateManyPreQueryHook } from 'src/engine/core-modules/actor/query-hooks/created-by.create-many.pre-query-hook';
import { CreatedByCreateOnePreQueryHook } from 'src/engine/core-modules/actor/query-hooks/created-by.create-one.pre-query-hook';
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';

@Module({
imports: [TypeOrmModule.forFeature([FieldMetadataEntity], 'metadata')],
providers: [CreatedByPreQueryHook],
exports: [CreatedByPreQueryHook],
providers: [CreatedByCreateManyPreQueryHook, CreatedByCreateOnePreQueryHook],
exports: [CreatedByCreateManyPreQueryHook, CreatedByCreateOnePreQueryHook],
})
export class ActorModule {}
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ type CustomWorkspaceItem = Omit<
};

@WorkspaceQueryHook(`*.createMany`)
export class CreatedByPreQueryHook implements WorkspaceQueryHookInstance {
private readonly logger = new Logger(CreatedByPreQueryHook.name);
export class CreatedByCreateManyPreQueryHook
implements WorkspaceQueryHookInstance
{
private readonly logger = new Logger(CreatedByCreateManyPreQueryHook.name);

constructor(
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { Logger } from '@nestjs/common/services/logger.service';
import { InjectRepository } from '@nestjs/typeorm';

import { Repository } from 'typeorm';

import { WorkspaceQueryHookInstance } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/interfaces/workspace-query-hook.interface';
import { CreateOneResolverArgs } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';

import { WorkspaceQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/decorators/workspace-query-hook.decorator';
import { buildCreatedByFromWorkspaceMember } from 'src/engine/core-modules/actor/utils/build-created-by-from-workspace-member.util';
import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
import {
ActorMetadata,
FieldActorSource,
} from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type';
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { CustomWorkspaceEntity } from 'src/engine/twenty-orm/custom.workspace-entity';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';

type CustomWorkspaceItem = Omit<
CustomWorkspaceEntity,
'createdAt' | 'updatedAt'
> & {
createdAt: string;
updatedAt: string;
};

@WorkspaceQueryHook(`*.createOne`)
export class CreatedByCreateOnePreQueryHook
implements WorkspaceQueryHookInstance
{
private readonly logger = new Logger(CreatedByCreateOnePreQueryHook.name);

constructor(
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
@InjectRepository(FieldMetadataEntity, 'metadata')
private readonly fieldMetadataRepository: Repository<FieldMetadataEntity>,
) {}

async execute(
authContext: AuthContext,
objectName: string,
payload: CreateOneResolverArgs<CustomWorkspaceItem>,
): Promise<CreateOneResolverArgs<CustomWorkspaceItem>> {
let createdBy: ActorMetadata | null = null;

// TODO: Once all objects have it, we can remove this check
const createdByFieldMetadata = await this.fieldMetadataRepository.findOne({
where: {
object: {
nameSingular: objectName,
},
name: 'createdBy',
workspaceId: authContext.workspace.id,
},
});

if (!createdByFieldMetadata) {
return payload;
}

// If user is logged in, we use the workspace member
if (authContext.workspaceMemberId && authContext.user) {
createdBy = buildCreatedByFromWorkspaceMember(
authContext.workspaceMemberId,
authContext.user,
);
// TODO: remove that code once we have the workspace member id in all tokens
} else if (authContext.user) {
this.logger.warn("User doesn't have a workspace member id in the token");
const workspaceMemberRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkspaceMemberWorkspaceEntity>(
authContext.workspace.id,
'workspaceMember',
);

const workspaceMember = await workspaceMemberRepository.findOne({
where: {
userId: authContext.user?.id,
},
});

if (!workspaceMember) {
throw new Error(
`Workspace member can't be found for user ${authContext.user.id}`,
);
}

createdBy = {
source: FieldActorSource.MANUAL,
workspaceMemberId: workspaceMember.id,
name: `${workspaceMember.name.firstName} ${workspaceMember.name.lastName}`,
};
}

if (authContext.apiKey) {
createdBy = {
source: FieldActorSource.API,
name: authContext.apiKey.name,
};
}

// Front-end can fill the source field
if (
createdBy &&
(!payload.data.createdBy || !payload.data.createdBy?.name)
) {
payload.data.createdBy = {
...createdBy,
source: payload.data.createdBy?.source ?? createdBy.source,
};
}

return payload;
}
}

0 comments on commit 04c359a

Please sign in to comment.