2018-09-10 16:05:00 +08:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* GraphQL.js service
|
|
|
|
*
|
|
|
|
* @description: A set of functions similar to controller's actions to avoid code duplication.
|
|
|
|
*/
|
|
|
|
|
|
|
|
const _ = require('lodash');
|
2019-11-04 11:29:19 +01:00
|
|
|
|
2018-09-10 16:05:00 +08:00
|
|
|
const Aggregator = require('./Aggregator');
|
|
|
|
const Query = require('./Query.js');
|
|
|
|
const Mutation = require('./Mutation.js');
|
|
|
|
const Types = require('./Types.js');
|
|
|
|
const Schema = require('./Schema.js');
|
2019-11-04 11:29:19 +01:00
|
|
|
const { toSingular, toPlural } = require('./naming');
|
2018-09-10 16:05:00 +08:00
|
|
|
|
2019-07-18 10:55:13 +02:00
|
|
|
const convertAttributes = (attributes, globalId) => {
|
|
|
|
return Object.keys(attributes)
|
2019-07-17 15:45:26 +02:00
|
|
|
.filter(attribute => attributes[attribute].private !== true)
|
|
|
|
.reduce((acc, attribute) => {
|
|
|
|
// Convert our type to the GraphQL type.
|
|
|
|
acc[attribute] = Types.convertType({
|
|
|
|
definition: attributes[attribute],
|
|
|
|
modelName: globalId,
|
|
|
|
attributeName: attribute,
|
|
|
|
});
|
|
|
|
return acc;
|
|
|
|
}, {});
|
2019-07-18 10:55:13 +02:00
|
|
|
};
|
2019-07-17 15:45:26 +02:00
|
|
|
|
2019-07-18 10:55:13 +02:00
|
|
|
const generateEnumDefinitions = (attributes, globalId) => {
|
|
|
|
return Object.keys(attributes)
|
2019-07-17 15:45:26 +02:00
|
|
|
.filter(attribute => attributes[attribute].type === 'enumeration')
|
|
|
|
.map(attribute => {
|
|
|
|
const definition = attributes[attribute];
|
|
|
|
|
2019-07-18 10:55:13 +02:00
|
|
|
const name = Types.convertEnumType(definition, globalId, attribute);
|
|
|
|
const values = definition.enum.map(v => `\t${v}`).join('\n');
|
|
|
|
return `enum ${name} {\n${values}\n}\n`;
|
2019-07-17 15:45:26 +02:00
|
|
|
})
|
2019-07-18 10:55:13 +02:00
|
|
|
.join('');
|
|
|
|
};
|
2019-07-17 15:45:26 +02:00
|
|
|
|
2019-10-01 17:45:16 +02:00
|
|
|
const mutateAssocAttributes = (associations = [], attributes) => {
|
2019-07-18 10:55:13 +02:00
|
|
|
associations
|
2019-07-17 15:45:26 +02:00
|
|
|
.filter(association => association.type === 'collection')
|
|
|
|
.forEach(association => {
|
2019-07-18 10:55:13 +02:00
|
|
|
attributes[
|
2019-07-17 15:45:26 +02:00
|
|
|
`${association.alias}(sort: String, limit: Int, start: Int, where: JSON)`
|
|
|
|
] = attributes[association.alias];
|
|
|
|
|
2019-07-18 10:55:13 +02:00
|
|
|
delete attributes[association.alias];
|
2019-07-17 15:45:26 +02:00
|
|
|
});
|
2019-07-18 10:55:13 +02:00
|
|
|
};
|
2019-07-17 15:45:26 +02:00
|
|
|
|
2019-07-18 10:55:13 +02:00
|
|
|
const buildAssocResolvers = (model, name, { plugin }) => {
|
|
|
|
const contentManager =
|
|
|
|
strapi.plugins['content-manager'].services['contentmanager'];
|
2019-07-17 15:45:26 +02:00
|
|
|
|
2019-07-18 10:55:13 +02:00
|
|
|
const { primaryKey, associations = [] } = model;
|
2019-07-17 15:45:26 +02:00
|
|
|
|
2019-11-07 09:19:35 +01:00
|
|
|
return associations
|
|
|
|
.filter(association => model.attributes[association.alias].private !== true)
|
|
|
|
.reduce((resolver, association) => {
|
2019-07-17 15:45:26 +02:00
|
|
|
switch (association.nature) {
|
2019-07-18 10:55:13 +02:00
|
|
|
case 'oneToManyMorph': {
|
|
|
|
resolver[association.alias] = async obj => {
|
|
|
|
const entry = await contentManager.fetch(
|
|
|
|
{
|
|
|
|
id: obj[primaryKey],
|
|
|
|
model: name,
|
|
|
|
},
|
|
|
|
plugin,
|
|
|
|
[association.alias]
|
|
|
|
);
|
|
|
|
|
|
|
|
// Set the _type only when the value is defined
|
|
|
|
if (entry[association.alias]) {
|
|
|
|
entry[association.alias]._type = _.upperFirst(association.model);
|
|
|
|
}
|
|
|
|
|
|
|
|
return entry[association.alias];
|
|
|
|
};
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'manyMorphToOne':
|
|
|
|
case 'manyMorphToMany':
|
|
|
|
case 'manyToManyMorph': {
|
|
|
|
resolver[association.alias] = async obj => {
|
|
|
|
// eslint-disable-line no-unused-vars
|
|
|
|
const [withRelated, withoutRelated] = await Promise.all([
|
|
|
|
contentManager.fetch(
|
2019-07-17 15:45:26 +02:00
|
|
|
{
|
2019-07-18 10:55:13 +02:00
|
|
|
id: obj[primaryKey],
|
2019-07-17 15:45:26 +02:00
|
|
|
model: name,
|
|
|
|
},
|
|
|
|
plugin,
|
|
|
|
[association.alias],
|
|
|
|
false
|
2019-07-18 10:55:13 +02:00
|
|
|
),
|
|
|
|
contentManager.fetch(
|
|
|
|
{
|
|
|
|
id: obj[primaryKey],
|
|
|
|
model: name,
|
|
|
|
},
|
|
|
|
plugin,
|
|
|
|
[]
|
|
|
|
),
|
|
|
|
]);
|
|
|
|
|
|
|
|
const entry =
|
|
|
|
withRelated && withRelated.toJSON
|
|
|
|
? withRelated.toJSON()
|
|
|
|
: withRelated;
|
|
|
|
|
|
|
|
entry[association.alias].map((entry, index) => {
|
|
|
|
const type =
|
|
|
|
_.get(withoutRelated, `${association.alias}.${index}.kind`) ||
|
|
|
|
_.upperFirst(
|
|
|
|
_.camelCase(
|
|
|
|
_.get(
|
|
|
|
withoutRelated,
|
|
|
|
`${association.alias}.${index}.${association.alias}_type`
|
2019-07-17 15:45:26 +02:00
|
|
|
)
|
2019-07-18 10:55:13 +02:00
|
|
|
)
|
|
|
|
) ||
|
|
|
|
_.upperFirst(_.camelCase(association[association.type]));
|
2019-07-17 15:45:26 +02:00
|
|
|
|
2019-07-18 10:55:13 +02:00
|
|
|
entry._type = type;
|
2019-07-17 15:45:26 +02:00
|
|
|
|
2019-07-18 10:55:13 +02:00
|
|
|
return entry;
|
|
|
|
});
|
2019-07-17 15:45:26 +02:00
|
|
|
|
2019-07-18 10:55:13 +02:00
|
|
|
return entry[association.alias];
|
2019-07-17 15:45:26 +02:00
|
|
|
};
|
2019-07-18 10:55:13 +02:00
|
|
|
break;
|
|
|
|
}
|
2019-07-17 15:45:26 +02:00
|
|
|
|
2019-07-18 10:55:13 +02:00
|
|
|
default: {
|
|
|
|
resolver[association.alias] = async (obj, options) => {
|
|
|
|
// Construct parameters object to retrieve the correct related entries.
|
|
|
|
const params = {
|
|
|
|
model: association.model || association.collection,
|
|
|
|
};
|
2019-07-17 15:45:26 +02:00
|
|
|
|
2019-07-18 10:55:13 +02:00
|
|
|
let queryOpts = {
|
|
|
|
source: association.plugin,
|
2019-07-17 15:45:26 +02:00
|
|
|
};
|
|
|
|
|
2019-07-18 10:55:13 +02:00
|
|
|
// Get refering model.
|
|
|
|
const ref = association.plugin
|
|
|
|
? strapi.plugins[association.plugin].models[params.model]
|
|
|
|
: strapi.models[params.model];
|
|
|
|
|
|
|
|
if (association.type === 'model') {
|
|
|
|
params[ref.primaryKey] = _.get(
|
|
|
|
obj,
|
|
|
|
[association.alias, ref.primaryKey],
|
|
|
|
obj[association.alias]
|
2019-07-17 15:45:26 +02:00
|
|
|
);
|
|
|
|
} else {
|
2019-07-18 10:55:13 +02:00
|
|
|
const queryParams = Query.amountLimiting(options);
|
|
|
|
queryOpts = {
|
|
|
|
...queryOpts,
|
|
|
|
...Query.convertToParams(_.omit(queryParams, 'where')), // Convert filters (sort, limit and start/skip)
|
|
|
|
...Query.convertToQuery(queryParams.where),
|
|
|
|
};
|
|
|
|
|
|
|
|
if (
|
2019-10-21 09:39:56 +02:00
|
|
|
((association.nature === 'manyToMany' && association.dominant) ||
|
|
|
|
association.nature === 'manyWay') &&
|
|
|
|
_.has(obj, association.alias) // if populated
|
2019-07-18 10:55:13 +02:00
|
|
|
) {
|
|
|
|
_.set(
|
|
|
|
queryOpts,
|
|
|
|
['query', ref.primaryKey],
|
2019-10-01 17:56:52 +02:00
|
|
|
obj[association.alias]
|
2019-10-04 09:46:06 +02:00
|
|
|
? obj[association.alias]
|
|
|
|
.map(val => val[ref.primaryKey] || val)
|
|
|
|
.sort()
|
2019-10-01 17:56:52 +02:00
|
|
|
: []
|
2019-07-18 10:55:13 +02:00
|
|
|
);
|
|
|
|
} else {
|
|
|
|
_.set(queryOpts, ['query', association.via], obj[ref.primaryKey]);
|
|
|
|
}
|
2019-07-17 15:45:26 +02:00
|
|
|
}
|
|
|
|
|
2019-07-18 10:55:13 +02:00
|
|
|
const loaderName = association.plugin
|
|
|
|
? `${association.plugin}__${params.model}`
|
|
|
|
: params.model;
|
|
|
|
|
|
|
|
return association.model
|
|
|
|
? strapi.plugins.graphql.services.loaders.loaders[loaderName].load({
|
|
|
|
params,
|
|
|
|
options: queryOpts,
|
|
|
|
single: true,
|
|
|
|
})
|
|
|
|
: strapi.plugins.graphql.services.loaders.loaders[loaderName].load({
|
|
|
|
options: queryOpts,
|
|
|
|
association,
|
|
|
|
});
|
|
|
|
};
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return resolver;
|
|
|
|
}, {});
|
|
|
|
};
|
|
|
|
|
2019-10-22 18:01:03 +02:00
|
|
|
const buildModel = (model, name, { plugin, isComponent = false } = {}) => {
|
2019-07-18 10:55:13 +02:00
|
|
|
const { globalId, primaryKey } = model;
|
|
|
|
|
|
|
|
let definition = '';
|
|
|
|
const initialState = {
|
|
|
|
id: 'ID!',
|
|
|
|
[primaryKey]: 'ID!',
|
|
|
|
};
|
|
|
|
|
|
|
|
if (_.isArray(_.get(model, 'options.timestamps'))) {
|
|
|
|
const [createdAtKey, updatedAtKey] = model.options.timestamps;
|
|
|
|
initialState[createdAtKey] = 'DateTime!';
|
|
|
|
initialState[updatedAtKey] = 'DateTime!';
|
|
|
|
}
|
|
|
|
|
|
|
|
const attributes = convertAttributes(model.attributes, globalId);
|
2019-10-01 17:45:16 +02:00
|
|
|
mutateAssocAttributes(model.associations, attributes);
|
2019-07-18 10:55:13 +02:00
|
|
|
_.merge(attributes, initialState);
|
|
|
|
|
|
|
|
definition += generateEnumDefinitions(model.attributes, globalId);
|
|
|
|
|
|
|
|
const description = Schema.getDescription({}, model);
|
|
|
|
const fields = Schema.formatGQL(attributes, {}, model);
|
|
|
|
const typeDef = `${description}type ${globalId} {${fields}}\n`;
|
|
|
|
|
|
|
|
definition += typeDef;
|
|
|
|
definition += Types.generateInputModel(model, globalId, {
|
2019-10-22 18:01:03 +02:00
|
|
|
allowIds: isComponent,
|
2019-07-17 15:45:26 +02:00
|
|
|
});
|
|
|
|
|
2019-07-18 10:55:13 +02:00
|
|
|
const resolver = {
|
|
|
|
[globalId]: {
|
|
|
|
id: obj => obj[primaryKey],
|
|
|
|
...buildAssocResolvers(model, name, { plugin }),
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2019-07-17 15:45:26 +02:00
|
|
|
return {
|
|
|
|
definition,
|
|
|
|
resolver,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2019-03-25 16:37:46 +01:00
|
|
|
/**
|
|
|
|
* Construct the GraphQL query & definition and apply the right resolvers.
|
|
|
|
*
|
|
|
|
* @return Object
|
|
|
|
*/
|
|
|
|
|
|
|
|
const buildShadowCRUD = (models, plugin) => {
|
|
|
|
const initialState = {
|
|
|
|
definition: '',
|
|
|
|
query: {},
|
|
|
|
mutation: {},
|
|
|
|
resolver: { Query: {}, Mutation: {} },
|
|
|
|
};
|
2018-09-10 16:05:00 +08:00
|
|
|
|
2019-03-25 16:37:46 +01:00
|
|
|
if (_.isEmpty(models)) {
|
|
|
|
return initialState;
|
|
|
|
}
|
2018-09-10 16:05:00 +08:00
|
|
|
|
2019-07-17 15:45:26 +02:00
|
|
|
return Object.keys(models).reduce((acc, name) => {
|
|
|
|
const model = models[name];
|
2019-03-25 16:37:46 +01:00
|
|
|
|
2019-07-17 13:13:07 +02:00
|
|
|
const { globalId, primaryKey } = model;
|
2019-11-04 11:29:19 +01:00
|
|
|
|
2019-03-25 16:37:46 +01:00
|
|
|
// Setup initial state with default attribute that should be displayed
|
|
|
|
// but these attributes are not properly defined in the models.
|
2018-09-10 16:05:00 +08:00
|
|
|
const initialState = {
|
2019-07-17 13:13:07 +02:00
|
|
|
[primaryKey]: 'ID!',
|
2018-09-10 16:05:00 +08:00
|
|
|
};
|
|
|
|
|
2019-07-17 13:13:07 +02:00
|
|
|
// always add an id field to make the api database agnostic
|
|
|
|
if (primaryKey !== 'id') {
|
2019-03-25 16:37:46 +01:00
|
|
|
initialState['id'] = 'ID!';
|
2018-09-10 16:05:00 +08:00
|
|
|
}
|
|
|
|
|
2019-07-18 10:55:13 +02:00
|
|
|
acc.resolver[globalId] = {
|
|
|
|
// define the default id resolver
|
|
|
|
id(parent) {
|
|
|
|
return parent[model.primaryKey];
|
|
|
|
},
|
|
|
|
};
|
2018-09-10 16:05:00 +08:00
|
|
|
|
2019-03-25 16:37:46 +01:00
|
|
|
// Add timestamps attributes.
|
|
|
|
if (_.isArray(_.get(model, 'options.timestamps'))) {
|
2019-07-17 13:13:07 +02:00
|
|
|
const [createdAtKey, updatedAtKey] = model.options.timestamps;
|
|
|
|
initialState[createdAtKey] = 'DateTime!';
|
|
|
|
initialState[updatedAtKey] = 'DateTime!';
|
2019-03-25 16:37:46 +01:00
|
|
|
}
|
2018-09-10 16:05:00 +08:00
|
|
|
|
2019-07-17 13:13:07 +02:00
|
|
|
const _schema = _.cloneDeep(
|
|
|
|
_.get(strapi.plugins, 'graphql.config._schema.graphql', {})
|
|
|
|
);
|
|
|
|
|
2019-03-25 16:37:46 +01:00
|
|
|
const { type = {}, resolver = {} } = _schema;
|
|
|
|
|
|
|
|
// Convert our layer Model to the GraphQL DL.
|
2019-07-18 10:55:13 +02:00
|
|
|
const attributes = convertAttributes(model.attributes, globalId);
|
2019-10-01 17:45:16 +02:00
|
|
|
mutateAssocAttributes(model.associations, attributes);
|
2019-07-18 10:55:13 +02:00
|
|
|
_.merge(attributes, initialState);
|
2018-09-10 16:05:00 +08:00
|
|
|
|
2019-07-18 10:55:13 +02:00
|
|
|
acc.definition += generateEnumDefinitions(model.attributes, globalId);
|
2018-09-10 16:05:00 +08:00
|
|
|
|
2019-07-18 10:55:13 +02:00
|
|
|
const description = Schema.getDescription(type[globalId], model);
|
|
|
|
const fields = Schema.formatGQL(attributes, type[globalId], model);
|
|
|
|
const typeDef = `${description}type ${globalId} {${fields}}\n`;
|
|
|
|
|
|
|
|
acc.definition += typeDef;
|
2018-09-10 16:05:00 +08:00
|
|
|
|
2019-03-25 16:37:46 +01:00
|
|
|
// Add definition to the schema but this type won't be "queriable" or "mutable".
|
2019-07-09 11:24:11 +02:00
|
|
|
if (
|
|
|
|
type[model.globalId] === false ||
|
|
|
|
_.get(type, `${model.globalId}.enabled`) === false
|
|
|
|
) {
|
2019-03-25 16:37:46 +01:00
|
|
|
return acc;
|
|
|
|
}
|
2018-09-10 16:05:00 +08:00
|
|
|
|
2019-11-04 11:29:19 +01:00
|
|
|
const singularName = toSingular(name);
|
|
|
|
const pluralName = toPlural(name);
|
2019-03-25 16:37:46 +01:00
|
|
|
// Build resolvers.
|
|
|
|
const queries = {
|
|
|
|
singular:
|
|
|
|
_.get(resolver, `Query.${singularName}`) !== false
|
2019-09-11 21:55:26 +08:00
|
|
|
? Query.composeQueryResolver({
|
|
|
|
_schema,
|
|
|
|
plugin,
|
|
|
|
name,
|
|
|
|
isSingular: true,
|
|
|
|
})
|
2019-03-25 16:37:46 +01:00
|
|
|
: null,
|
|
|
|
plural:
|
|
|
|
_.get(resolver, `Query.${pluralName}`) !== false
|
2019-09-11 21:55:26 +08:00
|
|
|
? Query.composeQueryResolver({
|
|
|
|
_schema,
|
|
|
|
plugin,
|
|
|
|
name,
|
|
|
|
isSingular: false,
|
|
|
|
})
|
2019-03-25 16:37:46 +01:00
|
|
|
: null,
|
|
|
|
};
|
|
|
|
|
|
|
|
// check if errors
|
|
|
|
Object.keys(queries).forEach(type => {
|
|
|
|
// The query cannot be built.
|
|
|
|
if (_.isError(queries[type])) {
|
|
|
|
strapi.log.error(queries[type]);
|
|
|
|
strapi.stop();
|
2018-09-10 16:05:00 +08:00
|
|
|
}
|
2019-03-25 16:37:46 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
if (_.isFunction(queries.singular)) {
|
|
|
|
_.merge(acc, {
|
|
|
|
query: {
|
|
|
|
[`${singularName}(id: ID!)`]: model.globalId,
|
|
|
|
},
|
|
|
|
resolver: {
|
|
|
|
Query: {
|
|
|
|
[singularName]: queries.singular,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
2018-09-10 16:05:00 +08:00
|
|
|
|
2019-03-25 16:37:46 +01:00
|
|
|
if (_.isFunction(queries.plural)) {
|
|
|
|
_.merge(acc, {
|
|
|
|
query: {
|
2019-07-09 11:24:11 +02:00
|
|
|
[`${pluralName}(sort: String, limit: Int, start: Int, where: JSON)`]: `[${model.globalId}]`,
|
2019-03-25 16:37:46 +01:00
|
|
|
},
|
|
|
|
resolver: {
|
|
|
|
Query: {
|
|
|
|
[pluralName]: queries.plural,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO:
|
|
|
|
// - Implement batch methods (need to update the content-manager as well).
|
|
|
|
// - Implement nested transactional methods (create/update).
|
2019-11-04 11:29:19 +01:00
|
|
|
const capitalizedName = _.upperFirst(singularName);
|
2019-03-25 16:37:46 +01:00
|
|
|
const mutations = {
|
|
|
|
create:
|
|
|
|
_.get(resolver, `Mutation.create${capitalizedName}`) !== false
|
2019-09-11 21:55:26 +08:00
|
|
|
? Mutation.composeMutationResolver({
|
|
|
|
_schema,
|
|
|
|
plugin,
|
|
|
|
name,
|
|
|
|
action: 'create',
|
|
|
|
})
|
2019-03-25 16:37:46 +01:00
|
|
|
: null,
|
|
|
|
update:
|
|
|
|
_.get(resolver, `Mutation.update${capitalizedName}`) !== false
|
2019-09-11 21:55:26 +08:00
|
|
|
? Mutation.composeMutationResolver({
|
|
|
|
_schema,
|
|
|
|
plugin,
|
|
|
|
name,
|
|
|
|
action: 'update',
|
|
|
|
})
|
2019-03-25 16:37:46 +01:00
|
|
|
: null,
|
|
|
|
delete:
|
|
|
|
_.get(resolver, `Mutation.delete${capitalizedName}`) !== false
|
2019-09-11 21:55:26 +08:00
|
|
|
? Mutation.composeMutationResolver({
|
|
|
|
_schema,
|
|
|
|
plugin,
|
|
|
|
name,
|
|
|
|
action: 'delete',
|
|
|
|
})
|
2019-03-25 16:37:46 +01:00
|
|
|
: null,
|
|
|
|
};
|
|
|
|
|
|
|
|
// Add model Input definition.
|
|
|
|
acc.definition += Types.generateInputModel(model, name);
|
|
|
|
|
|
|
|
Object.keys(mutations).forEach(type => {
|
|
|
|
if (_.isFunction(mutations[type])) {
|
|
|
|
let mutationDefinition;
|
|
|
|
let mutationName = `${type}${capitalizedName}`;
|
2018-09-10 16:05:00 +08:00
|
|
|
|
2019-03-25 16:37:46 +01:00
|
|
|
// Generate the Input for this specific action.
|
2019-07-09 11:24:11 +02:00
|
|
|
acc.definition += Types.generateInputPayloadArguments(
|
|
|
|
model,
|
|
|
|
name,
|
|
|
|
type
|
|
|
|
);
|
2019-03-25 16:37:46 +01:00
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case 'create':
|
|
|
|
mutationDefinition = {
|
|
|
|
[`${mutationName}(input: ${mutationName}Input)`]: `${mutationName}Payload`,
|
|
|
|
};
|
|
|
|
|
|
|
|
break;
|
|
|
|
case 'update':
|
|
|
|
mutationDefinition = {
|
|
|
|
[`${mutationName}(input: ${mutationName}Input)`]: `${mutationName}Payload`,
|
|
|
|
};
|
|
|
|
|
|
|
|
break;
|
|
|
|
case 'delete':
|
|
|
|
mutationDefinition = {
|
|
|
|
[`${mutationName}(input: ${mutationName}Input)`]: `${mutationName}Payload`,
|
|
|
|
};
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// Nothing.
|
2018-09-10 16:05:00 +08:00
|
|
|
}
|
|
|
|
|
2019-03-25 16:37:46 +01:00
|
|
|
// Assign mutation definition to global definition.
|
2019-03-13 19:27:18 +01:00
|
|
|
_.merge(acc, {
|
2019-03-25 16:37:46 +01:00
|
|
|
mutation: mutationDefinition,
|
2019-03-13 19:27:18 +01:00
|
|
|
resolver: {
|
2019-03-25 16:37:46 +01:00
|
|
|
Mutation: {
|
|
|
|
[`${mutationName}`]: mutations[type],
|
2019-03-13 19:27:18 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
2019-03-25 16:37:46 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
// TODO:
|
|
|
|
// - Add support for Graphql Aggregation in Bookshelf ORM
|
|
|
|
if (model.orm === 'mongoose') {
|
|
|
|
// Generation the aggregation for the given model
|
|
|
|
const modelAggregator = Aggregator.formatModelConnectionsGQL(
|
|
|
|
attributes,
|
|
|
|
model,
|
|
|
|
name,
|
|
|
|
queries.plural
|
|
|
|
);
|
|
|
|
if (modelAggregator) {
|
|
|
|
acc.definition += modelAggregator.type;
|
|
|
|
if (!acc.resolver[modelAggregator.globalId]) {
|
|
|
|
acc.resolver[modelAggregator.globalId] = {};
|
|
|
|
}
|
2018-09-10 16:05:00 +08:00
|
|
|
|
2019-03-25 16:37:46 +01:00
|
|
|
_.merge(acc.resolver, modelAggregator.resolver);
|
|
|
|
_.merge(acc.query, modelAggregator.query);
|
2019-03-13 19:27:18 +01:00
|
|
|
}
|
2019-03-25 16:37:46 +01:00
|
|
|
}
|
2018-09-10 16:05:00 +08:00
|
|
|
|
2019-03-25 16:37:46 +01:00
|
|
|
// Build associations queries.
|
2019-07-18 10:55:13 +02:00
|
|
|
_.merge(acc.resolver, {
|
|
|
|
[globalId]: buildAssocResolvers(model, name, { plugin }),
|
2019-03-25 16:37:46 +01:00
|
|
|
});
|
2018-09-10 16:05:00 +08:00
|
|
|
|
2019-03-25 16:37:46 +01:00
|
|
|
return acc;
|
|
|
|
}, initialState);
|
|
|
|
};
|
|
|
|
|
|
|
|
module.exports = {
|
|
|
|
buildShadowCRUD,
|
2019-07-17 15:45:26 +02:00
|
|
|
buildModel,
|
2018-09-10 16:05:00 +08:00
|
|
|
};
|