| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382 |
- import { CustomFieldType } from '@vendure/common/lib/shared-types';
- import { assertNever } from '@vendure/common/lib/shared-utils';
- import {
- buildSchema,
- extendSchema,
- GraphQLInputObjectType,
- GraphQLSchema,
- InputObjectTypeDefinitionNode,
- ObjectTypeDefinitionNode,
- parse,
- } from 'graphql';
- import { CustomFieldConfig, CustomFields } from '../../config/custom-field/custom-field-types';
- /**
- * Given a CustomFields config object, generates an SDL string extending the built-in
- * types with a customFields property for all entities, translations and inputs for which
- * custom fields are defined.
- */
- export function addGraphQLCustomFields(
- typeDefsOrSchema: string | GraphQLSchema,
- customFieldConfig: CustomFields,
- publicOnly: boolean,
- ): GraphQLSchema {
- const schema = typeof typeDefsOrSchema === 'string' ? buildSchema(typeDefsOrSchema) : typeDefsOrSchema;
- let customFieldTypeDefs = '';
- if (!schema.getType('JSON')) {
- customFieldTypeDefs += `
- scalar JSON
- `;
- }
- if (!schema.getType('DateTime')) {
- customFieldTypeDefs += `
- scalar DateTime
- `;
- }
- for (const entityName of Object.keys(customFieldConfig)) {
- const customEntityFields = (customFieldConfig[entityName as keyof CustomFields] || []).filter(
- config => {
- return !config.internal && (publicOnly === true ? config.public !== false : true);
- },
- );
- const localeStringFields = customEntityFields.filter(field => field.type === 'localeString');
- const nonLocaleStringFields = customEntityFields.filter(field => field.type !== 'localeString');
- const writeableLocaleStringFields = localeStringFields.filter(field => !field.readonly);
- const writeableNonLocaleStringFields = nonLocaleStringFields.filter(field => !field.readonly);
- if (schema.getType(entityName)) {
- if (customEntityFields.length) {
- customFieldTypeDefs += `
- type ${entityName}CustomFields {
- ${mapToFields(customEntityFields, getGraphQlType)}
- }
- extend type ${entityName} {
- customFields: ${entityName}CustomFields
- }
- `;
- // For custom fields on the Address entity, we also extend the OrderAddress
- // type (which is used to store address snapshots on Orders)
- if (entityName === 'Address' && schema.getType('OrderAddress')) {
- customFieldTypeDefs += `
- extend type OrderAddress {
- customFields: ${entityName}CustomFields
- }
- `;
- }
- } else {
- customFieldTypeDefs += `
- extend type ${entityName} {
- customFields: JSON
- }
- `;
- }
- }
- if (localeStringFields.length && schema.getType(`${entityName}Translation`)) {
- customFieldTypeDefs += `
- type ${entityName}TranslationCustomFields {
- ${mapToFields(localeStringFields, getGraphQlType)}
- }
- extend type ${entityName}Translation {
- customFields: ${entityName}TranslationCustomFields
- }
- `;
- }
- if (schema.getType(`Create${entityName}Input`)) {
- if (writeableNonLocaleStringFields.length) {
- customFieldTypeDefs += `
- input Create${entityName}CustomFieldsInput {
- ${mapToFields(writeableNonLocaleStringFields, getGraphQlType)}
- }
- extend input Create${entityName}Input {
- customFields: Create${entityName}CustomFieldsInput
- }
- `;
- } else {
- customFieldTypeDefs += `
- extend input Create${entityName}Input {
- customFields: JSON
- }
- `;
- }
- }
- if (schema.getType(`Update${entityName}Input`)) {
- if (writeableNonLocaleStringFields.length) {
- customFieldTypeDefs += `
- input Update${entityName}CustomFieldsInput {
- ${mapToFields(writeableNonLocaleStringFields, getGraphQlType)}
- }
- extend input Update${entityName}Input {
- customFields: Update${entityName}CustomFieldsInput
- }
- `;
- } else {
- customFieldTypeDefs += `
- extend input Update${entityName}Input {
- customFields: JSON
- }
- `;
- }
- }
- if (customEntityFields.length && schema.getType(`${entityName}SortParameter`)) {
- customFieldTypeDefs += `
- extend input ${entityName}SortParameter {
- ${mapToFields(customEntityFields, () => 'SortOrder')}
- }
- `;
- }
- if (customEntityFields.length && schema.getType(`${entityName}FilterParameter`)) {
- customFieldTypeDefs += `
- extend input ${entityName}FilterParameter {
- ${mapToFields(customEntityFields, getFilterOperator)}
- }
- `;
- }
- if (writeableLocaleStringFields) {
- const translationInputs = [
- `${entityName}TranslationInput`,
- `Create${entityName}TranslationInput`,
- `Update${entityName}TranslationInput`,
- ];
- for (const inputName of translationInputs) {
- if (schema.getType(inputName)) {
- if (writeableLocaleStringFields.length) {
- customFieldTypeDefs += `
- input ${inputName}CustomFields {
- ${mapToFields(writeableLocaleStringFields, getGraphQlType)}
- }
- extend input ${inputName} {
- customFields: ${inputName}CustomFields
- }
- `;
- } else {
- customFieldTypeDefs += `
- extend input ${inputName} {
- customFields: JSON
- }
- `;
- }
- }
- }
- }
- }
- return extendSchema(schema, parse(customFieldTypeDefs));
- }
- export function addServerConfigCustomFields(
- typeDefsOrSchema: string | GraphQLSchema,
- customFieldConfig: CustomFields,
- ) {
- const schema = typeof typeDefsOrSchema === 'string' ? buildSchema(typeDefsOrSchema) : typeDefsOrSchema;
- const customFieldTypeDefs = `
- type CustomFields {
- ${Object.keys(customFieldConfig).reduce(
- (output, name) => output + name + `: [CustomFieldConfig!]!\n`,
- '',
- )}
- }
- extend type ServerConfig {
- customFieldConfig: CustomFields!
- }
- `;
- return extendSchema(schema, parse(customFieldTypeDefs));
- }
- /**
- * If CustomFields are defined on the Customer entity, then an extra `customFields` field is added to
- * the `RegisterCustomerInput` so that public writable custom fields can be set when a new customer
- * is registered.
- */
- export function addRegisterCustomerCustomFieldsInput(
- typeDefsOrSchema: string | GraphQLSchema,
- customerCustomFields: CustomFieldConfig[],
- ): GraphQLSchema {
- const schema = typeof typeDefsOrSchema === 'string' ? buildSchema(typeDefsOrSchema) : typeDefsOrSchema;
- if (!customerCustomFields || customerCustomFields.length === 0) {
- return schema;
- }
- const publicWritableCustomFields = customerCustomFields.filter(fieldDef => {
- return fieldDef.public !== false && !fieldDef.readonly && !fieldDef.internal;
- });
- if (publicWritableCustomFields.length < 1) {
- return schema;
- }
- const customFieldTypeDefs = `
- input RegisterCustomerCustomFieldsInput {
- ${mapToFields(publicWritableCustomFields, getGraphQlType)}
- }
- extend input RegisterCustomerInput {
- customFields: RegisterCustomerCustomFieldsInput
- }
- `;
- return extendSchema(schema, parse(customFieldTypeDefs));
- }
- /**
- * If CustomFields are defined on the Order entity, we add a `customFields` field to the ModifyOrderInput
- * type.
- */
- export function addModifyOrderCustomFields(
- typeDefsOrSchema: string | GraphQLSchema,
- orderCustomFields: CustomFieldConfig[],
- ): GraphQLSchema {
- const schema = typeof typeDefsOrSchema === 'string' ? buildSchema(typeDefsOrSchema) : typeDefsOrSchema;
- if (!orderCustomFields || orderCustomFields.length === 0) {
- return schema;
- }
- if (schema.getType('ModifyOrderInput') && schema.getType('UpdateOrderCustomFieldsInput')) {
- const customFieldTypeDefs = `
- extend input ModifyOrderInput {
- customFields: UpdateOrderCustomFieldsInput
- }
- `;
- return extendSchema(schema, parse(customFieldTypeDefs));
- }
- return schema;
- }
- /**
- * If CustomFields are defined on the OrderLine entity, then an extra `customFields` argument
- * must be added to the `addItemToOrder` and `adjustOrderLine` mutations, as well as the related
- * fields in the `ModifyOrderInput` type.
- */
- export function addOrderLineCustomFieldsInput(
- typeDefsOrSchema: string | GraphQLSchema,
- orderLineCustomFields: CustomFieldConfig[],
- ): GraphQLSchema {
- const schema = typeof typeDefsOrSchema === 'string' ? buildSchema(typeDefsOrSchema) : typeDefsOrSchema;
- if (!orderLineCustomFields || orderLineCustomFields.length === 0) {
- return schema;
- }
- const schemaConfig = schema.toConfig();
- const mutationType = schemaConfig.mutation;
- if (!mutationType) {
- return schema;
- }
- const input = new GraphQLInputObjectType({
- name: 'OrderLineCustomFieldsInput',
- fields: orderLineCustomFields.reduce((fields, field) => {
- return { ...fields, [field.name]: { type: schema.getType(getGraphQlType(field.type)) } };
- }, {}),
- });
- schemaConfig.types.push(input);
- const addItemToOrderMutation = mutationType.getFields().addItemToOrder;
- const adjustOrderLineMutation = mutationType.getFields().adjustOrderLine;
- if (addItemToOrderMutation) {
- addItemToOrderMutation.args.push({
- name: 'customFields',
- type: input,
- description: null,
- defaultValue: null,
- extensions: null,
- astNode: null,
- });
- }
- if (adjustOrderLineMutation) {
- adjustOrderLineMutation.args.push({
- name: 'customFields',
- type: input,
- description: null,
- defaultValue: null,
- extensions: null,
- astNode: null,
- });
- }
- let extendedSchema = new GraphQLSchema(schemaConfig);
- if (schema.getType('AddItemInput')) {
- const customFieldTypeDefs = `
- extend input AddItemInput {
- customFields: OrderLineCustomFieldsInput
- }
- `;
- extendedSchema = extendSchema(extendedSchema, parse(customFieldTypeDefs));
- }
- if (schema.getType('AdjustOrderLineInput')) {
- const customFieldTypeDefs = `
- extend input AdjustOrderLineInput {
- customFields: OrderLineCustomFieldsInput
- }
- `;
- extendedSchema = extendSchema(extendedSchema, parse(customFieldTypeDefs));
- }
- return extendedSchema;
- }
- type GraphQLFieldType = 'DateTime' | 'String' | 'Int' | 'Float' | 'Boolean' | 'ID';
- /**
- * Maps an array of CustomFieldConfig objects into a string of SDL fields.
- */
- function mapToFields(fieldDefs: CustomFieldConfig[], typeFn: (fieldType: CustomFieldType) => string): string {
- return fieldDefs
- .map(field => {
- const primitiveType = typeFn(field.type);
- const finalType = field.list ? `[${primitiveType}!]` : primitiveType;
- return `${field.name}: ${finalType}`;
- })
- .join('\n');
- }
- function getFilterOperator(type: CustomFieldType): string {
- switch (type) {
- case 'datetime':
- return 'DateOperators';
- case 'string':
- case 'localeString':
- return 'StringOperators';
- case 'boolean':
- return 'BooleanOperators';
- case 'int':
- case 'float':
- return 'NumberOperators';
- default:
- assertNever(type);
- }
- return 'String';
- }
- function getGraphQlType(type: CustomFieldType): GraphQLFieldType {
- switch (type) {
- case 'string':
- case 'localeString':
- return 'String';
- case 'datetime':
- return 'DateTime';
- case 'boolean':
- return 'Boolean';
- case 'int':
- return 'Int';
- case 'float':
- return 'Float';
- default:
- assertNever(type);
- }
- return 'String';
- }
|