Merge branch 'master' into graphql-tests

This commit is contained in:
Dan Ribbens
2020-07-12 20:45:01 -04:00
8 changed files with 107 additions and 39 deletions

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useReducer, useCallback } from 'react';
import React, { useEffect, useReducer, useCallback, useState } from 'react';
import PropTypes from 'prop-types';
import { DragDropContext, Droppable } from 'react-beautiful-dnd';
import { v4 as uuidv4 } from 'uuid';
@@ -17,7 +17,7 @@ import './index.scss';
const baseClass = 'field-type array';
const Array = (props) => {
const ArrayFieldType = (props) => {
const {
label,
name,
@@ -48,6 +48,8 @@ const Array = (props) => {
return validationResult;
}, [validate, maxRows, minRows, required]);
const [disableFormData, setDisableFormData] = useState(false);
const {
showError,
errorMessage,
@@ -56,7 +58,7 @@ const Array = (props) => {
} = useFieldType({
path,
validate: memoizedValidate,
disableFormData: true,
disableFormData,
initialData: initialData?.length,
defaultValue: defaultValue?.length,
required,
@@ -113,6 +115,16 @@ const Array = (props) => {
});
}, [dataToInitialize]);
useEffect(() => {
if (value === 0 && dataToInitialize.length > 0 && disableFormData) {
setDisableFormData(false);
setValue(value);
} else if (value > 0 && !disableFormData) {
setDisableFormData(true);
setValue(value);
}
}, [value, setValue, disableFormData, dataToInitialize]);
return (
<DragDropContext onDragEnd={onDragEnd}>
<div className={baseClass}>
@@ -171,7 +183,7 @@ const Array = (props) => {
);
};
Array.defaultProps = {
ArrayFieldType.defaultProps = {
label: '',
defaultValue: [],
initialData: [],
@@ -185,7 +197,7 @@ Array.defaultProps = {
permissions: {},
};
Array.propTypes = {
ArrayFieldType.propTypes = {
defaultValue: PropTypes.arrayOf(
PropTypes.shape({}),
),
@@ -211,4 +223,4 @@ Array.propTypes = {
}),
};
export default withCondition(Array);
export default withCondition(ArrayFieldType);

View File

@@ -1,5 +1,5 @@
import React, {
useEffect, useReducer, useCallback,
useEffect, useReducer, useCallback, useState,
} from 'react';
import PropTypes from 'prop-types';
import { DragDropContext, Droppable } from 'react-beautiful-dnd';
@@ -50,6 +50,8 @@ const Blocks = (props) => {
return validationResult;
}, [validate, maxRows, minRows, singularLabel, blocks, required]);
const [disableFormData, setDisableFormData] = useState(false);
const {
showError,
errorMessage,
@@ -58,7 +60,7 @@ const Blocks = (props) => {
} = useFieldType({
path,
validate: memoizedValidate,
disableFormData: true,
disableFormData,
initialData: initialData?.length,
defaultValue: defaultValue?.length,
required,
@@ -126,6 +128,17 @@ const Blocks = (props) => {
});
}, [dataToInitialize]);
useEffect(() => {
if (value === 0 && dataToInitialize.length > 0 && disableFormData) {
setDisableFormData(false);
setValue(value);
} else if (value > 0 && !disableFormData) {
setDisableFormData(true);
setValue(value);
}
}, [value, setValue, disableFormData, dataToInitialize]);
return (
<DragDropContext onDragEnd={onDragEnd}>
<div className={baseClass}>
@@ -237,6 +250,9 @@ Blocks.defaultProps = {
};
Blocks.propTypes = {
blocks: PropTypes.arrayOf(
PropTypes.shape({}),
).isRequired,
defaultValue: PropTypes.arrayOf(
PropTypes.shape({}),
),

View File

@@ -28,6 +28,7 @@ function registerCollections() {
plural,
},
fields: initialFields,
timestamps,
},
} = collection;
@@ -48,18 +49,48 @@ function registerCollections() {
collection.graphQL = {};
const baseFields = {
id: {
type: new GraphQLNonNull(GraphQLString),
},
};
const whereInputFields = [
...fields,
];
if (timestamps) {
baseFields.createdAt = {
type: new GraphQLNonNull(GraphQLString),
};
baseFields.updatedAt = {
type: new GraphQLNonNull(GraphQLString),
};
whereInputFields.push({
name: 'createdAt',
label: 'Created At',
type: 'date',
});
whereInputFields.push({
name: 'updatedAt',
label: 'Upated At',
type: 'date',
});
}
collection.graphQL.type = this.buildObjectType(
singularLabel,
fields,
singularLabel,
{
id: { type: GraphQLString },
},
baseFields,
);
collection.graphQL.whereInputType = this.buildWhereInputType(
singularLabel,
fields,
whereInputFields,
singularLabel,
);

View File

@@ -113,6 +113,11 @@ module.exports = async (config, entityConfig, operation) => {
const hasRowsOfData = Array.isArray(data[field.name]);
const rowCount = hasRowsOfData ? data[field.name].length : 0;
if (data[field.name] === '0' || data[field.name] === 0 || data[field.name] === null) {
const updatedData = data;
updatedData[field.name] = [];
}
validationPromises.push(createValidationPromise(rowCount, field, path));
} else {
validationPromises.push(createValidationPromise(data[field.name], field, path));

View File

@@ -14,11 +14,9 @@ const errorHandler = require('./errorHandler');
const { access } = require('../auth/graphql/resolvers');
class GraphQL {
constructor(init, req, res) {
constructor(init) {
Object.assign(this, init);
this.init = this.init.bind(this);
this.req = req;
this.res = res;
this.types = {
blockTypes: {},
@@ -46,9 +44,7 @@ class GraphQL {
this.buildPoliciesType = buildPoliciesType.bind(this);
this.initCollections = initCollections.bind(this);
this.initGlobals = initGlobals.bind(this);
}
init() {
this.initCollections();
this.initGlobals();
@@ -69,34 +65,37 @@ class GraphQL {
const query = new GraphQLObjectType(this.Query);
const mutation = new GraphQLObjectType(this.Mutation);
const schema = new GraphQLSchema({
this.schema = new GraphQLSchema({
query,
mutation,
});
let errorExtensions = [];
let errorExtensionIteration = 0;
this.errorExtensions = [];
this.errorExtensionIteration = 0;
const extensions = async (info) => {
this.extensions = async (info) => {
const { result } = info;
if (result.errors) {
const afterErrorHook = typeof this.config.hooks.afterError === 'function' ? this.config.hooks.afterError : null;
errorExtensions = await errorHandler(info, this.config.debug, afterErrorHook);
this.errorExtensions = await errorHandler(info, this.config.debug, afterErrorHook);
}
return null;
};
}
init(req, res) {
return graphQLHTTP({
schema,
schema: this.schema,
customFormatErrorFn: () => {
const response = {
...errorExtensions[errorExtensionIteration],
...this.errorExtensions[this.errorExtensionIteration],
};
errorExtensionIteration += 1;
this.errorExtensionIteration += 1;
return response;
},
extensions,
context: { req: this.req, res: this.res },
extensions: this.extensions,
context: { req, res },
});
}
}

View File

@@ -1,7 +1,9 @@
const { GraphQLNonNull } = require('graphql');
const withNullableType = (field, type) => {
if (field.required && !field.localized) {
const hasReadAccessControl = field.access && field.access.read;
if (field.required && !field.localized && !hasReadAccessControl) {
return new GraphQLNonNull(type);
}

View File

@@ -55,10 +55,12 @@ class Payload {
this.router.get('/access', access(this.config));
const graphQLHandler = new GraphQL(this);
this.router.use(
this.config.routes.graphQL,
identifyAPI('GraphQL'),
(req, res) => new GraphQL(this, req, res).init()(req, res),
(req, res) => graphQLHandler.init(req, res)(req, res),
);
this.router.get(this.config.routes.graphQLPlayground, graphQLPlayground({

View File

@@ -1,3 +1,4 @@
/* eslint-disable no-console */
const mongoose = require('mongoose');
const connectMongoose = async (url) => {
@@ -12,16 +13,16 @@ const connectMongoose = async (url) => {
successfulConnectionMessage = 'Connected to in-memory Mongo server successfully!';
}
mongoose.connect(urlToConnect, {
useNewUrlParser: true,
useUnifiedTopology: true,
}, (err) => {
if (err) {
console.log('Unable to connect to the Mongo server. Please start the server. Error:', err);
} else {
console.log(successfulConnectionMessage);
}
});
try {
await mongoose.connect(urlToConnect, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
console.log(successfulConnectionMessage);
} catch (err) {
console.error('Error: cannot connect to MongoDB. Details: ', err);
process.exit(1);
}
};
module.exports = connectMongoose;