mirror of
https://github.com/twentyhq/twenty.git
synced 2024-09-11 15:35:36 +03:00
Remove relations for remotes (#5455)
For remotes, we will only create the foreign key, without the relation metadata. Expected behavior will be: - possible to create an activity. But the remote object will not be displayed in the relations of the activity - the remote objects should not be available in the search for relations Also switched the number settings to an enum, since we now have to handle `BigInt` case. --------- Co-authored-by: Thomas Trompette <thomast@twenty.com>
This commit is contained in:
parent
b098027174
commit
4d479ee8ea
@ -95,6 +95,7 @@
|
||||
"graphql-fields": "^2.0.3",
|
||||
"graphql-middleware": "^6.1.35",
|
||||
"graphql-rate-limit": "^3.3.0",
|
||||
"graphql-scalars": "^1.23.0",
|
||||
"graphql-subscriptions": "2.0.0",
|
||||
"graphql-tag": "^2.12.6",
|
||||
"graphql-type-json": "^0.3.2",
|
||||
|
@ -29,19 +29,19 @@ export const useMultiObjectSearchMatchesSearchFilterAndToSelectQuery = ({
|
||||
}) => {
|
||||
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||
|
||||
const nonSystemObjectMetadataItems = objectMetadataItems.filter(
|
||||
({ isSystem }) => !isSystem,
|
||||
const selectableObjectMetadataItems = objectMetadataItems.filter(
|
||||
({ isSystem, isRemote }) => !isSystem && !isRemote,
|
||||
);
|
||||
|
||||
const { searchFilterPerMetadataItemNameSingular } =
|
||||
useSearchFilterPerMetadataItem({
|
||||
objectMetadataItems: nonSystemObjectMetadataItems,
|
||||
objectMetadataItems: selectableObjectMetadataItems,
|
||||
searchFilterValue,
|
||||
});
|
||||
|
||||
const objectRecordsToSelectAndMatchesSearchFilterTextFilterPerMetadataItem =
|
||||
Object.fromEntries(
|
||||
nonSystemObjectMetadataItems
|
||||
selectableObjectMetadataItems
|
||||
.map(({ nameSingular }) => {
|
||||
const selectedIds = selectedObjectRecordIds
|
||||
.filter(
|
||||
@ -74,16 +74,16 @@ export const useMultiObjectSearchMatchesSearchFilterAndToSelectQuery = ({
|
||||
);
|
||||
|
||||
const { orderByFieldPerMetadataItem } = useOrderByFieldPerMetadataItem({
|
||||
objectMetadataItems: nonSystemObjectMetadataItems,
|
||||
objectMetadataItems: selectableObjectMetadataItems,
|
||||
});
|
||||
|
||||
const { limitPerMetadataItem } = useLimitPerMetadataItem({
|
||||
objectMetadataItems: nonSystemObjectMetadataItems,
|
||||
objectMetadataItems: selectableObjectMetadataItems,
|
||||
limit,
|
||||
});
|
||||
|
||||
const multiSelectQuery = useGenerateCombinedFindManyRecordsQuery({
|
||||
operationSignatures: nonSystemObjectMetadataItems.map(
|
||||
operationSignatures: selectableObjectMetadataItems.map(
|
||||
(objectMetadataItem) => ({
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
variables: {},
|
||||
|
@ -1,22 +1,18 @@
|
||||
import {
|
||||
GraphQLInputObjectType,
|
||||
GraphQLList,
|
||||
GraphQLNonNull,
|
||||
GraphQLInt,
|
||||
} from 'graphql';
|
||||
import { GraphQLInputObjectType, GraphQLList, GraphQLNonNull } from 'graphql';
|
||||
import { GraphQLBigInt } from 'graphql-scalars';
|
||||
|
||||
import { FilterIs } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/filter-is.input-type';
|
||||
|
||||
export const BigIntFilterType = new GraphQLInputObjectType({
|
||||
name: 'BigIntFilter',
|
||||
fields: {
|
||||
eq: { type: GraphQLInt },
|
||||
gt: { type: GraphQLInt },
|
||||
gte: { type: GraphQLInt },
|
||||
in: { type: new GraphQLList(new GraphQLNonNull(GraphQLInt)) },
|
||||
lt: { type: GraphQLInt },
|
||||
lte: { type: GraphQLInt },
|
||||
neq: { type: GraphQLInt },
|
||||
eq: { type: GraphQLBigInt },
|
||||
gt: { type: GraphQLBigInt },
|
||||
gte: { type: GraphQLBigInt },
|
||||
in: { type: new GraphQLList(new GraphQLNonNull(GraphQLBigInt)) },
|
||||
lt: { type: GraphQLBigInt },
|
||||
lte: { type: GraphQLBigInt },
|
||||
neq: { type: GraphQLBigInt },
|
||||
is: { type: FilterIs },
|
||||
},
|
||||
});
|
||||
|
@ -8,7 +8,6 @@ import {
|
||||
GraphQLID,
|
||||
GraphQLInputObjectType,
|
||||
GraphQLInputType,
|
||||
GraphQLInt,
|
||||
GraphQLList,
|
||||
GraphQLNonNull,
|
||||
GraphQLScalarType,
|
||||
@ -26,7 +25,6 @@ import {
|
||||
BooleanFilterType,
|
||||
BigFloatFilterType,
|
||||
RawJsonFilterType,
|
||||
IntFilterType,
|
||||
} from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input';
|
||||
import { OrderByDirectionType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/enum';
|
||||
import {
|
||||
@ -36,6 +34,8 @@ import {
|
||||
import { PositionScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars/position.scalar';
|
||||
import { RawJSONScalar } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars/raw-json.scalar';
|
||||
import { IDFilterType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/id-filter.input-type';
|
||||
import { getNumberFilterType } from 'src/engine/api/graphql/workspace-schema-builder/utils/get-number-filter-type.util';
|
||||
import { getNumberScalarType } from 'src/engine/api/graphql/workspace-schema-builder/utils/get-number-scalar-type.util';
|
||||
|
||||
export interface TypeOptions<T = any> {
|
||||
nullable?: boolean;
|
||||
@ -57,13 +57,6 @@ export class TypeMapperService {
|
||||
return GraphQLID;
|
||||
}
|
||||
|
||||
const numberScalar =
|
||||
fieldMetadataType === FieldMetadataType.NUMBER &&
|
||||
(settings as FieldMetadataSettings<FieldMetadataType.NUMBER>)
|
||||
?.precision === 0
|
||||
? GraphQLInt
|
||||
: GraphQLFloat;
|
||||
|
||||
const typeScalarMapping = new Map<FieldMetadataType, GraphQLScalarType>([
|
||||
[FieldMetadataType.UUID, UUIDScalarType],
|
||||
[FieldMetadataType.TEXT, GraphQLString],
|
||||
@ -72,7 +65,13 @@ export class TypeMapperService {
|
||||
[FieldMetadataType.DATE_TIME, GraphQLISODateTime],
|
||||
[FieldMetadataType.DATE, GraphQLISODateTime],
|
||||
[FieldMetadataType.BOOLEAN, GraphQLBoolean],
|
||||
[FieldMetadataType.NUMBER, numberScalar],
|
||||
[
|
||||
FieldMetadataType.NUMBER,
|
||||
getNumberScalarType(
|
||||
(settings as FieldMetadataSettings<FieldMetadataType.NUMBER>)
|
||||
?.dataType,
|
||||
),
|
||||
],
|
||||
[FieldMetadataType.NUMERIC, BigFloatScalarType],
|
||||
[FieldMetadataType.PROBABILITY, GraphQLFloat],
|
||||
[FieldMetadataType.POSITION, PositionScalarType],
|
||||
@ -91,13 +90,6 @@ export class TypeMapperService {
|
||||
return IDFilterType;
|
||||
}
|
||||
|
||||
const numberScalar =
|
||||
fieldMetadataType === FieldMetadataType.NUMBER &&
|
||||
(settings as FieldMetadataSettings<FieldMetadataType.NUMBER>)
|
||||
?.precision === 0
|
||||
? IntFilterType
|
||||
: FloatFilterType;
|
||||
|
||||
const typeFilterMapping = new Map<
|
||||
FieldMetadataType,
|
||||
GraphQLInputObjectType | GraphQLScalarType
|
||||
@ -109,7 +101,13 @@ export class TypeMapperService {
|
||||
[FieldMetadataType.DATE_TIME, DateFilterType],
|
||||
[FieldMetadataType.DATE, DateFilterType],
|
||||
[FieldMetadataType.BOOLEAN, BooleanFilterType],
|
||||
[FieldMetadataType.NUMBER, numberScalar],
|
||||
[
|
||||
FieldMetadataType.NUMBER,
|
||||
getNumberFilterType(
|
||||
(settings as FieldMetadataSettings<FieldMetadataType.NUMBER>)
|
||||
?.dataType,
|
||||
),
|
||||
],
|
||||
[FieldMetadataType.NUMERIC, BigFloatFilterType],
|
||||
[FieldMetadataType.PROBABILITY, FloatFilterType],
|
||||
[FieldMetadataType.POSITION, FloatFilterType],
|
||||
|
@ -0,0 +1,24 @@
|
||||
import { GraphQLInputObjectType } from 'graphql';
|
||||
|
||||
import { NumberDataType } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-settings.interface';
|
||||
|
||||
import {
|
||||
BigIntFilterType,
|
||||
FloatFilterType,
|
||||
IntFilterType,
|
||||
} from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input';
|
||||
|
||||
export const getNumberFilterType = (
|
||||
subType: NumberDataType | undefined,
|
||||
): GraphQLInputObjectType => {
|
||||
switch (subType) {
|
||||
case NumberDataType.FLOAT:
|
||||
return FloatFilterType;
|
||||
case NumberDataType.BIGINT:
|
||||
return BigIntFilterType;
|
||||
case NumberDataType.INT:
|
||||
return IntFilterType;
|
||||
default:
|
||||
return FloatFilterType;
|
||||
}
|
||||
};
|
@ -0,0 +1,19 @@
|
||||
import { GraphQLInt, GraphQLFloat, GraphQLScalarType } from 'graphql';
|
||||
import { GraphQLBigInt } from 'graphql-scalars';
|
||||
|
||||
import { NumberDataType } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-settings.interface';
|
||||
|
||||
export const getNumberScalarType = (
|
||||
dataType: NumberDataType,
|
||||
): GraphQLScalarType => {
|
||||
switch (dataType) {
|
||||
case NumberDataType.FLOAT:
|
||||
return GraphQLFloat;
|
||||
case NumberDataType.BIGINT:
|
||||
return GraphQLBigInt;
|
||||
case NumberDataType.INT:
|
||||
return GraphQLInt;
|
||||
default:
|
||||
return GraphQLFloat;
|
||||
}
|
||||
};
|
@ -1,11 +1,17 @@
|
||||
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
|
||||
export enum NumberDataType {
|
||||
FLOAT = 'float',
|
||||
INT = 'int',
|
||||
BIGINT = 'bigint',
|
||||
}
|
||||
|
||||
type FieldMetadataDefaultSettings = {
|
||||
isForeignKey?: boolean;
|
||||
};
|
||||
|
||||
type FieldMetadataNumberSettings = {
|
||||
precision: number;
|
||||
dataType: NumberDataType;
|
||||
};
|
||||
|
||||
type FieldMetadataSettingsMapping = {
|
||||
|
@ -8,12 +8,7 @@ import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import console from 'console';
|
||||
|
||||
import {
|
||||
DataSource,
|
||||
FindManyOptions,
|
||||
FindOneOptions,
|
||||
Repository,
|
||||
} from 'typeorm';
|
||||
import { FindManyOptions, FindOneOptions, Repository } from 'typeorm';
|
||||
import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm';
|
||||
import { Query, QueryOptions } from '@ptc-org/nestjs-query-core';
|
||||
|
||||
@ -57,7 +52,6 @@ import {
|
||||
import { createWorkspaceMigrationsForCustomObjectRelations } from 'src/engine/metadata-modules/object-metadata/utils/create-migrations-for-custom-object-relations.util';
|
||||
import { createWorkspaceMigrationsForRemoteObjectRelations } from 'src/engine/metadata-modules/object-metadata/utils/create-workspace-migrations-for-remote-object-relations.util';
|
||||
import { computeColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util';
|
||||
import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity';
|
||||
import { validateObjectMetadataInputOrThrow } from 'src/engine/metadata-modules/object-metadata/utils/validate-object-metadata-input.util';
|
||||
import { mapUdtNameToFieldType } from 'src/engine/metadata-modules/remote-server/remote-table/utils/udt-name-mapper.util';
|
||||
import { WorkspaceCacheVersionService } from 'src/engine/metadata-modules/workspace-cache-version/workspace-cache-version.service';
|
||||
@ -380,9 +374,6 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
await this.createObjectRelationsMetadataAndMigrations(
|
||||
objectMetadataInput,
|
||||
createdObjectMetadata,
|
||||
lastDataSourceMetadata,
|
||||
workspaceDataSource,
|
||||
objectMetadataInput.isRemote,
|
||||
);
|
||||
|
||||
await this.workspaceMigrationRunnerService.executeMigrationFromPendingMigrations(
|
||||
@ -515,10 +506,9 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
private async createObjectRelationsMetadataAndMigrations(
|
||||
objectMetadataInput: CreateObjectInput,
|
||||
createdObjectMetadata: ObjectMetadataEntity,
|
||||
lastDataSourceMetadata: DataSourceEntity,
|
||||
workspaceDataSource: DataSource | undefined,
|
||||
isRemoteObject = false,
|
||||
) {
|
||||
const isRemoteObject = objectMetadataInput.isRemote ?? false;
|
||||
|
||||
const { timelineActivityObjectMetadata } =
|
||||
await this.createTimelineActivityRelation(
|
||||
objectMetadataInput.workspaceId,
|
||||
@ -527,6 +517,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
objectMetadataInput.primaryKeyColumnType ?? 'uuid',
|
||||
),
|
||||
objectMetadataInput.primaryKeyFieldMetadataSettings,
|
||||
isRemoteObject,
|
||||
);
|
||||
|
||||
const { activityTargetObjectMetadata } =
|
||||
@ -537,6 +528,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
objectMetadataInput.primaryKeyColumnType ?? 'uuid',
|
||||
),
|
||||
objectMetadataInput.primaryKeyFieldMetadataSettings,
|
||||
isRemoteObject,
|
||||
);
|
||||
|
||||
const { favoriteObjectMetadata } = await this.createFavoriteRelation(
|
||||
@ -544,6 +536,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
createdObjectMetadata,
|
||||
mapUdtNameToFieldType(objectMetadataInput.primaryKeyColumnType ?? 'uuid'),
|
||||
objectMetadataInput.primaryKeyFieldMetadataSettings,
|
||||
isRemoteObject,
|
||||
);
|
||||
|
||||
const { attachmentObjectMetadata } = await this.createAttachmentRelation(
|
||||
@ -551,6 +544,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
createdObjectMetadata,
|
||||
mapUdtNameToFieldType(objectMetadataInput.primaryKeyColumnType ?? 'uuid'),
|
||||
objectMetadataInput.primaryKeyFieldMetadataSettings,
|
||||
isRemoteObject,
|
||||
);
|
||||
|
||||
return this.workspaceMigrationService.createCustomMigration(
|
||||
@ -563,9 +557,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
attachmentObjectMetadata,
|
||||
timelineActivityObjectMetadata,
|
||||
favoriteObjectMetadata,
|
||||
lastDataSourceMetadata.schema,
|
||||
objectMetadataInput.primaryKeyColumnType ?? 'uuid',
|
||||
workspaceDataSource,
|
||||
)
|
||||
: createWorkspaceMigrationsForCustomObjectRelations(
|
||||
createdObjectMetadata,
|
||||
@ -584,6 +576,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
objectPrimaryKeyFieldSettings:
|
||||
| FieldMetadataSettings<FieldMetadataType | 'default'>
|
||||
| undefined,
|
||||
isRemoteObject: boolean,
|
||||
) {
|
||||
const activityTargetObjectMetadata =
|
||||
await this.objectMetadataRepository.findOneByOrFail({
|
||||
@ -591,6 +584,30 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
workspaceId: workspaceId,
|
||||
});
|
||||
|
||||
await this.fieldMetadataRepository.save(
|
||||
// Foreign key
|
||||
{
|
||||
standardId: createForeignKeyDeterministicUuid({
|
||||
objectId: createdObjectMetadata.id,
|
||||
standardId: ACTIVITY_TARGET_STANDARD_FIELD_IDS.custom,
|
||||
}),
|
||||
objectMetadataId: activityTargetObjectMetadata.id,
|
||||
workspaceId: workspaceId,
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
type: objectPrimaryKeyType,
|
||||
name: `${createdObjectMetadata.nameSingular}Id`,
|
||||
label: `${createdObjectMetadata.labelSingular} ID (foreign key)`,
|
||||
description: `ActivityTarget ${createdObjectMetadata.labelSingular} id foreign key`,
|
||||
icon: undefined,
|
||||
isNullable: true,
|
||||
isSystem: true,
|
||||
defaultValue: undefined,
|
||||
settings: { ...objectPrimaryKeyFieldSettings, isForeignKey: true },
|
||||
},
|
||||
);
|
||||
|
||||
if (!isRemoteObject) {
|
||||
const activityTargetRelationFieldMetadata =
|
||||
await this.fieldMetadataRepository.save([
|
||||
// FROM
|
||||
@ -624,26 +641,6 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
isNullable: true,
|
||||
},
|
||||
// Foreign key
|
||||
{
|
||||
standardId: createForeignKeyDeterministicUuid({
|
||||
objectId: createdObjectMetadata.id,
|
||||
standardId: ACTIVITY_TARGET_STANDARD_FIELD_IDS.custom,
|
||||
}),
|
||||
objectMetadataId: activityTargetObjectMetadata.id,
|
||||
workspaceId: workspaceId,
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
type: objectPrimaryKeyType,
|
||||
name: `${createdObjectMetadata.nameSingular}Id`,
|
||||
label: `${createdObjectMetadata.labelSingular} ID (foreign key)`,
|
||||
description: `ActivityTarget ${createdObjectMetadata.labelSingular} id foreign key`,
|
||||
icon: undefined,
|
||||
isNullable: true,
|
||||
isSystem: true,
|
||||
defaultValue: undefined,
|
||||
settings: { ...objectPrimaryKeyFieldSettings, isForeignKey: true },
|
||||
},
|
||||
]);
|
||||
|
||||
const activityTargetRelationFieldMetadataMap =
|
||||
@ -673,6 +670,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
onDeleteAction: RelationOnDeleteAction.CASCADE,
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
return { activityTargetObjectMetadata };
|
||||
}
|
||||
@ -684,6 +682,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
objectPrimaryKeyFieldSettings:
|
||||
| FieldMetadataSettings<FieldMetadataType | 'default'>
|
||||
| undefined,
|
||||
isRemoteObject: boolean,
|
||||
) {
|
||||
const attachmentObjectMetadata =
|
||||
await this.objectMetadataRepository.findOneByOrFail({
|
||||
@ -691,6 +690,30 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
workspaceId: workspaceId,
|
||||
});
|
||||
|
||||
await this.fieldMetadataRepository.save(
|
||||
// Foreign key
|
||||
{
|
||||
standardId: createForeignKeyDeterministicUuid({
|
||||
objectId: createdObjectMetadata.id,
|
||||
standardId: ATTACHMENT_STANDARD_FIELD_IDS.custom,
|
||||
}),
|
||||
objectMetadataId: attachmentObjectMetadata.id,
|
||||
workspaceId: workspaceId,
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
type: objectPrimaryKeyType,
|
||||
name: `${createdObjectMetadata.nameSingular}Id`,
|
||||
label: `${createdObjectMetadata.labelSingular} ID (foreign key)`,
|
||||
description: `Attachment ${createdObjectMetadata.labelSingular} id foreign key`,
|
||||
icon: undefined,
|
||||
isNullable: true,
|
||||
isSystem: true,
|
||||
defaultValue: undefined,
|
||||
settings: { ...objectPrimaryKeyFieldSettings, isForeignKey: true },
|
||||
},
|
||||
);
|
||||
|
||||
if (!isRemoteObject) {
|
||||
const attachmentRelationFieldMetadata =
|
||||
await this.fieldMetadataRepository.save([
|
||||
// FROM
|
||||
@ -724,26 +747,6 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
isNullable: true,
|
||||
},
|
||||
// Foreign key
|
||||
{
|
||||
standardId: createForeignKeyDeterministicUuid({
|
||||
objectId: createdObjectMetadata.id,
|
||||
standardId: ATTACHMENT_STANDARD_FIELD_IDS.custom,
|
||||
}),
|
||||
objectMetadataId: attachmentObjectMetadata.id,
|
||||
workspaceId: workspaceId,
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
type: objectPrimaryKeyType,
|
||||
name: `${createdObjectMetadata.nameSingular}Id`,
|
||||
label: `${createdObjectMetadata.labelSingular} ID (foreign key)`,
|
||||
description: `Attachment ${createdObjectMetadata.labelSingular} id foreign key`,
|
||||
icon: undefined,
|
||||
isNullable: true,
|
||||
isSystem: true,
|
||||
defaultValue: undefined,
|
||||
settings: { ...objectPrimaryKeyFieldSettings, isForeignKey: true },
|
||||
},
|
||||
]);
|
||||
|
||||
const attachmentRelationFieldMetadataMap =
|
||||
@ -771,6 +774,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
onDeleteAction: RelationOnDeleteAction.CASCADE,
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
return { attachmentObjectMetadata };
|
||||
}
|
||||
@ -782,6 +786,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
objectPrimaryKeyFieldSettings:
|
||||
| FieldMetadataSettings<FieldMetadataType | 'default'>
|
||||
| undefined,
|
||||
isRemoteObject: boolean,
|
||||
) {
|
||||
const timelineActivityObjectMetadata =
|
||||
await this.objectMetadataRepository.findOneByOrFail({
|
||||
@ -789,6 +794,30 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
workspaceId: workspaceId,
|
||||
});
|
||||
|
||||
await this.fieldMetadataRepository.save(
|
||||
// Foreign key
|
||||
{
|
||||
standardId: createForeignKeyDeterministicUuid({
|
||||
objectId: createdObjectMetadata.id,
|
||||
standardId: TIMELINE_ACTIVITY_STANDARD_FIELD_IDS.custom,
|
||||
}),
|
||||
objectMetadataId: timelineActivityObjectMetadata.id,
|
||||
workspaceId: workspaceId,
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
type: objectPrimaryKeyType,
|
||||
name: `${createdObjectMetadata.nameSingular}Id`,
|
||||
label: `${createdObjectMetadata.labelSingular} ID (foreign key)`,
|
||||
description: `Timeline Activity ${createdObjectMetadata.labelSingular} id foreign key`,
|
||||
icon: undefined,
|
||||
isNullable: true,
|
||||
isSystem: true,
|
||||
defaultValue: undefined,
|
||||
settings: { ...objectPrimaryKeyFieldSettings, isForeignKey: true },
|
||||
},
|
||||
);
|
||||
|
||||
if (!isRemoteObject) {
|
||||
const timelineActivityRelationFieldMetadata =
|
||||
await this.fieldMetadataRepository.save([
|
||||
// FROM
|
||||
@ -822,26 +851,6 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
isNullable: true,
|
||||
},
|
||||
// Foreign key
|
||||
{
|
||||
standardId: createForeignKeyDeterministicUuid({
|
||||
objectId: createdObjectMetadata.id,
|
||||
standardId: TIMELINE_ACTIVITY_STANDARD_FIELD_IDS.custom,
|
||||
}),
|
||||
objectMetadataId: timelineActivityObjectMetadata.id,
|
||||
workspaceId: workspaceId,
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
type: objectPrimaryKeyType,
|
||||
name: `${createdObjectMetadata.nameSingular}Id`,
|
||||
label: `${createdObjectMetadata.labelSingular} ID (foreign key)`,
|
||||
description: `Timeline Activity ${createdObjectMetadata.labelSingular} id foreign key`,
|
||||
icon: undefined,
|
||||
isNullable: true,
|
||||
isSystem: true,
|
||||
defaultValue: undefined,
|
||||
settings: { ...objectPrimaryKeyFieldSettings, isForeignKey: true },
|
||||
},
|
||||
]);
|
||||
|
||||
const timelineActivityRelationFieldMetadataMap =
|
||||
@ -863,7 +872,8 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
fromObjectMetadataId: createdObjectMetadata.id,
|
||||
toObjectMetadataId: timelineActivityObjectMetadata.id,
|
||||
fromFieldMetadataId:
|
||||
timelineActivityRelationFieldMetadataMap[createdObjectMetadata.id].id,
|
||||
timelineActivityRelationFieldMetadataMap[createdObjectMetadata.id]
|
||||
.id,
|
||||
toFieldMetadataId:
|
||||
timelineActivityRelationFieldMetadataMap[
|
||||
timelineActivityObjectMetadata.id
|
||||
@ -871,6 +881,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
onDeleteAction: RelationOnDeleteAction.CASCADE,
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
return { timelineActivityObjectMetadata };
|
||||
}
|
||||
@ -882,6 +893,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
objectPrimaryKeyFieldSettings:
|
||||
| FieldMetadataSettings<FieldMetadataType | 'default'>
|
||||
| undefined,
|
||||
isRemoteObject: boolean,
|
||||
) {
|
||||
const favoriteObjectMetadata =
|
||||
await this.objectMetadataRepository.findOneByOrFail({
|
||||
@ -889,6 +901,30 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
workspaceId: workspaceId,
|
||||
});
|
||||
|
||||
await this.fieldMetadataRepository.save(
|
||||
// Foreign key
|
||||
{
|
||||
standardId: createForeignKeyDeterministicUuid({
|
||||
objectId: createdObjectMetadata.id,
|
||||
standardId: FAVORITE_STANDARD_FIELD_IDS.custom,
|
||||
}),
|
||||
objectMetadataId: favoriteObjectMetadata.id,
|
||||
workspaceId: workspaceId,
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
type: objectPrimaryKeyType,
|
||||
name: `${createdObjectMetadata.nameSingular}Id`,
|
||||
label: `${createdObjectMetadata.labelSingular} ID (foreign key)`,
|
||||
description: `Favorite ${createdObjectMetadata.labelSingular} id foreign key`,
|
||||
icon: undefined,
|
||||
isNullable: true,
|
||||
isSystem: true,
|
||||
defaultValue: undefined,
|
||||
settings: { ...objectPrimaryKeyFieldSettings, isForeignKey: true },
|
||||
},
|
||||
);
|
||||
|
||||
if (!isRemoteObject) {
|
||||
const favoriteRelationFieldMetadata =
|
||||
await this.fieldMetadataRepository.save([
|
||||
// FROM
|
||||
@ -923,26 +959,6 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
isNullable: true,
|
||||
},
|
||||
// Foreign key
|
||||
{
|
||||
standardId: createForeignKeyDeterministicUuid({
|
||||
objectId: createdObjectMetadata.id,
|
||||
standardId: FAVORITE_STANDARD_FIELD_IDS.custom,
|
||||
}),
|
||||
objectMetadataId: favoriteObjectMetadata.id,
|
||||
workspaceId: workspaceId,
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
type: objectPrimaryKeyType,
|
||||
name: `${createdObjectMetadata.nameSingular}Id`,
|
||||
label: `${createdObjectMetadata.labelSingular} ID (foreign key)`,
|
||||
description: `Favorite ${createdObjectMetadata.labelSingular} id foreign key`,
|
||||
icon: undefined,
|
||||
isNullable: true,
|
||||
isSystem: true,
|
||||
defaultValue: undefined,
|
||||
settings: { ...objectPrimaryKeyFieldSettings, isForeignKey: true },
|
||||
},
|
||||
]);
|
||||
|
||||
const favoriteRelationFieldMetadataMap =
|
||||
@ -970,6 +986,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
onDeleteAction: RelationOnDeleteAction.CASCADE,
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
return { favoriteObjectMetadata };
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ export const createWorkspaceMigrationsForCustomObjectRelations = (
|
||||
createdObjectMetadata: ObjectMetadataEntity,
|
||||
activityTargetObjectMetadata: ObjectMetadataEntity,
|
||||
attachmentObjectMetadata: ObjectMetadataEntity,
|
||||
eventObjectMetadata: ObjectMetadataEntity,
|
||||
timelineActivityObjectMetadata: ObjectMetadataEntity,
|
||||
favoriteObjectMetadata: ObjectMetadataEntity,
|
||||
): WorkspaceMigrationTableAction[] => [
|
||||
{
|
||||
@ -80,9 +80,9 @@ export const createWorkspaceMigrationsForCustomObjectRelations = (
|
||||
},
|
||||
],
|
||||
},
|
||||
// Add event relation
|
||||
// Add timeline activity relation
|
||||
{
|
||||
name: computeObjectTargetTable(eventObjectMetadata),
|
||||
name: computeObjectTargetTable(timelineActivityObjectMetadata),
|
||||
action: WorkspaceMigrationTableActionType.ALTER,
|
||||
columns: [
|
||||
{
|
||||
@ -96,7 +96,7 @@ export const createWorkspaceMigrationsForCustomObjectRelations = (
|
||||
],
|
||||
},
|
||||
{
|
||||
name: computeObjectTargetTable(eventObjectMetadata),
|
||||
name: computeObjectTargetTable(timelineActivityObjectMetadata),
|
||||
action: WorkspaceMigrationTableActionType.ALTER,
|
||||
columns: [
|
||||
{
|
||||
|
@ -1,5 +1,3 @@
|
||||
import { DataSource } from 'typeorm';
|
||||
|
||||
import { computeColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import {
|
||||
@ -10,55 +8,14 @@ import {
|
||||
} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
|
||||
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
|
||||
|
||||
const buildCommentForRemoteObjectForeignKey = async (
|
||||
localObjectMetadataName: string,
|
||||
remoteObjectMetadataName: string,
|
||||
schema: string,
|
||||
workspaceDataSource: DataSource | undefined,
|
||||
): Promise<string> => {
|
||||
const existingComment = await workspaceDataSource?.query(
|
||||
`SELECT col_description('${schema}."${localObjectMetadataName}"'::regclass, 0)`,
|
||||
);
|
||||
|
||||
if (!existingComment[0]?.col_description) {
|
||||
return `@graphql({"totalCount":{"enabled": true},"foreign_keys":[{"local_name":"${localObjectMetadataName}Collection","local_columns":["${remoteObjectMetadataName}Id"],"foreign_name":"${remoteObjectMetadataName}","foreign_schema":"${schema}","foreign_table":"${remoteObjectMetadataName}","foreign_columns":["id"]}]})`;
|
||||
}
|
||||
|
||||
const commentWithoutGraphQL = existingComment[0].col_description
|
||||
.replace('@graphql(', '')
|
||||
.replace(')', '');
|
||||
const parsedComment = JSON.parse(commentWithoutGraphQL);
|
||||
|
||||
const foreignKey = {
|
||||
local_name: `${localObjectMetadataName}Collection`,
|
||||
local_columns: [`${remoteObjectMetadataName}Id`],
|
||||
foreign_name: `${remoteObjectMetadataName}`,
|
||||
foreign_schema: schema,
|
||||
foreign_table: remoteObjectMetadataName,
|
||||
foreign_columns: ['id'],
|
||||
};
|
||||
|
||||
if (parsedComment.foreign_keys) {
|
||||
parsedComment.foreign_keys.push(foreignKey);
|
||||
} else {
|
||||
parsedComment.foreign_keys = [foreignKey];
|
||||
}
|
||||
|
||||
return `@graphql(${JSON.stringify(parsedComment)})`;
|
||||
};
|
||||
|
||||
export const createWorkspaceMigrationsForRemoteObjectRelations = async (
|
||||
createdObjectMetadata: ObjectMetadataEntity,
|
||||
activityTargetObjectMetadata: ObjectMetadataEntity,
|
||||
attachmentObjectMetadata: ObjectMetadataEntity,
|
||||
eventObjectMetadata: ObjectMetadataEntity,
|
||||
timelineActivityObjectMetadata: ObjectMetadataEntity,
|
||||
favoriteObjectMetadata: ObjectMetadataEntity,
|
||||
schema: string,
|
||||
primaryKeyColumnType: string,
|
||||
workspaceDataSource: DataSource | undefined,
|
||||
): Promise<WorkspaceMigrationTableAction[]> => {
|
||||
const createdObjectName = createdObjectMetadata.nameSingular;
|
||||
|
||||
return [
|
||||
{
|
||||
name: computeObjectTargetTable(activityTargetObjectMetadata),
|
||||
@ -74,22 +31,6 @@ export const createWorkspaceMigrationsForRemoteObjectRelations = async (
|
||||
} satisfies WorkspaceMigrationColumnCreate,
|
||||
],
|
||||
},
|
||||
{
|
||||
name: computeObjectTargetTable(activityTargetObjectMetadata),
|
||||
action: WorkspaceMigrationTableActionType.ALTER,
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE_COMMENT,
|
||||
comment: await buildCommentForRemoteObjectForeignKey(
|
||||
activityTargetObjectMetadata.nameSingular,
|
||||
createdObjectName,
|
||||
schema,
|
||||
workspaceDataSource,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
// Add attachment relation
|
||||
{
|
||||
name: computeObjectTargetTable(attachmentObjectMetadata),
|
||||
action: WorkspaceMigrationTableActionType.ALTER,
|
||||
@ -105,53 +46,7 @@ export const createWorkspaceMigrationsForRemoteObjectRelations = async (
|
||||
],
|
||||
},
|
||||
{
|
||||
name: computeObjectTargetTable(attachmentObjectMetadata),
|
||||
action: WorkspaceMigrationTableActionType.ALTER,
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE_COMMENT,
|
||||
comment: await buildCommentForRemoteObjectForeignKey(
|
||||
attachmentObjectMetadata.nameSingular,
|
||||
createdObjectName,
|
||||
schema,
|
||||
workspaceDataSource,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
// Add event relation
|
||||
{
|
||||
name: computeObjectTargetTable(eventObjectMetadata),
|
||||
action: WorkspaceMigrationTableActionType.ALTER,
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
||||
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
|
||||
isForeignKey: true,
|
||||
}),
|
||||
columnType: primaryKeyColumnType,
|
||||
isNullable: true,
|
||||
} satisfies WorkspaceMigrationColumnCreate,
|
||||
],
|
||||
},
|
||||
{
|
||||
name: computeObjectTargetTable(eventObjectMetadata),
|
||||
action: WorkspaceMigrationTableActionType.ALTER,
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE_COMMENT,
|
||||
comment: await buildCommentForRemoteObjectForeignKey(
|
||||
eventObjectMetadata.nameSingular,
|
||||
createdObjectName,
|
||||
schema,
|
||||
workspaceDataSource,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
// Add favorite relation
|
||||
{
|
||||
name: computeObjectTargetTable(favoriteObjectMetadata),
|
||||
name: computeObjectTargetTable(timelineActivityObjectMetadata),
|
||||
action: WorkspaceMigrationTableActionType.ALTER,
|
||||
columns: [
|
||||
{
|
||||
@ -169,14 +64,13 @@ export const createWorkspaceMigrationsForRemoteObjectRelations = async (
|
||||
action: WorkspaceMigrationTableActionType.ALTER,
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE_COMMENT,
|
||||
comment: await buildCommentForRemoteObjectForeignKey(
|
||||
favoriteObjectMetadata.nameSingular,
|
||||
createdObjectName,
|
||||
schema,
|
||||
workspaceDataSource,
|
||||
),
|
||||
},
|
||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
||||
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
|
||||
isForeignKey: true,
|
||||
}),
|
||||
columnType: primaryKeyColumnType,
|
||||
isNullable: true,
|
||||
} satisfies WorkspaceMigrationColumnCreate,
|
||||
],
|
||||
},
|
||||
];
|
||||
|
@ -1,4 +1,7 @@
|
||||
import { FieldMetadataSettings } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-settings.interface';
|
||||
import {
|
||||
FieldMetadataSettings,
|
||||
NumberDataType,
|
||||
} from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-settings.interface';
|
||||
|
||||
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
|
||||
@ -8,8 +11,6 @@ export const mapUdtNameToFieldType = (udtName: string): FieldMetadataType => {
|
||||
return FieldMetadataType.UUID;
|
||||
case 'varchar':
|
||||
case 'text':
|
||||
case 'bigint':
|
||||
case 'int8':
|
||||
return FieldMetadataType.TEXT;
|
||||
case 'bool':
|
||||
return FieldMetadataType.BOOLEAN;
|
||||
@ -19,6 +20,8 @@ export const mapUdtNameToFieldType = (udtName: string): FieldMetadataType => {
|
||||
case 'integer':
|
||||
case 'int2':
|
||||
case 'int4':
|
||||
case 'int8':
|
||||
case 'bigint':
|
||||
return FieldMetadataType.NUMBER;
|
||||
default:
|
||||
return FieldMetadataType.TEXT;
|
||||
@ -33,7 +36,12 @@ export const mapUdtNameToFieldSettings = (
|
||||
case 'int2':
|
||||
case 'int4':
|
||||
return {
|
||||
precision: 0,
|
||||
dataType: NumberDataType.INT,
|
||||
} satisfies FieldMetadataSettings<FieldMetadataType.NUMBER>;
|
||||
case 'int8':
|
||||
case 'bigint':
|
||||
return {
|
||||
dataType: NumberDataType.BIGINT,
|
||||
} satisfies FieldMetadataSettings<FieldMetadataType.NUMBER>;
|
||||
default:
|
||||
return undefined;
|
||||
|
12
yarn.lock
12
yarn.lock
@ -28910,6 +28910,17 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"graphql-scalars@npm:^1.23.0":
|
||||
version: 1.23.0
|
||||
resolution: "graphql-scalars@npm:1.23.0"
|
||||
dependencies:
|
||||
tslib: "npm:^2.5.0"
|
||||
peerDependencies:
|
||||
graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0
|
||||
checksum: 7666c305b8367528c4fbb583a2731d93d52f36043d71f4957ecc1d2db71d710d268e25535243beb1b2497db921daa94d3cfa83daaf4a3101a667f358ddbf3436
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"graphql-shield@npm:^7.5.0":
|
||||
version: 7.6.5
|
||||
resolution: "graphql-shield@npm:7.6.5"
|
||||
@ -46585,6 +46596,7 @@ __metadata:
|
||||
graphql-fields: "npm:^2.0.3"
|
||||
graphql-middleware: "npm:^6.1.35"
|
||||
graphql-rate-limit: "npm:^3.3.0"
|
||||
graphql-scalars: "npm:^1.23.0"
|
||||
graphql-subscriptions: "npm:2.0.0"
|
||||
graphql-tag: "npm:^2.12.6"
|
||||
graphql-type-json: "npm:^0.3.2"
|
||||
|
Loading…
Reference in New Issue
Block a user