feat: ensures nested fields not affecting data can be traversed properly

This commit is contained in:
James
2022-07-15 21:06:12 -07:00
parent 68e7c41fdc
commit 3b9fdf3ffd
10 changed files with 328 additions and 348 deletions

View File

@@ -1,5 +1,5 @@
import React, { useState } from 'react';
import flattenTopLevelFields from '../../../../utilities/flattenTopLevelFields';
import flattenTopLevelFields from '../../../utilities/flattenTopLevelFields';
import Pill from '../Pill';
import Plus from '../../icons/Plus';
import X from '../../icons/X';
@@ -16,7 +16,7 @@ const ColumnSelector: React.FC<Props> = (props) => {
setColumns,
} = props;
const [fields] = useState(() => flattenTopLevelFields(collection.fields));
const [fields] = useState(() => flattenTopLevelFields(collection.fields, true));
return (
<div className={baseClass}>

View File

@@ -7,7 +7,7 @@ import Button from '../Button';
import reducer from './reducer';
import Condition from './Condition';
import fieldTypes from './field-types';
import flattenTopLevelFields from '../../../../utilities/flattenTopLevelFields';
import flattenTopLevelFields from '../../../utilities/flattenTopLevelFields';
import { useSearchParams } from '../../utilities/SearchParams';
import validateWhereQuery from './validateWhereQuery';
import { Where } from '../../../../types';

View File

@@ -3,75 +3,51 @@ import Cell from './Cell';
import SortColumn from '../../../elements/SortColumn';
import { SanitizedCollectionConfig } from '../../../../../collections/config/types';
import { Column } from '../../../elements/Table/types';
import { fieldHasSubFields, Field, fieldAffectsData, fieldIsPresentationalOnly } from '../../../../../fields/config/types';
import { fieldIsPresentationalOnly } from '../../../../../fields/config/types';
import flattenFields from '../../../../utilities/flattenTopLevelFields';
const buildColumns = (collection: SanitizedCollectionConfig, columns: string[]): Column[] => (columns || []).reduce((cols, col, colIndex) => {
let field = null;
const buildColumns = (collection: SanitizedCollectionConfig, columns: string[]): Column[] => {
const flattenedFields = flattenFields(collection.fields, true);
const fields = [
...collection.fields,
{
name: 'id',
type: 'text',
label: 'ID',
} as Field,
{
name: 'updatedAt',
type: 'date',
label: 'Updated At',
} as Field,
{
name: 'createdAt',
type: 'date',
label: 'Created At',
} as Field,
];
return (columns || []).reduce((cols, col, colIndex) => {
let field = null;
fields.forEach((fieldToCheck) => {
if ((fieldAffectsData(fieldToCheck) || fieldIsPresentationalOnly(fieldToCheck)) && fieldToCheck.name === col) {
field = fieldToCheck;
}
flattenedFields.forEach((fieldToCheck) => {
if (fieldToCheck.name === col) {
field = fieldToCheck;
}
});
if (!fieldAffectsData(fieldToCheck) && fieldHasSubFields(fieldToCheck)) {
fieldToCheck.fields.forEach((subField) => {
if (fieldAffectsData(subField) && subField.name === col) {
field = subField;
}
});
}
return false;
});
if (field) {
return [
...cols,
{
accessor: field.name,
components: {
Heading: (
<SortColumn
label={field.label || field.name}
name={field.name}
disable={(field.disableSort || fieldIsPresentationalOnly(field)) || undefined}
/>
),
renderCell: (rowData, cellData) => (
<Cell
key={JSON.stringify(cellData)}
field={field}
colIndex={colIndex}
collection={collection}
rowData={rowData}
cellData={cellData}
/>
),
if (field) {
return [
...cols,
{
accessor: field.name,
components: {
Heading: (
<SortColumn
label={field.label || field.name}
name={field.name}
disable={(field.disableSort || fieldIsPresentationalOnly(field)) || undefined}
/>
),
renderCell: (rowData, cellData) => (
<Cell
key={JSON.stringify(cellData)}
field={field}
colIndex={colIndex}
collection={collection}
rowData={rowData}
cellData={cellData}
/>
),
},
},
},
];
}
];
}
return cols;
}, []);
return cols;
}, []);
};
export default buildColumns;

View File

@@ -1,5 +1,33 @@
import { Field, fieldHasSubFields, fieldAffectsData } from '../../../../../fields/config/types';
const getRemainingColumns = (fields: Field[], useAsTitle: string): string[] => fields.reduce((remaining, field) => {
if (fieldAffectsData(field) && field.name === useAsTitle) {
return remaining;
}
if (!fieldAffectsData(field) && fieldHasSubFields(field)) {
return [
...remaining,
...getRemainingColumns(field.fields, useAsTitle),
];
}
if (field.type === 'tabs') {
return [
...remaining,
...field.tabs.reduce((tabFieldColumns, tab) => [
...tabFieldColumns,
...getRemainingColumns(tab.fields, useAsTitle),
], []),
];
}
return [
...remaining,
field.name,
];
}, []);
const getInitialColumnState = (fields: Field[], useAsTitle: string, defaultColumns: string[]): string[] => {
let initialColumns = [];
@@ -12,32 +40,7 @@ const getInitialColumnState = (fields: Field[], useAsTitle: string, defaultColum
initialColumns.push(useAsTitle);
}
const remainingColumns = fields.reduce((remaining, field) => {
if (fieldAffectsData(field) && field.name === useAsTitle) {
return remaining;
}
if (!fieldAffectsData(field) && fieldHasSubFields(field)) {
return [
...remaining,
...field.fields.reduce((subFields, subField) => {
if (fieldAffectsData(subField)) {
return [
...subFields,
subField.name,
];
}
return subFields;
}, []),
];
}
return [
...remaining,
field.name,
];
}, []);
const remainingColumns = getRemainingColumns(fields, useAsTitle);
initialColumns = initialColumns.concat(remainingColumns);
initialColumns = initialColumns.slice(0, 4);

View File

@@ -0,0 +1,35 @@
import { Field, FieldAffectingData, fieldAffectsData, fieldHasSubFields, fieldIsPresentationalOnly, FieldPresentationalOnly } from '../../fields/config/types';
const flattenFields = (fields: Field[], keepPresentationalFields?: boolean): (FieldAffectingData | FieldPresentationalOnly)[] => {
return fields.reduce((fieldsToUse, field) => {
if (fieldAffectsData(field) || (keepPresentationalFields && fieldIsPresentationalOnly(field))) {
return [
...fieldsToUse,
field,
];
}
if (fieldHasSubFields(field)) {
return [
...fieldsToUse,
...flattenFields(field.fields, keepPresentationalFields),
];
}
if (field.type === 'tabs') {
return [
...fieldsToUse,
...field.tabs.reduce((tabFields, tab) => {
return [
...tabFields,
...flattenFields(tab.fields, keepPresentationalFields),
];
}, []),
];
}
return fieldsToUse;
}, []);
};
export default flattenFields;

View File

@@ -10,7 +10,6 @@ import {
import formatName from '../../graphql/utilities/formatName';
import buildPaginatedListType from '../../graphql/schema/buildPaginatedListType';
import { BaseFields } from './types';
import buildMutationInputType, { getCollectionIDType } from '../../graphql/schema/buildMutationInputType';
import { buildVersionCollectionFields } from '../../versions/buildCollectionFields';
import createResolver from './resolvers/create';
@@ -31,7 +30,7 @@ import unlock from '../../auth/graphql/resolvers/unlock';
import refresh from '../../auth/graphql/resolvers/refresh';
import { Payload } from '../..';
import { Field, fieldAffectsData } from '../../fields/config/types';
import buildObjectType from '../../graphql/schema/buildObjectType';
import buildObjectType, { ObjectTypeConfig } from '../../graphql/schema/buildObjectType';
import buildWhereInputType from '../../graphql/schema/buildWhereInputType';
import getDeleteResolver from './resolvers/delete';
@@ -66,7 +65,7 @@ function initCollectionsGraphQL(payload: Payload): void {
const idField = fields.find((field) => fieldAffectsData(field) && field.name === 'id');
const idType = getCollectionIDType(collection.config);
const baseFields: BaseFields = {};
const baseFields: ObjectTypeConfig = {};
const whereInputFields = [
...fields,

View File

@@ -3,6 +3,7 @@ import {
GraphQLBoolean,
GraphQLEnumType,
GraphQLFloat,
GraphQLInputFieldConfig,
GraphQLInputObjectType,
GraphQLInt,
GraphQLList,
@@ -15,7 +16,7 @@ import { GraphQLJSON } from 'graphql-type-json';
import withNullableType from './withNullableType';
import formatName from '../utilities/formatName';
import combineParentName from '../utilities/combineParentName';
import { ArrayField, CodeField, DateField, EmailField, Field, fieldHasSubFields, fieldAffectsData, fieldIsPresentationalOnly, GroupField, NumberField, PointField, RadioField, RelationshipField, RichTextField, RowField, SelectField, TextareaField, TextField, UploadField, CollapsibleField, TabsField } from '../../fields/config/types';
import { ArrayField, CodeField, DateField, EmailField, Field, fieldAffectsData, fieldIsPresentationalOnly, GroupField, NumberField, PointField, RadioField, RelationshipField, RichTextField, RowField, SelectField, TextareaField, TextField, UploadField, CollapsibleField, TabsField, CheckboxField, BlockField } from '../../fields/config/types';
import { toWords } from '../../utilities/formatLabels';
import { Payload } from '../../index';
import { SanitizedCollectionConfig } from '../../collections/config/types';
@@ -31,23 +32,60 @@ export const getCollectionIDType = (config: SanitizedCollectionConfig): GraphQLS
}
};
export type InputObjectTypeConfig = {
[path: string]: GraphQLInputFieldConfig
}
function buildMutationInputType(payload: Payload, name: string, fields: Field[], parentName: string, forceNullable = false): GraphQLInputObjectType {
const fieldToSchemaMap = {
number: (field: NumberField) => {
number: (inputObjectTypeConfig: InputObjectTypeConfig, field: NumberField) => {
const type = field.name === 'id' ? GraphQLInt : GraphQLFloat;
return { type: withNullableType(field, type, forceNullable) };
return {
...inputObjectTypeConfig,
[field.name]: { type: withNullableType(field, type, forceNullable) },
};
},
text: (field: TextField) => ({ type: withNullableType(field, GraphQLString, forceNullable) }),
email: (field: EmailField) => ({ type: withNullableType(field, GraphQLString, forceNullable) }),
textarea: (field: TextareaField) => ({ type: withNullableType(field, GraphQLString, forceNullable) }),
richText: (field: RichTextField) => ({ type: withNullableType(field, GraphQLJSON, forceNullable) }),
code: (field: CodeField) => ({ type: withNullableType(field, GraphQLString, forceNullable) }),
date: (field: DateField) => ({ type: withNullableType(field, GraphQLString, forceNullable) }),
upload: (field: UploadField) => ({ type: withNullableType(field, GraphQLString, forceNullable) }),
radio: (field: RadioField) => ({ type: withNullableType(field, GraphQLString, forceNullable) }),
point: (field: PointField) => ({ type: withNullableType(field, GraphQLList(GraphQLFloat), forceNullable) }),
checkbox: () => ({ type: GraphQLBoolean }),
select: (field: SelectField) => {
text: (inputObjectTypeConfig: InputObjectTypeConfig, field: TextField) => ({
...inputObjectTypeConfig,
[field.name]: { type: withNullableType(field, GraphQLString, forceNullable) },
}),
email: (inputObjectTypeConfig: InputObjectTypeConfig, field: EmailField) => ({
...inputObjectTypeConfig,
[field.name]: { type: withNullableType(field, GraphQLString, forceNullable) },
}),
textarea: (inputObjectTypeConfig: InputObjectTypeConfig, field: TextareaField) => ({
...inputObjectTypeConfig,
[field.name]: { type: withNullableType(field, GraphQLString, forceNullable) },
}),
richText: (inputObjectTypeConfig: InputObjectTypeConfig, field: RichTextField) => ({
...inputObjectTypeConfig,
[field.name]: { type: withNullableType(field, GraphQLJSON, forceNullable) },
}),
code: (inputObjectTypeConfig: InputObjectTypeConfig, field: CodeField) => ({
...inputObjectTypeConfig,
[field.name]: { type: withNullableType(field, GraphQLString, forceNullable) },
}),
date: (inputObjectTypeConfig: InputObjectTypeConfig, field: DateField) => ({
...inputObjectTypeConfig,
[field.name]: { type: withNullableType(field, GraphQLString, forceNullable) },
}),
upload: (inputObjectTypeConfig: InputObjectTypeConfig, field: UploadField) => ({
...inputObjectTypeConfig,
[field.name]: { type: withNullableType(field, GraphQLString, forceNullable) },
}),
radio: (inputObjectTypeConfig: InputObjectTypeConfig, field: RadioField) => ({
...inputObjectTypeConfig,
[field.name]: { type: withNullableType(field, GraphQLString, forceNullable) },
}),
point: (inputObjectTypeConfig: InputObjectTypeConfig, field: PointField) => ({
...inputObjectTypeConfig,
[field.name]: { type: withNullableType(field, GraphQLList(GraphQLFloat), forceNullable) },
}),
checkbox: (inputObjectTypeConfig: InputObjectTypeConfig, field: CheckboxField) => ({
...inputObjectTypeConfig,
[field.name]: { type: GraphQLBoolean },
}),
select: (inputObjectTypeConfig: InputObjectTypeConfig, field: SelectField) => {
const formattedName = `${combineParentName(parentName, field.name)}_MutationInput`;
let type: GraphQLType = new GraphQLEnumType({
name: formattedName,
@@ -77,9 +115,12 @@ function buildMutationInputType(payload: Payload, name: string, fields: Field[],
type = field.hasMany ? new GraphQLList(type) : type;
type = withNullableType(field, type, forceNullable);
return { type };
return {
...inputObjectTypeConfig,
[field.name]: { type },
};
},
relationship: (field: RelationshipField) => {
relationship: (inputObjectTypeConfig: InputObjectTypeConfig, field: RelationshipField) => {
const { relationTo } = field;
type PayloadGraphQLRelationshipType = GraphQLScalarType | GraphQLList<GraphQLScalarType> | GraphQLInputObjectType;
let type: PayloadGraphQLRelationshipType;
@@ -107,138 +148,65 @@ function buildMutationInputType(payload: Payload, name: string, fields: Field[],
type = getCollectionIDType(payload.collections[relationTo].config);
}
return { type: field.hasMany ? new GraphQLList(type) : type };
return {
...inputObjectTypeConfig,
[field.name]: { type: field.hasMany ? new GraphQLList(type) : type },
};
},
array: (field: ArrayField) => {
array: (inputObjectTypeConfig: InputObjectTypeConfig, field: ArrayField) => {
const fullName = combineParentName(parentName, field.label === false ? toWords(field.name, true) : field.label);
let type: GraphQLType | GraphQLList<GraphQLType> = buildMutationInputType(payload, fullName, field.fields, fullName);
type = new GraphQLList(withNullableType(field, type, forceNullable));
return { type };
return {
...inputObjectTypeConfig,
[field.name]: { type },
};
},
group: (field: GroupField) => {
group: (inputObjectTypeConfig: InputObjectTypeConfig, field: GroupField) => {
const requiresAtLeastOneField = field.fields.some((subField) => (!fieldIsPresentationalOnly(subField) && subField.required && !subField.localized));
const fullName = combineParentName(parentName, field.label === false ? toWords(field.name, true) : field.label);
let type: GraphQLType = buildMutationInputType(payload, fullName, field.fields, fullName);
if (requiresAtLeastOneField) type = new GraphQLNonNull(type);
return { type };
return {
...inputObjectTypeConfig,
[field.name]: { type },
};
},
blocks: () => ({ type: GraphQLJSON }),
row: (field: RowField) => field.fields.reduce((acc, rowField: RowField) => {
const getFieldSchema = fieldToSchemaMap[rowField.type];
if (getFieldSchema) {
const fieldSchema = getFieldSchema(rowField);
return [
...acc,
fieldSchema,
];
}
return acc;
}, []),
collapsible: (field: CollapsibleField) => field.fields.reduce((acc, collapsibleField: CollapsibleField) => {
const getFieldSchema = fieldToSchemaMap[collapsibleField.type];
if (getFieldSchema) {
const fieldSchema = getFieldSchema(collapsibleField);
return [
...acc,
fieldSchema,
];
}
return acc;
}, []),
tabs: (field: TabsField) => field.tabs.reduce((acc, tab) => {
const test = [
blocks: (inputObjectTypeConfig: InputObjectTypeConfig, field: BlockField) => ({
...inputObjectTypeConfig,
[field.name]: { type: GraphQLJSON },
}),
row: (inputObjectTypeConfig: InputObjectTypeConfig, field: RowField) => field.fields.reduce((acc, subField: Field) => {
const addSubField = fieldToSchemaMap[subField.type];
return addSubField(acc, subField);
}, inputObjectTypeConfig),
collapsible: (inputObjectTypeConfig: InputObjectTypeConfig, field: CollapsibleField) => field.fields.reduce((acc, subField: CollapsibleField) => {
const addSubField = fieldToSchemaMap[subField.type];
return addSubField(acc, subField);
}, inputObjectTypeConfig),
tabs: (inputObjectTypeConfig: InputObjectTypeConfig, field: TabsField) => field.tabs.reduce((acc, tab) => {
return {
...acc,
...tab.fields.reduce((subAcc, rowField: TabsField) => {
const getFieldSchema = fieldToSchemaMap[rowField.type];
if (getFieldSchema) {
const fieldSchema = getFieldSchema(rowField);
return [
...subAcc,
fieldSchema,
];
}
return subAcc;
}, []),
];
return test;
}, []),
...tab.fields.reduce((subFieldSchema, subField) => {
const addSubField = fieldToSchemaMap[subField.type];
return addSubField(subFieldSchema, subField);
}, acc),
};
}, inputObjectTypeConfig),
};
const fieldTypes = fields.reduce((schema, field: Field) => {
if (!fieldIsPresentationalOnly(field) && !field.hidden) {
const getFieldSchema: (field: Field) => { type: GraphQLType } = fieldToSchemaMap[field.type];
if (getFieldSchema) {
const fieldSchema = getFieldSchema(field);
if (Array.isArray(fieldSchema)) {
let subFields: Field[] = [];
if (fieldHasSubFields(field)) {
subFields = field.fields;
}
if (field.type === 'tabs') {
subFields = field.tabs.reduce((flattenedFields, tab) => {
return [
...flattenedFields,
...tab.fields,
];
}, []);
}
if (subFields.length > 0) {
return fieldSchema.reduce((acc, subField, i) => {
const currentSubField = subFields[i];
if (fieldAffectsData(currentSubField)) {
return {
...acc,
[currentSubField.name]: subField,
};
}
return {
...acc,
...fieldSchema,
};
}, schema);
}
}
if (fieldAffectsData(field)) {
return {
...schema,
[field.name]: fieldSchema,
};
}
return {
...schema,
...fieldSchema,
};
}
}
return schema;
}, {});
const fieldName = formatName(name);
return new GraphQLInputObjectType({
name: `mutation${fieldName}Input`,
fields: {
...fieldTypes,
},
fields: fields.reduce((inputObjectTypeConfig, field) => {
const fieldSchema = fieldToSchemaMap[field.type];
return {
...inputObjectTypeConfig,
...fieldSchema(inputObjectTypeConfig, field),
};
}, {}),
});
}

View File

@@ -5,6 +5,7 @@
import { GraphQLJSON } from 'graphql-type-json';
import {
GraphQLBoolean, GraphQLEnumType,
GraphQLFieldConfig,
GraphQLFloat,
GraphQLInt,
GraphQLList,
@@ -14,11 +15,10 @@ import {
GraphQLUnionType,
} from 'graphql';
import { DateTimeResolver, EmailAddressResolver } from 'graphql-scalars';
import { Field, RadioField, RelationshipField, SelectField, UploadField, ArrayField, GroupField, RichTextField, fieldAffectsData, NumberField, TextField, EmailField, TextareaField, CodeField, DateField, PointField, CheckboxField, BlockField, RowField, fieldIsPresentationalOnly } from '../../fields/config/types';
import { Field, RadioField, RelationshipField, SelectField, UploadField, ArrayField, GroupField, RichTextField, fieldAffectsData, NumberField, TextField, EmailField, TextareaField, CodeField, DateField, PointField, CheckboxField, BlockField, RowField, fieldIsPresentationalOnly, CollapsibleField, TabsField } from '../../fields/config/types';
import formatName from '../utilities/formatName';
import combineParentName from '../utilities/combineParentName';
import withNullableType from './withNullableType';
import { BaseFields } from '../../collections/graphql/types';
import { toWords } from '../../utilities/formatLabels';
import createRichTextRelationshipPromise from '../../fields/richText/relationshipPromise';
import formatOptions from '../utilities/formatOptions';
@@ -39,37 +39,65 @@ type LocaleInputType = {
}
}
function buildObjectType(payload: Payload, name: string, fields: Field[], parentName: string, baseFields: BaseFields = {}): GraphQLObjectType {
const fieldToSchemaMap = {
number: (field: NumberField) => ({ type: withNullableType(field, GraphQLFloat) }),
text: (field: TextField) => ({ type: withNullableType(field, GraphQLString) }),
email: (field: EmailField) => ({ type: withNullableType(field, EmailAddressResolver) }),
textarea: (field: TextareaField) => ({ type: withNullableType(field, GraphQLString) }),
code: (field: CodeField) => ({ type: withNullableType(field, GraphQLString) }),
date: (field: DateField) => ({ type: withNullableType(field, DateTimeResolver) }),
point: (field: PointField) => ({ type: withNullableType(field, new GraphQLList(GraphQLFloat)) }),
richText: (field: RichTextField) => ({
type: withNullableType(field, GraphQLJSON),
async resolve(parent, args, context) {
if (args.depth > 0) {
await createRichTextRelationshipPromise({
req: context.req,
siblingDoc: parent,
depth: args.depth,
field,
showHiddenFields: false,
});
}
export type ObjectTypeConfig = {
[path: string]: GraphQLFieldConfig<any, any>
}
return parent[field.name];
},
args: {
depth: {
type: GraphQLInt,
function buildObjectType(payload: Payload, name: string, fields: Field[], parentName: string, baseFields: ObjectTypeConfig = {}): GraphQLObjectType {
const fieldToSchemaMap = {
number: (objectTypeConfig: ObjectTypeConfig, field: NumberField) => ({
...objectTypeConfig,
[field.name]: { type: withNullableType(field, GraphQLFloat) },
}),
text: (objectTypeConfig: ObjectTypeConfig, field: TextField) => ({
...objectTypeConfig,
[field.name]: { type: withNullableType(field, GraphQLString) },
}),
email: (objectTypeConfig: ObjectTypeConfig, field: EmailField) => ({
...objectTypeConfig,
[field.name]: { type: withNullableType(field, EmailAddressResolver) },
}),
textarea: (objectTypeConfig: ObjectTypeConfig, field: TextareaField) => ({
...objectTypeConfig,
[field.name]: { type: withNullableType(field, GraphQLString) },
}),
code: (objectTypeConfig: ObjectTypeConfig, field: CodeField) => ({
...objectTypeConfig,
[field.name]: { type: withNullableType(field, GraphQLString) },
}),
date: (objectTypeConfig: ObjectTypeConfig, field: DateField) => ({
...objectTypeConfig,
[field.name]: { type: withNullableType(field, DateTimeResolver) },
}),
point: (objectTypeConfig: ObjectTypeConfig, field: PointField) => ({
...objectTypeConfig,
[field.name]: { type: withNullableType(field, new GraphQLList(GraphQLFloat)) },
}),
richText: (objectTypeConfig: ObjectTypeConfig, field: RichTextField) => ({
...objectTypeConfig,
[field.name]: {
type: withNullableType(field, GraphQLJSON),
async resolve(parent, args, context) {
if (args.depth > 0) {
await createRichTextRelationshipPromise({
req: context.req,
siblingDoc: parent,
depth: args.depth,
field,
showHiddenFields: false,
});
}
return parent[field.name];
},
args: {
depth: {
type: GraphQLInt,
},
},
},
}),
upload: (field: UploadField) => {
upload: (objectTypeConfig: ObjectTypeConfig, field: UploadField) => {
const { relationTo, label } = field;
const uploadName = combineParentName(parentName, label === false ? toWords(field.name, true) : label);
@@ -149,19 +177,28 @@ function buildObjectType(payload: Payload, name: string, fields: Field[], parent
),
};
return upload;
return {
...objectTypeConfig,
[field.name]: upload,
};
},
radio: (field: RadioField) => ({
type: withNullableType(
field,
new GraphQLEnumType({
name: combineParentName(parentName, field.name),
values: formatOptions(field),
}),
),
radio: (objectTypeConfig: ObjectTypeConfig, field: RadioField) => ({
...objectTypeConfig,
[field.name]: {
type: withNullableType(
field,
new GraphQLEnumType({
name: combineParentName(parentName, field.name),
values: formatOptions(field),
}),
),
},
}),
checkbox: (field: CheckboxField) => ({ type: withNullableType(field, GraphQLBoolean) }),
select: (field: SelectField) => {
checkbox: (objectTypeConfig: ObjectTypeConfig, field: CheckboxField) => ({
...objectTypeConfig,
[field.name]: { type: withNullableType(field, GraphQLBoolean) },
}),
select: (objectTypeConfig: ObjectTypeConfig, field: SelectField) => {
const fullName = combineParentName(parentName, field.name);
let type: GraphQLType = new GraphQLEnumType({
@@ -172,9 +209,12 @@ function buildObjectType(payload: Payload, name: string, fields: Field[], parent
type = field.hasMany ? new GraphQLList(type) : type;
type = withNullableType(field, type);
return { type };
return {
...objectTypeConfig,
[field.name]: { type },
};
},
relationship: (field: RelationshipField) => {
relationship: (objectTypeConfig: ObjectTypeConfig, field: RelationshipField) => {
const { relationTo, label } = field;
const isRelatedToManyCollections = Array.isArray(relationTo);
const hasManyValues = field.hasMany;
@@ -388,22 +428,31 @@ function buildObjectType(payload: Payload, name: string, fields: Field[], parent
};
}
return relationship;
return {
...objectTypeConfig,
[field.name]: relationship,
};
},
array: (field: ArrayField) => {
array: (objectTypeConfig: ObjectTypeConfig, field: ArrayField) => {
const fullName = combineParentName(parentName, field.label === false ? toWords(field.name, true) : field.label);
const type = buildObjectType(payload, fullName, field.fields, fullName);
const arrayType = new GraphQLList(withNullableType(field, type));
return { type: arrayType };
return {
...objectTypeConfig,
[field.name]: { type: arrayType },
};
},
group: (field: GroupField) => {
group: (objectTypeConfig: ObjectTypeConfig, field: GroupField) => {
const fullName = combineParentName(parentName, field.label === false ? toWords(field.name, true) : field.label);
const type = buildObjectType(payload, fullName, field.fields, fullName);
return { type };
return {
...objectTypeConfig,
[field.name]: { type },
};
},
blocks: (field: BlockField) => {
blocks: (objectTypeConfig: ObjectTypeConfig, field: BlockField) => {
const blockTypes = field.blocks.map((block) => {
buildBlockType(payload, block);
return payload.types.blockTypes[block.slug];
@@ -417,72 +466,38 @@ function buildObjectType(payload: Payload, name: string, fields: Field[], parent
resolveType: (data) => payload.types.blockTypes[data.blockType].name,
}));
return { type };
return {
...objectTypeConfig,
[field.name]: { type },
};
},
row: (field) => field.fields.reduce((subFieldSchema, subField) => {
const buildSchemaType = fieldToSchemaMap[subField.type];
if (!fieldIsPresentationalOnly(subField) && buildSchemaType) {
return {
...subFieldSchema,
[formatName(subField.name)]: buildSchemaType(subField),
};
}
return subFieldSchema;
}, {}),
collapsible: (field) => field.fields.reduce((subFieldSchema, subField) => {
const buildSchemaType = fieldToSchemaMap[subField.type];
if (!fieldIsPresentationalOnly(subField) && buildSchemaType) {
return {
...subFieldSchema,
[formatName(subField.name)]: buildSchemaType(subField),
};
}
return subFieldSchema;
}, {}),
tabs: (field) => field.tabs.reduce((tabSchema, tab) => {
row: (objectTypeConfig: ObjectTypeConfig, field: RowField) => field.fields.reduce((objectTypeConfigWithRowFields, subField) => {
const addSubField = fieldToSchemaMap[subField.type];
return addSubField(objectTypeConfigWithRowFields, subField);
}, objectTypeConfig),
collapsible: (objectTypeConfig: ObjectTypeConfig, field: CollapsibleField) => field.fields.reduce((objectTypeConfigWithCollapsibleFields, subField) => {
const addSubField = fieldToSchemaMap[subField.type];
return addSubField(objectTypeConfigWithCollapsibleFields, subField);
}, objectTypeConfig),
tabs: (objectTypeConfig: ObjectTypeConfig, field: TabsField) => field.tabs.reduce((tabSchema, tab) => {
return {
...tabSchema,
...tab.fields.reduce((subFieldSchema, subField) => {
const buildSchemaType = fieldToSchemaMap[subField.type];
if (!fieldIsPresentationalOnly(subField) && buildSchemaType) {
return {
...subFieldSchema,
[formatName(subField.name)]: buildSchemaType(subField),
};
}
return subFieldSchema;
}, {}),
const addSubField = fieldToSchemaMap[subField.type];
return addSubField(subFieldSchema, subField);
}, tabSchema),
};
}, {}),
}, objectTypeConfig),
};
const objectSchema = {
name,
fields: () => fields.reduce((schema, field) => {
if (!fieldIsPresentationalOnly(field) && !field.hidden) {
const fieldSchema = fieldToSchemaMap[field.type];
if (fieldSchema) {
if (fieldAffectsData(field)) {
return {
...schema,
[formatName(field.name)]: fieldSchema(field),
};
}
return {
...schema,
...fieldSchema(field),
};
}
}
return schema;
fields: () => fields.reduce((objectTypeConfig, field) => {
const fieldSchema = fieldToSchemaMap[field.type];
return {
...objectTypeConfig,
...fieldSchema(objectTypeConfig, field),
};
}, baseFields),
};

View File

@@ -1,7 +1,6 @@
import { GraphQLNonNull, GraphQLType } from 'graphql';
import { NonPresentationalField } from '../../fields/config/types';
const withNullableType = (field: NonPresentationalField, type: GraphQLType, forceNullable = false): GraphQLType => {
const hasReadAccessControl = field.access && field.access.read;
const condition = field.admin && field.admin.condition;

View File

@@ -1,15 +0,0 @@
const flattenTopLevelFields = (fields) => fields.reduce((flattened, field) => {
if (!field.name && Array.isArray(field.fields)) {
return [
...flattened,
...field.fields.filter((subField) => subField.name),
];
}
return [
...flattened,
field,
];
}, []);
export default flattenTopLevelFields;