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:
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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user