fix: more strict field typing
This commit is contained in:
10
.eslintrc.js
10
.eslintrc.js
@@ -28,8 +28,8 @@ module.exports = {
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
],
|
||||
rules: {
|
||||
"no-shadow": "off",
|
||||
"@typescript-eslint/no-shadow": ["error"],
|
||||
'no-shadow': 'off',
|
||||
'@typescript-eslint/no-shadow': ['error'],
|
||||
'import/no-unresolved': [
|
||||
2,
|
||||
{
|
||||
@@ -38,18 +38,20 @@ module.exports = {
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
rules: {
|
||||
'no-sparse-arrays': 'off',
|
||||
'import/no-extraneous-dependencies': ["error", { "packageDir": "./" }],
|
||||
'import/no-extraneous-dependencies': ['error', { packageDir: './' }],
|
||||
'react/jsx-filename-extension': [2, { extensions: ['.js', '.jsx', '.ts', '.tsx'] }],
|
||||
'import/prefer-default-export': 'off',
|
||||
'react/prop-types': 'off',
|
||||
'react/require-default-props': 'off',
|
||||
'react/no-unused-prop-types': 'off',
|
||||
'no-underscore-dangle': 'off',
|
||||
'no-use-before-define': 'off',
|
||||
'@typescript-eslint/no-use-before-define': ['error'],
|
||||
'import/extensions': [
|
||||
'error',
|
||||
'ignorePackages',
|
||||
|
||||
20
package.json
20
package.json
@@ -248,19 +248,19 @@
|
||||
"@types/webpack-dev-middleware": "4.0.0",
|
||||
"@types/webpack-env": "^1.15.3",
|
||||
"@types/webpack-hot-middleware": "2.25.3",
|
||||
"@typescript-eslint/eslint-plugin": "^4.8.1",
|
||||
"@typescript-eslint/parser": "4.0.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.0.0",
|
||||
"@typescript-eslint/parser": "^5.0.0",
|
||||
"babel-eslint": "^10.0.1",
|
||||
"babel-plugin-ignore-html-and-css-imports": "^0.1.0",
|
||||
"copyfiles": "^2.4.0",
|
||||
"cross-env": "^7.0.2",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-plugin-import": "^2.20.0",
|
||||
"eslint-plugin-jest": "^23.16.0",
|
||||
"eslint-plugin-jest-dom": "^3.0.1",
|
||||
"eslint-plugin-jsx-a11y": "^6.2.1",
|
||||
"eslint-plugin-react": "^7.18.0",
|
||||
"eslint-plugin-react-hooks": "^2.3.0",
|
||||
"eslint": "^8.0.0",
|
||||
"eslint-plugin-import": "^2.25.2",
|
||||
"eslint-plugin-jest": "^25.0.5",
|
||||
"eslint-plugin-jest-dom": "^3.9.2",
|
||||
"eslint-plugin-jsx-a11y": "^6.4.1",
|
||||
"eslint-plugin-react": "^7.26.1",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"form-data": "^3.0.0",
|
||||
"graphql-request": "^3.4.0",
|
||||
"mongodb": "^3.6.2",
|
||||
@@ -270,7 +270,7 @@
|
||||
"passport-strategy": "^1.0.0",
|
||||
"release-it": "^14.2.2",
|
||||
"rimraf": "^3.0.2",
|
||||
"typescript": "^4.1.2"
|
||||
"typescript": "^4.4.4"
|
||||
},
|
||||
"files": [
|
||||
"bin.js",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { useState } from 'react';
|
||||
import AnimateHeight from 'react-animate-height';
|
||||
import { fieldIsNamed } from '../../../../fields/config/types';
|
||||
import SearchFilter from '../SearchFilter';
|
||||
import ColumnSelector from '../ColumnSelector';
|
||||
import WhereBuilder from '../WhereBuilder';
|
||||
@@ -8,9 +9,10 @@ import Button from '../Button';
|
||||
import { Props } from './types';
|
||||
import { useSearchParams } from '../../utilities/SearchParams';
|
||||
|
||||
import './index.scss';
|
||||
import validateWhereQuery from '../WhereBuilder/validateWhereQuery';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
const baseClass = 'list-controls';
|
||||
|
||||
const ListControls: React.FC<Props> = (props) => {
|
||||
@@ -34,17 +36,17 @@ const ListControls: React.FC<Props> = (props) => {
|
||||
const params = useSearchParams();
|
||||
const shouldInitializeWhereOpened = validateWhereQuery(params?.where);
|
||||
|
||||
const [titleField] = useState(() => fields.find((field) => field.name === useAsTitle));
|
||||
const [titleField] = useState(() => fields.find((field) => fieldIsNamed(field) && field.name === useAsTitle));
|
||||
const [visibleDrawer, setVisibleDrawer] = useState<'where' | 'sort' | 'columns'>(shouldInitializeWhereOpened ? 'where' : undefined);
|
||||
|
||||
return (
|
||||
<div className={baseClass}>
|
||||
<div className={`${baseClass}__wrap`}>
|
||||
<SearchFilter
|
||||
fieldName={titleField?.name}
|
||||
fieldName={titleField && fieldIsNamed(titleField) ? titleField.name : undefined}
|
||||
handleChange={handleWhereChange}
|
||||
modifySearchQuery={modifySearchQuery}
|
||||
fieldLabel={titleField?.label ? titleField?.label : undefined}
|
||||
fieldLabel={titleField && titleField.label ? titleField.label : undefined}
|
||||
/>
|
||||
<div className={`${baseClass}__buttons`}>
|
||||
<div className={`${baseClass}__buttons-wrap`}>
|
||||
|
||||
@@ -4,9 +4,10 @@ import { useHistory } from 'react-router-dom';
|
||||
import { Props } from './types';
|
||||
import ReactSelect from '../ReactSelect';
|
||||
import sortableFieldTypes from '../../../../fields/sortableFieldTypes';
|
||||
import { useSearchParams } from '../../utilities/SearchParams';
|
||||
import { fieldIsNamed } from '../../../../fields/config/types';
|
||||
|
||||
import './index.scss';
|
||||
import { useSearchParams } from '../../utilities/SearchParams';
|
||||
|
||||
const baseClass = 'sort-complex';
|
||||
|
||||
@@ -23,7 +24,7 @@ const SortComplex: React.FC<Props> = (props) => {
|
||||
const params = useSearchParams();
|
||||
|
||||
const [sortFields] = useState(() => collection.fields.reduce((fields, field) => {
|
||||
if (field.name && sortableFieldTypes.indexOf(field.type) > -1) {
|
||||
if (fieldIsNamed(field) && sortableFieldTypes.indexOf(field.type) > -1) {
|
||||
return [
|
||||
...fields,
|
||||
{ label: field.label, value: field.name },
|
||||
|
||||
@@ -13,6 +13,7 @@ import { Props } from './types';
|
||||
import HiddenInput from '../field-types/HiddenInput';
|
||||
|
||||
import './index.scss';
|
||||
import { fieldIsNamed } from '../../../../fields/config/types';
|
||||
|
||||
const baseClass = 'draggable-section';
|
||||
|
||||
@@ -111,7 +112,7 @@ const DraggableSection: React.FC<Props> = (props) => {
|
||||
permissions={permissions?.fields}
|
||||
fieldSchema={fieldSchema.map((field) => ({
|
||||
...field,
|
||||
path: `${parentPath}.${rowIndex}${field.name ? `.${field.name}` : ''}`,
|
||||
path: `${parentPath}.${rowIndex}${fieldIsNamed(field) ? `.${field.name}` : ''}`,
|
||||
}))}
|
||||
/>
|
||||
</NegativeFieldGutterProvider>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import ObjectID from 'bson-objectid';
|
||||
import { Field as FieldSchema } from '../../../../fields/config/types';
|
||||
import { Field as FieldSchema, fieldIsNamed, NamedField } from '../../../../fields/config/types';
|
||||
import { Fields, Field, Data } from './types';
|
||||
|
||||
const buildValidationPromise = async (fieldState: Field, field: FieldSchema) => {
|
||||
@@ -44,13 +44,13 @@ const buildStateFromSchema = async (fieldSchema: FieldSchema[], fullData: Data =
|
||||
let initialData = data;
|
||||
|
||||
if (!field?.admin?.disabled) {
|
||||
if (field.name && field.defaultValue && typeof initialData?.[field.name] === 'undefined') {
|
||||
if (fieldIsNamed(field) && field.defaultValue && typeof initialData?.[field.name] === 'undefined') {
|
||||
initialData = { [field.name]: field.defaultValue };
|
||||
}
|
||||
|
||||
const passesCondition = Boolean((field?.admin?.condition ? field.admin.condition(fullData || {}, initialData || {}) : true) && parentPassesCondition);
|
||||
|
||||
if (field.name) {
|
||||
if (fieldIsNamed(field)) {
|
||||
if (field.type === 'relationship' && initialData?.[field.name] === null) {
|
||||
initialData[field.name] = 'null';
|
||||
}
|
||||
@@ -135,10 +135,12 @@ const buildStateFromSchema = async (fieldSchema: FieldSchema[], fullData: Data =
|
||||
};
|
||||
}
|
||||
|
||||
const namedField = field as NamedField;
|
||||
|
||||
// Handle normal fields
|
||||
return {
|
||||
...state,
|
||||
[`${path}${field.name}`]: structureFieldState(field, passesCondition, data),
|
||||
[`${path}${namedField.name}`]: structureFieldState(field, passesCondition, data),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import React, { createContext, useEffect, useContext, useState } from 'react';
|
||||
import RenderCustomComponent from '../../utilities/RenderCustomComponent';
|
||||
import useIntersect from '../../../hooks/useIntersect';
|
||||
import { Props, Context } from './types';
|
||||
import { fieldIsNamed } from '../../../../fields/config/types';
|
||||
|
||||
const baseClass = 'render-fields';
|
||||
|
||||
@@ -69,14 +70,16 @@ const RenderFields: React.FC<Props> = (props) => {
|
||||
if ((filter && typeof filter === 'function' && filter(field)) || !filter) {
|
||||
const FieldComponent = field?.admin?.hidden ? fieldTypes.hidden : fieldTypes[field.type];
|
||||
|
||||
const fieldPermissions = field?.name ? permissions?.[field.name] : permissions;
|
||||
const isNamedField = fieldIsNamed(field);
|
||||
|
||||
const fieldPermissions = isNamedField ? permissions?.[field.name] : permissions;
|
||||
|
||||
let { admin: { readOnly } = {} } = field;
|
||||
|
||||
if (readOnlyOverride) readOnly = true;
|
||||
|
||||
if (permissions?.[field?.name]?.read?.permission !== false) {
|
||||
if (permissions?.[field?.name]?.[operation]?.permission === false) {
|
||||
if ((isNamedField && permissions?.[field?.name]?.read?.permission !== false) || !isNamedField) {
|
||||
if (isNamedField && permissions?.[field?.name]?.[operation]?.permission === false) {
|
||||
readOnly = true;
|
||||
}
|
||||
|
||||
@@ -88,7 +91,7 @@ const RenderFields: React.FC<Props> = (props) => {
|
||||
DefaultComponent={FieldComponent}
|
||||
componentProps={{
|
||||
...field,
|
||||
path: field.path || field.name,
|
||||
path: field.path || (isNamedField ? field.name : undefined),
|
||||
fieldTypes,
|
||||
admin: {
|
||||
...(field.admin || {}),
|
||||
|
||||
@@ -5,6 +5,7 @@ import FieldDescription from '../../FieldDescription';
|
||||
import FieldTypeGutter from '../../FieldTypeGutter';
|
||||
import { NegativeFieldGutterProvider } from '../../FieldTypeGutter/context';
|
||||
import { Props } from './types';
|
||||
import { fieldIsNamed } from '../../../../../fields/config/types';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
@@ -41,7 +42,7 @@ const Group: React.FC<Props> = (props) => {
|
||||
width,
|
||||
}}
|
||||
>
|
||||
{ !hideGutter && (<FieldTypeGutter />) }
|
||||
{!hideGutter && (<FieldTypeGutter />)}
|
||||
|
||||
<div className={`${baseClass}__content-wrapper`}>
|
||||
{(label || description) && (
|
||||
@@ -63,7 +64,7 @@ const Group: React.FC<Props> = (props) => {
|
||||
fieldTypes={fieldTypes}
|
||||
fieldSchema={fields.map((subField) => ({
|
||||
...subField,
|
||||
path: `${path}${subField.name ? `.${subField.name}` : ''}`,
|
||||
path: `${path}${fieldIsNamed(subField) ? `.${subField.name}` : ''}`,
|
||||
}))}
|
||||
/>
|
||||
</NegativeFieldGutterProvider>
|
||||
|
||||
@@ -2,6 +2,7 @@ import React from 'react';
|
||||
import RenderFields from '../../RenderFields';
|
||||
import withCondition from '../../withCondition';
|
||||
import { Props } from './types';
|
||||
import { fieldIsNamed } from '../../../../../fields/config/types';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
@@ -24,7 +25,7 @@ const Row: React.FC<Props> = (props) => {
|
||||
fieldTypes={fieldTypes}
|
||||
fieldSchema={fields.map((field) => ({
|
||||
...field,
|
||||
path: `${path ? `${path}.` : ''}${field.name}`,
|
||||
path: `${path ? `${path}.` : ''}${fieldIsNamed(field) ? field.name : ''}`,
|
||||
}))}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { SanitizedCollectionConfig } from '../../../../../collections/config/types';
|
||||
import { Field } from '../../../../../fields/config/types';
|
||||
import { Field, fieldIsNamed } from '../../../../../fields/config/types';
|
||||
|
||||
const formatFields = (collection: SanitizedCollectionConfig, isEditing: boolean): Field[] => (isEditing
|
||||
? collection.fields.filter(({ name }) => name !== 'id')
|
||||
? collection.fields.filter((field) => fieldIsNamed(field) && field.name !== 'id')
|
||||
: collection.fields);
|
||||
|
||||
export default formatFields;
|
||||
|
||||
@@ -3,7 +3,7 @@ import Cell from './Cell';
|
||||
import SortColumn from '../../../elements/SortColumn';
|
||||
import { SanitizedCollectionConfig } from '../../../../../collections/config/types';
|
||||
import { Column } from '../../../elements/Table/types';
|
||||
import { fieldHasSubFields, Field } from '../../../../../fields/config/types';
|
||||
import { fieldHasSubFields, Field, fieldIsNamed } from '../../../../../fields/config/types';
|
||||
|
||||
const buildColumns = (collection: SanitizedCollectionConfig, columns: string[]): Column[] => (columns || []).reduce((cols, col, colIndex) => {
|
||||
let field = null;
|
||||
@@ -28,13 +28,13 @@ const buildColumns = (collection: SanitizedCollectionConfig, columns: string[]):
|
||||
];
|
||||
|
||||
fields.forEach((fieldToCheck) => {
|
||||
if (fieldToCheck.name === col) {
|
||||
if (fieldIsNamed(fieldToCheck) && fieldToCheck.name === col) {
|
||||
field = fieldToCheck;
|
||||
}
|
||||
|
||||
if (!fieldToCheck.name && fieldHasSubFields(fieldToCheck)) {
|
||||
if (!fieldIsNamed(fieldToCheck) && fieldHasSubFields(fieldToCheck)) {
|
||||
fieldToCheck.fields.forEach((subField) => {
|
||||
if (subField.name === col) {
|
||||
if (fieldIsNamed(subField) && subField.name === col) {
|
||||
field = subField;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { SanitizedCollectionConfig } from '../../../../../collections/config/types';
|
||||
import { Field } from '../../../../../fields/config/types';
|
||||
import { Field, fieldIsNamed } from '../../../../../fields/config/types';
|
||||
|
||||
const formatFields = (config: SanitizedCollectionConfig): Field[] => {
|
||||
const hasID = config.fields.findIndex(({ name }) => name === 'id') > -1;
|
||||
const hasID = config.fields.findIndex((field) => fieldIsNamed(field) && field.name === 'id') > -1;
|
||||
let fields: Field[] = config.fields.reduce((formatted, field) => {
|
||||
if (field.hidden === true || field?.admin?.disabled === true) {
|
||||
return formatted;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Field, fieldHasSubFields } from '../../../../../fields/config/types';
|
||||
import { Field, fieldHasSubFields, fieldIsNamed } from '../../../../../fields/config/types';
|
||||
|
||||
const getInitialColumnState = (fields: Field[], useAsTitle: string, defaultColumns: string[]): string[] => {
|
||||
let initialColumns = [];
|
||||
@@ -13,14 +13,23 @@ const getInitialColumnState = (fields: Field[], useAsTitle: string, defaultColum
|
||||
}
|
||||
|
||||
const remainingColumns = fields.reduce((remaining, field) => {
|
||||
if (field.name === useAsTitle) {
|
||||
if (fieldIsNamed(field) && field.name === useAsTitle) {
|
||||
return remaining;
|
||||
}
|
||||
|
||||
if (!field.name && fieldHasSubFields(field)) {
|
||||
if (!fieldIsNamed(field) && fieldHasSubFields(field)) {
|
||||
return [
|
||||
...remaining,
|
||||
...field.fields.map((subField) => subField.name),
|
||||
...field.fields.reduce((subFields, subField) => {
|
||||
if (fieldIsNamed(subField)) {
|
||||
return [
|
||||
...subFields,
|
||||
subField.name
|
||||
];
|
||||
}
|
||||
|
||||
return subFields;
|
||||
}, []),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { PayloadRequest } from '../../express/types';
|
||||
import getCookieExpiration from '../../utilities/getCookieExpiration';
|
||||
import isLocked from '../isLocked';
|
||||
import sanitizeInternalFields from '../../utilities/sanitizeInternalFields';
|
||||
import { Field, fieldHasSubFields } from '../../fields/config/types';
|
||||
import { Field, fieldHasSubFields, fieldIsNamed } from '../../fields/config/types';
|
||||
import { User } from '../types';
|
||||
import { Collection } from '../../collections/config/types';
|
||||
|
||||
@@ -108,15 +108,15 @@ async function login(incomingArgs: Arguments): Promise<Result> {
|
||||
...signedFields,
|
||||
};
|
||||
|
||||
if (!field.name && fieldHasSubFields(field)) {
|
||||
if (!fieldIsNamed(field) && fieldHasSubFields(field)) {
|
||||
field.fields.forEach((subField) => {
|
||||
if (subField.saveToJWT) {
|
||||
if (subField.saveToJWT && fieldIsNamed(subField)) {
|
||||
result[subField.name] = user[subField.name];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (field.saveToJWT) {
|
||||
if (field.saveToJWT && fieldIsNamed(field)) {
|
||||
result[field.name] = user[field.name];
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Collection } from '../../collections/config/types';
|
||||
import { APIError } from '../../errors';
|
||||
import getCookieExpiration from '../../utilities/getCookieExpiration';
|
||||
import { UserDocument } from '../types';
|
||||
import { fieldIsNamed } from '../../fields/config/types';
|
||||
|
||||
export type Result = {
|
||||
token: string
|
||||
@@ -51,7 +52,7 @@ async function resetPassword(args: Arguments): Promise<Result> {
|
||||
await user.authenticate(data.password);
|
||||
|
||||
const fieldsToSign = collectionConfig.fields.reduce((signedFields, field) => {
|
||||
if (field.saveToJWT) {
|
||||
if (field.saveToJWT && fieldIsNamed(field)) {
|
||||
return {
|
||||
...signedFields,
|
||||
[field.name]: user[field.name],
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import merge from 'deepmerge';
|
||||
import { fieldIsNamed } from '../../fields/config/types';
|
||||
import { SanitizedCollectionConfig, CollectionConfig } from './types';
|
||||
import sanitizeFields from '../../fields/config/sanitize';
|
||||
import toKebabCase from '../../utilities/toKebabCase';
|
||||
@@ -75,7 +76,7 @@ const sanitizeCollection = (collections: CollectionConfig[], collection: Collect
|
||||
let uploadFields = baseUploadFields;
|
||||
|
||||
if (sanitized.upload.mimeTypes) {
|
||||
uploadFields.find((f) => f.name === 'mimeType').validate = mimeTypeValidator(sanitized.upload.mimeTypes);
|
||||
uploadFields.find((field) => fieldIsNamed(field) && field.name === 'mimeType').validate = mimeTypeValidator(sanitized.upload.mimeTypes);
|
||||
}
|
||||
|
||||
if (sanitized.upload.imageSizes && Array.isArray(sanitized.upload.imageSizes)) {
|
||||
|
||||
@@ -19,6 +19,7 @@ import { PayloadRequest } from '../../express/types';
|
||||
import { Document } from '../../types';
|
||||
import { Payload } from '../..';
|
||||
import saveBufferToFile from '../../uploads/saveBufferToFile';
|
||||
import { fieldIsNamed } from '../../fields/config/types';
|
||||
|
||||
export type Arguments = {
|
||||
collection: Collection
|
||||
@@ -74,7 +75,7 @@ async function create(this: Payload, incomingArgs: Arguments): Promise<Document>
|
||||
// Custom id
|
||||
// /////////////////////////////////////
|
||||
|
||||
const hasIdField = collectionConfig.fields.findIndex(({ name }) => name === 'id') > -1;
|
||||
const hasIdField = collectionConfig.fields.findIndex((field) => fieldIsNamed(field) && field.name === 'id') > -1;
|
||||
if (hasIdField) {
|
||||
data = {
|
||||
_id: data.id,
|
||||
|
||||
@@ -7,6 +7,7 @@ import { SanitizedCollectionConfig } from '../collections/config/types';
|
||||
import fieldSchema, { idField } from '../fields/config/schema';
|
||||
import { SanitizedGlobalConfig } from '../globals/config/types';
|
||||
import globalSchema from '../globals/config/schema';
|
||||
import { fieldIsNamed } from '../fields/config/types';
|
||||
|
||||
const logger = Logger();
|
||||
|
||||
@@ -14,19 +15,19 @@ const validateFields = (context: string, entity: SanitizedCollectionConfig | San
|
||||
const errors: string[] = [];
|
||||
entity.fields.forEach((field) => {
|
||||
let idResult: Partial<ValidationResult> = { error: null };
|
||||
if (field.name === 'id') {
|
||||
if (fieldIsNamed(field) && field.name === 'id') {
|
||||
idResult = idField.validate(field, { abortEarly: false });
|
||||
}
|
||||
|
||||
const result = fieldSchema.validate(field, { abortEarly: false });
|
||||
if (idResult.error) {
|
||||
idResult.error.details.forEach(({ message }) => {
|
||||
errors.push(`${context} "${entity.slug}" > Field "${field.name}" > ${message}`);
|
||||
errors.push(`${context} "${entity.slug}" > Field${fieldIsNamed(field) ? `"${field.name}" >` : ''} ${message}`);
|
||||
});
|
||||
}
|
||||
if (result.error) {
|
||||
result.error.details.forEach(({ message }) => {
|
||||
errors.push(`${context} "${entity.slug}" > Field "${field.name}" > ${message}`);
|
||||
errors.push(`${context} "${entity.slug}" > Field${fieldIsNamed(field) ? `"${field.name}" >` : ''} ${message}`);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Field } from '../fields/config/types';
|
||||
import { Field, fieldIsNamed } from '../fields/config/types';
|
||||
import APIError from './APIError';
|
||||
|
||||
class MissingFieldType extends APIError {
|
||||
constructor(field: Field) {
|
||||
super(`Field "${field.name}" is either missing a field type or it does not match an available field type`);
|
||||
super(`Field${fieldIsNamed(field) ? ` "${field.name}"` : ''} is either missing a field type or it does not match an available field type`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Payload } from '..';
|
||||
import { Field, HookName } from './config/types';
|
||||
import { HookName, NamedField } from './config/types';
|
||||
import relationshipPopulationPromise from './relationshipPopulationPromise';
|
||||
import { Operation } from '../types';
|
||||
import { PayloadRequest } from '../express/types';
|
||||
@@ -8,7 +8,7 @@ type Arguments = {
|
||||
data: Record<string, unknown>
|
||||
fullData: Record<string, unknown>
|
||||
originalDoc: Record<string, unknown>
|
||||
field: Field
|
||||
field: NamedField
|
||||
operation: Operation
|
||||
overrideAccess: boolean
|
||||
req: PayloadRequest
|
||||
|
||||
@@ -146,7 +146,6 @@ export type RowAdmin = Omit<Admin, 'description'> & {
|
||||
};
|
||||
|
||||
export type RowField = Omit<FieldBase, 'admin' | 'name'> & {
|
||||
name?: string;
|
||||
admin?: RowAdmin;
|
||||
type: 'row';
|
||||
fields: Field[];
|
||||
@@ -267,6 +266,24 @@ export type Field =
|
||||
| PointField
|
||||
| RowField;
|
||||
|
||||
export type NamedField =
|
||||
TextField
|
||||
| NumberField
|
||||
| EmailField
|
||||
| TextareaField
|
||||
| CheckboxField
|
||||
| DateField
|
||||
| BlockField
|
||||
| GroupField
|
||||
| RadioField
|
||||
| RelationshipField
|
||||
| ArrayField
|
||||
| RichTextField
|
||||
| SelectField
|
||||
| UploadField
|
||||
| CodeField
|
||||
| PointField
|
||||
|
||||
export type FieldWithPath = Field & {
|
||||
path?: string
|
||||
}
|
||||
@@ -316,4 +333,8 @@ export function fieldHasMaxDepth(field: Field): field is FieldWithMaxDepth {
|
||||
return (field.type === 'upload' || field.type === 'relationship') && typeof field.maxDepth === 'number';
|
||||
}
|
||||
|
||||
export function fieldIsNamed(field: Field): field is NamedField {
|
||||
return 'name' in field;
|
||||
}
|
||||
|
||||
export type HookName = 'beforeChange' | 'beforeValidate' | 'afterChange' | 'afterRead';
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { PayloadRequest } from '../express/types';
|
||||
import { Operation } from '../types';
|
||||
import { Field, HookName } from './config/types';
|
||||
import { HookName, NamedField } from './config/types';
|
||||
|
||||
type Arguments = {
|
||||
data: Record<string, unknown>
|
||||
field: Field
|
||||
field: NamedField
|
||||
hook: HookName
|
||||
req: PayloadRequest
|
||||
operation: Operation
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { PayloadRequest } from '../express/types';
|
||||
import { Field, RelationshipField, fieldSupportsMany, fieldHasMaxDepth } from './config/types';
|
||||
import { RelationshipField, fieldSupportsMany, fieldHasMaxDepth, UploadField } from './config/types';
|
||||
import { Payload } from '..';
|
||||
|
||||
type PopulateArgs = {
|
||||
@@ -9,7 +9,7 @@ type PopulateArgs = {
|
||||
overrideAccess: boolean
|
||||
dataReference: Record<string, any>
|
||||
data: Record<string, unknown>
|
||||
field: Field
|
||||
field: RelationshipField | UploadField
|
||||
index?: number
|
||||
payload: Payload
|
||||
}
|
||||
@@ -27,12 +27,11 @@ const populate = async ({
|
||||
}: PopulateArgs) => {
|
||||
const dataToUpdate = dataReference;
|
||||
|
||||
const fieldAsRelationship = field as RelationshipField;
|
||||
const relation = Array.isArray(fieldAsRelationship.relationTo) ? (data.relationTo as string) : fieldAsRelationship.relationTo;
|
||||
const relation = Array.isArray(field.relationTo) ? (data.relationTo as string) : field.relationTo;
|
||||
const relatedCollection = payload.collections[relation];
|
||||
|
||||
if (relatedCollection) {
|
||||
let idString = Array.isArray(fieldAsRelationship.relationTo) ? data.value : data;
|
||||
let idString = Array.isArray(field.relationTo) ? data.value : data;
|
||||
|
||||
if (typeof idString !== 'string' && typeof idString?.toString === 'function') {
|
||||
idString = idString.toString();
|
||||
@@ -55,12 +54,12 @@ const populate = async ({
|
||||
// If populatedRelationship comes back, update value
|
||||
if (populatedRelationship || populatedRelationship === null) {
|
||||
if (typeof index === 'number') {
|
||||
if (Array.isArray(fieldAsRelationship.relationTo)) {
|
||||
if (Array.isArray(field.relationTo)) {
|
||||
dataToUpdate[field.name][index].value = populatedRelationship;
|
||||
} else {
|
||||
dataToUpdate[field.name][index] = populatedRelationship;
|
||||
}
|
||||
} else if (Array.isArray(fieldAsRelationship.relationTo)) {
|
||||
} else if (Array.isArray(field.relationTo)) {
|
||||
dataToUpdate[field.name].value = populatedRelationship;
|
||||
} else {
|
||||
dataToUpdate[field.name] = populatedRelationship;
|
||||
@@ -71,7 +70,7 @@ const populate = async ({
|
||||
|
||||
type PromiseArgs = {
|
||||
data: Record<string, any>
|
||||
field: Field
|
||||
field: RelationshipField | UploadField
|
||||
depth: number
|
||||
currentDepth: number
|
||||
req: PayloadRequest
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import validationPromise from './validationPromise';
|
||||
import accessPromise from './accessPromise';
|
||||
import hookPromise from './hookPromise';
|
||||
import { Field, fieldHasSubFields, fieldIsArrayType, fieldIsBlockType, HookName } from './config/types';
|
||||
import { Field, fieldHasSubFields, fieldIsArrayType, fieldIsBlockType, fieldIsNamed, HookName } from './config/types';
|
||||
import { Operation } from '../types';
|
||||
import { PayloadRequest } from '../express/types';
|
||||
import { Payload } from '..';
|
||||
@@ -82,7 +82,7 @@ const traverseFields = (args: Arguments): void => {
|
||||
}
|
||||
}
|
||||
|
||||
if (field.hidden && typeof data[field.name] !== 'undefined' && !showHiddenFields) {
|
||||
if (field.hidden && fieldIsNamed(field) && typeof data[field.name] !== 'undefined' && !showHiddenFields) {
|
||||
delete data[field.name];
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ const traverseFields = (args: Arguments): void => {
|
||||
dataCopy[field.name] = parseFloat(data[field.name]);
|
||||
}
|
||||
|
||||
if (field.name === 'id') {
|
||||
if (fieldIsNamed(field) && field.name === 'id') {
|
||||
if (field.type === 'number' && typeof data[field.name] === 'string') {
|
||||
dataCopy[field.name] = parseFloat(data[field.name]);
|
||||
}
|
||||
@@ -151,7 +151,8 @@ const traverseFields = (args: Arguments): void => {
|
||||
}
|
||||
}
|
||||
|
||||
const hasLocalizedValue = (typeof data?.[field.name] === 'object' && data?.[field.name] !== null)
|
||||
const hasLocalizedValue = fieldIsNamed(field)
|
||||
&& (typeof data?.[field.name] === 'object' && data?.[field.name] !== null)
|
||||
&& field.name
|
||||
&& field.localized
|
||||
&& locale !== 'all'
|
||||
@@ -165,7 +166,7 @@ const traverseFields = (args: Arguments): void => {
|
||||
dataCopy[field.name] = localizedValue;
|
||||
}
|
||||
|
||||
if (field.localized && unflattenLocales) {
|
||||
if (fieldIsNamed(field) && field.localized && unflattenLocales) {
|
||||
unflattenLocaleActions.push(() => {
|
||||
const localeData = payload.config.localization.locales.reduce((locales, localeID) => {
|
||||
let valueToSet;
|
||||
@@ -197,37 +198,40 @@ const traverseFields = (args: Arguments): void => {
|
||||
});
|
||||
}
|
||||
|
||||
accessPromises.push(() => accessPromise({
|
||||
data,
|
||||
fullData,
|
||||
originalDoc,
|
||||
field,
|
||||
operation,
|
||||
overrideAccess,
|
||||
req,
|
||||
id,
|
||||
relationshipPopulations,
|
||||
depth,
|
||||
currentDepth,
|
||||
hook,
|
||||
payload,
|
||||
}));
|
||||
if (fieldIsNamed(field)) {
|
||||
accessPromises.push(() => accessPromise({
|
||||
data,
|
||||
fullData,
|
||||
originalDoc,
|
||||
field,
|
||||
operation,
|
||||
overrideAccess,
|
||||
req,
|
||||
id,
|
||||
relationshipPopulations,
|
||||
depth,
|
||||
currentDepth,
|
||||
hook,
|
||||
payload,
|
||||
}));
|
||||
|
||||
hookPromises.push(() => hookPromise({
|
||||
data,
|
||||
field,
|
||||
hook,
|
||||
req,
|
||||
operation,
|
||||
fullOriginalDoc,
|
||||
fullData,
|
||||
}));
|
||||
}
|
||||
|
||||
hookPromises.push(() => hookPromise({
|
||||
data,
|
||||
field,
|
||||
hook,
|
||||
req,
|
||||
operation,
|
||||
fullOriginalDoc,
|
||||
fullData,
|
||||
}));
|
||||
|
||||
const passesCondition = (field.admin?.condition && hook === 'beforeChange') ? field.admin.condition(fullData, data) : true;
|
||||
const skipValidationFromHere = skipValidation || !passesCondition;
|
||||
|
||||
if (fieldHasSubFields(field)) {
|
||||
if (field.name === undefined) {
|
||||
if (!fieldIsNamed(field)) {
|
||||
traverseFields({
|
||||
...args,
|
||||
fields: field.fields,
|
||||
@@ -284,7 +288,7 @@ const traverseFields = (args: Arguments): void => {
|
||||
}
|
||||
}
|
||||
|
||||
if (hook === 'beforeChange' && field.name) {
|
||||
if (hook === 'beforeChange' && fieldIsNamed(field)) {
|
||||
const updatedData = data;
|
||||
|
||||
if (data?.[field.name] === undefined && originalDoc?.[field.name] === undefined && field.defaultValue) {
|
||||
@@ -296,7 +300,7 @@ const traverseFields = (args: Arguments): void => {
|
||||
if (Array.isArray(dataCopy[field.name])) {
|
||||
dataCopy[field.name].forEach((relatedDoc: {value: unknown, relationTo: string}, i) => {
|
||||
const relatedCollection = payload.config.collections.find((collection) => collection.slug === relatedDoc.relationTo);
|
||||
const relationshipIDField = relatedCollection.fields.find((collectionField) => collectionField.name === 'id');
|
||||
const relationshipIDField = relatedCollection.fields.find((collectionField) => fieldIsNamed(collectionField) && collectionField.name === 'id');
|
||||
if (relationshipIDField?.type === 'number') {
|
||||
dataCopy[field.name][i] = { ...relatedDoc, value: parseFloat(relatedDoc.value as string) };
|
||||
}
|
||||
@@ -304,7 +308,7 @@ const traverseFields = (args: Arguments): void => {
|
||||
}
|
||||
if (field.type === 'relationship' && field.hasMany !== true && dataCopy[field.name]?.relationTo) {
|
||||
const relatedCollection = payload.config.collections.find((collection) => collection.slug === dataCopy[field.name].relationTo);
|
||||
const relationshipIDField = relatedCollection.fields.find((collectionField) => collectionField.name === 'id');
|
||||
const relationshipIDField = relatedCollection.fields.find((collectionField) => fieldIsNamed(collectionField) && collectionField.name === 'id');
|
||||
if (relationshipIDField?.type === 'number') {
|
||||
dataCopy[field.name] = { ...dataCopy[field.name], value: parseFloat(dataCopy[field.name].value as string) };
|
||||
}
|
||||
@@ -313,7 +317,7 @@ const traverseFields = (args: Arguments): void => {
|
||||
if (Array.isArray(dataCopy[field.name])) {
|
||||
dataCopy[field.name].forEach((relatedDoc: unknown, i) => {
|
||||
const relatedCollection = payload.config.collections.find((collection) => collection.slug === field.relationTo);
|
||||
const relationshipIDField = relatedCollection.fields.find((collectionField) => collectionField.name === 'id');
|
||||
const relationshipIDField = relatedCollection.fields.find((collectionField) => fieldIsNamed(collectionField) && collectionField.name === 'id');
|
||||
if (relationshipIDField?.type === 'number') {
|
||||
dataCopy[field.name][i] = parseFloat(relatedDoc as string);
|
||||
}
|
||||
@@ -321,7 +325,7 @@ const traverseFields = (args: Arguments): void => {
|
||||
}
|
||||
if (field.type === 'relationship' && field.hasMany !== true && dataCopy[field.name]) {
|
||||
const relatedCollection = payload.config.collections.find((collection) => collection.slug === field.relationTo);
|
||||
const relationshipIDField = relatedCollection.fields.find((collectionField) => collectionField.name === 'id');
|
||||
const relationshipIDField = relatedCollection.fields.find((collectionField) => fieldIsNamed(collectionField) && collectionField.name === 'id');
|
||||
if (relationshipIDField?.type === 'number') {
|
||||
dataCopy[field.name] = parseFloat(dataCopy[field.name]);
|
||||
}
|
||||
@@ -364,7 +368,7 @@ const traverseFields = (args: Arguments): void => {
|
||||
path,
|
||||
skipValidation: skipValidationFromHere,
|
||||
}));
|
||||
} else {
|
||||
} else if (fieldIsNamed(field)) {
|
||||
validationPromises.push(() => validationPromise({
|
||||
errors,
|
||||
hook,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Field, HookName } from './config/types';
|
||||
import { HookName, NamedField } from './config/types';
|
||||
|
||||
type Arguments = {
|
||||
hook: HookName
|
||||
field: Field
|
||||
field: NamedField
|
||||
path: string
|
||||
errors: {message: string, field: string}[]
|
||||
newData: Record<string, unknown>
|
||||
|
||||
@@ -15,13 +15,13 @@ import { GraphQLJSON } from 'graphql-type-json';
|
||||
import withNullableType from './withNullableType';
|
||||
import formatName from '../utilities/formatName';
|
||||
import combineParentName from '../utilities/combineParentName';
|
||||
import { ArrayField, Field, FieldWithSubFields, GroupField, RelationshipField, RowField, SelectField } from '../../fields/config/types';
|
||||
import { ArrayField, CodeField, DateField, EmailField, Field, fieldHasSubFields, fieldIsNamed, GroupField, NumberField, PointField, RadioField, RelationshipField, RichTextField, RowField, SelectField, TextareaField, TextField, UploadField } from '../../fields/config/types';
|
||||
import { toWords } from '../../utilities/formatLabels';
|
||||
import payload from '../../index';
|
||||
import { SanitizedCollectionConfig } from '../../collections/config/types';
|
||||
|
||||
export const getCollectionIDType = (config: SanitizedCollectionConfig): GraphQLScalarType => {
|
||||
const idField = config.fields.find(({ name }) => name === 'id');
|
||||
const idField = config.fields.find((field) => fieldIsNamed(field) && field.name === 'id');
|
||||
if (!idField) return GraphQLString;
|
||||
switch (idField.type) {
|
||||
case 'number':
|
||||
@@ -33,21 +33,19 @@ export const getCollectionIDType = (config: SanitizedCollectionConfig): GraphQLS
|
||||
|
||||
function buildMutationInputType(name: string, fields: Field[], parentName: string, forceNullable = false): GraphQLInputObjectType {
|
||||
const fieldToSchemaMap = {
|
||||
number: (field: Field) => {
|
||||
number: (field: NumberField) => {
|
||||
const type = field.name === 'id' ? GraphQLInt : GraphQLFloat;
|
||||
return { type: withNullableType(field, type, forceNullable) };
|
||||
},
|
||||
text: (field: Field) => ({ type: withNullableType(field, GraphQLString, forceNullable) }),
|
||||
email: (field: Field) => ({ type: withNullableType(field, GraphQLString, forceNullable) }),
|
||||
textarea: (field: Field) => ({ type: withNullableType(field, GraphQLString, forceNullable) }),
|
||||
richText: (field: Field) => ({ type: withNullableType(field, GraphQLJSON, forceNullable) }),
|
||||
code: (field: Field) => ({ type: withNullableType(field, GraphQLString, forceNullable) }),
|
||||
date: (field: Field) => ({ type: withNullableType(field, GraphQLString, forceNullable) }),
|
||||
upload: (field: Field) => ({ type: withNullableType(field, GraphQLString, forceNullable) }),
|
||||
'rich-text': (field: Field) => ({ type: withNullableType(field, GraphQLString, forceNullable) }),
|
||||
html: (field: Field) => ({ type: withNullableType(field, GraphQLString, forceNullable) }),
|
||||
radio: (field: Field) => ({ type: withNullableType(field, GraphQLString, forceNullable) }),
|
||||
point: (field: Field) => ({ type: withNullableType(field, GraphQLList(GraphQLFloat), 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) => {
|
||||
const formattedName = `${combineParentName(parentName, field.name)}_MutationInput`;
|
||||
@@ -148,16 +146,33 @@ function buildMutationInputType(name: string, fields: Field[], parentName: strin
|
||||
if (getFieldSchema) {
|
||||
const fieldSchema = getFieldSchema(field);
|
||||
|
||||
if (Array.isArray(fieldSchema)) {
|
||||
return fieldSchema.reduce((acc, subField, i) => ({
|
||||
...acc,
|
||||
[(field as FieldWithSubFields).fields[i].name]: subField,
|
||||
}), schema);
|
||||
if (fieldHasSubFields(field) && Array.isArray(fieldSchema)) {
|
||||
return fieldSchema.reduce((acc, subField, i) => {
|
||||
const currentSubField = field.fields[i];
|
||||
if (fieldIsNamed(currentSubField)) {
|
||||
return {
|
||||
...acc,
|
||||
[currentSubField.name]: subField,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...acc,
|
||||
...fieldSchema,
|
||||
};
|
||||
}, schema);
|
||||
}
|
||||
|
||||
if (fieldIsNamed(field)) {
|
||||
return {
|
||||
...schema,
|
||||
[field.name]: fieldSchema,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...schema,
|
||||
[field.name]: fieldSchema,
|
||||
...fieldSchema,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
GraphQLUnionType,
|
||||
} from 'graphql';
|
||||
import { DateTimeResolver, EmailAddressResolver } from 'graphql-scalars';
|
||||
import { Field, RadioField, RelationshipField, SelectField, UploadField, optionIsObject, ArrayField, GroupField, RichTextField } from '../../fields/config/types';
|
||||
import { Field, RadioField, RelationshipField, SelectField, UploadField, optionIsObject, ArrayField, GroupField, RichTextField, fieldIsNamed } from '../../fields/config/types';
|
||||
import formatName from '../utilities/formatName';
|
||||
import combineParentName from '../utilities/combineParentName';
|
||||
import withNullableType from './withNullableType';
|
||||
@@ -478,7 +478,7 @@ function buildObjectType(name: string, fields: Field[], parentName: string, base
|
||||
if (!field.hidden) {
|
||||
const fieldSchema = fieldToSchemaMap[field.type];
|
||||
if (fieldSchema) {
|
||||
if (field.name) {
|
||||
if (fieldIsNamed(field)) {
|
||||
return {
|
||||
...schema,
|
||||
[formatName(field.name)]: fieldSchema(field),
|
||||
|
||||
@@ -32,6 +32,9 @@ import {
|
||||
TextField,
|
||||
UploadField,
|
||||
PointField,
|
||||
NamedField,
|
||||
fieldIsNamed,
|
||||
fieldHasSubFields,
|
||||
} from '../../fields/config/types';
|
||||
import formatName from '../utilities/formatName';
|
||||
import combineParentName from '../utilities/combineParentName';
|
||||
@@ -47,10 +50,10 @@ import withOperators from './withOperators';
|
||||
const buildWhereInputType = (name: string, fields: Field[], parentName: string): GraphQLInputObjectType => {
|
||||
// This is the function that builds nested paths for all
|
||||
// field types with nested paths.
|
||||
const recursivelyBuildNestedPaths = (field: FieldWithSubFields) => {
|
||||
const recursivelyBuildNestedPaths = (field: FieldWithSubFields & NamedField) => {
|
||||
const nestedPaths = field.fields.reduce((nestedFields, nestedField) => {
|
||||
const getFieldSchema = fieldToSchemaMap[nestedField.type];
|
||||
const nestedFieldName = `${field.name}__${nestedField.name}`;
|
||||
const nestedFieldName = fieldIsNamed(nestedField) ? `${field.name}__${nestedField.name}` : undefined;
|
||||
|
||||
if (getFieldSchema) {
|
||||
const fieldSchema = getFieldSchema({
|
||||
@@ -294,7 +297,7 @@ const buildWhereInputType = (name: string, fields: Field[], parentName: string):
|
||||
if (getFieldSchema) {
|
||||
const rowFieldSchema = getFieldSchema(rowField);
|
||||
|
||||
if (Array.isArray(rowFieldSchema)) {
|
||||
if (fieldHasSubFields(rowField)) {
|
||||
return [
|
||||
...rowSchema,
|
||||
...rowFieldSchema,
|
||||
@@ -321,7 +324,7 @@ const buildWhereInputType = (name: string, fields: Field[], parentName: string):
|
||||
if (getFieldSchema) {
|
||||
const fieldSchema = getFieldSchema(field);
|
||||
|
||||
if (Array.isArray(fieldSchema)) {
|
||||
if (fieldHasSubFields(field)) {
|
||||
return {
|
||||
...schema,
|
||||
...(fieldSchema.reduce((subFields, subField) => ({
|
||||
@@ -343,7 +346,7 @@ const buildWhereInputType = (name: string, fields: Field[], parentName: string):
|
||||
|
||||
fieldTypes.id = {
|
||||
type: withOperators(
|
||||
{ name: 'id' } as Field,
|
||||
{ name: 'id' } as NamedField,
|
||||
GraphQLJSON,
|
||||
parentName,
|
||||
[...operators.equality, ...operators.contains],
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { GraphQLBoolean, GraphQLInputObjectType, GraphQLList, GraphQLType } from 'graphql';
|
||||
import { Field } from '../../fields/config/types';
|
||||
import { NamedField } from '../../fields/config/types';
|
||||
import combineParentName from '../utilities/combineParentName';
|
||||
|
||||
const withOperators = (field: Field, type: GraphQLType, parentName: string, operators: string[]): GraphQLInputObjectType => {
|
||||
const withOperators = (field: NamedField, type: GraphQLType, parentName: string, operators: string[]): GraphQLInputObjectType => {
|
||||
const name = `${combineParentName(parentName, field.name)}_operator`;
|
||||
const listOperators = ['in', 'not_in', 'all'];
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* eslint-disable no-use-before-define */
|
||||
import { Schema, SchemaDefinition, SchemaOptions } from 'mongoose';
|
||||
import { SanitizedConfig } from '../config/types';
|
||||
import { ArrayField, Block, BlockField, Field, GroupField, RadioField, RelationshipField, RowField, SelectField, UploadField } from '../fields/config/types';
|
||||
import { ArrayField, Block, BlockField, CheckboxField, CodeField, DateField, EmailField, Field, fieldIsNamed, GroupField, NumberField, PointField, RadioField, RelationshipField, RichTextField, RowField, SelectField, TextareaField, TextField, UploadField } from '../fields/config/types';
|
||||
import sortableFieldTypes from '../fields/sortableFieldTypes';
|
||||
|
||||
type BuildSchemaOptions = {
|
||||
@@ -76,12 +76,12 @@ const buildSchema = (config: SanitizedConfig, configFields: Field[], buildSchema
|
||||
const indexFields = [];
|
||||
|
||||
if (!allowIDField) {
|
||||
const idField = schemaFields.find(({ name }) => name === 'id');
|
||||
const idField = schemaFields.find((field) => fieldIsNamed(field) && field.name === 'id');
|
||||
if (idField) {
|
||||
fields = {
|
||||
_id: idField.type === 'number' ? Number : String,
|
||||
};
|
||||
schemaFields = schemaFields.filter(({ name }) => name !== 'id');
|
||||
schemaFields = schemaFields.filter((field) => fieldIsNamed(field) && field.name !== 'id');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ const buildSchema = (config: SanitizedConfig, configFields: Field[], buildSchema
|
||||
indexFields.push(...fieldIndexMap[field.type](field, config));
|
||||
}
|
||||
|
||||
if (config.indexSortableFields && !buildSchemaOptions.global && !field.index && !field.hidden && sortableFieldTypes.indexOf(field.type) > -1) {
|
||||
if (config.indexSortableFields && !buildSchemaOptions.global && !field.index && !field.hidden && sortableFieldTypes.indexOf(field.type) > -1 && fieldIsNamed(field)) {
|
||||
indexFields.push({ [field.name]: 1 });
|
||||
}
|
||||
});
|
||||
@@ -113,7 +113,7 @@ const buildSchema = (config: SanitizedConfig, configFields: Field[], buildSchema
|
||||
};
|
||||
|
||||
const fieldIndexMap = {
|
||||
point: (field: Field, config: SanitizedConfig) => {
|
||||
point: (field: PointField, config: SanitizedConfig) => {
|
||||
if (field.localized) {
|
||||
return config.localization.locales.map((locale) => ({ [`${field.name}.${locale}`]: field.index === false ? undefined : field.index || '2dsphere' }));
|
||||
}
|
||||
@@ -122,7 +122,7 @@ const fieldIndexMap = {
|
||||
};
|
||||
|
||||
const fieldToSchemaMap = {
|
||||
number: (field: Field, fields: SchemaDefinition, config: SanitizedConfig, buildSchemaOptions: BuildSchemaOptions): SchemaDefinition => {
|
||||
number: (field: NumberField, fields: SchemaDefinition, config: SanitizedConfig, buildSchemaOptions: BuildSchemaOptions): SchemaDefinition => {
|
||||
const baseSchema = { ...formatBaseSchema(field, buildSchemaOptions), type: Number };
|
||||
|
||||
return {
|
||||
@@ -130,7 +130,7 @@ const fieldToSchemaMap = {
|
||||
[field.name]: localizeSchema(field, baseSchema, config.localization.locales),
|
||||
};
|
||||
},
|
||||
text: (field: Field, fields: SchemaDefinition, config: SanitizedConfig, buildSchemaOptions: BuildSchemaOptions): SchemaDefinition => {
|
||||
text: (field: TextField, fields: SchemaDefinition, config: SanitizedConfig, buildSchemaOptions: BuildSchemaOptions): SchemaDefinition => {
|
||||
const baseSchema = { ...formatBaseSchema(field, buildSchemaOptions), type: String };
|
||||
|
||||
return {
|
||||
@@ -138,7 +138,7 @@ const fieldToSchemaMap = {
|
||||
[field.name]: localizeSchema(field, baseSchema, config.localization.locales),
|
||||
};
|
||||
},
|
||||
email: (field: Field, fields: SchemaDefinition, config: SanitizedConfig, buildSchemaOptions: BuildSchemaOptions): SchemaDefinition => {
|
||||
email: (field: EmailField, fields: SchemaDefinition, config: SanitizedConfig, buildSchemaOptions: BuildSchemaOptions): SchemaDefinition => {
|
||||
const baseSchema = { ...formatBaseSchema(field, buildSchemaOptions), type: String };
|
||||
|
||||
return {
|
||||
@@ -146,7 +146,7 @@ const fieldToSchemaMap = {
|
||||
[field.name]: localizeSchema(field, baseSchema, config.localization.locales),
|
||||
};
|
||||
},
|
||||
textarea: (field: Field, fields: SchemaDefinition, config: SanitizedConfig, buildSchemaOptions: BuildSchemaOptions): SchemaDefinition => {
|
||||
textarea: (field: TextareaField, fields: SchemaDefinition, config: SanitizedConfig, buildSchemaOptions: BuildSchemaOptions): SchemaDefinition => {
|
||||
const baseSchema = { ...formatBaseSchema(field, buildSchemaOptions), type: String };
|
||||
|
||||
return {
|
||||
@@ -154,7 +154,7 @@ const fieldToSchemaMap = {
|
||||
[field.name]: localizeSchema(field, baseSchema, config.localization.locales),
|
||||
};
|
||||
},
|
||||
richText: (field: Field, fields: SchemaDefinition, config: SanitizedConfig, buildSchemaOptions: BuildSchemaOptions): SchemaDefinition => {
|
||||
richText: (field: RichTextField, fields: SchemaDefinition, config: SanitizedConfig, buildSchemaOptions: BuildSchemaOptions): SchemaDefinition => {
|
||||
const baseSchema = { ...formatBaseSchema(field, buildSchemaOptions), type: Schema.Types.Mixed };
|
||||
|
||||
return {
|
||||
@@ -162,7 +162,7 @@ const fieldToSchemaMap = {
|
||||
[field.name]: localizeSchema(field, baseSchema, config.localization.locales),
|
||||
};
|
||||
},
|
||||
code: (field: Field, fields: SchemaDefinition, config: SanitizedConfig, buildSchemaOptions: BuildSchemaOptions): SchemaDefinition => {
|
||||
code: (field: CodeField, fields: SchemaDefinition, config: SanitizedConfig, buildSchemaOptions: BuildSchemaOptions): SchemaDefinition => {
|
||||
const baseSchema = { ...formatBaseSchema(field, buildSchemaOptions), type: String };
|
||||
|
||||
return {
|
||||
@@ -170,7 +170,7 @@ const fieldToSchemaMap = {
|
||||
[field.name]: localizeSchema(field, baseSchema, config.localization.locales),
|
||||
};
|
||||
},
|
||||
point: (field: Field, fields: SchemaDefinition, config: SanitizedConfig): SchemaDefinition => {
|
||||
point: (field: PointField, fields: SchemaDefinition, config: SanitizedConfig): SchemaDefinition => {
|
||||
const baseSchema = {
|
||||
type: {
|
||||
type: String,
|
||||
@@ -205,7 +205,7 @@ const fieldToSchemaMap = {
|
||||
[field.name]: localizeSchema(field, baseSchema, config.localization.locales),
|
||||
};
|
||||
},
|
||||
checkbox: (field: Field, fields: SchemaDefinition, config: SanitizedConfig, buildSchemaOptions: BuildSchemaOptions): SchemaDefinition => {
|
||||
checkbox: (field: CheckboxField, fields: SchemaDefinition, config: SanitizedConfig, buildSchemaOptions: BuildSchemaOptions): SchemaDefinition => {
|
||||
const baseSchema = { ...formatBaseSchema(field, buildSchemaOptions), type: Boolean };
|
||||
|
||||
return {
|
||||
@@ -213,7 +213,7 @@ const fieldToSchemaMap = {
|
||||
[field.name]: localizeSchema(field, baseSchema, config.localization.locales),
|
||||
};
|
||||
},
|
||||
date: (field: Field, fields: SchemaDefinition, config: SanitizedConfig, buildSchemaOptions: BuildSchemaOptions): SchemaDefinition => {
|
||||
date: (field: DateField, fields: SchemaDefinition, config: SanitizedConfig, buildSchemaOptions: BuildSchemaOptions): SchemaDefinition => {
|
||||
const baseSchema = { ...formatBaseSchema(field, buildSchemaOptions), type: Date };
|
||||
|
||||
return {
|
||||
@@ -294,7 +294,7 @@ const fieldToSchemaMap = {
|
||||
field.fields.forEach((rowField: Field) => {
|
||||
const fieldSchemaMap: FieldSchemaGenerator = fieldToSchemaMap[rowField.type];
|
||||
|
||||
if (fieldSchemaMap) {
|
||||
if (fieldSchemaMap && fieldIsNamed(rowField)) {
|
||||
const fieldSchema = fieldSchemaMap(rowField, fields, config, buildSchemaOptions);
|
||||
newFields[rowField.name] = fieldSchema[rowField.name];
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
"build",
|
||||
"tests",
|
||||
"**/*.spec.js",
|
||||
"node_modules"
|
||||
"node_modules",
|
||||
".eslintrc.js"
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user