Added all field types on pet custom object (#9248)

- Added all usable composite field types on pet custom object
- Fixed missing createdBy on people and company seeds
- DEFAULT_SUBDOMAIN is now used for login (could be improved for multi
workspace)
- Refactored ObjectMetadataStandardIdToIdMap to disambiguate from
ObjectMetadataMap
- Refactored seedCustomObjects
This commit is contained in:
Lucas Bordeau
2024-12-27 15:01:09 +01:00
committed by GitHub
parent 58c92e036b
commit a8bb3e6bdf
27 changed files with 742 additions and 5428 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -22,6 +22,24 @@ export const PETS_METADATA_SEEDS: ObjectMetadataSeed = {
{ label: 'Hamster', value: 'hamster', position: 5, color: 'orange' },
],
},
{
type: FieldMetadataType.MULTI_SELECT,
label: 'Traits',
name: 'traits',
options: [
{ label: 'Playful', value: 'playful', position: 0, color: 'blue' },
{ label: 'Friendly', value: 'friendly', position: 1, color: 'red' },
{
label: 'Protective',
value: 'protective',
position: 2,
color: 'green',
},
{ label: 'Shy', value: 'shy', position: 3, color: 'yellow' },
{ label: 'Brave', value: 'brave', position: 4, color: 'purple' },
{ label: 'Curious', value: 'curious', position: 5, color: 'orange' },
],
},
{
type: FieldMetadataType.TEXT,
label: 'Comments',
@@ -37,5 +55,60 @@ export const PETS_METADATA_SEEDS: ObjectMetadataSeed = {
label: 'Location',
name: 'location',
},
{
type: FieldMetadataType.PHONES,
label: 'Vet phone',
name: 'vetPhone',
},
{
type: FieldMetadataType.EMAILS,
label: 'Vet email',
name: 'vetEmail',
},
{
type: FieldMetadataType.DATE,
label: 'Birthday',
name: 'birthday',
},
{
type: FieldMetadataType.BOOLEAN,
label: 'Is good with kids',
name: 'isGoodWithKids',
},
{
type: FieldMetadataType.LINKS,
label: 'Pictures',
name: 'pictures',
},
{
type: FieldMetadataType.CURRENCY,
label: 'Average cost of kibble per month',
name: 'averageCostOfKibblePerMonth',
},
{
type: FieldMetadataType.FULL_NAME,
label: 'Makes its owner think of',
name: 'makesOwnerThinkOf',
},
{
type: FieldMetadataType.RATING,
label: 'Sound swag (bark style, meow style, etc.)',
name: 'soundSwag',
},
{
type: FieldMetadataType.RICH_TEXT,
label: 'Bio',
name: 'bio',
},
{
type: FieldMetadataType.ARRAY,
label: 'Interesting facts',
name: 'interestingFacts',
},
{
type: FieldMetadataType.RAW_JSON,
label: 'Extra data',
name: 'extraData',
},
],
};
@@ -2,11 +2,14 @@ import { Injectable } from '@nestjs/common';
import { ObjectMetadataSeed } from 'src/engine/seeder/interfaces/object-metadata-seed';
import { DEV_SEED_WORKSPACE_MEMBER_IDS } from 'src/database/typeorm-seeds/workspace/workspace-members';
import { compositeTypeDefinitions } from 'src/engine/metadata-modules/field-metadata/composite-types';
import { CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input';
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { FieldMetadataService } from 'src/engine/metadata-modules/field-metadata/field-metadata.service';
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service';
import { computeTableName } from 'src/engine/utils/compute-table-name.util';
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
import { capitalize } from 'src/utils/capitalize';
import { isDefined } from 'src/utils/is-defined';
@@ -22,11 +25,11 @@ export class SeederService {
public async seedCustomObjects(
dataSourceId: string,
workspaceId: string,
metadataSeeds: ObjectMetadataSeed,
dataSeeds: Record<string, any>[],
objectMetadataSeed: ObjectMetadataSeed,
objectRecordSeeds: Record<string, any>[],
): Promise<void> {
const createdObjectMetadata = await this.objectMetadataService.createOne({
...metadataSeeds,
...objectMetadataSeed,
dataSourceId,
workspaceId,
});
@@ -35,9 +38,9 @@ export class SeederService {
throw new Error("Object metadata couldn't be created");
}
for (const customField of metadataSeeds.fields) {
for (const fieldMetadataSeed of objectMetadataSeed.fields) {
await this.fieldMetadataService.createOne({
...customField,
...fieldMetadataSeed,
objectMetadataId: createdObjectMetadata.id,
workspaceId,
});
@@ -45,7 +48,7 @@ export class SeederService {
const objectMetadataAfterFieldCreation =
await this.objectMetadataService.findOneWithinWorkspace(workspaceId, {
where: { nameSingular: metadataSeeds.nameSingular },
where: { nameSingular: objectMetadataSeed.nameSingular },
});
if (!objectMetadataAfterFieldCreation) {
@@ -64,98 +67,163 @@ export class SeederService {
const entityManager = workspaceDataSource.createEntityManager();
const filteredFields = metadataSeeds.fields.filter((field) =>
objectMetadataAfterFieldCreation.fields.some(
(f) => f.name === field.name || f.name === `name`,
),
const filteredFieldMetadataSeeds = objectMetadataSeed.fields.filter(
(field) =>
objectMetadataAfterFieldCreation.fields.some(
(f) => f.name === field.name || f.name === `name`,
),
);
if (filteredFields.length === 0) {
throw new Error('No fields found for seeding, check metadata');
if (filteredFieldMetadataSeeds.length === 0) {
throw new Error('No fields found for seeding, check metadata file');
}
filteredFields.unshift({
name: 'name',
type: FieldMetadataType.TEXT,
label: 'Name',
});
this.addNameFieldToFieldMetadataSeeds(filteredFieldMetadataSeeds);
const fieldMetadataMap = filteredFields
.map((field) => {
if (isCompositeFieldMetadataType(field.type)) {
const compositeFieldTypeDefinition = compositeTypeDefinitions.get(
field.type,
);
const objectRecordSeedsAsSQLFlattenedSeeds = objectRecordSeeds.map(
(recordSeed) => {
const objectRecordSeedsAsSQLFlattenedSeeds = {};
if (!isDefined(compositeFieldTypeDefinition)) {
throw new Error(
`Composite field type definition not found for ${field.type}`,
for (const field of filteredFieldMetadataSeeds) {
if (isCompositeFieldMetadataType(field.type)) {
const compositeFieldTypeDefinition = compositeTypeDefinitions.get(
field.type,
);
}
const fieldNames = compositeFieldTypeDefinition.properties?.map(
(property) => property.name,
);
if (!isDefined(compositeFieldTypeDefinition)) {
throw new Error(
`Composite field type definition not found for ${field.type}`,
);
}
return (
fieldNames?.map(
(subFieldName: string) =>
`${field.name}${capitalize(subFieldName)}`,
) ?? []
);
} else {
return field.name;
}
})
.flat()
.filter(isDefined);
const fieldNames = compositeFieldTypeDefinition.properties
?.map((property) => property.name)
.filter(isDefined);
const flattenedSeeds = dataSeeds.map((seed) => {
const flattenedSeed = {};
for (const subFieldName of fieldNames) {
const subFieldValue = recordSeed?.[field.name]?.[subFieldName];
for (const field of filteredFields) {
if (isCompositeFieldMetadataType(field.type)) {
const compositeFieldTypeDefinition = compositeTypeDefinitions.get(
field.type,
);
const subFieldValueAsSQLValue =
this.turnCompositeSubFieldValueAsSQLValue(
field.type,
subFieldName,
subFieldValue,
);
if (!isDefined(compositeFieldTypeDefinition)) {
throw new Error(
`Composite field type definition not found for ${field.type}`,
const subFieldNameAsSQLColumnName = `${field.name}${capitalize(subFieldName)}`;
objectRecordSeedsAsSQLFlattenedSeeds[
subFieldNameAsSQLColumnName
] = subFieldValueAsSQLValue;
}
} else {
const fieldValue = recordSeed[field.name];
const fieldValueAsSQLValue = this.turnFieldValueAsSQLValue(
field.type,
fieldValue,
);
}
const fieldNames = compositeFieldTypeDefinition.properties
?.map((property) => property.name)
.filter(isDefined);
for (const subFieldName of fieldNames) {
flattenedSeed[`${field.name}${capitalize(subFieldName)}`] =
seed?.[field.name]?.[subFieldName];
objectRecordSeedsAsSQLFlattenedSeeds[field.name] =
fieldValueAsSQLValue;
}
} else {
flattenedSeed[field.name] = seed[field.name];
}
}
return flattenedSeed;
});
return objectRecordSeedsAsSQLFlattenedSeeds;
},
);
if (!(objectRecordSeedsAsSQLFlattenedSeeds.length > 0)) {
return;
}
const fieldMetadataNamesAsFlattenedSQLColumnNames = Object.keys(
objectRecordSeedsAsSQLFlattenedSeeds[0],
);
const sqlColumnNames = [
...fieldMetadataNamesAsFlattenedSQLColumnNames,
'position',
'createdBySource',
'createdByWorkspaceMemberId',
'createdByName',
];
const sqlValues = objectRecordSeedsAsSQLFlattenedSeeds.map(
(flattenedSeed, index) => ({
...flattenedSeed,
position: index,
createdBySource: 'MANUAL',
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
createdByName: 'Tim Apple',
}),
);
await entityManager
.createQueryBuilder()
.insert()
.into(`${schemaName}._${objectMetadataAfterFieldCreation.nameSingular}`, [
...fieldMetadataMap,
'position',
])
.orIgnore()
.values(
flattenedSeeds.map((flattenedSeed, index) => ({
...flattenedSeed,
position: index,
})),
.into(
`${schemaName}.${computeTableName(objectMetadataAfterFieldCreation.nameSingular, true)}`,
sqlColumnNames,
)
.orIgnore()
.values(sqlValues)
.returning('*')
.execute();
}
private addNameFieldToFieldMetadataSeeds(
arrayOfMetadataFields: Pick<CreateFieldInput, 'name' | 'type' | 'label'>[],
) {
arrayOfMetadataFields.unshift({
name: 'name',
type: FieldMetadataType.TEXT,
label: 'Name',
});
}
private turnCompositeSubFieldValueAsSQLValue(
fieldType: FieldMetadataType,
subFieldName: string,
subFieldValue: any,
) {
if (!isCompositeFieldMetadataType(fieldType)) {
throw new Error(
`${subFieldName} is not a sub field of a composite field type.`,
);
}
const compositeFieldTypeDefinition =
compositeTypeDefinitions.get(fieldType);
const compositeSubFieldType =
compositeFieldTypeDefinition?.properties.find(
(property) => property.name === subFieldName,
)?.type ?? null;
if (!isDefined(compositeSubFieldType)) {
throw new Error(
`Cannot find ${subFieldName} in properties of composite type ${fieldType}.`,
);
}
return this.turnFieldValueAsSQLValue(compositeSubFieldType, subFieldValue);
}
private turnFieldValueAsSQLValue(
fieldType: FieldMetadataType,
fieldValue: any,
) {
if (fieldType === FieldMetadataType.RAW_JSON) {
try {
return JSON.stringify(fieldValue);
} catch (error) {
throw new Error(
`Error while trying to turn field value as stringified JSON : ${error.message}`,
);
}
}
return fieldValue;
}
}