613 lines
17 KiB
JavaScript
Raw Normal View History

'use strict';
const _ = require('lodash');
2021-04-29 13:51:12 +02:00
const { contentTypes } = require('@strapi/utils');
2019-11-28 16:10:19 +01:00
const {
hasDraftAndPublish,
constants: { DP_PUB_STATE_LIVE },
} = contentTypes;
2019-11-28 16:10:19 +01:00
const DynamicZoneScalar = require('../types/dynamiczoneScalar');
const { formatModelConnectionsGQL } = require('./build-aggregation');
const types = require('./type-builder');
const {
actionExists,
mergeSchemas,
convertToParams,
convertToQuery,
amountLimiting,
createDefaultSchema,
} = require('./utils');
const { toSDL, getTypeDescription } = require('./schema-definitions');
const { toSingular, toPlural } = require('./naming');
const { buildQuery, buildMutation } = require('./resolvers-builder');
const OPTIONS = Symbol();
const FIND_QUERY_ARGUMENTS = {
sort: 'String',
limit: 'Int',
start: 'Int',
where: 'JSON',
publicationState: 'PublicationState',
};
const FIND_ONE_QUERY_ARGUMENTS = {
id: 'ID!',
publicationState: 'PublicationState',
};
/**
* Builds a graphql schema from all the contentTypes & components loaded
* @param {{ schema: object }} ctx
* @returns {object}
*/
const buildShadowCrud = ctx => {
const models = Object.values(strapi.contentTypes).filter(model => model.plugin !== 'admin');
const components = Object.values(strapi.components);
const allSchemas = buildModels([...models, ...components], ctx);
return mergeSchemas(createDefaultSchema(), ...allSchemas);
};
const assignOptions = (element, parent) => {
if (Array.isArray(element)) {
return element.map(el => assignOptions(el, parent));
}
return _.set(element, OPTIONS, _.get(parent, OPTIONS, {}));
};
const isQueryEnabled = (schema, name) => {
return _.get(schema, `resolver.Query.${name}`) !== false;
};
const getQueryInfo = (schema, name) => {
return _.get(schema, `resolver.Query.${name}`, {});
};
const isMutationEnabled = (schema, name) => {
return _.get(schema, `resolver.Mutation.${name}`) !== false;
};
const getMutationInfo = (schema, name) => {
return _.get(schema, `resolver.Mutation.${name}`, {});
};
GraphQL Plugin (bug): Content type's attributes marked as private are being exposed in the exported GraphQL schema (#9805) * Fixes #9804 * Fixes isPrivateAttribute function in strapi utils to take into account the private property within an attribute * removing comment * removing isNotPrivate function + adding call to already existing isPrivateAttribute function instead of isNotPrivate * isPrivateAttribute fixed based on coments from Alexandre and confirmed that the model does indeed contain all private fields in an array at startup time * removing isNotPrivate function and fixing generateEnumDefinitions to use the already available isPrivateAttribute function * checking if created_at and update_at are also marked as private and if this is the case then hiding them * allowIds by default when generating the input model to see if the tests will pass * moving allowIds back to false by default * adding isTypeAttributeEnabled function to check if a type attribute has been disabled and remove it from the exported graphql schema * removing unused var * only using isTypeAttributeEnabled instead of isPrivateAttribute. * adding isPrivateAttribute at association level * Adding isNotPrivate back together with isTypeAttributeEnabled * adding unit test for removing a disabled attribute from a graphql schema * renaming person to player in the test model * Deleting the file as it has been renamed in the strapi master branch * fixing lint issues * fixing some lint issues * adding isTypeAttributeEnabled support * adding enumeration field in graphql * adding use strict; to test file * fixing test checking that disabled attributes in graphql are removed from the graphql schema * adding test for buidShadowCrud * Update packages/strapi-plugin-graphql/services/type-builder.js Co-authored-by: Jean-Sébastien Herbaux <jean-sebastien.herbaux@epitech.eu> Co-authored-by: Rmaroun <rmaroun@outlook.com> Co-authored-by: Jean-Sébastien Herbaux <jean-sebastien.herbaux@epitech.eu>
2021-05-10 09:35:07 +02:00
const isTypeAttributeEnabled = (model, attr) =>
_.get(strapi.plugins.graphql, `config._schema.graphql.type.${model.globalId}.${attr}`) !== false;
Hide creator fields from public api by default (#8052) * Add model option to hide/show creators fields in public API response Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Add content-types util, rework sanitize-entity's private handling Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Update search e2e tests, fix an issue on empty search for the core-api controller (find) Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Fix GraphQL plugin (handle privates attributes on typeDefs + resolver builds) Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Fix sanitizeEntity import Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Move doc update from beta to stable Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Fix e2e test Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Fix pr comments Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Remove creator's field from upload controller routes Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Fix typedef build for graphql association Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Fix pr (comments + several issues) Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Add tests for search behavior in content-manager Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Rename files variables to meaningful names (upload controllers) Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Fix test with search id matching serialNumber Signed-off-by: Alexandre Bodin <bodin.alex@gmail.com> * Add toHasBeenCalledWith check for config.get (utils/content-types.test.js) Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> Co-authored-by: Alexandre Bodin <bodin.alex@gmail.com>
2020-10-01 17:47:08 +02:00
const isNotPrivate = _.curry((model, attributeName) => {
return !contentTypes.isPrivateAttribute(model, attributeName);
});
const wrapPublicationStateResolver = query => async (parent, args, ctx, ast) => {
const results = await query(parent, args, ctx, ast);
const queryOptions = _.pick(args, 'publicationState');
return assignOptions(results, { [OPTIONS]: queryOptions });
};
const buildTypeDefObj = model => {
const { associations = [], attributes, primaryKey, globalId } = model;
const typeDef = {
id: 'ID!',
[primaryKey]: 'ID!',
};
// Add timestamps attributes.
if (_.isArray(_.get(model, 'options.timestamps'))) {
const [createdAtKey, updatedAtKey] = model.options.timestamps;
typeDef[createdAtKey] = 'DateTime!';
typeDef[updatedAtKey] = 'DateTime!';
}
Object.keys(attributes)
Hide creator fields from public api by default (#8052) * Add model option to hide/show creators fields in public API response Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Add content-types util, rework sanitize-entity's private handling Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Update search e2e tests, fix an issue on empty search for the core-api controller (find) Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Fix GraphQL plugin (handle privates attributes on typeDefs + resolver builds) Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Fix sanitizeEntity import Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Move doc update from beta to stable Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Fix e2e test Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Fix pr comments Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Remove creator's field from upload controller routes Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Fix typedef build for graphql association Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Fix pr (comments + several issues) Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Add tests for search behavior in content-manager Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Rename files variables to meaningful names (upload controllers) Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Fix test with search id matching serialNumber Signed-off-by: Alexandre Bodin <bodin.alex@gmail.com> * Add toHasBeenCalledWith check for config.get (utils/content-types.test.js) Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> Co-authored-by: Alexandre Bodin <bodin.alex@gmail.com>
2020-10-01 17:47:08 +02:00
.filter(isNotPrivate(model))
GraphQL Plugin (bug): Content type's attributes marked as private are being exposed in the exported GraphQL schema (#9805) * Fixes #9804 * Fixes isPrivateAttribute function in strapi utils to take into account the private property within an attribute * removing comment * removing isNotPrivate function + adding call to already existing isPrivateAttribute function instead of isNotPrivate * isPrivateAttribute fixed based on coments from Alexandre and confirmed that the model does indeed contain all private fields in an array at startup time * removing isNotPrivate function and fixing generateEnumDefinitions to use the already available isPrivateAttribute function * checking if created_at and update_at are also marked as private and if this is the case then hiding them * allowIds by default when generating the input model to see if the tests will pass * moving allowIds back to false by default * adding isTypeAttributeEnabled function to check if a type attribute has been disabled and remove it from the exported graphql schema * removing unused var * only using isTypeAttributeEnabled instead of isPrivateAttribute. * adding isPrivateAttribute at association level * Adding isNotPrivate back together with isTypeAttributeEnabled * adding unit test for removing a disabled attribute from a graphql schema * renaming person to player in the test model * Deleting the file as it has been renamed in the strapi master branch * fixing lint issues * fixing some lint issues * adding isTypeAttributeEnabled support * adding enumeration field in graphql * adding use strict; to test file * fixing test checking that disabled attributes in graphql are removed from the graphql schema * adding test for buidShadowCrud * Update packages/strapi-plugin-graphql/services/type-builder.js Co-authored-by: Jean-Sébastien Herbaux <jean-sebastien.herbaux@epitech.eu> Co-authored-by: Rmaroun <rmaroun@outlook.com> Co-authored-by: Jean-Sébastien Herbaux <jean-sebastien.herbaux@epitech.eu>
2021-05-10 09:35:07 +02:00
.filter(attributeName => isTypeAttributeEnabled(model, attributeName))
.forEach(attributeName => {
const attribute = attributes[attributeName];
// Convert our type to the GraphQL type.
typeDef[attributeName] = types.convertType({
attribute,
modelName: globalId,
attributeName,
});
});
// Change field definition for collection relations
associations
.filter(association => association.type === 'collection')
Hide creator fields from public api by default (#8052) * Add model option to hide/show creators fields in public API response Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Add content-types util, rework sanitize-entity's private handling Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Update search e2e tests, fix an issue on empty search for the core-api controller (find) Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Fix GraphQL plugin (handle privates attributes on typeDefs + resolver builds) Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Fix sanitizeEntity import Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Move doc update from beta to stable Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Fix e2e test Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Fix pr comments Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Remove creator's field from upload controller routes Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Fix typedef build for graphql association Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Fix pr (comments + several issues) Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Add tests for search behavior in content-manager Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Rename files variables to meaningful names (upload controllers) Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Fix test with search id matching serialNumber Signed-off-by: Alexandre Bodin <bodin.alex@gmail.com> * Add toHasBeenCalledWith check for config.get (utils/content-types.test.js) Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> Co-authored-by: Alexandre Bodin <bodin.alex@gmail.com>
2020-10-01 17:47:08 +02:00
.filter(association => isNotPrivate(model, association.alias))
GraphQL Plugin (bug): Content type's attributes marked as private are being exposed in the exported GraphQL schema (#9805) * Fixes #9804 * Fixes isPrivateAttribute function in strapi utils to take into account the private property within an attribute * removing comment * removing isNotPrivate function + adding call to already existing isPrivateAttribute function instead of isNotPrivate * isPrivateAttribute fixed based on coments from Alexandre and confirmed that the model does indeed contain all private fields in an array at startup time * removing isNotPrivate function and fixing generateEnumDefinitions to use the already available isPrivateAttribute function * checking if created_at and update_at are also marked as private and if this is the case then hiding them * allowIds by default when generating the input model to see if the tests will pass * moving allowIds back to false by default * adding isTypeAttributeEnabled function to check if a type attribute has been disabled and remove it from the exported graphql schema * removing unused var * only using isTypeAttributeEnabled instead of isPrivateAttribute. * adding isPrivateAttribute at association level * Adding isNotPrivate back together with isTypeAttributeEnabled * adding unit test for removing a disabled attribute from a graphql schema * renaming person to player in the test model * Deleting the file as it has been renamed in the strapi master branch * fixing lint issues * fixing some lint issues * adding isTypeAttributeEnabled support * adding enumeration field in graphql * adding use strict; to test file * fixing test checking that disabled attributes in graphql are removed from the graphql schema * adding test for buidShadowCrud * Update packages/strapi-plugin-graphql/services/type-builder.js Co-authored-by: Jean-Sébastien Herbaux <jean-sebastien.herbaux@epitech.eu> Co-authored-by: Rmaroun <rmaroun@outlook.com> Co-authored-by: Jean-Sébastien Herbaux <jean-sebastien.herbaux@epitech.eu>
2021-05-10 09:35:07 +02:00
.filter(attributeName => isTypeAttributeEnabled(model, attributeName))
.forEach(association => {
typeDef[`${association.alias}(sort: String, limit: Int, start: Int, where: JSON)`] =
typeDef[association.alias];
delete typeDef[association.alias];
});
return typeDef;
2019-07-18 10:55:13 +02:00
};
GraphQL Plugin (bug): Content type's attributes marked as private are being exposed in the exported GraphQL schema (#9805) * Fixes #9804 * Fixes isPrivateAttribute function in strapi utils to take into account the private property within an attribute * removing comment * removing isNotPrivate function + adding call to already existing isPrivateAttribute function instead of isNotPrivate * isPrivateAttribute fixed based on coments from Alexandre and confirmed that the model does indeed contain all private fields in an array at startup time * removing isNotPrivate function and fixing generateEnumDefinitions to use the already available isPrivateAttribute function * checking if created_at and update_at are also marked as private and if this is the case then hiding them * allowIds by default when generating the input model to see if the tests will pass * moving allowIds back to false by default * adding isTypeAttributeEnabled function to check if a type attribute has been disabled and remove it from the exported graphql schema * removing unused var * only using isTypeAttributeEnabled instead of isPrivateAttribute. * adding isPrivateAttribute at association level * Adding isNotPrivate back together with isTypeAttributeEnabled * adding unit test for removing a disabled attribute from a graphql schema * renaming person to player in the test model * Deleting the file as it has been renamed in the strapi master branch * fixing lint issues * fixing some lint issues * adding isTypeAttributeEnabled support * adding enumeration field in graphql * adding use strict; to test file * fixing test checking that disabled attributes in graphql are removed from the graphql schema * adding test for buidShadowCrud * Update packages/strapi-plugin-graphql/services/type-builder.js Co-authored-by: Jean-Sébastien Herbaux <jean-sebastien.herbaux@epitech.eu> Co-authored-by: Rmaroun <rmaroun@outlook.com> Co-authored-by: Jean-Sébastien Herbaux <jean-sebastien.herbaux@epitech.eu>
2021-05-10 09:35:07 +02:00
const generateEnumDefinitions = (model, globalId) => {
const { attributes } = model;
2019-07-18 10:55:13 +02:00
return Object.keys(attributes)
.filter(attribute => attributes[attribute].type === 'enumeration')
GraphQL Plugin (bug): Content type's attributes marked as private are being exposed in the exported GraphQL schema (#9805) * Fixes #9804 * Fixes isPrivateAttribute function in strapi utils to take into account the private property within an attribute * removing comment * removing isNotPrivate function + adding call to already existing isPrivateAttribute function instead of isNotPrivate * isPrivateAttribute fixed based on coments from Alexandre and confirmed that the model does indeed contain all private fields in an array at startup time * removing isNotPrivate function and fixing generateEnumDefinitions to use the already available isPrivateAttribute function * checking if created_at and update_at are also marked as private and if this is the case then hiding them * allowIds by default when generating the input model to see if the tests will pass * moving allowIds back to false by default * adding isTypeAttributeEnabled function to check if a type attribute has been disabled and remove it from the exported graphql schema * removing unused var * only using isTypeAttributeEnabled instead of isPrivateAttribute. * adding isPrivateAttribute at association level * Adding isNotPrivate back together with isTypeAttributeEnabled * adding unit test for removing a disabled attribute from a graphql schema * renaming person to player in the test model * Deleting the file as it has been renamed in the strapi master branch * fixing lint issues * fixing some lint issues * adding isTypeAttributeEnabled support * adding enumeration field in graphql * adding use strict; to test file * fixing test checking that disabled attributes in graphql are removed from the graphql schema * adding test for buidShadowCrud * Update packages/strapi-plugin-graphql/services/type-builder.js Co-authored-by: Jean-Sébastien Herbaux <jean-sebastien.herbaux@epitech.eu> Co-authored-by: Rmaroun <rmaroun@outlook.com> Co-authored-by: Jean-Sébastien Herbaux <jean-sebastien.herbaux@epitech.eu>
2021-05-10 09:35:07 +02:00
.filter(attribute => isTypeAttributeEnabled(model, attribute))
.map(attribute => {
const definition = attributes[attribute];
const name = types.convertEnumType(definition, globalId, attribute);
2019-07-18 10:55:13 +02:00
const values = definition.enum.map(v => `\t${v}`).join('\n');
return `enum ${name} {\n${values}\n}\n`;
})
2019-07-18 10:55:13 +02:00
.join('');
};
2019-11-28 15:33:37 +01:00
const generateDynamicZoneDefinitions = (attributes, globalId, schema) => {
Object.keys(attributes)
.filter(attribute => attributes[attribute].type === 'dynamiczone')
.forEach(attribute => {
const { components } = attributes[attribute];
const typeName = `${globalId}${_.upperFirst(_.camelCase(attribute))}DynamicZone`;
2019-11-28 15:33:37 +01:00
if (components.length === 0) {
// Create dummy type because graphql doesn't support empty ones
2019-11-28 16:17:46 +01:00
schema.definition += `type ${typeName} { _:Boolean}`;
} else {
const componentsTypeNames = components.map(componentUID => {
const compo = strapi.components[componentUID];
if (!compo) {
throw new Error(
`Trying to creating dynamiczone type with unkown component ${componentUID}`
);
}
return compo.globalId;
});
const unionType = `union ${typeName} = ${componentsTypeNames.join(' | ')}`;
2019-11-28 16:17:46 +01:00
schema.definition += `\n${unionType}\n`;
}
2019-11-28 15:33:37 +01:00
const inputTypeName = `${typeName}Input`;
2019-11-28 16:17:46 +01:00
schema.definition += `\nscalar ${inputTypeName}\n`;
2019-11-28 15:33:37 +01:00
schema.resolvers[typeName] = {
__resolveType(obj) {
return strapi.components[obj.__component].globalId;
},
};
2019-11-28 16:10:19 +01:00
schema.resolvers[inputTypeName] = new DynamicZoneScalar({
2019-11-28 15:33:37 +01:00
name: inputTypeName,
2019-11-28 16:10:19 +01:00
attribute,
globalId,
components,
2019-11-28 15:33:37 +01:00
});
});
};
const initQueryOptions = (targetModel, parent) => {
if (hasDraftAndPublish(targetModel)) {
return {
_publicationState: _.get(parent, [OPTIONS, 'publicationState'], DP_PUB_STATE_LIVE),
};
}
return {};
};
2019-12-10 16:21:21 +01:00
const buildAssocResolvers = model => {
2019-07-18 10:55:13 +02:00
const { primaryKey, associations = [] } = model;
return associations
Hide creator fields from public api by default (#8052) * Add model option to hide/show creators fields in public API response Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Add content-types util, rework sanitize-entity's private handling Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Update search e2e tests, fix an issue on empty search for the core-api controller (find) Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Fix GraphQL plugin (handle privates attributes on typeDefs + resolver builds) Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Fix sanitizeEntity import Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Move doc update from beta to stable Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Fix e2e test Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Fix pr comments Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Remove creator's field from upload controller routes Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Fix typedef build for graphql association Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Fix pr (comments + several issues) Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Add tests for search behavior in content-manager Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Rename files variables to meaningful names (upload controllers) Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Fix test with search id matching serialNumber Signed-off-by: Alexandre Bodin <bodin.alex@gmail.com> * Add toHasBeenCalledWith check for config.get (utils/content-types.test.js) Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> Co-authored-by: Alexandre Bodin <bodin.alex@gmail.com>
2020-10-01 17:47:08 +02:00
.filter(association => isNotPrivate(model, association.alias))
GraphQL Plugin (bug): Content type's attributes marked as private are being exposed in the exported GraphQL schema (#9805) * Fixes #9804 * Fixes isPrivateAttribute function in strapi utils to take into account the private property within an attribute * removing comment * removing isNotPrivate function + adding call to already existing isPrivateAttribute function instead of isNotPrivate * isPrivateAttribute fixed based on coments from Alexandre and confirmed that the model does indeed contain all private fields in an array at startup time * removing isNotPrivate function and fixing generateEnumDefinitions to use the already available isPrivateAttribute function * checking if created_at and update_at are also marked as private and if this is the case then hiding them * allowIds by default when generating the input model to see if the tests will pass * moving allowIds back to false by default * adding isTypeAttributeEnabled function to check if a type attribute has been disabled and remove it from the exported graphql schema * removing unused var * only using isTypeAttributeEnabled instead of isPrivateAttribute. * adding isPrivateAttribute at association level * Adding isNotPrivate back together with isTypeAttributeEnabled * adding unit test for removing a disabled attribute from a graphql schema * renaming person to player in the test model * Deleting the file as it has been renamed in the strapi master branch * fixing lint issues * fixing some lint issues * adding isTypeAttributeEnabled support * adding enumeration field in graphql * adding use strict; to test file * fixing test checking that disabled attributes in graphql are removed from the graphql schema * adding test for buidShadowCrud * Update packages/strapi-plugin-graphql/services/type-builder.js Co-authored-by: Jean-Sébastien Herbaux <jean-sebastien.herbaux@epitech.eu> Co-authored-by: Rmaroun <rmaroun@outlook.com> Co-authored-by: Jean-Sébastien Herbaux <jean-sebastien.herbaux@epitech.eu>
2021-05-10 09:35:07 +02:00
.filter(association => isTypeAttributeEnabled(model, association.alias))
2019-11-28 15:33:37 +01:00
.reduce((resolver, association) => {
2019-12-10 16:21:21 +01:00
const target = association.model || association.collection;
const targetModel = strapi.getModel(target, association.plugin);
const { nature, alias } = association;
switch (nature) {
2019-12-10 16:21:21 +01:00
case 'oneToManyMorph':
case 'manyMorphToOne':
case 'manyMorphToMany':
case 'manyToManyMorph': {
resolver[alias] = async obj => {
if (obj[alias]) {
return assignOptions(obj[alias], obj);
2019-12-10 16:21:21 +01:00
}
const params = {
...initQueryOptions(targetModel, obj),
id: obj[primaryKey],
};
const entry = await strapi.query(model.uid).findOne(params, [alias]);
return assignOptions(entry[alias], obj);
};
2019-11-28 15:33:37 +01:00
break;
}
default: {
resolver[alias] = async (obj, options) => {
// force component relations to be refetched
if (model.modelType === 'component') {
obj[alias] = _.get(obj[alias], targetModel.primaryKey, obj[alias]);
}
const loader = strapi.plugins.graphql.services['data-loaders'].loaders[targetModel.uid];
const localId = obj[model.primaryKey];
const targetPK = targetModel.primaryKey;
const foreignId = _.get(obj[alias], targetModel.primaryKey, obj[alias]);
2019-11-28 15:33:37 +01:00
const params = {
...initQueryOptions(targetModel, obj),
...convertToParams(_.omit(amountLimiting(options), 'where')),
...convertToQuery(options.where),
2019-11-28 15:33:37 +01:00
};
2019-07-18 10:55:13 +02:00
if (['oneToOne', 'oneWay', 'manyToOne'].includes(nature)) {
if (!_.has(obj, alias) || _.isNil(foreignId)) {
return null;
}
2021-01-05 09:38:12 +01:00
// check this is an entity and not a mongo ID
if (_.has(obj[alias], targetPK)) {
return assignOptions(obj[alias], obj);
}
const query = {
single: true,
filters: {
...params,
[targetPK]: foreignId,
},
2019-11-28 15:33:37 +01:00
};
return loader.load(query).then(r => assignOptions(r, obj));
}
if (
nature === 'oneToMany' ||
(nature === 'manyToMany' && association.dominant !== true)
) {
const { via } = association;
const filters = {
...params,
[via]: localId,
};
return loader.load({ filters }).then(r => assignOptions(r, obj));
}
if (
nature === 'manyWay' ||
(nature === 'manyToMany' && association.dominant === true)
) {
let targetIds = [];
// find the related ids to query them and apply the filters
if (Array.isArray(obj[alias])) {
targetIds = obj[alias].map(value => value[targetPK] || value);
2019-11-28 15:33:37 +01:00
} else {
const entry = await strapi
.query(model.uid)
.findOne({ [primaryKey]: obj[primaryKey] }, [alias]);
if (_.isEmpty(entry[alias])) {
return [];
}
targetIds = entry[alias].map(el => el[targetPK]);
2019-11-28 15:33:37 +01:00
}
const filters = {
...params,
[`${targetPK}_in`]: targetIds.map(_.toString),
};
return loader.load({ filters }).then(r => assignOptions(r, obj));
}
2019-11-28 15:33:37 +01:00
};
break;
}
2019-07-18 10:55:13 +02:00
}
2019-11-28 15:33:37 +01:00
return resolver;
}, {});
2019-07-18 10:55:13 +02:00
};
/**
* Construct the GraphQL query & definition and apply the right resolvers.
*
* @return Object
*/
const buildModels = (models, ctx) => {
return models.map(model => {
const { kind, modelType } = model;
if (modelType === 'component') {
2021-04-09 10:46:10 +02:00
return buildComponent(model);
}
switch (kind) {
case 'singleType':
return buildSingleType(model, ctx);
default:
return buildCollectionType(model, ctx);
}
});
};
const buildModelDefinition = (model, globalType = {}) => {
const { globalId, primaryKey } = model;
const typeDefObj = buildTypeDefObj(model);
const schema = {
definition: '',
query: {},
mutation: {},
resolvers: {
Query: {},
Mutation: {},
[globalId]: {
id: parent => parent[primaryKey] || parent.id,
...buildAssocResolvers(model),
},
},
typeDefObj,
};
GraphQL Plugin (bug): Content type's attributes marked as private are being exposed in the exported GraphQL schema (#9805) * Fixes #9804 * Fixes isPrivateAttribute function in strapi utils to take into account the private property within an attribute * removing comment * removing isNotPrivate function + adding call to already existing isPrivateAttribute function instead of isNotPrivate * isPrivateAttribute fixed based on coments from Alexandre and confirmed that the model does indeed contain all private fields in an array at startup time * removing isNotPrivate function and fixing generateEnumDefinitions to use the already available isPrivateAttribute function * checking if created_at and update_at are also marked as private and if this is the case then hiding them * allowIds by default when generating the input model to see if the tests will pass * moving allowIds back to false by default * adding isTypeAttributeEnabled function to check if a type attribute has been disabled and remove it from the exported graphql schema * removing unused var * only using isTypeAttributeEnabled instead of isPrivateAttribute. * adding isPrivateAttribute at association level * Adding isNotPrivate back together with isTypeAttributeEnabled * adding unit test for removing a disabled attribute from a graphql schema * renaming person to player in the test model * Deleting the file as it has been renamed in the strapi master branch * fixing lint issues * fixing some lint issues * adding isTypeAttributeEnabled support * adding enumeration field in graphql * adding use strict; to test file * fixing test checking that disabled attributes in graphql are removed from the graphql schema * adding test for buidShadowCrud * Update packages/strapi-plugin-graphql/services/type-builder.js Co-authored-by: Jean-Sébastien Herbaux <jean-sebastien.herbaux@epitech.eu> Co-authored-by: Rmaroun <rmaroun@outlook.com> Co-authored-by: Jean-Sébastien Herbaux <jean-sebastien.herbaux@epitech.eu>
2021-05-10 09:35:07 +02:00
schema.definition += generateEnumDefinitions(model, globalId);
generateDynamicZoneDefinitions(model.attributes, globalId, schema);
const description = getTypeDescription(globalType, model);
const fields = toSDL(typeDefObj, globalType, model);
const typeDef = `${description}type ${globalId} {${fields}}\n`;
schema.definition += typeDef;
return schema;
};
const buildComponent = component => {
const { globalId } = component;
const schema = buildModelDefinition(component);
schema.definition += types.generateInputModel(component, globalId, {
allowIds: true,
});
return schema;
};
const buildSingleType = (model, ctx) => {
const { uid, modelName } = model;
const singularName = toSingular(modelName);
const globalType = _.get(ctx.schema, `type.${model.globalId}`, {});
const localSchema = buildModelDefinition(model, globalType);
// Add definition to the schema but this type won't be "queriable" or "mutable".
if (globalType === false) {
return localSchema;
}
if (isQueryEnabled(ctx.schema, singularName)) {
const resolverOpts = {
resolver: `${uid}.find`,
...getQueryInfo(ctx.schema, singularName),
};
const resolver = buildQuery(singularName, resolverOpts);
const query = {
query: {
[singularName]: {
args: {
publicationState: 'PublicationState',
...(resolverOpts.args || {}),
},
type: model.globalId,
},
},
resolvers: {
Query: {
[singularName]: wrapPublicationStateResolver(resolver),
},
},
};
_.merge(localSchema, query);
}
// Add model Input definition.
localSchema.definition += types.generateInputModel(model, modelName);
// build every mutation
['update', 'delete'].forEach(action => {
const mutationSchema = buildMutationTypeDef({ model, action }, ctx);
mergeSchemas(localSchema, mutationSchema);
});
return localSchema;
};
const buildCollectionType = (model, ctx) => {
const { plugin, modelName, uid } = model;
const singularName = toSingular(modelName);
const pluralName = toPlural(modelName);
const globalType = _.get(ctx.schema, `type.${model.globalId}`, {});
const localSchema = buildModelDefinition(model, globalType);
const { typeDefObj } = localSchema;
// Add definition to the schema but this type won't be "queriable" or "mutable".
if (globalType === false) {
return localSchema;
}
if (isQueryEnabled(ctx.schema, singularName)) {
const resolverOpts = {
resolver: `${uid}.findOne`,
...getQueryInfo(ctx.schema, singularName),
};
if (actionExists(resolverOpts)) {
const resolver = buildQuery(singularName, resolverOpts);
const query = {
query: {
[singularName]: {
args: {
...FIND_ONE_QUERY_ARGUMENTS,
...(resolverOpts.args || {}),
},
type: model.globalId,
},
},
resolvers: {
Query: {
[singularName]: wrapPublicationStateResolver(resolver),
},
},
};
_.merge(localSchema, query);
}
}
if (isQueryEnabled(ctx.schema, pluralName)) {
const resolverOpts = {
resolver: `${uid}.find`,
...getQueryInfo(ctx.schema, pluralName),
};
if (actionExists(resolverOpts)) {
const resolver = buildQuery(pluralName, resolverOpts);
const query = {
query: {
[pluralName]: {
args: {
...FIND_QUERY_ARGUMENTS,
...(resolverOpts.args || {}),
},
type: `[${model.globalId}]`,
},
},
resolvers: {
Query: {
[pluralName]: wrapPublicationStateResolver(resolver),
},
},
};
_.merge(localSchema, query);
if (isQueryEnabled(ctx.schema, `${pluralName}Connection`)) {
// Generate the aggregation for the given model
const aggregationSchema = formatModelConnectionsGQL({
fields: typeDefObj,
model,
name: modelName,
resolver: resolverOpts,
plugin,
});
mergeSchemas(localSchema, aggregationSchema);
}
}
}
// Add model Input definition.
localSchema.definition += types.generateInputModel(model, modelName);
// build every mutation
['create', 'update', 'delete'].forEach(action => {
const mutationSchema = buildMutationTypeDef({ model, action }, ctx);
mergeSchemas(localSchema, mutationSchema);
});
return localSchema;
};
// TODO:
// - Implement batch methods (need to update the content-manager as well).
// - Implement nested transactional methods (create/update).
const buildMutationTypeDef = ({ model, action }, ctx) => {
const capitalizedName = _.upperFirst(toSingular(model.modelName));
const mutationName = `${action}${capitalizedName}`;
const resolverOpts = {
resolver: `${model.uid}.${action}`,
transformOutput: result => ({ [toSingular(model.modelName)]: result }),
...getMutationInfo(ctx.schema, mutationName),
2021-04-14 17:14:45 +02:00
isShadowCrud: true,
};
if (!actionExists(resolverOpts)) {
return {};
}
const definition = types.generateInputPayloadArguments({
model,
name: model.modelName,
mutationName,
action,
});
// ignore if disabled
if (!isMutationEnabled(ctx.schema, mutationName)) {
return {
definition,
};
}
const { kind } = model;
const args = {};
if (kind !== 'singleType' || action !== 'delete') {
Object.assign(args, {
input: `${mutationName}Input`,
});
}
return {
definition,
mutation: {
[mutationName]: {
args: {
...args,
...(resolverOpts.args || {}),
},
type: `${mutationName}Payload`,
},
},
resolvers: {
Mutation: {
[mutationName]: buildMutation(mutationName, resolverOpts),
},
},
};
};
module.exports = buildShadowCrud;