feat: drop target column map (#4670)
This PR is dropping the column `targetColumnMap` of fieldMetadata entities. The goal of this column was to properly map field to their respecting column in the table. We decide to drop it and instead compute the column name on the fly when we need it, as it's more easier to support. Some parts of the code has been refactored to try making implementation of composite type more easier to understand and maintain. Fix #3760 --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -1,188 +1,61 @@
|
||||
import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface';
|
||||
import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface';
|
||||
import { CompositeType } from 'src/engine/metadata-modules/field-metadata/interfaces/composite-type.interface';
|
||||
|
||||
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { generateTargetColumnMap } from 'src/engine/metadata-modules/field-metadata/utils/generate-target-column-map.util';
|
||||
|
||||
export const addressFields = (
|
||||
fieldMetadata?: FieldMetadataInterface,
|
||||
): FieldMetadataInterface[] => {
|
||||
const inferredFieldMetadata = fieldMetadata as
|
||||
| FieldMetadataInterface<FieldMetadataType.ADDRESS>
|
||||
| undefined;
|
||||
const targetColumnMap = inferredFieldMetadata
|
||||
? generateTargetColumnMap(
|
||||
inferredFieldMetadata.type,
|
||||
inferredFieldMetadata.isCustom ?? false,
|
||||
inferredFieldMetadata.name,
|
||||
)
|
||||
: {
|
||||
addressStreet1: 'addressStreet1',
|
||||
addressStreet2: 'addressStreet2',
|
||||
addressCity: 'addressCity',
|
||||
addressPostcode: 'addressPostcode',
|
||||
addressState: 'addressState',
|
||||
addressCountry: 'addressCountry',
|
||||
addressLat: 'addressLat',
|
||||
addressLng: 'addressLng',
|
||||
};
|
||||
|
||||
return [
|
||||
export const addressCompositeType: CompositeType = {
|
||||
type: FieldMetadataType.ADDRESS,
|
||||
properties: [
|
||||
{
|
||||
id: 'addressStreet1',
|
||||
type: FieldMetadataType.TEXT,
|
||||
objectMetadataId: FieldMetadataType.ADDRESS.toString(),
|
||||
name: 'addressStreet1',
|
||||
label: 'Address',
|
||||
targetColumnMap: {
|
||||
value: targetColumnMap.addressStreet1,
|
||||
},
|
||||
isNullable: true,
|
||||
...(inferredFieldMetadata
|
||||
? {
|
||||
defaultValue:
|
||||
inferredFieldMetadata.defaultValue?.addressStreet1 ?? undefined,
|
||||
}
|
||||
: {}),
|
||||
} satisfies FieldMetadataInterface<FieldMetadataType.TEXT>,
|
||||
{
|
||||
id: 'addressStreet2',
|
||||
type: FieldMetadataType.TEXT,
|
||||
objectMetadataId: FieldMetadataType.ADDRESS.toString(),
|
||||
hidden: false,
|
||||
isRequired: false,
|
||||
},
|
||||
{
|
||||
name: 'addressStreet2',
|
||||
label: 'Address 2',
|
||||
targetColumnMap: {
|
||||
value: targetColumnMap.addressStreet2,
|
||||
},
|
||||
isNullable: true,
|
||||
...(inferredFieldMetadata
|
||||
? {
|
||||
defaultValue:
|
||||
inferredFieldMetadata.defaultValue?.addressStreet2 ?? null,
|
||||
}
|
||||
: {}),
|
||||
} satisfies FieldMetadataInterface<FieldMetadataType.TEXT>,
|
||||
{
|
||||
id: 'addressCity',
|
||||
type: FieldMetadataType.TEXT,
|
||||
objectMetadataId: FieldMetadataType.ADDRESS.toString(),
|
||||
hidden: false,
|
||||
isRequired: false,
|
||||
},
|
||||
{
|
||||
name: 'addressCity',
|
||||
label: 'City',
|
||||
targetColumnMap: {
|
||||
value: targetColumnMap.addressCity,
|
||||
},
|
||||
isNullable: true,
|
||||
...(inferredFieldMetadata
|
||||
? {
|
||||
defaultValue:
|
||||
inferredFieldMetadata.defaultValue?.addressCity ?? null,
|
||||
}
|
||||
: {}),
|
||||
} satisfies FieldMetadataInterface<FieldMetadataType.TEXT>,
|
||||
{
|
||||
id: 'addressPostcode',
|
||||
type: FieldMetadataType.TEXT,
|
||||
objectMetadataId: FieldMetadataType.ADDRESS.toString(),
|
||||
hidden: false,
|
||||
isRequired: false,
|
||||
},
|
||||
{
|
||||
name: 'addressPostcode',
|
||||
label: 'Postcode',
|
||||
targetColumnMap: {
|
||||
value: targetColumnMap.addressPostcode,
|
||||
},
|
||||
isNullable: true,
|
||||
...(inferredFieldMetadata
|
||||
? {
|
||||
defaultValue:
|
||||
inferredFieldMetadata.defaultValue?.addressPostcode ?? null,
|
||||
}
|
||||
: {}),
|
||||
} satisfies FieldMetadataInterface<FieldMetadataType.TEXT>,
|
||||
{
|
||||
id: 'addressState',
|
||||
type: FieldMetadataType.TEXT,
|
||||
objectMetadataId: FieldMetadataType.ADDRESS.toString(),
|
||||
hidden: false,
|
||||
isRequired: false,
|
||||
},
|
||||
{
|
||||
name: 'addressState',
|
||||
label: 'State',
|
||||
targetColumnMap: {
|
||||
value: targetColumnMap.addressState,
|
||||
},
|
||||
isNullable: true,
|
||||
...(inferredFieldMetadata
|
||||
? {
|
||||
defaultValue:
|
||||
inferredFieldMetadata.defaultValue?.addressState ?? null,
|
||||
}
|
||||
: {}),
|
||||
} satisfies FieldMetadataInterface<FieldMetadataType.TEXT>,
|
||||
{
|
||||
id: 'addressCountry',
|
||||
type: FieldMetadataType.TEXT,
|
||||
objectMetadataId: FieldMetadataType.ADDRESS.toString(),
|
||||
hidden: false,
|
||||
isRequired: false,
|
||||
},
|
||||
{
|
||||
name: 'addressCountry',
|
||||
label: 'Country',
|
||||
targetColumnMap: {
|
||||
value: targetColumnMap.addressCountry,
|
||||
},
|
||||
isNullable: true,
|
||||
...(inferredFieldMetadata
|
||||
? {
|
||||
defaultValue:
|
||||
inferredFieldMetadata.defaultValue?.addressCountry ?? null,
|
||||
}
|
||||
: {}),
|
||||
} satisfies FieldMetadataInterface<FieldMetadataType.TEXT>,
|
||||
type: FieldMetadataType.TEXT,
|
||||
hidden: false,
|
||||
isRequired: false,
|
||||
},
|
||||
{
|
||||
id: 'addressLat',
|
||||
type: FieldMetadataType.NUMBER,
|
||||
objectMetadataId: FieldMetadataType.ADDRESS.toString(),
|
||||
name: 'addressLat',
|
||||
label: 'Latitude',
|
||||
targetColumnMap: {
|
||||
value: targetColumnMap.addressLat,
|
||||
},
|
||||
isNullable: true,
|
||||
...(inferredFieldMetadata
|
||||
? {
|
||||
defaultValue:
|
||||
inferredFieldMetadata.defaultValue?.addressLat ?? null,
|
||||
}
|
||||
: {}),
|
||||
} satisfies FieldMetadataInterface<FieldMetadataType.NUMBER>,
|
||||
type: FieldMetadataType.NUMERIC,
|
||||
hidden: false,
|
||||
isRequired: false,
|
||||
},
|
||||
{
|
||||
id: 'addressLng',
|
||||
type: FieldMetadataType.NUMBER,
|
||||
objectMetadataId: FieldMetadataType.ADDRESS.toString(),
|
||||
name: 'addressLng',
|
||||
label: 'Longitude',
|
||||
targetColumnMap: {
|
||||
value: targetColumnMap.addressLng,
|
||||
},
|
||||
isNullable: true,
|
||||
...(inferredFieldMetadata
|
||||
? {
|
||||
defaultValue:
|
||||
inferredFieldMetadata.defaultValue?.addressLng ?? null,
|
||||
}
|
||||
: {}),
|
||||
} satisfies FieldMetadataInterface<FieldMetadataType.NUMBER>,
|
||||
];
|
||||
type: FieldMetadataType.NUMERIC,
|
||||
hidden: false,
|
||||
isRequired: false,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const addressObjectDefinition = {
|
||||
id: FieldMetadataType.ADDRESS.toString(),
|
||||
nameSingular: 'address',
|
||||
namePlural: 'address',
|
||||
labelSingular: 'Address',
|
||||
labelPlural: 'Addresses',
|
||||
targetTableName: '',
|
||||
fields: addressFields(),
|
||||
fromRelations: [],
|
||||
toRelations: [],
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
isCustom: false,
|
||||
isRemote: false,
|
||||
} satisfies ObjectMetadataInterface;
|
||||
|
||||
export type AddressMetadata = {
|
||||
addressStreet1: string;
|
||||
addressStreet2: string;
|
||||
|
||||
@ -1,80 +1,25 @@
|
||||
import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface';
|
||||
import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface';
|
||||
import { CompositeType } from 'src/engine/metadata-modules/field-metadata/interfaces/composite-type.interface';
|
||||
|
||||
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { generateTargetColumnMap } from 'src/engine/metadata-modules/field-metadata/utils/generate-target-column-map.util';
|
||||
|
||||
export const currencyFields = (
|
||||
fieldMetadata?: FieldMetadataInterface,
|
||||
): FieldMetadataInterface[] => {
|
||||
const inferredFieldMetadata = fieldMetadata as
|
||||
| FieldMetadataInterface<FieldMetadataType.CURRENCY>
|
||||
| undefined;
|
||||
const targetColumnMap = inferredFieldMetadata
|
||||
? generateTargetColumnMap(
|
||||
inferredFieldMetadata.type,
|
||||
inferredFieldMetadata.isCustom ?? false,
|
||||
inferredFieldMetadata.name,
|
||||
)
|
||||
: {
|
||||
amountMicros: 'amountMicros',
|
||||
currencyCode: 'currencyCode',
|
||||
};
|
||||
|
||||
return [
|
||||
export const currencyCompositeType: CompositeType = {
|
||||
type: FieldMetadataType.CURRENCY,
|
||||
properties: [
|
||||
{
|
||||
id: 'amountMicros',
|
||||
type: FieldMetadataType.NUMERIC,
|
||||
objectMetadataId: FieldMetadataType.CURRENCY.toString(),
|
||||
name: 'amountMicros',
|
||||
label: 'AmountMicros',
|
||||
targetColumnMap: {
|
||||
value: targetColumnMap.amountMicros,
|
||||
},
|
||||
isNullable: true,
|
||||
...(inferredFieldMetadata
|
||||
? {
|
||||
defaultValue:
|
||||
inferredFieldMetadata.defaultValue?.amountMicros ?? null,
|
||||
}
|
||||
: {}),
|
||||
} satisfies FieldMetadataInterface<FieldMetadataType.NUMERIC>,
|
||||
type: FieldMetadataType.NUMERIC,
|
||||
hidden: false,
|
||||
isRequired: false,
|
||||
},
|
||||
{
|
||||
id: 'currencyCode',
|
||||
type: FieldMetadataType.TEXT,
|
||||
objectMetadataId: FieldMetadataType.CURRENCY.toString(),
|
||||
name: 'currencyCode',
|
||||
label: 'Currency Code',
|
||||
targetColumnMap: {
|
||||
value: targetColumnMap.currencyCode,
|
||||
},
|
||||
isNullable: true,
|
||||
...(inferredFieldMetadata
|
||||
? {
|
||||
defaultValue:
|
||||
inferredFieldMetadata.defaultValue?.currencyCode ?? null,
|
||||
}
|
||||
: {}),
|
||||
} satisfies FieldMetadataInterface<FieldMetadataType.TEXT>,
|
||||
];
|
||||
type: FieldMetadataType.TEXT,
|
||||
hidden: false,
|
||||
isRequired: false,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const currencyObjectDefinition = {
|
||||
id: FieldMetadataType.CURRENCY.toString(),
|
||||
nameSingular: 'currency',
|
||||
namePlural: 'currency',
|
||||
labelSingular: 'Currency',
|
||||
labelPlural: 'Currency',
|
||||
targetTableName: '',
|
||||
fields: currencyFields(),
|
||||
fromRelations: [],
|
||||
toRelations: [],
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
isCustom: false,
|
||||
isRemote: false,
|
||||
} satisfies ObjectMetadataInterface;
|
||||
|
||||
export type CurrencyMetadata = {
|
||||
amountMicros: number;
|
||||
currencyCode: string;
|
||||
|
||||
@ -1,78 +1,25 @@
|
||||
import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface';
|
||||
import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface';
|
||||
import { CompositeType } from 'src/engine/metadata-modules/field-metadata/interfaces/composite-type.interface';
|
||||
|
||||
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { generateTargetColumnMap } from 'src/engine/metadata-modules/field-metadata/utils/generate-target-column-map.util';
|
||||
|
||||
export const fullNameFields = (
|
||||
fieldMetadata?: FieldMetadataInterface,
|
||||
): FieldMetadataInterface[] => {
|
||||
const inferredFieldMetadata = fieldMetadata as
|
||||
| FieldMetadataInterface<FieldMetadataType.FULL_NAME>
|
||||
| undefined;
|
||||
const targetColumnMap = inferredFieldMetadata
|
||||
? generateTargetColumnMap(
|
||||
inferredFieldMetadata.type,
|
||||
inferredFieldMetadata.isCustom ?? false,
|
||||
inferredFieldMetadata.name,
|
||||
)
|
||||
: {
|
||||
firstName: 'firstName',
|
||||
lastName: 'lastName',
|
||||
};
|
||||
|
||||
return [
|
||||
export const fullNameCompositeType: CompositeType = {
|
||||
type: FieldMetadataType.FULL_NAME,
|
||||
properties: [
|
||||
{
|
||||
id: 'firstName',
|
||||
type: FieldMetadataType.TEXT,
|
||||
objectMetadataId: FieldMetadataType.FULL_NAME.toString(),
|
||||
name: 'firstName',
|
||||
label: 'First Name',
|
||||
targetColumnMap: {
|
||||
value: targetColumnMap.firstName,
|
||||
},
|
||||
isNullable: true,
|
||||
...(inferredFieldMetadata
|
||||
? {
|
||||
defaultValue: inferredFieldMetadata.defaultValue?.firstName ?? null,
|
||||
}
|
||||
: {}),
|
||||
} satisfies FieldMetadataInterface<FieldMetadataType.TEXT>,
|
||||
{
|
||||
id: 'lastName',
|
||||
type: FieldMetadataType.TEXT,
|
||||
objectMetadataId: FieldMetadataType.FULL_NAME.toString(),
|
||||
hidden: false,
|
||||
isRequired: false,
|
||||
},
|
||||
{
|
||||
name: 'lastName',
|
||||
label: 'Last Name',
|
||||
targetColumnMap: {
|
||||
value: targetColumnMap.lastName,
|
||||
},
|
||||
isNullable: true,
|
||||
...(inferredFieldMetadata
|
||||
? {
|
||||
defaultValue: inferredFieldMetadata.defaultValue?.lastName ?? null,
|
||||
}
|
||||
: {}),
|
||||
} satisfies FieldMetadataInterface<FieldMetadataType.TEXT>,
|
||||
];
|
||||
type: FieldMetadataType.TEXT,
|
||||
hidden: false,
|
||||
isRequired: false,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const fullNameObjectDefinition = {
|
||||
id: FieldMetadataType.FULL_NAME.toString(),
|
||||
nameSingular: 'fullName',
|
||||
namePlural: 'fullName',
|
||||
labelSingular: 'FullName',
|
||||
labelPlural: 'FullName',
|
||||
targetTableName: '',
|
||||
fields: fullNameFields(),
|
||||
fromRelations: [],
|
||||
toRelations: [],
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
isCustom: false,
|
||||
isRemote: false,
|
||||
} satisfies ObjectMetadataInterface;
|
||||
|
||||
export type FullNameMetadata = {
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
|
||||
@ -1,21 +1,22 @@
|
||||
import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface';
|
||||
import { CompositeType } from 'src/engine/metadata-modules/field-metadata/interfaces/composite-type.interface';
|
||||
|
||||
import { currencyFields } from 'src/engine/metadata-modules/field-metadata/composite-types/currency.composite-type';
|
||||
import { fullNameFields } from 'src/engine/metadata-modules/field-metadata/composite-types/full-name.composite-type';
|
||||
import { linkFields } from 'src/engine/metadata-modules/field-metadata/composite-types/link.composite-type';
|
||||
import { currencyCompositeType } from 'src/engine/metadata-modules/field-metadata/composite-types/currency.composite-type';
|
||||
import { fullNameCompositeType } from 'src/engine/metadata-modules/field-metadata/composite-types/full-name.composite-type';
|
||||
import { linkCompositeType } from 'src/engine/metadata-modules/field-metadata/composite-types/link.composite-type';
|
||||
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { addressFields } from 'src/engine/metadata-modules/field-metadata/composite-types/address.composite-type';
|
||||
import { addressCompositeType } from 'src/engine/metadata-modules/field-metadata/composite-types/address.composite-type';
|
||||
|
||||
export type CompositeFieldsDefinitionFunction = (
|
||||
fieldMetadata?: FieldMetadataInterface,
|
||||
) => FieldMetadataInterface[];
|
||||
|
||||
export const compositeDefinitions = new Map<
|
||||
string,
|
||||
CompositeFieldsDefinitionFunction
|
||||
export const compositeTypeDefintions = new Map<
|
||||
FieldMetadataType,
|
||||
CompositeType
|
||||
>([
|
||||
[FieldMetadataType.LINK, linkFields],
|
||||
[FieldMetadataType.CURRENCY, currencyFields],
|
||||
[FieldMetadataType.FULL_NAME, fullNameFields],
|
||||
[FieldMetadataType.ADDRESS, addressFields],
|
||||
[FieldMetadataType.LINK, linkCompositeType],
|
||||
[FieldMetadataType.CURRENCY, currencyCompositeType],
|
||||
[FieldMetadataType.FULL_NAME, fullNameCompositeType],
|
||||
[FieldMetadataType.ADDRESS, addressCompositeType],
|
||||
]);
|
||||
|
||||
@ -1,78 +1,25 @@
|
||||
import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface';
|
||||
import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface';
|
||||
import { CompositeType } from 'src/engine/metadata-modules/field-metadata/interfaces/composite-type.interface';
|
||||
|
||||
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { generateTargetColumnMap } from 'src/engine/metadata-modules/field-metadata/utils/generate-target-column-map.util';
|
||||
|
||||
export const linkFields = (
|
||||
fieldMetadata?: FieldMetadataInterface,
|
||||
): FieldMetadataInterface[] => {
|
||||
const inferredFieldMetadata = fieldMetadata as
|
||||
| FieldMetadataInterface<FieldMetadataType.LINK>
|
||||
| undefined;
|
||||
const targetColumnMap = inferredFieldMetadata
|
||||
? generateTargetColumnMap(
|
||||
inferredFieldMetadata.type,
|
||||
inferredFieldMetadata.isCustom ?? false,
|
||||
inferredFieldMetadata.name,
|
||||
)
|
||||
: {
|
||||
label: 'label',
|
||||
url: 'url',
|
||||
};
|
||||
|
||||
return [
|
||||
export const linkCompositeType: CompositeType = {
|
||||
type: FieldMetadataType.LINK,
|
||||
properties: [
|
||||
{
|
||||
id: 'label',
|
||||
type: FieldMetadataType.TEXT,
|
||||
objectMetadataId: FieldMetadataType.LINK.toString(),
|
||||
name: 'label',
|
||||
label: 'Label',
|
||||
targetColumnMap: {
|
||||
value: targetColumnMap.label,
|
||||
},
|
||||
isNullable: true,
|
||||
...(inferredFieldMetadata
|
||||
? {
|
||||
defaultValue: inferredFieldMetadata.defaultValue?.label ?? null,
|
||||
}
|
||||
: {}),
|
||||
} satisfies FieldMetadataInterface<FieldMetadataType.TEXT>,
|
||||
{
|
||||
id: 'url',
|
||||
type: FieldMetadataType.TEXT,
|
||||
objectMetadataId: FieldMetadataType.LINK.toString(),
|
||||
hidden: false,
|
||||
isRequired: false,
|
||||
},
|
||||
{
|
||||
name: 'url',
|
||||
label: 'Url',
|
||||
targetColumnMap: {
|
||||
value: targetColumnMap.url,
|
||||
},
|
||||
isNullable: true,
|
||||
...(inferredFieldMetadata
|
||||
? {
|
||||
defaultValue: inferredFieldMetadata.defaultValue?.url ?? null,
|
||||
}
|
||||
: {}),
|
||||
} satisfies FieldMetadataInterface<FieldMetadataType.TEXT>,
|
||||
];
|
||||
type: FieldMetadataType.TEXT,
|
||||
hidden: false,
|
||||
isRequired: false,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const linkObjectDefinition = {
|
||||
id: FieldMetadataType.LINK.toString(),
|
||||
nameSingular: 'link',
|
||||
namePlural: 'link',
|
||||
labelSingular: 'Link',
|
||||
labelPlural: 'Link',
|
||||
targetTableName: '',
|
||||
fields: linkFields(),
|
||||
fromRelations: [],
|
||||
toRelations: [],
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
isCustom: false,
|
||||
isRemote: false,
|
||||
} satisfies ObjectMetadataInterface;
|
||||
|
||||
export type LinkMetadata = {
|
||||
label: string;
|
||||
url: string;
|
||||
|
||||
@ -11,7 +11,6 @@ import {
|
||||
} from 'typeorm';
|
||||
|
||||
import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface';
|
||||
import { FieldMetadataTargetColumnMap } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-target-column-map.interface';
|
||||
import { FieldMetadataDefaultValue } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface';
|
||||
import { FieldMetadataOptions } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-options.interface';
|
||||
|
||||
@ -74,9 +73,6 @@ export class FieldMetadataEntity<
|
||||
@Column({ nullable: false })
|
||||
label: string;
|
||||
|
||||
@Column({ nullable: false, type: 'jsonb' })
|
||||
targetColumnMap: FieldMetadataTargetColumnMap<T>;
|
||||
|
||||
@Column({ nullable: true, type: 'jsonb' })
|
||||
defaultValue: FieldMetadataDefaultValue<T>;
|
||||
|
||||
|
||||
@ -19,7 +19,6 @@ import {
|
||||
WorkspaceMigrationColumnDrop,
|
||||
WorkspaceMigrationTableAction,
|
||||
} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
|
||||
import { generateTargetColumnMap } from 'src/engine/metadata-modules/field-metadata/utils/generate-target-column-map.util';
|
||||
import { TypeORMService } from 'src/database/typeorm/typeorm.service';
|
||||
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
|
||||
import { UpdateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/update-field.input';
|
||||
@ -37,7 +36,7 @@ import {
|
||||
RelationMetadataType,
|
||||
} from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
|
||||
import { DeleteOneFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/delete-field.input';
|
||||
import { computeCustomName } from 'src/engine/utils/compute-custom-name.util';
|
||||
import { computeColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util';
|
||||
import { assertMutationNotOnRemoteObject } from 'src/engine/metadata-modules/object-metadata/utils/assert-mutation-not-on-remote-object.util';
|
||||
|
||||
import {
|
||||
@ -128,11 +127,6 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
||||
|
||||
const createdFieldMetadata = await fieldMetadataRepository.save({
|
||||
...fieldMetadataInput,
|
||||
targetColumnMap: generateTargetColumnMap(
|
||||
fieldMetadataInput.type,
|
||||
!fieldMetadataInput.isRemoteCreation,
|
||||
fieldMetadataInput.name,
|
||||
),
|
||||
isNullable: generateNullable(
|
||||
fieldMetadataInput.type,
|
||||
fieldMetadataInput.isNullable,
|
||||
@ -318,14 +312,6 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
||||
: updatableFieldInput.defaultValue !== null
|
||||
? updatableFieldInput.defaultValue
|
||||
: null,
|
||||
// If the name is updated, the targetColumnMap should be updated as well
|
||||
targetColumnMap: updatableFieldInput.name
|
||||
? generateTargetColumnMap(
|
||||
existingFieldMetadata.type,
|
||||
existingFieldMetadata.isCustom,
|
||||
updatableFieldInput.name,
|
||||
)
|
||||
: existingFieldMetadata.targetColumnMap,
|
||||
});
|
||||
const updatedFieldMetadata = await fieldMetadataRepository.findOneOrFail({
|
||||
where: { id },
|
||||
@ -417,10 +403,7 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.DROP,
|
||||
columnName: computeCustomName(
|
||||
fieldMetadata.name,
|
||||
fieldMetadata.isCustom,
|
||||
),
|
||||
columnName: computeColumnName(fieldMetadata),
|
||||
} satisfies WorkspaceMigrationColumnDrop,
|
||||
],
|
||||
} satisfies WorkspaceMigrationTableAction,
|
||||
|
||||
@ -0,0 +1,14 @@
|
||||
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
|
||||
export interface CompositeProperty {
|
||||
name: string;
|
||||
description?: string;
|
||||
type: FieldMetadataType;
|
||||
hidden: 'input' | 'output' | true | false;
|
||||
isRequired: boolean;
|
||||
}
|
||||
|
||||
export interface CompositeType {
|
||||
type: FieldMetadataType;
|
||||
properties: CompositeProperty[];
|
||||
}
|
||||
@ -1,54 +0,0 @@
|
||||
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
|
||||
export interface FieldMetadataTargetColumnMapValue {
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface FieldMetadataTargetColumnMapLink {
|
||||
label: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface FieldMetadataTargetColumnMapCurrency {
|
||||
amountMicros: string;
|
||||
currencyCode: string;
|
||||
}
|
||||
|
||||
export interface FieldMetadataTargetColumnMapFullName {
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
}
|
||||
|
||||
export type FieldMetadataTargetColumnMapAddress = {
|
||||
addressStreet1: string;
|
||||
addressStreet2: string;
|
||||
addressCity: string;
|
||||
addressState: string;
|
||||
addressZipCode: string;
|
||||
addressCountry: string;
|
||||
addressLat: number;
|
||||
addressLng: number;
|
||||
};
|
||||
|
||||
type AllFieldMetadataTypes = {
|
||||
[key: string]: string;
|
||||
};
|
||||
|
||||
type FieldMetadataTypeMapping = {
|
||||
[FieldMetadataType.LINK]: FieldMetadataTargetColumnMapLink;
|
||||
[FieldMetadataType.CURRENCY]: FieldMetadataTargetColumnMapCurrency;
|
||||
[FieldMetadataType.FULL_NAME]: FieldMetadataTargetColumnMapFullName;
|
||||
[FieldMetadataType.ADDRESS]: FieldMetadataTargetColumnMapAddress;
|
||||
};
|
||||
|
||||
type TypeByFieldMetadata<T extends FieldMetadataType | 'default'> = [
|
||||
T,
|
||||
] extends [keyof FieldMetadataTypeMapping]
|
||||
? FieldMetadataTypeMapping[T]
|
||||
: T extends 'default'
|
||||
? AllFieldMetadataTypes
|
||||
: FieldMetadataTargetColumnMapValue;
|
||||
|
||||
export type FieldMetadataTargetColumnMap<
|
||||
T extends FieldMetadataType | 'default' = 'default',
|
||||
> = TypeByFieldMetadata<T>;
|
||||
@ -1,4 +1,3 @@
|
||||
import { FieldMetadataTargetColumnMap } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-target-column-map.interface';
|
||||
import { FieldMetadataDefaultValue } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface';
|
||||
import { FieldMetadataOptions } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-options.interface';
|
||||
|
||||
@ -12,7 +11,6 @@ export interface FieldMetadataInterface<
|
||||
type: FieldMetadataType;
|
||||
name: string;
|
||||
label: string;
|
||||
targetColumnMap: FieldMetadataTargetColumnMap<T>;
|
||||
defaultValue?: FieldMetadataDefaultValue<T>;
|
||||
options?: FieldMetadataOptions<T>;
|
||||
objectMetadataId: string;
|
||||
|
||||
@ -1,41 +0,0 @@
|
||||
import { BadRequestException } from '@nestjs/common';
|
||||
|
||||
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { generateTargetColumnMap } from 'src/engine/metadata-modules/field-metadata/utils/generate-target-column-map.util';
|
||||
|
||||
describe('generateTargetColumnMap', () => {
|
||||
it('should generate a target column map for a given type', () => {
|
||||
const textMap = generateTargetColumnMap(
|
||||
FieldMetadataType.TEXT,
|
||||
false,
|
||||
'name',
|
||||
);
|
||||
|
||||
expect(textMap).toEqual({ value: 'name' });
|
||||
|
||||
const linkMap = generateTargetColumnMap(
|
||||
FieldMetadataType.LINK,
|
||||
false,
|
||||
'website',
|
||||
);
|
||||
|
||||
expect(linkMap).toEqual({ label: 'websiteLabel', url: 'websiteUrl' });
|
||||
|
||||
const currencyMap = generateTargetColumnMap(
|
||||
FieldMetadataType.CURRENCY,
|
||||
true,
|
||||
'price',
|
||||
);
|
||||
|
||||
expect(currencyMap).toEqual({
|
||||
amountMicros: '_priceAmountMicros',
|
||||
currencyCode: '_priceCurrencyCode',
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw an error for an unknown type', () => {
|
||||
expect(() =>
|
||||
generateTargetColumnMap('invalid' as FieldMetadataType, false, 'name'),
|
||||
).toThrow(BadRequestException);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,53 @@
|
||||
import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface';
|
||||
import { CompositeProperty } from 'src/engine/metadata-modules/field-metadata/interfaces/composite-type.interface';
|
||||
|
||||
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||
import { pascalCase } from 'src/utils/pascal-case';
|
||||
|
||||
type ComputeColumnNameOptions = { isForeignKey?: boolean };
|
||||
|
||||
export function computeColumnName(
|
||||
fieldName: string,
|
||||
options?: ComputeColumnNameOptions,
|
||||
): string;
|
||||
export function computeColumnName<T extends FieldMetadataType | 'default'>(
|
||||
fieldMetadata: FieldMetadataInterface<T>,
|
||||
ioptions?: ComputeColumnNameOptions,
|
||||
): string;
|
||||
// TODO: If we need to implement custom name logic for columns, we can do it here
|
||||
export function computeColumnName<T extends FieldMetadataType | 'default'>(
|
||||
fieldMetadataOrFieldName: FieldMetadataInterface<T> | string,
|
||||
options?: ComputeColumnNameOptions,
|
||||
): string {
|
||||
const generateName = (name: string) => {
|
||||
return options?.isForeignKey ? `${name}Id` : name;
|
||||
};
|
||||
|
||||
if (typeof fieldMetadataOrFieldName === 'string') {
|
||||
return generateName(fieldMetadataOrFieldName);
|
||||
}
|
||||
|
||||
if (isCompositeFieldMetadataType(fieldMetadataOrFieldName.type)) {
|
||||
throw new Error(
|
||||
`Cannot compute column name for composite field metadata type: ${fieldMetadataOrFieldName.type}`,
|
||||
);
|
||||
}
|
||||
|
||||
return generateName(fieldMetadataOrFieldName.name);
|
||||
}
|
||||
|
||||
export const computeCompositeColumnName = <
|
||||
T extends FieldMetadataType | 'default',
|
||||
>(
|
||||
fieldMetadata: FieldMetadataInterface<T>,
|
||||
compositeProperty: CompositeProperty,
|
||||
): string => {
|
||||
if (!isCompositeFieldMetadataType(fieldMetadata.type)) {
|
||||
throw new Error(
|
||||
`Cannot compute composite column name for non-composite field metadata type: ${fieldMetadata.type}`,
|
||||
);
|
||||
}
|
||||
|
||||
return `${fieldMetadata.name}${pascalCase(compositeProperty.name)}`;
|
||||
};
|
||||
@ -1,73 +0,0 @@
|
||||
import { BadRequestException } from '@nestjs/common';
|
||||
|
||||
import { FieldMetadataTargetColumnMap } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-target-column-map.interface';
|
||||
|
||||
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { createCustomColumnName } from 'src/engine/utils/create-custom-column-name.util';
|
||||
|
||||
/**
|
||||
* Generate a target column map for a given type, this is used to map the field to the correct column(s) in the database.
|
||||
* This is used to support fields that map to multiple columns in the database.
|
||||
*
|
||||
* @param type string
|
||||
* @returns FieldMetadataTargetColumnMap
|
||||
*/
|
||||
export function generateTargetColumnMap(
|
||||
type: FieldMetadataType,
|
||||
isCustomField: boolean,
|
||||
fieldName: string,
|
||||
): FieldMetadataTargetColumnMap {
|
||||
const columnName = isCustomField
|
||||
? createCustomColumnName(fieldName)
|
||||
: fieldName;
|
||||
|
||||
switch (type) {
|
||||
case FieldMetadataType.UUID:
|
||||
case FieldMetadataType.TEXT:
|
||||
case FieldMetadataType.PHONE:
|
||||
case FieldMetadataType.EMAIL:
|
||||
case FieldMetadataType.NUMBER:
|
||||
case FieldMetadataType.NUMERIC:
|
||||
case FieldMetadataType.PROBABILITY:
|
||||
case FieldMetadataType.BOOLEAN:
|
||||
case FieldMetadataType.DATE_TIME:
|
||||
case FieldMetadataType.RATING:
|
||||
case FieldMetadataType.SELECT:
|
||||
case FieldMetadataType.MULTI_SELECT:
|
||||
case FieldMetadataType.POSITION:
|
||||
case FieldMetadataType.RAW_JSON:
|
||||
return {
|
||||
value: columnName,
|
||||
};
|
||||
case FieldMetadataType.LINK:
|
||||
return {
|
||||
label: `${columnName}Label`,
|
||||
url: `${columnName}Url`,
|
||||
};
|
||||
case FieldMetadataType.CURRENCY:
|
||||
return {
|
||||
amountMicros: `${columnName}AmountMicros`,
|
||||
currencyCode: `${columnName}CurrencyCode`,
|
||||
};
|
||||
case FieldMetadataType.FULL_NAME:
|
||||
return {
|
||||
firstName: `${columnName}FirstName`,
|
||||
lastName: `${columnName}LastName`,
|
||||
};
|
||||
case FieldMetadataType.ADDRESS:
|
||||
return {
|
||||
addressStreet1: `${columnName}AddressStreet1`,
|
||||
addressStreet2: `${columnName}AddressStreet2`,
|
||||
addressCity: `${columnName}AddressCity`,
|
||||
addressPostcode: `${columnName}AddressPostcode`,
|
||||
addressState: `${columnName}AddressState`,
|
||||
addressCountry: `${columnName}AddressCountry`,
|
||||
addressLat: `${columnName}AddressLat`,
|
||||
addressLng: `${columnName}AddressLng`,
|
||||
};
|
||||
case FieldMetadataType.RELATION:
|
||||
return {};
|
||||
default:
|
||||
throw new BadRequestException(`Unknown type ${type}`);
|
||||
}
|
||||
}
|
||||
@ -28,7 +28,7 @@ import {
|
||||
RelationMetadataType,
|
||||
RelationOnDeleteAction,
|
||||
} from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
|
||||
import { computeCustomName } from 'src/engine/utils/compute-custom-name.util';
|
||||
import { computeTableName } from 'src/engine/utils/compute-table-name.util';
|
||||
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
|
||||
import { DeleteOneObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/delete-object.input';
|
||||
import { RelationToDelete } from 'src/engine/metadata-modules/relation-metadata/types/relation-to-delete';
|
||||
@ -47,6 +47,7 @@ import {
|
||||
} from 'src/engine/workspace-manager/workspace-sync-metadata/utils/create-deterministic-uuid.util';
|
||||
import { buildWorkspaceMigrationsForCustomObject } from 'src/engine/metadata-modules/object-metadata/utils/build-workspace-migrations-for-custom-object.util';
|
||||
import { buildWorkspaceMigrationsForRemoteObject } from 'src/engine/metadata-modules/object-metadata/utils/build-workspace-migrations-for-remote-object.util';
|
||||
import { computeColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util';
|
||||
|
||||
import { ObjectMetadataEntity } from './object-metadata.entity';
|
||||
|
||||
@ -172,7 +173,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
workspaceId,
|
||||
[
|
||||
{
|
||||
name: computeCustomName(
|
||||
name: computeTableName(
|
||||
relationToDelete.toObjectName,
|
||||
relationToDelete.toObjectMetadataIsCustom,
|
||||
),
|
||||
@ -180,9 +181,9 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.DROP,
|
||||
columnName: computeCustomName(
|
||||
`${relationToDelete.toFieldMetadataName}Id`,
|
||||
relationToDelete.toFieldMetadataIsCustom,
|
||||
columnName: computeColumnName(
|
||||
relationToDelete.toFieldMetadataName,
|
||||
{ isForeignKey: true },
|
||||
),
|
||||
} satisfies WorkspaceMigrationColumnDrop,
|
||||
],
|
||||
@ -252,9 +253,6 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
type: FieldMetadataType.UUID,
|
||||
name: 'id',
|
||||
label: 'Id',
|
||||
targetColumnMap: {
|
||||
value: 'id',
|
||||
},
|
||||
icon: 'Icon123',
|
||||
description: 'Id',
|
||||
isNullable: false,
|
||||
@ -269,9 +267,6 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
type: FieldMetadataType.TEXT,
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
targetColumnMap: {
|
||||
value: 'name',
|
||||
},
|
||||
icon: 'IconAbc',
|
||||
description: 'Name',
|
||||
isNullable: false,
|
||||
@ -285,9 +280,6 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
type: FieldMetadataType.DATE_TIME,
|
||||
name: 'createdAt',
|
||||
label: 'Creation date',
|
||||
targetColumnMap: {
|
||||
value: 'createdAt',
|
||||
},
|
||||
icon: 'IconCalendar',
|
||||
description: 'Creation date',
|
||||
isNullable: false,
|
||||
@ -301,9 +293,6 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
type: FieldMetadataType.DATE_TIME,
|
||||
name: 'updatedAt',
|
||||
label: 'Update date',
|
||||
targetColumnMap: {
|
||||
value: 'updatedAt',
|
||||
},
|
||||
icon: 'IconCalendar',
|
||||
description: 'Update date',
|
||||
isNullable: false,
|
||||
@ -318,9 +307,6 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
type: FieldMetadataType.POSITION,
|
||||
name: 'position',
|
||||
label: 'Position',
|
||||
targetColumnMap: {
|
||||
value: 'position',
|
||||
},
|
||||
icon: 'IconHierarchy2',
|
||||
description: 'Position',
|
||||
isNullable: true,
|
||||
@ -518,7 +504,6 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'activityTargets',
|
||||
label: 'Activities',
|
||||
targetColumnMap: {},
|
||||
description: `Activities tied to the ${createdObjectMetadata.labelSingular}`,
|
||||
icon: 'IconCheckbox',
|
||||
isNullable: true,
|
||||
@ -536,7 +521,6 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: createdObjectMetadata.nameSingular,
|
||||
label: createdObjectMetadata.labelSingular,
|
||||
targetColumnMap: {},
|
||||
description: `ActivityTarget ${createdObjectMetadata.labelSingular}`,
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
isNullable: true,
|
||||
@ -554,12 +538,6 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
type: FieldMetadataType.UUID,
|
||||
name: `${createdObjectMetadata.nameSingular}Id`,
|
||||
label: `${createdObjectMetadata.labelSingular} ID (foreign key)`,
|
||||
targetColumnMap: {
|
||||
value: `${computeCustomName(
|
||||
createdObjectMetadata.nameSingular,
|
||||
false,
|
||||
)}Id`,
|
||||
},
|
||||
description: `ActivityTarget ${createdObjectMetadata.labelSingular} id foreign key`,
|
||||
icon: undefined,
|
||||
isNullable: true,
|
||||
@ -621,7 +599,6 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'attachments',
|
||||
label: 'Attachments',
|
||||
targetColumnMap: {},
|
||||
description: `Attachments tied to the ${createdObjectMetadata.labelSingular}`,
|
||||
icon: 'IconFileImport',
|
||||
isNullable: true,
|
||||
@ -639,7 +616,6 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: createdObjectMetadata.nameSingular,
|
||||
label: createdObjectMetadata.labelSingular,
|
||||
targetColumnMap: {},
|
||||
description: `Attachment ${createdObjectMetadata.labelSingular}`,
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
isNullable: true,
|
||||
@ -657,12 +633,6 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
type: FieldMetadataType.UUID,
|
||||
name: `${createdObjectMetadata.nameSingular}Id`,
|
||||
label: `${createdObjectMetadata.labelSingular} ID (foreign key)`,
|
||||
targetColumnMap: {
|
||||
value: `${computeCustomName(
|
||||
createdObjectMetadata.nameSingular,
|
||||
false,
|
||||
)}Id`,
|
||||
},
|
||||
description: `Attachment ${createdObjectMetadata.labelSingular} id foreign key`,
|
||||
icon: undefined,
|
||||
isNullable: true,
|
||||
@ -721,7 +691,6 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'events',
|
||||
label: 'Events',
|
||||
targetColumnMap: {},
|
||||
description: `Events tied to the ${createdObjectMetadata.labelSingular}`,
|
||||
icon: 'IconFileImport',
|
||||
isNullable: true,
|
||||
@ -739,7 +708,6 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: createdObjectMetadata.nameSingular,
|
||||
label: createdObjectMetadata.labelSingular,
|
||||
targetColumnMap: {},
|
||||
description: `Event ${createdObjectMetadata.labelSingular}`,
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
isNullable: true,
|
||||
@ -757,12 +725,6 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
type: FieldMetadataType.UUID,
|
||||
name: `${createdObjectMetadata.nameSingular}Id`,
|
||||
label: `${createdObjectMetadata.labelSingular} ID (foreign key)`,
|
||||
targetColumnMap: {
|
||||
value: `${computeCustomName(
|
||||
createdObjectMetadata.nameSingular,
|
||||
false,
|
||||
)}Id`,
|
||||
},
|
||||
description: `Event ${createdObjectMetadata.labelSingular} id foreign key`,
|
||||
icon: undefined,
|
||||
isNullable: true,
|
||||
@ -822,7 +784,6 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'favorites',
|
||||
label: 'Favorites',
|
||||
targetColumnMap: {},
|
||||
description: `Favorites tied to the ${createdObjectMetadata.labelSingular}`,
|
||||
icon: 'IconHeart',
|
||||
isNullable: true,
|
||||
@ -840,7 +801,6 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: createdObjectMetadata.nameSingular,
|
||||
label: createdObjectMetadata.labelSingular,
|
||||
targetColumnMap: {},
|
||||
description: `Favorite ${createdObjectMetadata.labelSingular}`,
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
isNullable: true,
|
||||
@ -858,12 +818,6 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
type: FieldMetadataType.UUID,
|
||||
name: `${createdObjectMetadata.nameSingular}Id`,
|
||||
label: `${createdObjectMetadata.labelSingular} ID (foreign key)`,
|
||||
targetColumnMap: {
|
||||
value: `${computeCustomName(
|
||||
createdObjectMetadata.nameSingular,
|
||||
false,
|
||||
)}Id`,
|
||||
},
|
||||
description: `Favorite ${createdObjectMetadata.labelSingular} id foreign key`,
|
||||
icon: undefined,
|
||||
isNullable: true,
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
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 { RelationOnDeleteAction } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
|
||||
import {
|
||||
@ -5,7 +6,6 @@ import {
|
||||
WorkspaceMigrationColumnActionType,
|
||||
WorkspaceMigrationColumnCreate,
|
||||
} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
|
||||
import { computeCustomName } from 'src/engine/utils/compute-custom-name.util';
|
||||
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
|
||||
|
||||
export const buildWorkspaceMigrationsForCustomObject = (
|
||||
@ -26,10 +26,9 @@ export const buildWorkspaceMigrationsForCustomObject = (
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
||||
columnName: `${computeCustomName(
|
||||
createdObjectMetadata.nameSingular,
|
||||
false,
|
||||
)}Id`,
|
||||
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
|
||||
isForeignKey: true,
|
||||
}),
|
||||
columnType: 'uuid',
|
||||
isNullable: true,
|
||||
} satisfies WorkspaceMigrationColumnCreate,
|
||||
@ -41,10 +40,9 @@ export const buildWorkspaceMigrationsForCustomObject = (
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE_FOREIGN_KEY,
|
||||
columnName: `${computeCustomName(
|
||||
createdObjectMetadata.nameSingular,
|
||||
false,
|
||||
)}Id`,
|
||||
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
|
||||
isForeignKey: true,
|
||||
}),
|
||||
referencedTableName: computeObjectTargetTable(createdObjectMetadata),
|
||||
referencedTableColumnName: 'id',
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
@ -58,10 +56,9 @@ export const buildWorkspaceMigrationsForCustomObject = (
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
||||
columnName: `${computeCustomName(
|
||||
createdObjectMetadata.nameSingular,
|
||||
false,
|
||||
)}Id`,
|
||||
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
|
||||
isForeignKey: true,
|
||||
}),
|
||||
columnType: 'uuid',
|
||||
isNullable: true,
|
||||
} satisfies WorkspaceMigrationColumnCreate,
|
||||
@ -73,10 +70,9 @@ export const buildWorkspaceMigrationsForCustomObject = (
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE_FOREIGN_KEY,
|
||||
columnName: `${computeCustomName(
|
||||
createdObjectMetadata.nameSingular,
|
||||
false,
|
||||
)}Id`,
|
||||
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
|
||||
isForeignKey: true,
|
||||
}),
|
||||
referencedTableName: computeObjectTargetTable(createdObjectMetadata),
|
||||
referencedTableColumnName: 'id',
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
@ -90,10 +86,9 @@ export const buildWorkspaceMigrationsForCustomObject = (
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
||||
columnName: `${computeCustomName(
|
||||
createdObjectMetadata.nameSingular,
|
||||
false,
|
||||
)}Id`,
|
||||
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
|
||||
isForeignKey: true,
|
||||
}),
|
||||
columnType: 'uuid',
|
||||
isNullable: true,
|
||||
} satisfies WorkspaceMigrationColumnCreate,
|
||||
@ -105,10 +100,9 @@ export const buildWorkspaceMigrationsForCustomObject = (
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE_FOREIGN_KEY,
|
||||
columnName: `${computeCustomName(
|
||||
createdObjectMetadata.nameSingular,
|
||||
false,
|
||||
)}Id`,
|
||||
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
|
||||
isForeignKey: true,
|
||||
}),
|
||||
referencedTableName: computeObjectTargetTable(createdObjectMetadata),
|
||||
referencedTableColumnName: 'id',
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
@ -122,10 +116,9 @@ export const buildWorkspaceMigrationsForCustomObject = (
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
||||
columnName: `${computeCustomName(
|
||||
createdObjectMetadata.nameSingular,
|
||||
false,
|
||||
)}Id`,
|
||||
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
|
||||
isForeignKey: true,
|
||||
}),
|
||||
columnType: 'uuid',
|
||||
isNullable: true,
|
||||
} satisfies WorkspaceMigrationColumnCreate,
|
||||
@ -137,10 +130,9 @@ export const buildWorkspaceMigrationsForCustomObject = (
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE_FOREIGN_KEY,
|
||||
columnName: `${computeCustomName(
|
||||
createdObjectMetadata.nameSingular,
|
||||
false,
|
||||
)}Id`,
|
||||
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
|
||||
isForeignKey: true,
|
||||
}),
|
||||
referencedTableName: computeObjectTargetTable(createdObjectMetadata),
|
||||
referencedTableColumnName: 'id',
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
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 {
|
||||
WorkspaceMigrationTableAction,
|
||||
WorkspaceMigrationColumnActionType,
|
||||
WorkspaceMigrationColumnCreate,
|
||||
} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
|
||||
import { computeCustomName } from 'src/engine/utils/compute-custom-name.util';
|
||||
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
|
||||
|
||||
const buildCommentForRemoteObjectForeignKey = async (
|
||||
@ -65,10 +65,9 @@ export const buildWorkspaceMigrationsForRemoteObject = async (
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
||||
columnName: `${computeCustomName(
|
||||
createdObjectMetadata.nameSingular,
|
||||
false,
|
||||
)}Id`,
|
||||
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
|
||||
isForeignKey: true,
|
||||
}),
|
||||
columnType: remoteTablePrimaryKeyColumnType,
|
||||
isNullable: true,
|
||||
} satisfies WorkspaceMigrationColumnCreate,
|
||||
@ -80,10 +79,9 @@ export const buildWorkspaceMigrationsForRemoteObject = async (
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
||||
columnName: `${computeCustomName(
|
||||
createdObjectMetadata.nameSingular,
|
||||
false,
|
||||
)}Id`,
|
||||
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
|
||||
isForeignKey: true,
|
||||
}),
|
||||
columnType: remoteTablePrimaryKeyColumnType,
|
||||
},
|
||||
],
|
||||
@ -110,10 +108,9 @@ export const buildWorkspaceMigrationsForRemoteObject = async (
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
||||
columnName: `${computeCustomName(
|
||||
createdObjectMetadata.nameSingular,
|
||||
false,
|
||||
)}Id`,
|
||||
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
|
||||
isForeignKey: true,
|
||||
}),
|
||||
columnType: remoteTablePrimaryKeyColumnType,
|
||||
isNullable: true,
|
||||
} satisfies WorkspaceMigrationColumnCreate,
|
||||
@ -125,10 +122,9 @@ export const buildWorkspaceMigrationsForRemoteObject = async (
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
||||
columnName: `${computeCustomName(
|
||||
createdObjectMetadata.nameSingular,
|
||||
false,
|
||||
)}Id`,
|
||||
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
|
||||
isForeignKey: true,
|
||||
}),
|
||||
columnType: remoteTablePrimaryKeyColumnType,
|
||||
},
|
||||
],
|
||||
@ -155,10 +151,9 @@ export const buildWorkspaceMigrationsForRemoteObject = async (
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
||||
columnName: `${computeCustomName(
|
||||
createdObjectMetadata.nameSingular,
|
||||
false,
|
||||
)}Id`,
|
||||
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
|
||||
isForeignKey: true,
|
||||
}),
|
||||
columnType: remoteTablePrimaryKeyColumnType,
|
||||
isNullable: true,
|
||||
} satisfies WorkspaceMigrationColumnCreate,
|
||||
@ -170,10 +165,9 @@ export const buildWorkspaceMigrationsForRemoteObject = async (
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
||||
columnName: `${computeCustomName(
|
||||
createdObjectMetadata.nameSingular,
|
||||
false,
|
||||
)}Id`,
|
||||
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
|
||||
isForeignKey: true,
|
||||
}),
|
||||
columnType: remoteTablePrimaryKeyColumnType,
|
||||
},
|
||||
],
|
||||
@ -200,10 +194,9 @@ export const buildWorkspaceMigrationsForRemoteObject = async (
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
||||
columnName: `${computeCustomName(
|
||||
createdObjectMetadata.nameSingular,
|
||||
false,
|
||||
)}Id`,
|
||||
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
|
||||
isForeignKey: true,
|
||||
}),
|
||||
columnType: remoteTablePrimaryKeyColumnType,
|
||||
isNullable: true,
|
||||
} satisfies WorkspaceMigrationColumnCreate,
|
||||
@ -215,10 +208,9 @@ export const buildWorkspaceMigrationsForRemoteObject = async (
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
||||
columnName: `${computeCustomName(
|
||||
createdObjectMetadata.nameSingular,
|
||||
false,
|
||||
)}Id`,
|
||||
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
|
||||
isForeignKey: true,
|
||||
}),
|
||||
columnType: remoteTablePrimaryKeyColumnType,
|
||||
},
|
||||
],
|
||||
|
||||
@ -19,9 +19,7 @@ import { WorkspaceMigrationService } from 'src/engine/metadata-modules/workspace
|
||||
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { WorkspaceMigrationColumnActionType } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { createCustomColumnName } from 'src/engine/utils/create-custom-column-name.util';
|
||||
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
|
||||
import { createRelationForeignKeyColumnName } from 'src/engine/metadata-modules/relation-metadata/utils/create-relation-foreign-key-column-name.util';
|
||||
import { generateMigrationName } from 'src/engine/metadata-modules/workspace-migration/utils/generate-migration-name.util';
|
||||
|
||||
import {
|
||||
@ -57,11 +55,7 @@ export class RelationMetadataService extends TypeOrmQueryService<RelationMetadat
|
||||
|
||||
// NOTE: this logic is called to create relation through metadata graphql endpoint (so only for custom field relations)
|
||||
const isCustom = true;
|
||||
const baseColumnName = `${camelCase(relationMetadataInput.toName)}Id`;
|
||||
const foreignKeyColumnName = createRelationForeignKeyColumnName(
|
||||
relationMetadataInput.toName,
|
||||
isCustom,
|
||||
);
|
||||
const columnName = `${camelCase(relationMetadataInput.toName)}Id`;
|
||||
|
||||
const fromId = uuidV4();
|
||||
const toId = uuidV4();
|
||||
@ -79,11 +73,7 @@ export class RelationMetadataService extends TypeOrmQueryService<RelationMetadat
|
||||
isCustom,
|
||||
toId,
|
||||
),
|
||||
this.createForeignKeyFieldMetadata(
|
||||
relationMetadataInput,
|
||||
baseColumnName,
|
||||
foreignKeyColumnName,
|
||||
),
|
||||
this.createForeignKeyFieldMetadata(relationMetadataInput, columnName),
|
||||
]);
|
||||
|
||||
const createdRelationMetadata = await super.createOne({
|
||||
@ -95,7 +85,7 @@ export class RelationMetadataService extends TypeOrmQueryService<RelationMetadat
|
||||
await this.createWorkspaceCustomMigration(
|
||||
relationMetadataInput,
|
||||
objectMetadataMap,
|
||||
foreignKeyColumnName,
|
||||
columnName,
|
||||
);
|
||||
|
||||
await this.workspaceMigrationRunnerService.executeMigrationFromPendingMigrations(
|
||||
@ -170,7 +160,7 @@ export class RelationMetadataService extends TypeOrmQueryService<RelationMetadat
|
||||
private async createWorkspaceCustomMigration(
|
||||
relationMetadataInput: CreateRelationInput,
|
||||
objectMetadataMap: { [key: string]: ObjectMetadataEntity },
|
||||
foreignKeyColumnName: string,
|
||||
columnName: string,
|
||||
) {
|
||||
await this.workspaceMigrationService.createCustomMigration(
|
||||
generateMigrationName(`create-${relationMetadataInput.fromName}`),
|
||||
@ -185,7 +175,7 @@ export class RelationMetadataService extends TypeOrmQueryService<RelationMetadat
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
||||
columnName: foreignKeyColumnName,
|
||||
columnName,
|
||||
columnType: 'uuid',
|
||||
isNullable: true,
|
||||
},
|
||||
@ -200,7 +190,7 @@ export class RelationMetadataService extends TypeOrmQueryService<RelationMetadat
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE_FOREIGN_KEY,
|
||||
columnName: foreignKeyColumnName,
|
||||
columnName,
|
||||
referencedTableName: computeObjectTargetTable(
|
||||
objectMetadataMap[relationMetadataInput.fromObjectMetadataId],
|
||||
),
|
||||
@ -229,12 +219,6 @@ export class RelationMetadataService extends TypeOrmQueryService<RelationMetadat
|
||||
description: relationMetadataInput[`${relationDirection}Description`],
|
||||
icon: relationMetadataInput[`${relationDirection}Icon`],
|
||||
isCustom: true,
|
||||
targetColumnMap:
|
||||
relationDirection === 'to'
|
||||
? isCustom
|
||||
? createCustomColumnName(relationMetadataInput.toName)
|
||||
: relationMetadataInput.toName
|
||||
: {},
|
||||
isActive: true,
|
||||
isNullable: true,
|
||||
type: FieldMetadataType.RELATION,
|
||||
@ -246,18 +230,16 @@ export class RelationMetadataService extends TypeOrmQueryService<RelationMetadat
|
||||
|
||||
private createForeignKeyFieldMetadata(
|
||||
relationMetadataInput: CreateRelationInput,
|
||||
baseColumnName: string,
|
||||
foreignKeyColumnName: string,
|
||||
columnName: string,
|
||||
) {
|
||||
return {
|
||||
name: baseColumnName,
|
||||
name: columnName,
|
||||
label: `${relationMetadataInput.toLabel} Foreign Key`,
|
||||
description: relationMetadataInput.toDescription
|
||||
? `${relationMetadataInput.toDescription} Foreign Key`
|
||||
: undefined,
|
||||
icon: undefined,
|
||||
isCustom: true,
|
||||
targetColumnMap: { value: foreignKeyColumnName },
|
||||
isActive: true,
|
||||
isNullable: true,
|
||||
isSystem: true,
|
||||
|
||||
@ -1,15 +0,0 @@
|
||||
import { createCustomColumnName } from 'src/engine/utils/create-custom-column-name.util';
|
||||
import { camelCase } from 'src/utils/camel-case';
|
||||
|
||||
export const createRelationForeignKeyColumnName = (
|
||||
name: string,
|
||||
isCustom: boolean,
|
||||
) => {
|
||||
const baseColumnName = `${camelCase(name)}Id`;
|
||||
|
||||
const foreignKeyColumnName = isCustom
|
||||
? createCustomColumnName(baseColumnName)
|
||||
: baseColumnName;
|
||||
|
||||
return foreignKeyColumnName;
|
||||
};
|
||||
@ -12,6 +12,7 @@ import {
|
||||
import { serializeDefaultValue } from 'src/engine/metadata-modules/field-metadata/utils/serialize-default-value';
|
||||
import { fieldMetadataTypeToColumnType } from 'src/engine/metadata-modules/workspace-migration/utils/field-metadata-type-to-column-type.util';
|
||||
import { ColumnActionAbstractFactory } from 'src/engine/metadata-modules/workspace-migration/factories/column-action-abstract.factory';
|
||||
import { computeColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util';
|
||||
|
||||
export type BasicFieldMetadataType =
|
||||
| FieldMetadataType.UUID
|
||||
@ -33,29 +34,32 @@ export class BasicColumnActionFactory extends ColumnActionAbstractFactory<BasicF
|
||||
protected handleCreateAction(
|
||||
fieldMetadata: FieldMetadataInterface<BasicFieldMetadataType>,
|
||||
options?: WorkspaceColumnActionOptions,
|
||||
): WorkspaceMigrationColumnCreate {
|
||||
): WorkspaceMigrationColumnCreate[] {
|
||||
const columnName = computeColumnName(fieldMetadata);
|
||||
const defaultValue = fieldMetadata.defaultValue ?? options?.defaultValue;
|
||||
const serializedDefaultValue = serializeDefaultValue(defaultValue);
|
||||
|
||||
return {
|
||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
||||
columnName: fieldMetadata.targetColumnMap.value,
|
||||
columnType: fieldMetadataTypeToColumnType(fieldMetadata.type),
|
||||
isNullable: fieldMetadata.isNullable,
|
||||
defaultValue: serializedDefaultValue,
|
||||
};
|
||||
return [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
||||
columnName,
|
||||
columnType: fieldMetadataTypeToColumnType(fieldMetadata.type),
|
||||
isNullable: fieldMetadata.isNullable,
|
||||
defaultValue: serializedDefaultValue,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
protected handleAlterAction(
|
||||
currentFieldMetadata: FieldMetadataInterface<BasicFieldMetadataType>,
|
||||
alteredFieldMetadata: FieldMetadataInterface<BasicFieldMetadataType>,
|
||||
options?: WorkspaceColumnActionOptions,
|
||||
): WorkspaceMigrationColumnAlter {
|
||||
): WorkspaceMigrationColumnAlter[] {
|
||||
const currentColumnName = computeColumnName(currentFieldMetadata);
|
||||
const alteredColumnName = computeColumnName(alteredFieldMetadata);
|
||||
const defaultValue =
|
||||
alteredFieldMetadata.defaultValue ?? options?.defaultValue;
|
||||
const serializedDefaultValue = serializeDefaultValue(defaultValue);
|
||||
const currentColumnName = currentFieldMetadata.targetColumnMap.value;
|
||||
const alteredColumnName = alteredFieldMetadata.targetColumnMap.value;
|
||||
|
||||
if (!currentColumnName || !alteredColumnName) {
|
||||
this.logger.error(
|
||||
@ -66,20 +70,24 @@ export class BasicColumnActionFactory extends ColumnActionAbstractFactory<BasicF
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
action: WorkspaceMigrationColumnActionType.ALTER,
|
||||
currentColumnDefinition: {
|
||||
columnName: currentColumnName,
|
||||
columnType: fieldMetadataTypeToColumnType(currentFieldMetadata.type),
|
||||
isNullable: currentFieldMetadata.isNullable,
|
||||
defaultValue: serializeDefaultValue(currentFieldMetadata.defaultValue),
|
||||
return [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.ALTER,
|
||||
currentColumnDefinition: {
|
||||
columnName: currentColumnName,
|
||||
columnType: fieldMetadataTypeToColumnType(currentFieldMetadata.type),
|
||||
isNullable: currentFieldMetadata.isNullable,
|
||||
defaultValue: serializeDefaultValue(
|
||||
currentFieldMetadata.defaultValue,
|
||||
),
|
||||
},
|
||||
alteredColumnDefinition: {
|
||||
columnName: alteredColumnName,
|
||||
columnType: fieldMetadataTypeToColumnType(alteredFieldMetadata.type),
|
||||
isNullable: alteredFieldMetadata.isNullable,
|
||||
defaultValue: serializedDefaultValue,
|
||||
},
|
||||
},
|
||||
alteredColumnDefinition: {
|
||||
columnName: alteredColumnName,
|
||||
columnType: fieldMetadataTypeToColumnType(alteredFieldMetadata.type),
|
||||
isNullable: alteredFieldMetadata.isNullable,
|
||||
defaultValue: serializedDefaultValue,
|
||||
},
|
||||
};
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,7 +26,7 @@ export class ColumnActionAbstractFactory<
|
||||
currentFieldMetadata: FieldMetadataInterface<T> | undefined,
|
||||
alteredFieldMetadata: FieldMetadataInterface<T>,
|
||||
options?: WorkspaceColumnActionOptions,
|
||||
): WorkspaceMigrationColumnAction {
|
||||
): WorkspaceMigrationColumnAction[] {
|
||||
switch (action) {
|
||||
case WorkspaceMigrationColumnActionType.CREATE:
|
||||
return this.handleCreateAction(alteredFieldMetadata, options);
|
||||
@ -52,7 +52,7 @@ export class ColumnActionAbstractFactory<
|
||||
protected handleCreateAction(
|
||||
_fieldMetadata: FieldMetadataInterface<T>,
|
||||
_options?: WorkspaceColumnActionOptions,
|
||||
): WorkspaceMigrationColumnCreate {
|
||||
): WorkspaceMigrationColumnCreate[] {
|
||||
throw new Error('handleCreateAction method not implemented.');
|
||||
}
|
||||
|
||||
@ -60,7 +60,7 @@ export class ColumnActionAbstractFactory<
|
||||
_currentFieldMetadata: FieldMetadataInterface<T>,
|
||||
_alteredFieldMetadata: FieldMetadataInterface<T>,
|
||||
_options?: WorkspaceColumnActionOptions,
|
||||
): WorkspaceMigrationColumnAlter {
|
||||
): WorkspaceMigrationColumnAlter[] {
|
||||
throw new Error('handleAlterAction method not implemented.');
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,132 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface';
|
||||
|
||||
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import {
|
||||
WorkspaceMigrationColumnActionType,
|
||||
WorkspaceMigrationColumnAlter,
|
||||
WorkspaceMigrationColumnCreate,
|
||||
} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
|
||||
import { serializeDefaultValue } from 'src/engine/metadata-modules/field-metadata/utils/serialize-default-value';
|
||||
import { fieldMetadataTypeToColumnType } from 'src/engine/metadata-modules/workspace-migration/utils/field-metadata-type-to-column-type.util';
|
||||
import { ColumnActionAbstractFactory } from 'src/engine/metadata-modules/workspace-migration/factories/column-action-abstract.factory';
|
||||
import { computeCompositeColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util';
|
||||
import { compositeTypeDefintions } from 'src/engine/metadata-modules/field-metadata/composite-types';
|
||||
|
||||
export type CompositeFieldMetadataType =
|
||||
| FieldMetadataType.ADDRESS
|
||||
| FieldMetadataType.CURRENCY
|
||||
| FieldMetadataType.FULL_NAME
|
||||
| FieldMetadataType.LINK;
|
||||
|
||||
@Injectable()
|
||||
export class CompositeColumnActionFactory extends ColumnActionAbstractFactory<CompositeFieldMetadataType> {
|
||||
protected readonly logger = new Logger(CompositeColumnActionFactory.name);
|
||||
|
||||
protected handleCreateAction(
|
||||
fieldMetadata: FieldMetadataInterface<CompositeFieldMetadataType>,
|
||||
): WorkspaceMigrationColumnCreate[] {
|
||||
const compositeType = compositeTypeDefintions.get(fieldMetadata.type);
|
||||
|
||||
if (!compositeType) {
|
||||
this.logger.error(
|
||||
`Composite type not found for field metadata type: ${fieldMetadata.type}`,
|
||||
);
|
||||
throw new Error(
|
||||
`Composite type not found for field metadata type: ${fieldMetadata.type}`,
|
||||
);
|
||||
}
|
||||
|
||||
const columnActions: WorkspaceMigrationColumnCreate[] = [];
|
||||
|
||||
for (const property of compositeType.properties) {
|
||||
const columnName = computeCompositeColumnName(fieldMetadata, property);
|
||||
const defaultValue = fieldMetadata.defaultValue?.[property.name];
|
||||
const serializedDefaultValue = serializeDefaultValue(defaultValue);
|
||||
|
||||
columnActions.push({
|
||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
||||
columnName,
|
||||
columnType: fieldMetadataTypeToColumnType(property.type),
|
||||
isNullable: fieldMetadata.isNullable || !property.isRequired,
|
||||
defaultValue: serializedDefaultValue,
|
||||
});
|
||||
}
|
||||
|
||||
return columnActions;
|
||||
}
|
||||
|
||||
protected handleAlterAction(
|
||||
currentFieldMetadata: FieldMetadataInterface<CompositeFieldMetadataType>,
|
||||
alteredFieldMetadata: FieldMetadataInterface<CompositeFieldMetadataType>,
|
||||
): WorkspaceMigrationColumnAlter[] {
|
||||
const currentCompositeType = compositeTypeDefintions.get(
|
||||
currentFieldMetadata.type,
|
||||
);
|
||||
const alteredCompositeType = compositeTypeDefintions.get(
|
||||
alteredFieldMetadata.type,
|
||||
);
|
||||
|
||||
if (!currentCompositeType || !alteredCompositeType) {
|
||||
this.logger.error(
|
||||
`Composite type not found for field metadata type: ${currentFieldMetadata.type} or ${alteredFieldMetadata.type}`,
|
||||
);
|
||||
throw new Error(
|
||||
`Composite type not found for field metadata type: ${currentFieldMetadata.type} or ${alteredFieldMetadata.type}`,
|
||||
);
|
||||
}
|
||||
|
||||
const columnActions: WorkspaceMigrationColumnAlter[] = [];
|
||||
|
||||
for (const alteredProperty of alteredCompositeType.properties) {
|
||||
// TODO: Based on the name for now, we can add a more robust check in the future
|
||||
const currentProperty = currentCompositeType.properties.find(
|
||||
(p) => p.name === alteredProperty.name,
|
||||
);
|
||||
|
||||
if (!currentProperty) {
|
||||
this.logger.error(
|
||||
`Current property not found for altered property: ${alteredProperty.name}`,
|
||||
);
|
||||
throw new Error(
|
||||
`Current property not found for altered property: ${alteredProperty.name}`,
|
||||
);
|
||||
}
|
||||
|
||||
const currentColumnName = computeCompositeColumnName(
|
||||
currentFieldMetadata,
|
||||
currentProperty,
|
||||
);
|
||||
const alteredColumnName = computeCompositeColumnName(
|
||||
alteredFieldMetadata,
|
||||
alteredProperty,
|
||||
);
|
||||
const defaultValue =
|
||||
alteredFieldMetadata.defaultValue?.[alteredProperty.name];
|
||||
const serializedDefaultValue = serializeDefaultValue(defaultValue);
|
||||
|
||||
columnActions.push({
|
||||
action: WorkspaceMigrationColumnActionType.ALTER,
|
||||
currentColumnDefinition: {
|
||||
columnName: currentColumnName,
|
||||
columnType: fieldMetadataTypeToColumnType(currentProperty.type),
|
||||
isNullable:
|
||||
currentFieldMetadata.isNullable || !currentProperty.isRequired,
|
||||
defaultValue: serializeDefaultValue(
|
||||
currentFieldMetadata.defaultValue?.[currentProperty.name],
|
||||
),
|
||||
},
|
||||
alteredColumnDefinition: {
|
||||
columnName: alteredColumnName,
|
||||
columnType: fieldMetadataTypeToColumnType(alteredProperty.type),
|
||||
isNullable:
|
||||
alteredFieldMetadata.isNullable || !alteredProperty.isRequired,
|
||||
defaultValue: serializedDefaultValue,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return columnActions;
|
||||
}
|
||||
}
|
||||
@ -12,6 +12,7 @@ import {
|
||||
import { serializeDefaultValue } from 'src/engine/metadata-modules/field-metadata/utils/serialize-default-value';
|
||||
import { fieldMetadataTypeToColumnType } from 'src/engine/metadata-modules/workspace-migration/utils/field-metadata-type-to-column-type.util';
|
||||
import { ColumnActionAbstractFactory } from 'src/engine/metadata-modules/workspace-migration/factories/column-action-abstract.factory';
|
||||
import { computeColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util';
|
||||
|
||||
export type EnumFieldMetadataType =
|
||||
| FieldMetadataType.RATING
|
||||
@ -25,29 +26,34 @@ export class EnumColumnActionFactory extends ColumnActionAbstractFactory<EnumFie
|
||||
protected handleCreateAction(
|
||||
fieldMetadata: FieldMetadataInterface<EnumFieldMetadataType>,
|
||||
options: WorkspaceColumnActionOptions,
|
||||
): WorkspaceMigrationColumnCreate {
|
||||
): WorkspaceMigrationColumnCreate[] {
|
||||
const columnName = computeColumnName(fieldMetadata);
|
||||
const defaultValue = fieldMetadata.defaultValue ?? options?.defaultValue;
|
||||
const serializedDefaultValue = serializeDefaultValue(defaultValue);
|
||||
const enumOptions = fieldMetadata.options
|
||||
? [...fieldMetadata.options.map((option) => option.value)]
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
||||
columnName: fieldMetadata.targetColumnMap.value,
|
||||
columnType: fieldMetadataTypeToColumnType(fieldMetadata.type),
|
||||
enum: enumOptions,
|
||||
isArray: fieldMetadata.type === FieldMetadataType.MULTI_SELECT,
|
||||
isNullable: fieldMetadata.isNullable,
|
||||
defaultValue: serializedDefaultValue,
|
||||
};
|
||||
return [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
||||
columnName,
|
||||
columnType: fieldMetadataTypeToColumnType(fieldMetadata.type),
|
||||
enum: enumOptions,
|
||||
isArray: fieldMetadata.type === FieldMetadataType.MULTI_SELECT,
|
||||
isNullable: fieldMetadata.isNullable,
|
||||
defaultValue: serializedDefaultValue,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
protected handleAlterAction(
|
||||
currentFieldMetadata: FieldMetadataInterface<EnumFieldMetadataType>,
|
||||
alteredFieldMetadata: FieldMetadataInterface<EnumFieldMetadataType>,
|
||||
options: WorkspaceColumnActionOptions,
|
||||
): WorkspaceMigrationColumnAlter {
|
||||
): WorkspaceMigrationColumnAlter[] {
|
||||
const currentColumnName = computeColumnName(currentFieldMetadata);
|
||||
const alteredColumnName = computeColumnName(alteredFieldMetadata);
|
||||
const defaultValue =
|
||||
alteredFieldMetadata.defaultValue ?? options?.defaultValue;
|
||||
const serializedDefaultValue = serializeDefaultValue(defaultValue);
|
||||
@ -71,8 +77,6 @@ export class EnumColumnActionFactory extends ColumnActionAbstractFactory<EnumFie
|
||||
}),
|
||||
]
|
||||
: undefined;
|
||||
const currentColumnName = currentFieldMetadata.targetColumnMap.value;
|
||||
const alteredColumnName = alteredFieldMetadata.targetColumnMap.value;
|
||||
|
||||
if (!currentColumnName || !alteredColumnName) {
|
||||
this.logger.error(
|
||||
@ -83,26 +87,30 @@ export class EnumColumnActionFactory extends ColumnActionAbstractFactory<EnumFie
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
action: WorkspaceMigrationColumnActionType.ALTER,
|
||||
currentColumnDefinition: {
|
||||
columnName: currentColumnName,
|
||||
columnType: fieldMetadataTypeToColumnType(currentFieldMetadata.type),
|
||||
enum: currentFieldMetadata.options
|
||||
? [...currentFieldMetadata.options.map((option) => option.value)]
|
||||
: undefined,
|
||||
isArray: currentFieldMetadata.type === FieldMetadataType.MULTI_SELECT,
|
||||
isNullable: currentFieldMetadata.isNullable,
|
||||
defaultValue: serializeDefaultValue(currentFieldMetadata.defaultValue),
|
||||
return [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.ALTER,
|
||||
currentColumnDefinition: {
|
||||
columnName: currentColumnName,
|
||||
columnType: fieldMetadataTypeToColumnType(currentFieldMetadata.type),
|
||||
enum: currentFieldMetadata.options
|
||||
? [...currentFieldMetadata.options.map((option) => option.value)]
|
||||
: undefined,
|
||||
isArray: currentFieldMetadata.type === FieldMetadataType.MULTI_SELECT,
|
||||
isNullable: currentFieldMetadata.isNullable,
|
||||
defaultValue: serializeDefaultValue(
|
||||
currentFieldMetadata.defaultValue,
|
||||
),
|
||||
},
|
||||
alteredColumnDefinition: {
|
||||
columnName: alteredColumnName,
|
||||
columnType: fieldMetadataTypeToColumnType(alteredFieldMetadata.type),
|
||||
enum: enumOptions,
|
||||
isArray: alteredFieldMetadata.type === FieldMetadataType.MULTI_SELECT,
|
||||
isNullable: alteredFieldMetadata.isNullable,
|
||||
defaultValue: serializedDefaultValue,
|
||||
},
|
||||
},
|
||||
alteredColumnDefinition: {
|
||||
columnName: alteredColumnName,
|
||||
columnType: fieldMetadataTypeToColumnType(alteredFieldMetadata.type),
|
||||
enum: enumOptions,
|
||||
isArray: alteredFieldMetadata.type === FieldMetadataType.MULTI_SELECT,
|
||||
isNullable: alteredFieldMetadata.isNullable,
|
||||
defaultValue: serializedDefaultValue,
|
||||
},
|
||||
};
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
import { BasicColumnActionFactory } from 'src/engine/metadata-modules/workspace-migration/factories/basic-column-action.factory';
|
||||
import { CompositeColumnActionFactory } from 'src/engine/metadata-modules/workspace-migration/factories/composite-column-action.factory';
|
||||
import { EnumColumnActionFactory } from 'src/engine/metadata-modules/workspace-migration/factories/enum-column-action.factory';
|
||||
|
||||
export const workspaceColumnActionFactories = [
|
||||
BasicColumnActionFactory,
|
||||
EnumColumnActionFactory,
|
||||
CompositeColumnActionFactory,
|
||||
];
|
||||
|
||||
@ -17,5 +17,5 @@ export interface WorkspaceColumnActionFactory<
|
||||
currentFieldMetadata: FieldMetadataInterface<T> | undefined,
|
||||
alteredFieldMetadata: FieldMetadataInterface<T>,
|
||||
options?: WorkspaceColumnActionOptions,
|
||||
): WorkspaceMigrationColumnAction;
|
||||
): WorkspaceMigrationColumnAction[];
|
||||
}
|
||||
|
||||
@ -5,14 +5,13 @@ import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metada
|
||||
import { WorkspaceColumnActionOptions } from 'src/engine/metadata-modules/workspace-migration/interfaces/workspace-column-action-options.interface';
|
||||
|
||||
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { BasicColumnActionFactory } from 'src/engine/metadata-modules/workspace-migration/factories/basic-column-action.factory';
|
||||
import { EnumColumnActionFactory } from 'src/engine/metadata-modules/workspace-migration/factories/enum-column-action.factory';
|
||||
import {
|
||||
WorkspaceMigrationColumnAction,
|
||||
WorkspaceMigrationColumnActionType,
|
||||
} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
|
||||
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||
import { compositeDefinitions } from 'src/engine/metadata-modules/field-metadata/composite-types';
|
||||
import { BasicColumnActionFactory } from 'src/engine/metadata-modules/workspace-migration/factories/basic-column-action.factory';
|
||||
import { EnumColumnActionFactory } from 'src/engine/metadata-modules/workspace-migration/factories/enum-column-action.factory';
|
||||
import { CompositeColumnActionFactory } from 'src/engine/metadata-modules/workspace-migration/factories/composite-column-action.factory';
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceMigrationFactory {
|
||||
@ -28,6 +27,7 @@ export class WorkspaceMigrationFactory {
|
||||
constructor(
|
||||
private readonly basicColumnActionFactory: BasicColumnActionFactory,
|
||||
private readonly enumColumnActionFactory: EnumColumnActionFactory,
|
||||
private readonly compositeColumnActionFactory: CompositeColumnActionFactory,
|
||||
) {
|
||||
this.factoriesMap = new Map<
|
||||
FieldMetadataType,
|
||||
@ -80,6 +80,19 @@ export class WorkspaceMigrationFactory {
|
||||
FieldMetadataType.MULTI_SELECT,
|
||||
{ factory: this.enumColumnActionFactory },
|
||||
],
|
||||
[FieldMetadataType.LINK, { factory: this.compositeColumnActionFactory }],
|
||||
[
|
||||
FieldMetadataType.CURRENCY,
|
||||
{ factory: this.compositeColumnActionFactory },
|
||||
],
|
||||
[
|
||||
FieldMetadataType.ADDRESS,
|
||||
{ factory: this.compositeColumnActionFactory },
|
||||
],
|
||||
[
|
||||
FieldMetadataType.FULL_NAME,
|
||||
{ factory: this.compositeColumnActionFactory },
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
@ -119,41 +132,13 @@ export class WorkspaceMigrationFactory {
|
||||
throw new Error(`No field metadata provided for action ${action}`);
|
||||
}
|
||||
|
||||
// If it's a composite field type, we need to create a column action for each of the fields
|
||||
if (isCompositeFieldMetadataType(alteredFieldMetadata.type)) {
|
||||
const fieldMetadataSplitterFunction = compositeDefinitions.get(
|
||||
alteredFieldMetadata.type,
|
||||
);
|
||||
|
||||
if (!fieldMetadataSplitterFunction) {
|
||||
this.logger.error(
|
||||
`No composite definition found for type ${alteredFieldMetadata.type}`,
|
||||
{
|
||||
alteredFieldMetadata,
|
||||
},
|
||||
);
|
||||
|
||||
throw new Error(
|
||||
`No composite definition found for type ${alteredFieldMetadata.type}`,
|
||||
);
|
||||
}
|
||||
|
||||
const fieldMetadataCollection =
|
||||
fieldMetadataSplitterFunction(alteredFieldMetadata);
|
||||
|
||||
return fieldMetadataCollection.map((fieldMetadata) =>
|
||||
this.createColumnAction(action, fieldMetadata, fieldMetadata),
|
||||
);
|
||||
}
|
||||
|
||||
// Otherwise, we create a single column action
|
||||
const columnAction = this.createColumnAction(
|
||||
const columnActions = this.createColumnAction(
|
||||
action,
|
||||
currentFieldMetadata,
|
||||
alteredFieldMetadata,
|
||||
);
|
||||
|
||||
return [columnAction];
|
||||
return columnActions;
|
||||
}
|
||||
|
||||
private createColumnAction(
|
||||
@ -162,7 +147,7 @@ export class WorkspaceMigrationFactory {
|
||||
| WorkspaceMigrationColumnActionType.ALTER,
|
||||
currentFieldMetadata: FieldMetadataInterface | undefined,
|
||||
alteredFieldMetadata: FieldMetadataInterface,
|
||||
): WorkspaceMigrationColumnAction {
|
||||
): WorkspaceMigrationColumnAction[] {
|
||||
const { factory, options } =
|
||||
this.factoriesMap.get(alteredFieldMetadata.type) ?? {};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user