create groupBy resolver + mock groupBy service (#14530)
To test :
```
query :
query companiesGroupBy($groupBy: CompanyGroupByInput){
companiesGroupBy(groupBy: $groupBy){
groupByDimensionValues
countEmptyId
sumAnnualRecurringRevenueAmountMicros
countEmptyAddress
percentageEmptySearchVector
}
}
input :
{"groupBy": {"name": true, "employees": true, "domainName": {"primaryLinkUrl": true}}}
response :
{
"data": {
"companiesGroupBy": [
{
"groupByDimensionValues": [
"Text 1",
"twenty.com",
"1"
],
"countEmptyId": 39,
"sumAnnualRecurringRevenueAmountMicros": 20,
"countEmptyAddress": 56,
"percentageEmptySearchVector": 88
},
{
"groupByDimensionValues": [
"Long long long long and long text 2",
"github.com/twentyhq/twenty",
"2"
],
"countEmptyId": 20,
"sumAnnualRecurringRevenueAmountMicros": 56,
"countEmptyAddress": 88,
"percentageEmptySearchVector": 2
},
{
"groupByDimensionValues": [
"Text 3",
"react.dev",
"3"
],
"countEmptyId": 56,
"sumAnnualRecurringRevenueAmountMicros": 88,
"countEmptyAddress": 2,
"percentageEmptySearchVector": 39
}
]
}
}
```
closes https://github.com/twentyhq/core-team-issues/issues/1439
This commit is contained in:
+1
@@ -3,4 +3,5 @@ export enum GqlInputTypeDefinitionKind {
|
||||
Update = 'Update',
|
||||
Filter = 'Filter',
|
||||
OrderBy = 'OrderBy',
|
||||
GroupBy = 'GroupBy',
|
||||
}
|
||||
|
||||
+1
@@ -1,4 +1,5 @@
|
||||
export enum ObjectTypeDefinitionKind {
|
||||
GroupByConnection = 'GroupByConnection',
|
||||
Connection = 'Connection',
|
||||
Edge = 'Edge',
|
||||
Plain = '',
|
||||
|
||||
+5
@@ -10,6 +10,7 @@ import { RelationConnectGqlInputTypeGenerator } from 'src/engine/api/graphql/wor
|
||||
import { CompositeFieldMetadataGqlObjectTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/object-types/composite-field-metadata-gql-object-type.generator';
|
||||
import { ConnectionGqlObjectTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/object-types/connection-gql-object-type.generator';
|
||||
import { EdgeGqlObjectTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/object-types/edge-gql-object-type.generator';
|
||||
import { GroupByConnectionGqlObjectTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/object-types/group-by-connection-gql-object-type.generator';
|
||||
import { ObjectMetadataGqlObjectTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/object-types/object-metadata-gql-object-type.generator';
|
||||
import { ObjectMetadataWithRelationsGqlObjectTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/object-types/object-metadata-with-relations-gql-object-type.generator';
|
||||
import { MutationTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/root-types/mutation-type.generator';
|
||||
@@ -32,6 +33,7 @@ export class GqlTypeGenerator {
|
||||
private readonly compositeFieldMetadataGqlInputTypeGenerator: CompositeFieldMetadataGqlInputTypeGenerator,
|
||||
private readonly edgeGqlObjectTypeGenerator: EdgeGqlObjectTypeGenerator,
|
||||
private readonly connectionGqlObjectTypeGenerator: ConnectionGqlObjectTypeGenerator,
|
||||
private readonly groupByConnectionGqlObjectTypeGenerator: GroupByConnectionGqlObjectTypeGenerator,
|
||||
private readonly objectMetadataWithRelationsGqlObjectTypeGenerator: ObjectMetadataWithRelationsGqlObjectTypeGenerator,
|
||||
private readonly relationConnectGqlInputTypeGenerator: RelationConnectGqlInputTypeGenerator,
|
||||
private readonly queryTypeGenerator: QueryTypeGenerator,
|
||||
@@ -77,6 +79,9 @@ export class GqlTypeGenerator {
|
||||
this.objectMetadataGqlObjectTypeGenerator.buildAndStore(objectMetadata);
|
||||
this.edgeGqlObjectTypeGenerator.buildAndStore(objectMetadata);
|
||||
this.connectionGqlObjectTypeGenerator.buildAndStore(objectMetadata);
|
||||
this.groupByConnectionGqlObjectTypeGenerator.buildAndStore(
|
||||
objectMetadata,
|
||||
);
|
||||
this.relationConnectGqlInputTypeGenerator.buildAndStore(objectMetadata);
|
||||
this.objectMetadataGqlInputTypeGenerator.buildAndStore(objectMetadata);
|
||||
|
||||
|
||||
+5
@@ -4,6 +4,7 @@ import { type CompositeType } from 'src/engine/metadata-modules/field-metadata/i
|
||||
|
||||
import { CompositeFieldMetadataCreateGqlInputTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/input-types/create-input/composite-field-metadata-create-gql-input-type.generator';
|
||||
import { CompositeFieldMetadataFilterGqlInputTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/input-types/filter-input/composite-field-metadata-filter-gql-input-types.generator';
|
||||
import { CompositeFieldMetadataGroupByGqlInputTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/input-types/group-by-input/composite-field-metadata-group-by-gql-input-type.generator';
|
||||
import { CompositeFieldMetadataOrderByGqlInputTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/input-types/order-by-input/composite-field-metadata-order-by-gql-input-type.generator';
|
||||
import { CompositeFieldMetadataUpdateGqlInputTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/input-types/update-input/composite-field-metadata-update-gql-input-type.generator';
|
||||
|
||||
@@ -14,6 +15,7 @@ export class CompositeFieldMetadataGqlInputTypeGenerator {
|
||||
private readonly compositeFieldMetadataUpdateGqlInputTypeGenerator: CompositeFieldMetadataUpdateGqlInputTypeGenerator,
|
||||
private readonly compositeFieldMetadataFilterGqlInputTypeGenerator: CompositeFieldMetadataFilterGqlInputTypeGenerator,
|
||||
private readonly compositeFieldMetadataOrderByGqlInputTypeGenerator: CompositeFieldMetadataOrderByGqlInputTypeGenerator,
|
||||
private readonly compositeFieldMetadataGroupByGqlInputTypeGenerator: CompositeFieldMetadataGroupByGqlInputTypeGenerator,
|
||||
) {}
|
||||
|
||||
public buildAndStore(compositeType: CompositeType) {
|
||||
@@ -29,5 +31,8 @@ export class CompositeFieldMetadataGqlInputTypeGenerator {
|
||||
this.compositeFieldMetadataOrderByGqlInputTypeGenerator.buildAndStore(
|
||||
compositeType,
|
||||
);
|
||||
this.compositeFieldMetadataGroupByGqlInputTypeGenerator.buildAndStore(
|
||||
compositeType,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+108
-70
@@ -1,25 +1,27 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
import { isDefined } from 'class-validator';
|
||||
import {
|
||||
GraphQLEnumType,
|
||||
GraphQLInputFieldConfigMap,
|
||||
GraphQLInputObjectType,
|
||||
GraphQLScalarType,
|
||||
isEnumType,
|
||||
isInputObjectType,
|
||||
isObjectType,
|
||||
} from 'graphql';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { GqlInputTypeDefinitionKind } from 'src/engine/api/graphql/workspace-schema-builder/enums/gql-input-type-definition-kind.enum';
|
||||
import { RelationFieldMetadataGqlInputTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/input-types/relation-field-metadata-gql-type.generator';
|
||||
import { TypeMapperService } from 'src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service';
|
||||
import {
|
||||
TypeMapperService,
|
||||
TypeOptions,
|
||||
} from 'src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service';
|
||||
import { GqlTypesStorage } from 'src/engine/api/graphql/workspace-schema-builder/storages/gql-types.storage';
|
||||
import { computeFieldInputTypeOptions } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-field-input-type-options.util';
|
||||
import { computeCompositeFieldInputTypeKey } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-stored-gql-type-key-utils/compute-composite-field-input-type-key.util';
|
||||
import { computeEnumFieldGqlTypeKey } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-stored-gql-type-key-utils/compute-enum-field-gql-type-key.util';
|
||||
import { computeObjectMetadataInputTypeKey } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-stored-gql-type-key-utils/compute-object-metadata-input-type.util';
|
||||
import { isFieldMetadataRelationOrMorphRelation } from 'src/engine/api/graphql/workspace-schema-builder/utils/is-field-metadata-relation-or-morph-relation.utils';
|
||||
import { FieldMetadataEntity } 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 { isEnumFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-enum-field-metadata-type.util';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
@@ -79,77 +81,113 @@ export class ObjectMetadataCreateGqlInputTypeGenerator {
|
||||
},
|
||||
),
|
||||
};
|
||||
} else if (isEnumFieldMetadataType(fieldMetadata.type)) {
|
||||
generatedFields = this.generateEnumFieldCreateInputType(
|
||||
objectMetadata,
|
||||
fieldMetadata,
|
||||
typeOptions,
|
||||
);
|
||||
} else if (isCompositeFieldMetadataType(fieldMetadata.type)) {
|
||||
generatedFields = this.generateCompositeFieldCreateInputType(
|
||||
fieldMetadata,
|
||||
typeOptions,
|
||||
);
|
||||
} else {
|
||||
let type:
|
||||
| GraphQLInputObjectType
|
||||
| GraphQLEnumType
|
||||
| GraphQLScalarType
|
||||
| undefined;
|
||||
|
||||
if (isEnumFieldMetadataType(fieldMetadata.type)) {
|
||||
const key = computeEnumFieldGqlTypeKey(
|
||||
objectMetadata.nameSingular,
|
||||
fieldMetadata.name,
|
||||
);
|
||||
|
||||
const enumType = this.gqlTypesStorage.getGqlTypeByKey(key);
|
||||
|
||||
if (!isDefined(enumType) || !isEnumType(enumType)) {
|
||||
const message = `Could not find a GraphQL input type for ${fieldMetadata.type} field metadata`;
|
||||
|
||||
this.logger.error(message, {
|
||||
type,
|
||||
typeOptions,
|
||||
});
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
type = enumType;
|
||||
} else if (isCompositeFieldMetadataType(fieldMetadata.type)) {
|
||||
const key = computeCompositeFieldInputTypeKey(
|
||||
fieldMetadata.type,
|
||||
GqlInputTypeDefinitionKind.Create,
|
||||
);
|
||||
|
||||
const compositeType = this.gqlTypesStorage.getGqlTypeByKey(key);
|
||||
|
||||
if (!isDefined(compositeType) || !isInputObjectType(compositeType)) {
|
||||
const message = `Could not find a GraphQL input type for ${fieldMetadata.type} field metadata`;
|
||||
|
||||
this.logger.error(message, {
|
||||
type,
|
||||
typeOptions,
|
||||
});
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
type = compositeType;
|
||||
} else {
|
||||
type = this.typeMapperService.mapToScalarType(
|
||||
fieldMetadata.type,
|
||||
typeOptions,
|
||||
);
|
||||
|
||||
if (!isDefined(type) || isObjectType(type)) {
|
||||
const message = `Could not find a GraphQL input type for ${fieldMetadata.type} field metadata`;
|
||||
|
||||
this.logger.error(message, {
|
||||
type,
|
||||
typeOptions,
|
||||
});
|
||||
throw new Error(message);
|
||||
}
|
||||
}
|
||||
generatedFields = {
|
||||
[fieldMetadata.name]: {
|
||||
type,
|
||||
description: fieldMetadata.description,
|
||||
},
|
||||
};
|
||||
generatedFields = this.generateAtomicFieldCreateInputType(
|
||||
fieldMetadata,
|
||||
typeOptions,
|
||||
);
|
||||
}
|
||||
|
||||
Object.assign(allGeneratedFields, generatedFields);
|
||||
}
|
||||
|
||||
return allGeneratedFields;
|
||||
}
|
||||
|
||||
private generateEnumFieldCreateInputType(
|
||||
objectMetadata: ObjectMetadataEntity,
|
||||
fieldMetadata: FieldMetadataEntity,
|
||||
typeOptions: TypeOptions,
|
||||
) {
|
||||
const key = computeEnumFieldGqlTypeKey(
|
||||
objectMetadata.nameSingular,
|
||||
fieldMetadata.name,
|
||||
);
|
||||
|
||||
const enumType = this.gqlTypesStorage.getGqlTypeByKey(key);
|
||||
|
||||
if (!isDefined(enumType) || !isEnumType(enumType)) {
|
||||
const message = `Could not find a GraphQL input type for ${fieldMetadata.type} field metadata`;
|
||||
|
||||
this.logger.error(message, {
|
||||
fieldMetadata,
|
||||
typeOptions,
|
||||
});
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
return {
|
||||
[fieldMetadata.name]: {
|
||||
type: enumType,
|
||||
description: fieldMetadata.description,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private generateCompositeFieldCreateInputType(
|
||||
fieldMetadata: FieldMetadataEntity,
|
||||
typeOptions: TypeOptions,
|
||||
) {
|
||||
const key = computeCompositeFieldInputTypeKey(
|
||||
fieldMetadata.type,
|
||||
GqlInputTypeDefinitionKind.Create,
|
||||
);
|
||||
|
||||
const compositeType = this.gqlTypesStorage.getGqlTypeByKey(key);
|
||||
|
||||
if (!isDefined(compositeType) || !isInputObjectType(compositeType)) {
|
||||
const message = `Could not find a GraphQL input type for ${fieldMetadata.type} field metadata`;
|
||||
|
||||
this.logger.error(message, {
|
||||
fieldMetadata,
|
||||
typeOptions,
|
||||
});
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
return {
|
||||
[fieldMetadata.name]: {
|
||||
type: compositeType,
|
||||
description: fieldMetadata.description,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private generateAtomicFieldCreateInputType(
|
||||
fieldMetadata: FieldMetadataEntity,
|
||||
typeOptions: TypeOptions,
|
||||
) {
|
||||
const type = this.typeMapperService.mapToScalarType(
|
||||
fieldMetadata.type,
|
||||
typeOptions,
|
||||
);
|
||||
|
||||
if (!isDefined(type) || isObjectType(type)) {
|
||||
const message = `Could not find a GraphQL input type for ${fieldMetadata.type} field metadata`;
|
||||
|
||||
this.logger.error(message, {
|
||||
type,
|
||||
typeOptions,
|
||||
});
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
return {
|
||||
[fieldMetadata.name]: {
|
||||
type,
|
||||
description: fieldMetadata.description,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+109
-65
@@ -3,7 +3,6 @@ import { Injectable, Logger } from '@nestjs/common';
|
||||
import {
|
||||
GraphQLInputFieldConfigMap,
|
||||
GraphQLInputObjectType,
|
||||
GraphQLInputType,
|
||||
isEnumType,
|
||||
isInputObjectType,
|
||||
isObjectType,
|
||||
@@ -12,7 +11,10 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { GqlInputTypeDefinitionKind } from 'src/engine/api/graphql/workspace-schema-builder/enums/gql-input-type-definition-kind.enum';
|
||||
import { RelationFieldMetadataGqlInputTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/input-types/relation-field-metadata-gql-type.generator';
|
||||
import { TypeMapperService } from 'src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service';
|
||||
import {
|
||||
TypeMapperService,
|
||||
TypeOptions,
|
||||
} from 'src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service';
|
||||
import { GqlTypesStorage } from 'src/engine/api/graphql/workspace-schema-builder/storages/gql-types.storage';
|
||||
import { computeFieldInputTypeOptions } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-field-input-type-options.util';
|
||||
import { computeCompositeFieldInputTypeKey } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-stored-gql-type-key-utils/compute-composite-field-input-type-key.util';
|
||||
@@ -20,6 +22,7 @@ import { computeEnumFieldGqlTypeKey } from 'src/engine/api/graphql/workspace-sch
|
||||
import { computeObjectMetadataInputTypeKey } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-stored-gql-type-key-utils/compute-object-metadata-input-type.util';
|
||||
import { createGqlEnumFilterType } from 'src/engine/api/graphql/workspace-schema-builder/utils/create-gql-enum-filter-type.util';
|
||||
import { isFieldMetadataRelationOrMorphRelation } from 'src/engine/api/graphql/workspace-schema-builder/utils/is-field-metadata-relation-or-morph-relation.utils';
|
||||
import { FieldMetadataEntity } 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 { isEnumFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-enum-field-metadata-type.util';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
@@ -67,7 +70,7 @@ export class ObjectMetadataFilterGqlInputTypeGenerator {
|
||||
|
||||
const typeOptions = computeFieldInputTypeOptions(
|
||||
fieldMetadata,
|
||||
GqlInputTypeDefinitionKind.Update,
|
||||
GqlInputTypeDefinitionKind.Filter,
|
||||
);
|
||||
|
||||
let generatedFields;
|
||||
@@ -80,69 +83,22 @@ export class ObjectMetadataFilterGqlInputTypeGenerator {
|
||||
typeOptions,
|
||||
},
|
||||
);
|
||||
} else if (isEnumFieldMetadataType(fieldMetadata.type)) {
|
||||
generatedFields = this.generateEnumFieldFilterInputType(
|
||||
objectMetadata,
|
||||
fieldMetadata,
|
||||
typeOptions,
|
||||
);
|
||||
} else if (isCompositeFieldMetadataType(fieldMetadata.type)) {
|
||||
generatedFields = this.generateCompositeFieldFilterInputType(
|
||||
fieldMetadata,
|
||||
typeOptions,
|
||||
);
|
||||
} else {
|
||||
let type: GraphQLInputType | undefined;
|
||||
|
||||
if (isEnumFieldMetadataType(fieldMetadata.type)) {
|
||||
const key = computeEnumFieldGqlTypeKey(
|
||||
objectMetadata.nameSingular,
|
||||
fieldMetadata.name,
|
||||
);
|
||||
|
||||
const enumType = this.gqlTypesStorage.getGqlTypeByKey(key);
|
||||
|
||||
if (!isDefined(enumType) || !isEnumType(enumType)) {
|
||||
const message = `Could not find a GraphQL input type for ${fieldMetadata.type} field metadata`;
|
||||
|
||||
this.logger.error(message, {
|
||||
type,
|
||||
typeOptions,
|
||||
});
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
type = createGqlEnumFilterType(enumType);
|
||||
} else if (isCompositeFieldMetadataType(fieldMetadata.type)) {
|
||||
const key = computeCompositeFieldInputTypeKey(
|
||||
fieldMetadata.type,
|
||||
GqlInputTypeDefinitionKind.Filter,
|
||||
);
|
||||
|
||||
const compositeType = this.gqlTypesStorage.getGqlTypeByKey(key);
|
||||
|
||||
if (!isDefined(compositeType) || !isInputObjectType(compositeType)) {
|
||||
const message = `Could not find a GraphQL input type for ${fieldMetadata.type} field metadata`;
|
||||
|
||||
this.logger.error(message, {
|
||||
type,
|
||||
typeOptions,
|
||||
});
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
type = compositeType;
|
||||
} else {
|
||||
type = this.typeMapperService.mapToFilterType(
|
||||
fieldMetadata.type,
|
||||
typeOptions,
|
||||
);
|
||||
|
||||
if (!isDefined(type) || isObjectType(type)) {
|
||||
const message = `Could not find a GraphQL input type for ${fieldMetadata.type} field metadata`;
|
||||
|
||||
this.logger.error(message, {
|
||||
type,
|
||||
typeOptions,
|
||||
});
|
||||
throw new Error(message);
|
||||
}
|
||||
}
|
||||
generatedFields = {
|
||||
[fieldMetadata.name]: {
|
||||
type,
|
||||
description: fieldMetadata.description,
|
||||
},
|
||||
};
|
||||
generatedFields = this.generateAtomicFieldFilterInputType(
|
||||
fieldMetadata,
|
||||
typeOptions,
|
||||
);
|
||||
}
|
||||
Object.assign(allGeneratedFields, generatedFields);
|
||||
}
|
||||
@@ -162,4 +118,92 @@ export class ObjectMetadataFilterGqlInputTypeGenerator {
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private generateEnumFieldFilterInputType(
|
||||
objectMetadata: ObjectMetadataEntity,
|
||||
fieldMetadata: FieldMetadataEntity,
|
||||
typeOptions: TypeOptions,
|
||||
) {
|
||||
const key = computeEnumFieldGqlTypeKey(
|
||||
objectMetadata.nameSingular,
|
||||
fieldMetadata.name,
|
||||
);
|
||||
|
||||
const enumType = this.gqlTypesStorage.getGqlTypeByKey(key);
|
||||
|
||||
if (!isDefined(enumType) || !isEnumType(enumType)) {
|
||||
const message = `Could not find a GraphQL input type for ${fieldMetadata.type} field metadata`;
|
||||
|
||||
this.logger.error(message, {
|
||||
fieldMetadata,
|
||||
typeOptions,
|
||||
});
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
const type = createGqlEnumFilterType(enumType);
|
||||
|
||||
return {
|
||||
[fieldMetadata.name]: {
|
||||
type,
|
||||
description: fieldMetadata.description,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private generateCompositeFieldFilterInputType(
|
||||
fieldMetadata: FieldMetadataEntity,
|
||||
typeOptions: TypeOptions,
|
||||
) {
|
||||
const key = computeCompositeFieldInputTypeKey(
|
||||
fieldMetadata.type,
|
||||
GqlInputTypeDefinitionKind.Filter,
|
||||
);
|
||||
|
||||
const compositeType = this.gqlTypesStorage.getGqlTypeByKey(key);
|
||||
|
||||
if (!isDefined(compositeType) || !isInputObjectType(compositeType)) {
|
||||
const message = `Could not find a GraphQL input type for ${fieldMetadata.type} field metadata`;
|
||||
|
||||
this.logger.error(message, {
|
||||
fieldMetadata,
|
||||
typeOptions,
|
||||
});
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
return {
|
||||
[fieldMetadata.name]: {
|
||||
type: compositeType,
|
||||
description: fieldMetadata.description,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private generateAtomicFieldFilterInputType(
|
||||
fieldMetadata: FieldMetadataEntity,
|
||||
typeOptions: TypeOptions,
|
||||
) {
|
||||
const type = this.typeMapperService.mapToFilterType(
|
||||
fieldMetadata.type,
|
||||
typeOptions,
|
||||
);
|
||||
|
||||
if (!isDefined(type) || isObjectType(type)) {
|
||||
const message = `Could not find a GraphQL input type for ${fieldMetadata.type} field metadata`;
|
||||
|
||||
this.logger.error(message, {
|
||||
fieldMetadata,
|
||||
typeOptions,
|
||||
});
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
return {
|
||||
[fieldMetadata.name]: {
|
||||
type,
|
||||
description: fieldMetadata.description,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
import {
|
||||
GraphQLBoolean,
|
||||
GraphQLInputFieldConfigMap,
|
||||
GraphQLInputObjectType,
|
||||
} from 'graphql';
|
||||
|
||||
import { CompositeType } from 'src/engine/metadata-modules/field-metadata/interfaces/composite-type.interface';
|
||||
|
||||
import { GqlInputTypeDefinitionKind } from 'src/engine/api/graphql/workspace-schema-builder/enums/gql-input-type-definition-kind.enum';
|
||||
import { TypeMapperService } from 'src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service';
|
||||
import { GqlTypesStorage } from 'src/engine/api/graphql/workspace-schema-builder/storages/gql-types.storage';
|
||||
import { computeCompositeFieldInputTypeKey } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-stored-gql-type-key-utils/compute-composite-field-input-type-key.util';
|
||||
import { isMorphOrRelationFieldMetadataType } from 'src/engine/utils/is-morph-or-relation-field-metadata-type.util';
|
||||
import { pascalCase } from 'src/utils/pascal-case';
|
||||
|
||||
@Injectable()
|
||||
export class CompositeFieldMetadataGroupByGqlInputTypeGenerator {
|
||||
private readonly logger = new Logger(
|
||||
CompositeFieldMetadataGroupByGqlInputTypeGenerator.name,
|
||||
);
|
||||
constructor(
|
||||
private readonly gqlTypesStorage: GqlTypesStorage,
|
||||
private readonly typeMapperService: TypeMapperService,
|
||||
) {}
|
||||
|
||||
public buildAndStore(compositeType: CompositeType) {
|
||||
const key = computeCompositeFieldInputTypeKey(
|
||||
compositeType.type,
|
||||
GqlInputTypeDefinitionKind.GroupBy,
|
||||
);
|
||||
|
||||
const type = new GraphQLInputObjectType({
|
||||
name: `${pascalCase(compositeType.type)}${GqlInputTypeDefinitionKind.GroupBy.toString()}Input`,
|
||||
fields: this.generateFields(compositeType),
|
||||
});
|
||||
|
||||
this.gqlTypesStorage.addGqlType(key, type);
|
||||
}
|
||||
|
||||
public generateFields(
|
||||
compositeType: CompositeType,
|
||||
): GraphQLInputFieldConfigMap {
|
||||
const fields: GraphQLInputFieldConfigMap = {};
|
||||
|
||||
for (const property of compositeType.properties) {
|
||||
// Relation fields are not supported in composite types
|
||||
if (isMorphOrRelationFieldMetadataType(property.type)) {
|
||||
this.logger.error(
|
||||
'Relation fields are not supported in composite types',
|
||||
{ compositeType, property },
|
||||
);
|
||||
|
||||
throw new Error('Relation fields are not supported in composite types');
|
||||
}
|
||||
|
||||
// Skip hidden fields
|
||||
if (property.hidden === true || property.hidden === 'input') {
|
||||
continue;
|
||||
}
|
||||
|
||||
const type = this.typeMapperService.applyTypeOptions(GraphQLBoolean, {});
|
||||
|
||||
fields[property.name] = {
|
||||
type,
|
||||
description: property.description,
|
||||
};
|
||||
}
|
||||
|
||||
return fields;
|
||||
}
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
import {
|
||||
GraphQLBoolean,
|
||||
GraphQLInputFieldConfigMap,
|
||||
GraphQLInputObjectType,
|
||||
isInputObjectType,
|
||||
} from 'graphql';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { GqlInputTypeDefinitionKind } from 'src/engine/api/graphql/workspace-schema-builder/enums/gql-input-type-definition-kind.enum';
|
||||
import { RelationFieldMetadataGqlInputTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/input-types/relation-field-metadata-gql-type.generator';
|
||||
import { TypeMapperService } from 'src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service';
|
||||
import { GqlTypesStorage } from 'src/engine/api/graphql/workspace-schema-builder/storages/gql-types.storage';
|
||||
import { computeCompositeFieldInputTypeKey } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-stored-gql-type-key-utils/compute-composite-field-input-type-key.util';
|
||||
import { computeObjectMetadataInputTypeKey } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-stored-gql-type-key-utils/compute-object-metadata-input-type.util';
|
||||
import { isFieldMetadataRelationOrMorphRelation } from 'src/engine/api/graphql/workspace-schema-builder/utils/is-field-metadata-relation-or-morph-relation.utils';
|
||||
import { FieldMetadataEntity } 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 { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { pascalCase } from 'src/utils/pascal-case';
|
||||
|
||||
@Injectable()
|
||||
export class ObjectMetadataGroupByGqlInputTypeGenerator {
|
||||
private readonly logger = new Logger(
|
||||
ObjectMetadataGroupByGqlInputTypeGenerator.name,
|
||||
);
|
||||
constructor(
|
||||
private readonly gqlTypesStorage: GqlTypesStorage,
|
||||
private readonly relationFieldMetadataGqlInputTypeGenerator: RelationFieldMetadataGqlInputTypeGenerator,
|
||||
private readonly typeMapperService: TypeMapperService,
|
||||
) {}
|
||||
|
||||
public buildAndStore(objectMetadata: ObjectMetadataEntity) {
|
||||
const inputType = new GraphQLInputObjectType({
|
||||
name: `${pascalCase(objectMetadata.nameSingular)}${GqlInputTypeDefinitionKind.GroupBy.toString()}Input`,
|
||||
description: objectMetadata.description,
|
||||
fields: () => this.generateFields(objectMetadata),
|
||||
}) as GraphQLInputObjectType;
|
||||
|
||||
const key = computeObjectMetadataInputTypeKey(
|
||||
objectMetadata.nameSingular,
|
||||
GqlInputTypeDefinitionKind.GroupBy,
|
||||
);
|
||||
|
||||
this.gqlTypesStorage.addGqlType(key, inputType);
|
||||
}
|
||||
|
||||
private generateFields(
|
||||
objectMetadata: ObjectMetadataEntity,
|
||||
): GraphQLInputFieldConfigMap {
|
||||
const allGeneratedFields: GraphQLInputFieldConfigMap = {};
|
||||
|
||||
for (const fieldMetadata of objectMetadata.fields) {
|
||||
const generatedField = isFieldMetadataRelationOrMorphRelation(
|
||||
fieldMetadata,
|
||||
)
|
||||
? this.relationFieldMetadataGqlInputTypeGenerator.generateSimpleRelationFieldGroupByInputType(
|
||||
fieldMetadata,
|
||||
)
|
||||
: this.generateField(fieldMetadata);
|
||||
|
||||
Object.assign(allGeneratedFields, generatedField);
|
||||
}
|
||||
|
||||
return allGeneratedFields;
|
||||
}
|
||||
|
||||
private generateField(fieldMetadata: FieldMetadataEntity) {
|
||||
if (isCompositeFieldMetadataType(fieldMetadata.type))
|
||||
return this.generateCompositeFieldGroupByInputType(fieldMetadata);
|
||||
|
||||
const type = this.typeMapperService.applyTypeOptions(GraphQLBoolean, {});
|
||||
|
||||
return {
|
||||
[fieldMetadata.name]: {
|
||||
type,
|
||||
description: fieldMetadata.description,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private generateCompositeFieldGroupByInputType(
|
||||
fieldMetadata: FieldMetadataEntity,
|
||||
) {
|
||||
const key = computeCompositeFieldInputTypeKey(
|
||||
fieldMetadata.type,
|
||||
GqlInputTypeDefinitionKind.GroupBy,
|
||||
);
|
||||
|
||||
const compositeType = this.gqlTypesStorage.getGqlTypeByKey(key);
|
||||
|
||||
if (!isDefined(compositeType) || !isInputObjectType(compositeType)) {
|
||||
const message = `Could not find a GraphQL input type for ${fieldMetadata.type} field metadata`;
|
||||
|
||||
this.logger.error(message, {
|
||||
fieldMetadata,
|
||||
});
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
return {
|
||||
[fieldMetadata.name]: {
|
||||
type: compositeType,
|
||||
description: fieldMetadata.description,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
+5
@@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { ObjectMetadataCreateGqlInputTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/input-types/create-input/object-metadata-create-gql-input-type.generator';
|
||||
import { ObjectMetadataFilterGqlInputTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/input-types/filter-input/object-metadata-filter-gql-input-type.generator';
|
||||
import { ObjectMetadataGroupByGqlInputTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/input-types/group-by-input/object-metadata-group-by-gql-input-type.generator';
|
||||
import { ObjectMetadataOrderByGqlInputTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/input-types/order-by-input/object-metadata-order-by-gql-input-type.generator';
|
||||
import { ObjectMetadataUpdateGqlInputTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/input-types/update-input/object-metadata-update-gql-input-type.generator';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
@@ -13,6 +14,7 @@ export class ObjectMetadataGqlInputTypeGenerator {
|
||||
private readonly objectMetadataUpdateGqlInputTypeGenerator: ObjectMetadataUpdateGqlInputTypeGenerator,
|
||||
private readonly objectMetadataFilterGqlInputTypeGenerator: ObjectMetadataFilterGqlInputTypeGenerator,
|
||||
private readonly objectMetadataOrderByGqlInputTypeGenerator: ObjectMetadataOrderByGqlInputTypeGenerator,
|
||||
private readonly objectMetadataGroupByGqlInputTypeGenerator: ObjectMetadataGroupByGqlInputTypeGenerator,
|
||||
) {}
|
||||
|
||||
public buildAndStore(objectMetadata: ObjectMetadataEntity) {
|
||||
@@ -28,5 +30,8 @@ export class ObjectMetadataGqlInputTypeGenerator {
|
||||
this.objectMetadataOrderByGqlInputTypeGenerator.buildAndStore(
|
||||
objectMetadata,
|
||||
);
|
||||
this.objectMetadataGroupByGqlInputTypeGenerator.buildAndStore(
|
||||
objectMetadata,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-11
@@ -13,9 +13,7 @@ import { GqlInputTypeDefinitionKind } from 'src/engine/api/graphql/workspace-sch
|
||||
import { TypeMapperService } from 'src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service';
|
||||
import { GqlTypesStorage } from 'src/engine/api/graphql/workspace-schema-builder/storages/gql-types.storage';
|
||||
import { computeCompositeFieldTypeOptions } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-composite-field-type-options.util';
|
||||
import { computeCompositeFieldEnumTypeKey } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-stored-gql-type-key-utils/compute-composite-field-enum-type-key.util';
|
||||
import { computeCompositeFieldInputTypeKey } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-stored-gql-type-key-utils/compute-composite-field-input-type-key.util';
|
||||
import { isEnumFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-enum-field-metadata-type.util';
|
||||
import { isMorphOrRelationFieldMetadataType } from 'src/engine/utils/is-morph-or-relation-field-metadata-type.util';
|
||||
import { pascalCase } from 'src/utils/pascal-case';
|
||||
|
||||
@@ -61,20 +59,13 @@ export class CompositeFieldMetadataOrderByGqlInputTypeGenerator {
|
||||
}
|
||||
|
||||
// Skip hidden fields
|
||||
if (property.hidden === true || property.hidden === 'input') {
|
||||
if (property.hidden === true) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const key = computeCompositeFieldEnumTypeKey(
|
||||
compositeType.type,
|
||||
property.name,
|
||||
);
|
||||
|
||||
const typeOptions = computeCompositeFieldTypeOptions(property);
|
||||
|
||||
const type = isEnumFieldMetadataType(property.type)
|
||||
? this.gqlTypesStorage.getGqlTypeByKey(key)
|
||||
: this.typeMapperService.mapToOrderByType(property.type);
|
||||
const type = this.typeMapperService.mapToOrderByType(property.type);
|
||||
|
||||
if (!isDefined(type) || isObjectType(type)) {
|
||||
const message = `Could not find a GraphQL input type for ${compositeType.type} ${property.name}`;
|
||||
|
||||
+109
-83
@@ -1,12 +1,8 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
import {
|
||||
GraphQLEnumType,
|
||||
GraphQLInputFieldConfigMap,
|
||||
GraphQLInputObjectType,
|
||||
GraphQLInputType,
|
||||
GraphQLList,
|
||||
GraphQLScalarType,
|
||||
isEnumType,
|
||||
isInputObjectType,
|
||||
} from 'graphql';
|
||||
@@ -14,13 +10,17 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { GqlInputTypeDefinitionKind } from 'src/engine/api/graphql/workspace-schema-builder/enums/gql-input-type-definition-kind.enum';
|
||||
import { RelationFieldMetadataGqlInputTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/input-types/relation-field-metadata-gql-type.generator';
|
||||
import { TypeMapperService } from 'src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service';
|
||||
import {
|
||||
TypeMapperService,
|
||||
TypeOptions,
|
||||
} from 'src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service';
|
||||
import { GqlTypesStorage } from 'src/engine/api/graphql/workspace-schema-builder/storages/gql-types.storage';
|
||||
import { computeFieldInputTypeOptions } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-field-input-type-options.util';
|
||||
import { computeCompositeFieldInputTypeKey } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-stored-gql-type-key-utils/compute-composite-field-input-type-key.util';
|
||||
import { computeEnumFieldGqlTypeKey } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-stored-gql-type-key-utils/compute-enum-field-gql-type-key.util';
|
||||
import { computeObjectMetadataInputTypeKey } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-stored-gql-type-key-utils/compute-object-metadata-input-type.util';
|
||||
import { isFieldMetadataRelationOrMorphRelation } from 'src/engine/api/graphql/workspace-schema-builder/utils/is-field-metadata-relation-or-morph-relation.utils';
|
||||
import { FieldMetadataEntity } 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 { isEnumFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-enum-field-metadata-type.util';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
@@ -68,93 +68,119 @@ export class ObjectMetadataOrderByGqlInputTypeGenerator {
|
||||
let generatedFields;
|
||||
|
||||
if (isFieldMetadataRelationOrMorphRelation(fieldMetadata)) {
|
||||
generatedFields = {
|
||||
...this.relationFieldMetadataGqlInputTypeGenerator.generateSimpleRelationFieldCreateOrUpdateInputType(
|
||||
generatedFields =
|
||||
this.relationFieldMetadataGqlInputTypeGenerator.generateSimpleRelationFieldOrderByInputType(
|
||||
{
|
||||
fieldMetadata,
|
||||
typeOptions,
|
||||
},
|
||||
),
|
||||
...this.relationFieldMetadataGqlInputTypeGenerator.generateConnectRelationFieldInputType(
|
||||
{
|
||||
fieldMetadata,
|
||||
typeOptions,
|
||||
},
|
||||
),
|
||||
};
|
||||
);
|
||||
} else if (isEnumFieldMetadataType(fieldMetadata.type)) {
|
||||
generatedFields = this.generateEnumFieldOrderByInputType(
|
||||
objectMetadata,
|
||||
fieldMetadata,
|
||||
typeOptions,
|
||||
);
|
||||
} else if (isCompositeFieldMetadataType(fieldMetadata.type)) {
|
||||
generatedFields = this.generateCompositeFieldOrderByInputType(
|
||||
fieldMetadata,
|
||||
typeOptions,
|
||||
);
|
||||
} else {
|
||||
let type:
|
||||
| GraphQLInputObjectType
|
||||
| GraphQLEnumType
|
||||
| GraphQLScalarType
|
||||
| GraphQLList<GraphQLInputType>
|
||||
| undefined;
|
||||
|
||||
if (isEnumFieldMetadataType(fieldMetadata.type)) {
|
||||
const key = computeEnumFieldGqlTypeKey(
|
||||
objectMetadata.nameSingular,
|
||||
fieldMetadata.name,
|
||||
);
|
||||
|
||||
const enumType = this.gqlTypesStorage.getGqlTypeByKey(key);
|
||||
|
||||
if (!isDefined(enumType) || !isEnumType(enumType)) {
|
||||
const message = `Could not find a GraphQL input type for ${fieldMetadata.type} field metadata`;
|
||||
|
||||
this.logger.error(message, {
|
||||
type,
|
||||
typeOptions,
|
||||
});
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
type = enumType;
|
||||
} else if (isCompositeFieldMetadataType(fieldMetadata.type)) {
|
||||
const key = computeCompositeFieldInputTypeKey(
|
||||
fieldMetadata.type,
|
||||
GqlInputTypeDefinitionKind.OrderBy,
|
||||
);
|
||||
|
||||
const compositeType = this.gqlTypesStorage.getGqlTypeByKey(key);
|
||||
|
||||
if (!isDefined(compositeType) || !isInputObjectType(compositeType)) {
|
||||
const message = `Could not find a GraphQL input type for ${fieldMetadata.type} field metadata`;
|
||||
|
||||
this.logger.error(message, {
|
||||
type,
|
||||
typeOptions,
|
||||
});
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
type = compositeType;
|
||||
} else {
|
||||
const orderByType = this.typeMapperService.mapToOrderByType(
|
||||
fieldMetadata.type,
|
||||
);
|
||||
|
||||
if (!isDefined(orderByType)) {
|
||||
const message = `Could not find a GraphQL input type for ${fieldMetadata.type} field metadata`;
|
||||
|
||||
this.logger.error(message, {
|
||||
type,
|
||||
typeOptions,
|
||||
});
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
type = orderByType;
|
||||
}
|
||||
generatedFields = {
|
||||
[fieldMetadata.name]: {
|
||||
type,
|
||||
description: fieldMetadata.description,
|
||||
},
|
||||
};
|
||||
generatedFields = this.generateAtomicFieldOrderByInputType(
|
||||
fieldMetadata,
|
||||
typeOptions,
|
||||
);
|
||||
}
|
||||
|
||||
Object.assign(allGeneratedFields, generatedFields);
|
||||
}
|
||||
|
||||
return allGeneratedFields;
|
||||
}
|
||||
|
||||
private generateCompositeFieldOrderByInputType(
|
||||
fieldMetadata: FieldMetadataEntity,
|
||||
typeOptions: TypeOptions,
|
||||
) {
|
||||
const key = computeCompositeFieldInputTypeKey(
|
||||
fieldMetadata.type,
|
||||
GqlInputTypeDefinitionKind.OrderBy,
|
||||
);
|
||||
|
||||
const compositeType = this.gqlTypesStorage.getGqlTypeByKey(key);
|
||||
|
||||
if (!isDefined(compositeType) || !isInputObjectType(compositeType)) {
|
||||
const message = `Could not find a GraphQL input type for ${fieldMetadata.type} field metadata`;
|
||||
|
||||
this.logger.error(message, {
|
||||
fieldMetadata,
|
||||
typeOptions,
|
||||
});
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
return {
|
||||
[fieldMetadata.name]: {
|
||||
type: compositeType,
|
||||
description: fieldMetadata.description,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private generateAtomicFieldOrderByInputType(
|
||||
fieldMetadata: FieldMetadataEntity,
|
||||
typeOptions: TypeOptions,
|
||||
) {
|
||||
const orderByType = this.typeMapperService.mapToOrderByType(
|
||||
fieldMetadata.type,
|
||||
);
|
||||
|
||||
if (!isDefined(orderByType)) {
|
||||
const message = `Could not find a GraphQL input type for ${fieldMetadata.type} field metadata`;
|
||||
|
||||
this.logger.error(message, {
|
||||
fieldMetadata,
|
||||
typeOptions,
|
||||
});
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
return {
|
||||
[fieldMetadata.name]: {
|
||||
type: orderByType,
|
||||
description: fieldMetadata.description,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private generateEnumFieldOrderByInputType(
|
||||
objectMetadata: ObjectMetadataEntity,
|
||||
fieldMetadata: FieldMetadataEntity,
|
||||
typeOptions: TypeOptions,
|
||||
) {
|
||||
const key = computeEnumFieldGqlTypeKey(
|
||||
objectMetadata.nameSingular,
|
||||
fieldMetadata.name,
|
||||
);
|
||||
|
||||
const enumType = this.gqlTypesStorage.getGqlTypeByKey(key);
|
||||
|
||||
if (!isDefined(enumType) || !isEnumType(enumType)) {
|
||||
const message = `Could not find a GraphQL input type for ${fieldMetadata.type} field metadata`;
|
||||
|
||||
this.logger.error(message, {
|
||||
fieldMetadata,
|
||||
typeOptions,
|
||||
});
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
return {
|
||||
[fieldMetadata.name]: {
|
||||
type: enumType,
|
||||
description: fieldMetadata.description,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+25
-1
@@ -1,6 +1,10 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
import { isObjectType } from 'graphql';
|
||||
import {
|
||||
GraphQLBoolean,
|
||||
GraphQLInputFieldConfigMap,
|
||||
isObjectType,
|
||||
} from 'graphql';
|
||||
import { FieldMetadataType, RelationType } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
@@ -147,6 +151,26 @@ export class RelationFieldMetadataGqlInputTypeGenerator {
|
||||
};
|
||||
}
|
||||
|
||||
public generateSimpleRelationFieldGroupByInputType(
|
||||
fieldMetadata: FieldMetadataEntity<
|
||||
FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION
|
||||
>,
|
||||
): GraphQLInputFieldConfigMap {
|
||||
if (fieldMetadata.settings?.relationType === RelationType.ONE_TO_MANY)
|
||||
return {};
|
||||
|
||||
const { joinColumnName } = extractGraphQLRelationFieldNames(fieldMetadata);
|
||||
|
||||
const type = this.typeMapperService.applyTypeOptions(GraphQLBoolean, {});
|
||||
|
||||
return {
|
||||
[joinColumnName]: {
|
||||
type,
|
||||
description: fieldMetadata.description,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public generateConnectRelationFieldInputType({
|
||||
fieldMetadata,
|
||||
typeOptions,
|
||||
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
import { GraphQLObjectType, isObjectType } from 'graphql';
|
||||
import GraphQLJSON from 'graphql-type-json';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { ObjectTypeDefinitionKind } from 'src/engine/api/graphql/workspace-schema-builder/enums/object-type-definition-kind.enum';
|
||||
import { TypeMapperService } from 'src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service';
|
||||
import { GqlTypesStorage } from 'src/engine/api/graphql/workspace-schema-builder/storages/gql-types.storage';
|
||||
import { GraphQLOutputTypeFieldConfigMap } from 'src/engine/api/graphql/workspace-schema-builder/types/graphql-field-config-map.types';
|
||||
import { computeObjectMetadataObjectTypeKey } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-stored-gql-type-key-utils/compute-object-metadata-object-type-key.util';
|
||||
import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { pascalCase } from 'src/utils/pascal-case';
|
||||
|
||||
@Injectable()
|
||||
export class GroupByConnectionGqlObjectTypeGenerator {
|
||||
private readonly logger = new Logger(
|
||||
GroupByConnectionGqlObjectTypeGenerator.name,
|
||||
);
|
||||
|
||||
constructor(
|
||||
private readonly typeMapperService: TypeMapperService,
|
||||
private readonly gqlTypesStorage: GqlTypesStorage,
|
||||
) {}
|
||||
|
||||
public buildAndStore(objectMetadata: ObjectMetadataEntity) {
|
||||
const kind = ObjectTypeDefinitionKind.GroupByConnection;
|
||||
|
||||
const key = computeObjectMetadataObjectTypeKey(
|
||||
objectMetadata.nameSingular,
|
||||
kind,
|
||||
);
|
||||
|
||||
this.gqlTypesStorage.addGqlType(
|
||||
key,
|
||||
new GraphQLObjectType({
|
||||
name: `${pascalCase(objectMetadata.nameSingular)}${kind.toString()}`,
|
||||
description: objectMetadata.description,
|
||||
fields: () => this.generateFields(objectMetadata),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
private generateFields(
|
||||
objectMetadata: ObjectMetadataEntity,
|
||||
): GraphQLOutputTypeFieldConfigMap {
|
||||
const fields: GraphQLOutputTypeFieldConfigMap = {};
|
||||
|
||||
const connection = this.gqlTypesStorage.getGqlTypeByKey(
|
||||
computeObjectMetadataObjectTypeKey(
|
||||
objectMetadata.nameSingular,
|
||||
ObjectTypeDefinitionKind.Connection,
|
||||
),
|
||||
);
|
||||
|
||||
if (!isDefined(connection) || !isObjectType(connection)) {
|
||||
this.logger.error(
|
||||
`Connection type for ${objectMetadata.nameSingular} was not found.`,
|
||||
{
|
||||
objectMetadata,
|
||||
},
|
||||
);
|
||||
|
||||
throw new Error(
|
||||
`Connection type for ${objectMetadata.nameSingular} was not found.`,
|
||||
);
|
||||
}
|
||||
|
||||
Object.assign(fields, connection.toConfig().fields);
|
||||
|
||||
fields.groupByDimensionValues = {
|
||||
type: this.typeMapperService.applyTypeOptions(GraphQLJSON, {
|
||||
nullable: false,
|
||||
}),
|
||||
};
|
||||
|
||||
return fields;
|
||||
}
|
||||
}
|
||||
+5
-2
@@ -100,19 +100,20 @@ export class RootTypeGenerator {
|
||||
);
|
||||
}
|
||||
|
||||
const allowedMethodNames = [
|
||||
const isMethodReturningArrayObjectType = [
|
||||
'updateMany',
|
||||
'deleteMany',
|
||||
'createMany',
|
||||
'findDuplicates',
|
||||
'restoreMany',
|
||||
'destroyMany',
|
||||
'groupBy',
|
||||
];
|
||||
|
||||
const outputType = this.typeMapperService.applyTypeOptions(
|
||||
objectType,
|
||||
{
|
||||
isArray: allowedMethodNames.includes(methodName),
|
||||
isArray: isMethodReturningArrayObjectType.includes(methodName),
|
||||
},
|
||||
);
|
||||
|
||||
@@ -135,6 +136,8 @@ export class RootTypeGenerator {
|
||||
case 'findMany':
|
||||
case 'findDuplicates':
|
||||
return ObjectTypeDefinitionKind.Connection;
|
||||
case 'groupBy':
|
||||
return ObjectTypeDefinitionKind.GroupByConnection;
|
||||
default:
|
||||
return ObjectTypeDefinitionKind.Plain;
|
||||
}
|
||||
|
||||
+6
@@ -6,6 +6,8 @@ import { CompositeFieldMetadataCreateGqlInputTypeGenerator } from 'src/engine/ap
|
||||
import { ObjectMetadataCreateGqlInputTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/input-types/create-input/object-metadata-create-gql-input-type.generator';
|
||||
import { CompositeFieldMetadataFilterGqlInputTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/input-types/filter-input/composite-field-metadata-filter-gql-input-types.generator';
|
||||
import { ObjectMetadataFilterGqlInputTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/input-types/filter-input/object-metadata-filter-gql-input-type.generator';
|
||||
import { CompositeFieldMetadataGroupByGqlInputTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/input-types/group-by-input/composite-field-metadata-group-by-gql-input-type.generator';
|
||||
import { ObjectMetadataGroupByGqlInputTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/input-types/group-by-input/object-metadata-group-by-gql-input-type.generator';
|
||||
import { ObjectMetadataGqlInputTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/input-types/object-metadata-gql-input-type.generator';
|
||||
import { CompositeFieldMetadataOrderByGqlInputTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/input-types/order-by-input/composite-field-metadata-order-by-gql-input-type.generator';
|
||||
import { ObjectMetadataOrderByGqlInputTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/input-types/order-by-input/object-metadata-order-by-gql-input-type.generator';
|
||||
@@ -17,6 +19,7 @@ import { AggregationObjectTypeGenerator } from 'src/engine/api/graphql/workspace
|
||||
import { CompositeFieldMetadataGqlObjectTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/object-types/composite-field-metadata-gql-object-type.generator';
|
||||
import { ConnectionGqlObjectTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/object-types/connection-gql-object-type.generator';
|
||||
import { EdgeGqlObjectTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/object-types/edge-gql-object-type.generator';
|
||||
import { GroupByConnectionGqlObjectTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/object-types/group-by-connection-gql-object-type.generator';
|
||||
import { ObjectMetadataGqlObjectTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/object-types/object-metadata-gql-object-type.generator';
|
||||
import { ObjectMetadataWithRelationsGqlObjectTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/object-types/object-metadata-with-relations-gql-object-type.generator';
|
||||
import { RelationFieldMetadataGqlObjectTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/object-types/relation-field-metadata-gql-object-type.generator';
|
||||
@@ -33,6 +36,7 @@ export const workspaceSchemaBuilderTypeGenerators = [
|
||||
CompositeFieldMetadataUpdateGqlInputTypeGenerator,
|
||||
CompositeFieldMetadataFilterGqlInputTypeGenerator,
|
||||
CompositeFieldMetadataOrderByGqlInputTypeGenerator,
|
||||
CompositeFieldMetadataGroupByGqlInputTypeGenerator,
|
||||
RelationFieldMetadataGqlInputTypeGenerator,
|
||||
RelationFieldMetadataGqlObjectTypeGenerator,
|
||||
ObjectMetadataGqlInputTypeGenerator,
|
||||
@@ -40,11 +44,13 @@ export const workspaceSchemaBuilderTypeGenerators = [
|
||||
ObjectMetadataUpdateGqlInputTypeGenerator,
|
||||
ObjectMetadataFilterGqlInputTypeGenerator,
|
||||
ObjectMetadataOrderByGqlInputTypeGenerator,
|
||||
ObjectMetadataGroupByGqlInputTypeGenerator,
|
||||
CompositeFieldMetadataGqlObjectTypeGenerator,
|
||||
ObjectMetadataGqlObjectTypeGenerator,
|
||||
RelationConnectGqlInputTypeGenerator,
|
||||
ConnectionGqlObjectTypeGenerator,
|
||||
EdgeGqlObjectTypeGenerator,
|
||||
GroupByConnectionGqlObjectTypeGenerator,
|
||||
ObjectMetadataWithRelationsGqlObjectTypeGenerator,
|
||||
AggregationObjectTypeGenerator,
|
||||
ArgsTypeGenerator,
|
||||
|
||||
+21
@@ -161,6 +161,27 @@ export const getResolverArgs = (
|
||||
isNullable: true,
|
||||
},
|
||||
};
|
||||
case 'groupBy':
|
||||
return {
|
||||
groupBy: {
|
||||
kind: GqlInputTypeDefinitionKind.GroupBy,
|
||||
isNullable: false,
|
||||
isArray: true,
|
||||
},
|
||||
filter: {
|
||||
kind: GqlInputTypeDefinitionKind.Filter,
|
||||
isNullable: true,
|
||||
},
|
||||
orderBy: {
|
||||
kind: GqlInputTypeDefinitionKind.OrderBy,
|
||||
isNullable: true,
|
||||
isArray: true,
|
||||
},
|
||||
viewId: {
|
||||
type: UUIDScalarType,
|
||||
isNullable: true,
|
||||
},
|
||||
};
|
||||
default:
|
||||
throw new Error(`Unknown resolver type: ${type}`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user