diff --git a/src/collections/graphql/resolvers/getFindByID.js b/src/collections/graphql/resolvers/getFindByID.js index a15f86fba7..35c16a58f5 100644 --- a/src/collections/graphql/resolvers/getFindByID.js +++ b/src/collections/graphql/resolvers/getFindByID.js @@ -4,8 +4,6 @@ const findByIDQuery = require('../../queries/findByID'); const findByID = ({ config, model }) => withPolicy( config.policies.read, async (parent, { id }, context) => { - console.log(parent); - const options = { depth: 0, model, diff --git a/src/collections/queries/find.js b/src/collections/queries/find.js index 3df5f83d9c..d7509334f8 100644 --- a/src/collections/queries/find.js +++ b/src/collections/queries/find.js @@ -15,21 +15,23 @@ const find = async (options) => { const mongooseQuery = await model.buildQuery(query, locale); - const paginateQuery = { + const mongooseOptions = { options: {}, }; - if (paginate.page) paginateQuery.page = paginate.page; - if (paginate.limit) paginateQuery.limit = paginate.limit; - if (paginate.sort) paginateQuery.sort = paginate.sort; + if (paginate.page) mongooseOptions.page = paginate.page; + if (paginate.limit) mongooseOptions.limit = paginate.limit; + if (paginate.sort) mongooseOptions.sort = paginate.sort; - if (depth) { - paginateQuery.options.autopopulate = { - maxDepth: depth, + if (depth && depth !== '0') { + mongooseOptions.options.autopopulate = { + maxDepth: parseInt(depth, 10), }; + } else { + mongooseOptions.options.autopopulate = false; } - const result = await model.paginate(mongooseQuery, paginateQuery); + const result = await model.paginate(mongooseQuery, mongooseOptions); // await post find hook here diff --git a/src/collections/queries/findByID.js b/src/collections/queries/findByID.js index 2b16df19bc..f41432881c 100644 --- a/src/collections/queries/findByID.js +++ b/src/collections/queries/findByID.js @@ -9,10 +9,12 @@ const findByID = async (options) => { const hasMultipleIDs = Array.isArray(id); - if (depth) { - mongooseOptions.autopopulate = { - maxDepth: depth, + if (depth && depth !== '0') { + mongooseOptions.options.autopopulate = { + maxDepth: parseInt(depth, 10), }; + } else { + mongooseOptions.options.autopopulate = false; } try { @@ -23,7 +25,7 @@ const findByID = async (options) => { if (hasMultipleIDs) { result = await model.find({ _id: { - $in: id.map(id => mongoose.Types.ObjectId(id)), + $in: id.map(docId => mongoose.Types.ObjectId(docId)), }, }, {}, mongooseOptions); } else { diff --git a/src/graphql/index.js b/src/graphql/index.js index 8963f82fc8..c698814d20 100644 --- a/src/graphql/index.js +++ b/src/graphql/index.js @@ -13,18 +13,21 @@ const buildWhereInputType = require('./schema/buildWhereInputType'); const formatName = require('./utilities/formatName'); const getLocaleStringType = require('./types/getLocaleStringType'); const getLocaleFloatType = require('./types/getLocaleFloatType'); -const getBuildLocaleObjectType = require('./types/getBuildLocaleObjectType'); +const getLocaleBooleanType = require('./types/getLocaleBooleanType'); +const getBuildLocaleCustomType = require('./types/getBuildLocaleCustomType'); +const getCheckIfLocaleObject = require('./utilities/getCheckIfLocaleObject'); const { getFind, getFindByID } = require('../collections/graphql/resolvers'); class GraphQL { constructor(config, collections) { + this.config = config; + this.collections = collections; + this.init = this.init.bind(this); this.registerUser = this.registerUser.bind(this); this.registerCollections = this.registerCollections.bind(this); this.buildBlockTypeIfMissing = this.buildBlockTypeIfMissing.bind(this); - - this.config = config; - this.collections = collections; + this.checkIfLocaleObject = getCheckIfLocaleObject(this.config.localization); this.Query = { name: 'Query', @@ -37,12 +40,13 @@ class GraphQL { }; this.types = { - LocaleStringType: getLocaleStringType(this.config.localization), - LocaleFloatType: getLocaleFloatType(this.config.localization), + LocaleStringType: getLocaleStringType(this), + LocaleFloatType: getLocaleFloatType(this), + LocaleBooleanType: getLocaleBooleanType(this), blockTypes: {}, }; - this.buildLocaleObjectType = getBuildLocaleObjectType(this); + this.buildLocaleCustomType = getBuildLocaleCustomType(this); this.buildObjectType = getBuildObjectType(this); } diff --git a/src/graphql/schema/getBuildObjectType.js b/src/graphql/schema/getBuildObjectType.js index 0bcbc2f819..be4fc05690 100644 --- a/src/graphql/schema/getBuildObjectType.js +++ b/src/graphql/schema/getBuildObjectType.js @@ -1,3 +1,4 @@ +/* eslint-disable no-use-before-define */ const { GraphQLString, GraphQLFloat, @@ -14,90 +15,76 @@ const combineParentName = require('../utilities/combineParentName'); const withNullableType = require('./withNullableType'); function getBuildObjectType(context) { - const withLocalizedType = (field, type) => { - if (context.config.localization && field.localized) { - if (type instanceof GraphQLObjectType) { - const LocaleObjectType = context.buildLocaleObjectType(field, type); - return LocaleObjectType; - } - - if (type === GraphQLString) { - return context.types.LocaleStringType; - } - - if (type === GraphQLFloat) { - return context.types.LocaleFloatType; - } - } - - return type; - }; - const buildObjectType = (name, fields, parent, resolver) => { const fieldToSchemaMap = { number: (field) => { return { - type: withLocalizedType(field, withNullableType(field, GraphQLFloat)), + type: withNullableType( + field, + field.localized ? context.types.LocaleFloatType : GraphQLFloat, + ), }; }, text: (field) => { return { - type: withLocalizedType(field, withNullableType(field, GraphQLString)), + type: withNullableType( + field, + field.localized ? context.types.LocaleStringType : GraphQLString, + ), }; }, email: (field) => { return { - type: withLocalizedType( + type: withNullableType( field, - withNullableType(field, GraphQLString), + field.localized ? context.types.LocaleStringType : GraphQLString, ), }; }, textarea: (field) => { return { - type: withLocalizedType( + type: withNullableType( field, - withNullableType(field, GraphQLString), + field.localized ? context.types.LocaleStringType : GraphQLString, ), }; }, WYSIWYG: (field) => { return { - type: withLocalizedType( + type: withNullableType( field, - withNullableType(field, GraphQLString), + field.localized ? context.types.LocaleStringType : GraphQLString, ), }; }, code: (field) => { return { - type: withLocalizedType( + type: withNullableType( field, - withNullableType(field, GraphQLString), + field.localized ? context.types.LocaleStringType : GraphQLString, ), }; }, date: (field) => { return { - type: withLocalizedType( + type: withNullableType( field, - withNullableType(field, GraphQLString), + field.localized ? context.types.LocaleStringType : GraphQLString, ), }; }, upload: (field) => { - const type = GraphQLString; return { - type: withLocalizedType( + type: withNullableType( field, - withNullableType(field, type), + field.localized ? context.types.LocaleStringType : GraphQLString, ), }; }, checkbox: field => ({ - type: withLocalizedType( + type: withNullableType( field, - new GraphQLNonNull(GraphQLBoolean), + field.localized ? context.types.LocaleBooleanType : GraphQLBoolean, ), }), select: (field) => { @@ -132,10 +119,7 @@ function getBuildObjectType(context) { const typeWithNullable = withNullableType(field, typeWithList); return { - type: withLocalizedType( - field, - typeWithNullable, - ), + type: field.localized ? context.buildLocaleCustomType(field, typeWithNullable, parent) : typeWithNullable, }; }, relationship: (field) => { @@ -158,16 +142,17 @@ function getBuildObjectType(context) { relationshipType = context.collections[relationTo].graphQLType; } - // eslint-disable-next-line no-use-before-define - relationshipType = relationshipType || blockType; + // If the relationshipType is undefined at this point, + // it can be assumed that this blockType can have a relationship + // to itself. Therefore, we set the relationshipType equal to the blockType + // that is currently being created. - const typeWithLocale = withLocalizedType( - field, - withNullableType(field, relationshipType), - ); + relationshipType = relationshipType || newlyCreatedBlockType; + + const localizedType = field.localized ? context.buildLocaleCustomType(field, relationshipType, parent) : relationshipType; return { - type: field.hasMany ? new GraphQLList(typeWithLocale) : typeWithLocale, + type: field.hasMany ? new GraphQLList(localizedType) : localizedType, }; }, repeater: (field) => { @@ -176,10 +161,7 @@ function getBuildObjectType(context) { const typeWithNullable = new GraphQLList(withNullableType(field, type)); return { - type: withLocalizedType( - field, - typeWithNullable, - ), + type: field.localized ? context.buildLocaleCustomType(field, typeWithNullable, parent) : typeWithNullable, }; }, group: (field) => { @@ -191,25 +173,35 @@ function getBuildObjectType(context) { }; }, flexible: (field) => { - const types = field.blocks.map((block) => { + const blockTypes = field.blocks.map((block) => { context.buildBlockTypeIfMissing(block); return context.types.blockTypes[block.slug]; }); - return { - type: withLocalizedType( - field, - new GraphQLList( - new GraphQLUnionType({ - name: combineParentName(parent, field.label), - types, - resolveType(data) { - return context.types.blockTypes[data.blockType]; - }, - }), - ), - ), - }; + const flexibleType = new GraphQLList( + new GraphQLUnionType({ + name: combineParentName(parent, field.label), + blockTypes, + resolveType(data) { + // If the field contains all locales, it's a locale object + // Send back the locale object type + // Otherwise, grab the appropriate block type and send that + if (context.checkIfLocaleObject(data)) { + return flexibleLocaleObjectType; + } + return context.types.blockTypes[data.blockType]; + }, + }), + ); + + const flexibleLocaleObjectType = context.buildLocaleCustomType(field, flexibleType, parent); + + if (field.localized) { + blockTypes.push(flexibleLocaleObjectType); + return { type: flexibleLocaleObjectType }; + } + + return flexibleType; }, }; @@ -232,9 +224,9 @@ function getBuildObjectType(context) { objectSchema.resolve = resolver; } - const blockType = new GraphQLObjectType(objectSchema); + const newlyCreatedBlockType = new GraphQLObjectType(objectSchema); - return blockType; + return newlyCreatedBlockType; }; return buildObjectType; diff --git a/src/graphql/types/getBuildLocaleCustomType.js b/src/graphql/types/getBuildLocaleCustomType.js new file mode 100644 index 0000000000..635a182b26 --- /dev/null +++ b/src/graphql/types/getBuildLocaleCustomType.js @@ -0,0 +1,34 @@ +const { GraphQLScalarType } = require('graphql'); +const combineParentName = require('../utilities/combineParentName'); + +module.exports = (context) => { + return (field, type, parent) => { + const fullName = combineParentName(parent, field.label); + + const coerceType = (value) => { + const isLocaleObject = context.checkIfLocaleObject(value); + + if (isLocaleObject) { + // const allKeysValid = Object.values(value).every(locale => isValidJSValue(locale, type)); + + // if (allKeysValid) { + return value; + // } + } + + return value; + + // throw new Error(`${fullName}LocaleType can only represent an object containing locales or a matchiing ${fullName} type.`); + }; + + const LocaleCustomType = new GraphQLScalarType({ + name: `${fullName}LocaleType`, + description: `Handles locale values that can either be an object containing locales or a matching ${fullName} type.`, + serialize: coerceType, + parseValue: coerceType, + parseLiteral: ast => ast.value, + }); + + return LocaleCustomType; + }; +}; diff --git a/src/graphql/types/getBuildLocaleObjectType.js b/src/graphql/types/getBuildLocaleObjectType.js deleted file mode 100644 index 5a47512dbe..0000000000 --- a/src/graphql/types/getBuildLocaleObjectType.js +++ /dev/null @@ -1,29 +0,0 @@ -const { GraphQLScalarType } = require('graphql'); -const getIsLocaleObject = require('../utilities/getIsLocaleObject'); -const combineParentName = require('../utilities/combineParentName'); - -module.exports = (config) => { - return (field, type, parent) => { - const fullName = combineParentName(parent, field.label); - - const coerceType = (value) => { - const isLocaleObject = getIsLocaleObject(config, value); - - if (value instanceof type || isLocaleObject) { - return value; - } - - throw new Error(`LocaleFloatType can only represent an object containing locales or ${fullName} properties.`); - }; - - const LocaleObjectType = new GraphQLScalarType({ - name: `${fullName}LocaleType`, - description: `Handles locale values that can either be an object containing all locales or an object containing ${fullName} properties.`, - serialize: coerceType, - parseValue: coerceType, - parseLiteral: ast => ast.value, - }); - - return LocaleObjectType; - }; -}; diff --git a/src/graphql/types/getLocaleBooleanType.js b/src/graphql/types/getLocaleBooleanType.js new file mode 100644 index 0000000000..712b871050 --- /dev/null +++ b/src/graphql/types/getLocaleBooleanType.js @@ -0,0 +1,23 @@ +const { GraphQLScalarType } = require('graphql'); + +module.exports = (context) => { + const coerceType = (value) => { + const isLocaleObject = context.checkIfLocaleObject(value); + + if (typeof value === 'boolean' || isLocaleObject) { + return value; + } + + throw new Error('LocaleBooleanType can only represent a boolean or an object containing all locales.'); + }; + + const LocaleBooleanType = new GraphQLScalarType({ + name: 'LocaleBooleanType', + description: 'Handles locale values that can either be an object containing all locales or a boolean of a single locale.', + serialize: coerceType, + parseValue: coerceType, + parseLiteral: ast => ast.value, + }); + + return LocaleBooleanType; +}; diff --git a/src/graphql/types/getLocaleFloatType.js b/src/graphql/types/getLocaleFloatType.js index 6f8397e08b..dbddfcb791 100644 --- a/src/graphql/types/getLocaleFloatType.js +++ b/src/graphql/types/getLocaleFloatType.js @@ -1,25 +1,23 @@ const { GraphQLScalarType } = require('graphql'); -const getIsLocaleObject = require('../utilities/getIsLocaleObject'); -module.exports = (localization) => { +module.exports = (context) => { const coerceType = (value) => { - const isLocaleObject = getIsLocaleObject(localization, value); + const isLocaleObject = context.checkIfLocaleObject(value); - if (typeof value === 'number' - || isLocaleObject) { + if (typeof value === 'number' || isLocaleObject) { return value; } throw new Error('LocaleFloatType can only represent a float or an object containing all locales.'); }; - const LocaleString = new GraphQLScalarType({ - name: 'LocaleType', + const LocaleFloatType = new GraphQLScalarType({ + name: 'LocaleFloatType', description: 'Handles locale values that can either be an object containing all locales or a float of a single locale.', serialize: coerceType, parseValue: coerceType, parseLiteral: ast => ast.value, }); - return LocaleString; + return LocaleFloatType; }; diff --git a/src/graphql/types/getLocaleStringType.js b/src/graphql/types/getLocaleStringType.js index 3de0925c50..85f3ac919c 100644 --- a/src/graphql/types/getLocaleStringType.js +++ b/src/graphql/types/getLocaleStringType.js @@ -1,25 +1,23 @@ const { GraphQLScalarType } = require('graphql'); -const getIsLocaleObject = require('../utilities/getIsLocaleObject'); -module.exports = (localization) => { +module.exports = (context) => { const coerceType = (value) => { - const isLocaleObject = getIsLocaleObject(localization, value); + const isLocaleObject = context.checkIfLocaleObject(value); - if (typeof value === 'string' - || isLocaleObject) { + if (typeof value === 'string' || isLocaleObject) { return value; } throw new Error('LocaleString can only represent a string or an object containing all locales.'); }; - const LocaleString = new GraphQLScalarType({ - name: 'LocaleType', + const LocaleStringType = new GraphQLScalarType({ + name: 'LocaleStringType', description: 'Handles locale values that can either be an object containing all locales or a string of a single locale.', serialize: coerceType, parseValue: coerceType, parseLiteral: ast => ast.value, }); - return LocaleString; + return LocaleStringType; }; diff --git a/src/graphql/utilities/getCheckIfLocaleObject.js b/src/graphql/utilities/getCheckIfLocaleObject.js new file mode 100644 index 0000000000..d80c3b6fc8 --- /dev/null +++ b/src/graphql/utilities/getCheckIfLocaleObject.js @@ -0,0 +1,6 @@ +module.exports = (localization) => { + return (value) => { + return typeof value === 'object' + && Object.keys(value).some(key => localization.locales.indexOf(key) > -1); + }; +}; diff --git a/src/graphql/utilities/getIsLocaleObject.js b/src/graphql/utilities/getIsLocaleObject.js deleted file mode 100644 index ad0065f259..0000000000 --- a/src/graphql/utilities/getIsLocaleObject.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = (localization, value) => { - return typeof value === 'object' - && Object.keys(value).every(key => localization.locales.indexOf(key) > -1); -};