Refactor backend folder structure (#4505)
* Refactor backend folder structure Co-authored-by: Charles Bochet <charles@twenty.com> * fix tests * fix * move yoga hooks --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -0,0 +1,22 @@
|
||||
import { cleanEntityName } from 'src/engine/api/graphql/workspace-schema-builder/utils/clean-entity-name.util';
|
||||
|
||||
describe('cleanEntityName', () => {
|
||||
test('should camelCase strings', () => {
|
||||
expect(cleanEntityName('hello world')).toBe('helloWorld');
|
||||
expect(cleanEntityName('my name is John')).toBe('myNameIsJohn');
|
||||
});
|
||||
|
||||
test('should remove numbers at the beginning', () => {
|
||||
expect(cleanEntityName('123hello')).toBe('hello');
|
||||
expect(cleanEntityName('456hello world')).toBe('helloWorld');
|
||||
});
|
||||
|
||||
test('should remove special characters', () => {
|
||||
expect(cleanEntityName('hello$world')).toBe('helloWorld');
|
||||
expect(cleanEntityName('some#special&chars')).toBe('someSpecialChars');
|
||||
});
|
||||
|
||||
test('should handle empty strings', () => {
|
||||
expect(cleanEntityName('')).toBe('');
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,22 @@
|
||||
import { FieldMetadataType } from 'src/engine-metadata/field-metadata/field-metadata.entity';
|
||||
import { getFieldMetadataType } from 'src/engine/api/graphql/workspace-schema-builder/utils/get-field-metadata-type.util';
|
||||
|
||||
describe('getFieldMetadataType', () => {
|
||||
it.each([
|
||||
['uuid', FieldMetadataType.UUID],
|
||||
['timestamp', FieldMetadataType.DATE_TIME],
|
||||
])(
|
||||
'should return correct FieldMetadataType for type %s',
|
||||
(type, expectedMetadataType) => {
|
||||
expect(getFieldMetadataType(type)).toBe(expectedMetadataType);
|
||||
},
|
||||
);
|
||||
|
||||
it('should throw an error for an unknown type', () => {
|
||||
const unknownType = 'unknownType';
|
||||
|
||||
expect(() => getFieldMetadataType(unknownType)).toThrow(
|
||||
`Unknown type ${unknownType}`,
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,59 @@
|
||||
import { WorkspaceResolverBuilderMethodNames } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
|
||||
|
||||
import { FieldMetadataType } from 'src/engine-metadata/field-metadata/field-metadata.entity';
|
||||
import { InputTypeDefinitionKind } from 'src/engine/api/graphql/workspace-schema-builder/factories/input-type-definition.factory';
|
||||
import { getResolverArgs } from 'src/engine/api/graphql/workspace-schema-builder/utils/get-resolver-args.util';
|
||||
|
||||
describe('getResolverArgs', () => {
|
||||
const expectedOutputs = {
|
||||
findMany: {
|
||||
first: { type: FieldMetadataType.NUMBER, isNullable: true },
|
||||
last: { type: FieldMetadataType.NUMBER, isNullable: true },
|
||||
before: { type: FieldMetadataType.TEXT, isNullable: true },
|
||||
after: { type: FieldMetadataType.TEXT, isNullable: true },
|
||||
filter: { kind: InputTypeDefinitionKind.Filter, isNullable: true },
|
||||
orderBy: { kind: InputTypeDefinitionKind.OrderBy, isNullable: true },
|
||||
},
|
||||
findOne: {
|
||||
filter: { kind: InputTypeDefinitionKind.Filter, isNullable: false },
|
||||
},
|
||||
createMany: {
|
||||
data: {
|
||||
kind: InputTypeDefinitionKind.Create,
|
||||
isNullable: false,
|
||||
isArray: true,
|
||||
},
|
||||
},
|
||||
createOne: {
|
||||
data: { kind: InputTypeDefinitionKind.Create, isNullable: false },
|
||||
},
|
||||
updateOne: {
|
||||
id: { type: FieldMetadataType.UUID, isNullable: false },
|
||||
data: { kind: InputTypeDefinitionKind.Update, isNullable: false },
|
||||
},
|
||||
deleteOne: {
|
||||
id: { type: FieldMetadataType.UUID, isNullable: false },
|
||||
},
|
||||
executeQuickActionOnOne: {
|
||||
id: { type: FieldMetadataType.UUID, isNullable: false },
|
||||
},
|
||||
};
|
||||
|
||||
// Test each resolver type
|
||||
Object.entries(expectedOutputs).forEach(([resolverType, expectedOutput]) => {
|
||||
it(`should return correct args for ${resolverType} resolver`, () => {
|
||||
expect(
|
||||
getResolverArgs(resolverType as WorkspaceResolverBuilderMethodNames),
|
||||
).toEqual(expectedOutput);
|
||||
});
|
||||
});
|
||||
|
||||
// Test for an unknown resolver type
|
||||
it('should throw an error for an unknown resolver type', () => {
|
||||
const unknownType = 'unknownType';
|
||||
|
||||
expect(() =>
|
||||
getResolverArgs(unknownType as WorkspaceResolverBuilderMethodNames),
|
||||
).toThrow(`Unknown resolver type: ${unknownType}`);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,17 @@
|
||||
import { camelCase } from 'src/utils/camel-case';
|
||||
|
||||
export const cleanEntityName = (entityName: string) => {
|
||||
// Remove all leading numbers
|
||||
let camelCasedEntityName = entityName.replace(/^[0-9]+/, '');
|
||||
|
||||
// Trim the string
|
||||
camelCasedEntityName = camelCasedEntityName.trim();
|
||||
|
||||
// Camel case the string
|
||||
camelCasedEntityName = camelCase(camelCasedEntityName);
|
||||
|
||||
// Remove all special characters but keep alphabets and numbers
|
||||
camelCasedEntityName = camelCasedEntityName.replace(/[^a-zA-Z0-9]/g, '');
|
||||
|
||||
return camelCasedEntityName;
|
||||
};
|
||||
@ -0,0 +1,17 @@
|
||||
import { FieldMetadataType } from 'src/engine-metadata/field-metadata/field-metadata.entity';
|
||||
|
||||
const typeOrmTypeMapping = new Map<string, FieldMetadataType>([
|
||||
['uuid', FieldMetadataType.UUID],
|
||||
['timestamp', FieldMetadataType.DATE_TIME],
|
||||
// Add more types here if we need to support more than id, and createdAt/updatedAt/deletedAt
|
||||
]);
|
||||
|
||||
export const getFieldMetadataType = (type: string) => {
|
||||
const fieldType = typeOrmTypeMapping.get(type);
|
||||
|
||||
if (fieldType === undefined || fieldType === null) {
|
||||
throw new Error(`Unknown type ${type}`);
|
||||
}
|
||||
|
||||
return fieldType;
|
||||
};
|
||||
@ -0,0 +1,111 @@
|
||||
import { WorkspaceResolverBuilderMethodNames } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
|
||||
import { ArgMetadata } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/param-metadata.interface';
|
||||
|
||||
import { FieldMetadataType } from 'src/engine-metadata/field-metadata/field-metadata.entity';
|
||||
import { InputTypeDefinitionKind } from 'src/engine/api/graphql/workspace-schema-builder/factories/input-type-definition.factory';
|
||||
|
||||
export const getResolverArgs = (
|
||||
type: WorkspaceResolverBuilderMethodNames,
|
||||
): { [key: string]: ArgMetadata } => {
|
||||
switch (type) {
|
||||
case 'findMany':
|
||||
return {
|
||||
first: {
|
||||
type: FieldMetadataType.NUMBER,
|
||||
isNullable: true,
|
||||
},
|
||||
last: {
|
||||
type: FieldMetadataType.NUMBER,
|
||||
isNullable: true,
|
||||
},
|
||||
before: {
|
||||
type: FieldMetadataType.TEXT,
|
||||
isNullable: true,
|
||||
},
|
||||
after: {
|
||||
type: FieldMetadataType.TEXT,
|
||||
isNullable: true,
|
||||
},
|
||||
filter: {
|
||||
kind: InputTypeDefinitionKind.Filter,
|
||||
isNullable: true,
|
||||
},
|
||||
orderBy: {
|
||||
kind: InputTypeDefinitionKind.OrderBy,
|
||||
isNullable: true,
|
||||
},
|
||||
};
|
||||
case 'findOne':
|
||||
case 'deleteMany':
|
||||
return {
|
||||
filter: {
|
||||
kind: InputTypeDefinitionKind.Filter,
|
||||
isNullable: false,
|
||||
},
|
||||
};
|
||||
case 'createMany':
|
||||
return {
|
||||
data: {
|
||||
kind: InputTypeDefinitionKind.Create,
|
||||
isNullable: false,
|
||||
isArray: true,
|
||||
},
|
||||
};
|
||||
case 'createOne':
|
||||
return {
|
||||
data: {
|
||||
kind: InputTypeDefinitionKind.Create,
|
||||
isNullable: false,
|
||||
},
|
||||
};
|
||||
case 'updateOne':
|
||||
return {
|
||||
id: {
|
||||
type: FieldMetadataType.UUID,
|
||||
isNullable: false,
|
||||
},
|
||||
data: {
|
||||
kind: InputTypeDefinitionKind.Update,
|
||||
isNullable: false,
|
||||
},
|
||||
};
|
||||
case 'findDuplicates':
|
||||
return {
|
||||
id: {
|
||||
type: FieldMetadataType.UUID,
|
||||
isNullable: true,
|
||||
},
|
||||
data: {
|
||||
kind: InputTypeDefinitionKind.Create,
|
||||
isNullable: true,
|
||||
},
|
||||
};
|
||||
case 'deleteOne':
|
||||
return {
|
||||
id: {
|
||||
type: FieldMetadataType.UUID,
|
||||
isNullable: false,
|
||||
},
|
||||
};
|
||||
case 'executeQuickActionOnOne':
|
||||
return {
|
||||
id: {
|
||||
type: FieldMetadataType.UUID,
|
||||
isNullable: false,
|
||||
},
|
||||
};
|
||||
case 'updateMany':
|
||||
return {
|
||||
data: {
|
||||
kind: InputTypeDefinitionKind.Update,
|
||||
isNullable: false,
|
||||
},
|
||||
filter: {
|
||||
kind: InputTypeDefinitionKind.Filter,
|
||||
isNullable: false,
|
||||
},
|
||||
};
|
||||
default:
|
||||
throw new Error(`Unknown resolver type: ${type}`);
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,11 @@
|
||||
import { ObjectMetadataInterface } from 'src/engine-metadata/field-metadata/interfaces/object-metadata.interface';
|
||||
|
||||
import { isRelationFieldMetadataType } from 'src/engine/utils/is-relation-field-metadata-type.util';
|
||||
|
||||
export const objectContainsRelationField = (
|
||||
objectMetadata: ObjectMetadataInterface,
|
||||
): boolean => {
|
||||
return objectMetadata.fields.some((field) =>
|
||||
isRelationFieldMetadataType(field.type),
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user