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

View File

@ -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',
},
],
};

View File

@ -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;
}
}