mirror of
https://github.com/strapi/strapi.git
synced 2025-11-03 11:25:17 +00:00
Cleanup graphql and use model uids
This commit is contained in:
parent
9b5bc9c367
commit
b5cceb8760
@ -20,6 +20,15 @@
|
||||
"via": "related",
|
||||
"plugin": "upload"
|
||||
},
|
||||
"images": {
|
||||
"collection": "file",
|
||||
"via": "related",
|
||||
"plugin": "upload"
|
||||
},
|
||||
"author": {
|
||||
"model": "user",
|
||||
"plugin": "users-permissions"
|
||||
},
|
||||
"metas": {
|
||||
"type": "component",
|
||||
"component": "default.metas",
|
||||
|
||||
@ -547,11 +547,10 @@ module.exports = ({ models, target, plugin = false }, ctx) => {
|
||||
: relation;
|
||||
|
||||
// Retrieve opposite model.
|
||||
const model = association.plugin
|
||||
? strapi.plugins[association.plugin].models[
|
||||
association.collection || association.model
|
||||
]
|
||||
: strapi.models[association.collection || association.model];
|
||||
const model = strapi.getModel(
|
||||
association.collection || association.model,
|
||||
association.plugin
|
||||
);
|
||||
|
||||
// Reformat data by bypassing the many-to-many relationship.
|
||||
switch (association.nature) {
|
||||
@ -564,15 +563,36 @@ module.exports = ({ models, target, plugin = false }, ctx) => {
|
||||
rel => rel[model.collectionName]
|
||||
);
|
||||
break;
|
||||
case 'oneMorphToOne':
|
||||
attrs[association.alias] =
|
||||
attrs[association.alias].related || null;
|
||||
case 'oneMorphToOne': {
|
||||
const obj = attrs[association.alias];
|
||||
|
||||
if (obj === undefined || obj === null) {
|
||||
break;
|
||||
}
|
||||
|
||||
const contentType = strapi.db.getModelByCollectionName(
|
||||
obj[`${association.alias}_type`]
|
||||
);
|
||||
|
||||
attrs[association.alias] = {
|
||||
__contentType: contentType ? contentType.globalId : null,
|
||||
...obj.related,
|
||||
};
|
||||
|
||||
break;
|
||||
}
|
||||
case 'manyMorphToOne':
|
||||
case 'manyMorphToMany':
|
||||
attrs[association.alias] = attrs[association.alias].map(
|
||||
obj => obj.related
|
||||
);
|
||||
attrs[association.alias] = attrs[association.alias].map(obj => {
|
||||
const contentType = strapi.db.getModelByCollectionName(
|
||||
obj[`${association.alias}_type`]
|
||||
);
|
||||
|
||||
return {
|
||||
__contentType: contentType ? contentType.globalId : null,
|
||||
...obj.related,
|
||||
};
|
||||
});
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
@ -107,6 +107,12 @@ class DatabaseManager {
|
||||
_.get(strapi, ['components', key])
|
||||
);
|
||||
}
|
||||
|
||||
getModelByCollectionName(collectionName) {
|
||||
return Array.from(this.models.values()).find(model => {
|
||||
return model.collectionName === collectionName;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function createDatabaseManager(strapi) {
|
||||
|
||||
@ -6,8 +6,8 @@ const uploadFiles = require('../utils/upload-files');
|
||||
* A set of functions called "actions" for `ContentManager`
|
||||
*/
|
||||
module.exports = {
|
||||
fetch(params) {
|
||||
return strapi.query(params.model).findOne({ id: params.id });
|
||||
fetch(params, populate) {
|
||||
return strapi.query(params.model).findOne({ id: params.id }, populate);
|
||||
},
|
||||
|
||||
fetchAll(params, query) {
|
||||
|
||||
@ -18,14 +18,16 @@ module.exports = {
|
||||
// Create loaders for each relational field (exclude core models).
|
||||
Object.keys(strapi.models)
|
||||
.filter(model => model !== 'core_store')
|
||||
.forEach(model => {
|
||||
this.createLoader(model);
|
||||
.forEach(modelKey => {
|
||||
const model = strapi.models[modelKey];
|
||||
this.createLoader(model.uid);
|
||||
});
|
||||
|
||||
// Reproduce the same pattern for each plugin.
|
||||
Object.keys(strapi.plugins).forEach(plugin => {
|
||||
Object.keys(strapi.plugins[plugin].models).forEach(model => {
|
||||
this.createLoader(model, plugin);
|
||||
Object.keys(strapi.plugins[plugin].models).forEach(modelKey => {
|
||||
const model = strapi.plugins[plugin].models[modelKey];
|
||||
this.createLoader(model.uid);
|
||||
});
|
||||
});
|
||||
},
|
||||
@ -34,29 +36,25 @@ module.exports = {
|
||||
this.loaders = {};
|
||||
},
|
||||
|
||||
createLoader: function(model, plugin) {
|
||||
const name = plugin ? `${plugin}__${model}` : model;
|
||||
|
||||
// Exclude polymorphic from loaders.
|
||||
if (name === undefined) {
|
||||
return;
|
||||
createLoader: function(modelUID) {
|
||||
if (this.loaders[modelUID]) {
|
||||
return this.loaders[modelUID];
|
||||
}
|
||||
|
||||
if (this.loaders[name]) {
|
||||
return this.loaders[name];
|
||||
}
|
||||
|
||||
this.loaders[name] = new DataLoader(
|
||||
this.loaders[modelUID] = new DataLoader(
|
||||
keys => {
|
||||
// Extract queries from keys and merge similar queries.
|
||||
const { queries, map } = this.extractQueries(model, _.cloneDeep(keys));
|
||||
const { queries, map } = this.extractQueries(
|
||||
modelUID,
|
||||
_.cloneDeep(keys)
|
||||
);
|
||||
|
||||
// Run queries in parallel.
|
||||
return Promise.all(
|
||||
queries.map(query => this.makeQuery(model, query))
|
||||
queries.map(query => this.makeQuery(modelUID, query))
|
||||
).then(results => {
|
||||
// Use to match initial queries order.
|
||||
return this.mapData(model, keys, map, results);
|
||||
return this.mapData(modelUID, keys, map, results);
|
||||
});
|
||||
},
|
||||
{
|
||||
@ -67,7 +65,7 @@ module.exports = {
|
||||
);
|
||||
},
|
||||
|
||||
mapData: function(model, originalMap, map, results) {
|
||||
mapData: function(modelUID, originalMap, map, results) {
|
||||
// Use map to re-dispatch data correctly based on initial keys.
|
||||
return originalMap.map((query, index) => {
|
||||
// Find the index of where we should extract the results.
|
||||
@ -77,7 +75,7 @@ module.exports = {
|
||||
const data = results[indexResults];
|
||||
|
||||
// Retrieving referring model.
|
||||
const ref = this.retrieveModel(model, query.options.source);
|
||||
const ref = strapi.getModel(modelUID);
|
||||
|
||||
if (query.single) {
|
||||
// Return object instead of array for one-to-many relationship.
|
||||
@ -98,8 +96,8 @@ module.exports = {
|
||||
|
||||
const ast = ref.associations.find(ast => ast.alias === ids.alias);
|
||||
const astModel = ast
|
||||
? this.retrieveModel(ast.model || ast.collection, ast.plugin)
|
||||
: this.retrieveModel(model);
|
||||
? strapi.getModel(ast.model || ast.collection, ast.plugin)
|
||||
: strapi.getModel(modelUID);
|
||||
|
||||
if (!_.isArray(ids)) {
|
||||
return data
|
||||
@ -144,12 +142,12 @@ module.exports = {
|
||||
};
|
||||
},
|
||||
|
||||
makeQuery: async function(model, query = {}) {
|
||||
makeQuery: async function(modelUID, query = {}) {
|
||||
if (_.isEmpty(query.ids)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const ref = this.retrieveModel(model, query.options.source);
|
||||
const ref = strapi.getModel(modelUID);
|
||||
const ast = ref.associations.find(ast => ast.alias === query.alias);
|
||||
|
||||
const params = {
|
||||
@ -169,15 +167,10 @@ module.exports = {
|
||||
// Run query and remove duplicated ID.
|
||||
return strapi.plugins['content-manager'].services[
|
||||
'contentmanager'
|
||||
].fetchAll({ model }, params);
|
||||
].fetchAll({ model: modelUID }, params);
|
||||
},
|
||||
|
||||
retrieveModel: function(model, source) {
|
||||
// Retrieve refering model.
|
||||
return source ? strapi.plugins[source].models[model] : strapi.models[model];
|
||||
},
|
||||
|
||||
extractQueries: function(model, keys) {
|
||||
extractQueries: function(modelUID, keys) {
|
||||
const queries = [];
|
||||
const map = [];
|
||||
|
||||
@ -188,16 +181,16 @@ module.exports = {
|
||||
const { query = {}, ...options } = current.options;
|
||||
|
||||
// Retrieving referring model.
|
||||
const ref = this.retrieveModel(model, options.source);
|
||||
const { primaryKey } = strapi.getModel(modelUID);
|
||||
|
||||
// Generate array of IDs to fetch.
|
||||
const ids = [];
|
||||
|
||||
// Only one entry to fetch.
|
||||
if (single) {
|
||||
ids.push(params[ref.primaryKey]);
|
||||
} else if (_.isArray(query[ref.primaryKey])) {
|
||||
ids.push(...query[ref.primaryKey]);
|
||||
ids.push(params[primaryKey]);
|
||||
} else if (_.isArray(query[primaryKey])) {
|
||||
ids.push(...query[primaryKey]);
|
||||
} else {
|
||||
ids.push(query[association.via]);
|
||||
}
|
||||
@ -205,7 +198,7 @@ module.exports = {
|
||||
queries.push({
|
||||
ids,
|
||||
options,
|
||||
alias: _.first(Object.keys(query)) || ref.primaryKey,
|
||||
alias: _.first(Object.keys(query)) || primaryKey,
|
||||
});
|
||||
|
||||
map[queries.length - 1 > 0 ? queries.length - 1 : 0] = [];
|
||||
|
||||
@ -108,7 +108,7 @@ const mutateAssocAttributes = (associations = [], attributes) => {
|
||||
});
|
||||
};
|
||||
|
||||
const buildAssocResolvers = (model, name, { plugin }) => {
|
||||
const buildAssocResolvers = model => {
|
||||
const contentManager =
|
||||
strapi.plugins['content-manager'].services['contentmanager'];
|
||||
|
||||
@ -117,100 +117,44 @@ const buildAssocResolvers = (model, name, { plugin }) => {
|
||||
return associations
|
||||
.filter(association => model.attributes[association.alias].private !== true)
|
||||
.reduce((resolver, association) => {
|
||||
const target = association.model || association.collection;
|
||||
const targetModel = strapi.getModel(target, association.plugin);
|
||||
|
||||
switch (association.nature) {
|
||||
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 'oneToManyMorph':
|
||||
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(
|
||||
{
|
||||
id: obj[primaryKey],
|
||||
model: name,
|
||||
},
|
||||
plugin,
|
||||
[association.alias],
|
||||
false
|
||||
),
|
||||
contentManager.fetch(
|
||||
{
|
||||
id: obj[primaryKey],
|
||||
model: name,
|
||||
},
|
||||
plugin,
|
||||
[]
|
||||
),
|
||||
]);
|
||||
if (obj[association.alias]) {
|
||||
return obj[association.alias];
|
||||
}
|
||||
|
||||
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`
|
||||
)
|
||||
)
|
||||
) ||
|
||||
_.upperFirst(_.camelCase(association[association.type]));
|
||||
|
||||
entry._type = type;
|
||||
|
||||
return entry;
|
||||
});
|
||||
const entry = await contentManager.fetch(
|
||||
{
|
||||
id: obj[primaryKey],
|
||||
model: model.uid,
|
||||
},
|
||||
[association.alias]
|
||||
);
|
||||
|
||||
return entry[association.alias];
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
resolver[association.alias] = async (obj, options) => {
|
||||
// Construct parameters object to retrieve the correct related entries.
|
||||
const params = {
|
||||
model: association.model || association.collection,
|
||||
model: targetModel.uid,
|
||||
};
|
||||
|
||||
let queryOpts = {
|
||||
source: association.plugin,
|
||||
};
|
||||
|
||||
// Get refering model.
|
||||
const ref = association.plugin
|
||||
? strapi.plugins[association.plugin].models[params.model]
|
||||
: strapi.models[params.model];
|
||||
let queryOpts = {};
|
||||
|
||||
if (association.type === 'model') {
|
||||
params[ref.primaryKey] = _.get(
|
||||
params[targetModel.primaryKey] = _.get(
|
||||
obj,
|
||||
[association.alias, ref.primaryKey],
|
||||
[association.alias, targetModel.primaryKey],
|
||||
obj[association.alias]
|
||||
);
|
||||
} else {
|
||||
@ -229,10 +173,10 @@ const buildAssocResolvers = (model, name, { plugin }) => {
|
||||
) {
|
||||
_.set(
|
||||
queryOpts,
|
||||
['query', ref.primaryKey],
|
||||
['query', targetModel.primaryKey],
|
||||
obj[association.alias]
|
||||
? obj[association.alias]
|
||||
.map(val => val[ref.primaryKey] || val)
|
||||
.map(val => val[targetModel.primaryKey] || val)
|
||||
.sort()
|
||||
: []
|
||||
);
|
||||
@ -240,25 +184,21 @@ const buildAssocResolvers = (model, name, { plugin }) => {
|
||||
_.set(
|
||||
queryOpts,
|
||||
['query', association.via],
|
||||
obj[ref.primaryKey]
|
||||
obj[targetModel.primaryKey]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const loaderName = association.plugin
|
||||
? `${association.plugin}__${params.model}`
|
||||
: params.model;
|
||||
|
||||
return association.model
|
||||
? strapi.plugins.graphql.services.loaders.loaders[
|
||||
loaderName
|
||||
targetModel.uid
|
||||
].load({
|
||||
params,
|
||||
options: queryOpts,
|
||||
single: true,
|
||||
})
|
||||
: strapi.plugins.graphql.services.loaders.loaders[
|
||||
loaderName
|
||||
targetModel.uid
|
||||
].load({
|
||||
options: queryOpts,
|
||||
association,
|
||||
@ -272,16 +212,12 @@ const buildAssocResolvers = (model, name, { plugin }) => {
|
||||
}, {});
|
||||
};
|
||||
|
||||
const buildModel = (
|
||||
model,
|
||||
name,
|
||||
{ schema, plugin, isComponent = false } = {}
|
||||
) => {
|
||||
const buildModel = (model, { schema, isComponent = false } = {}) => {
|
||||
const { globalId, primaryKey } = model;
|
||||
|
||||
schema.resolvers[globalId] = {
|
||||
id: obj => obj[primaryKey],
|
||||
...buildAssocResolvers(model, name, { plugin }),
|
||||
...buildAssocResolvers(model),
|
||||
};
|
||||
|
||||
const initialState = {
|
||||
@ -552,7 +488,7 @@ const buildShadowCRUD = (models, plugin) => {
|
||||
}
|
||||
|
||||
// Build associations queries.
|
||||
acc.resolvers[globalId] = buildAssocResolvers(model, name, { plugin });
|
||||
_.assign(acc.resolvers[globalId], buildAssocResolvers(model));
|
||||
|
||||
return acc;
|
||||
}, initialState);
|
||||
|
||||
@ -143,7 +143,7 @@ const schemaBuilder = {
|
||||
definition,
|
||||
query,
|
||||
mutation,
|
||||
resolver,
|
||||
resolvers,
|
||||
} = Resolvers.buildShadowCRUD(strapi.plugins[plugin].models, plugin);
|
||||
|
||||
// We cannot put this in the merge because it's a string.
|
||||
@ -151,7 +151,7 @@ const schemaBuilder = {
|
||||
|
||||
return _.merge(acc, {
|
||||
query,
|
||||
resolver,
|
||||
resolvers,
|
||||
mutation,
|
||||
});
|
||||
}, modelCruds);
|
||||
@ -163,8 +163,7 @@ const schemaBuilder = {
|
||||
};
|
||||
|
||||
Object.keys(strapi.components).forEach(key =>
|
||||
Resolvers.buildModel(strapi.components[key], key, {
|
||||
plugin: null,
|
||||
Resolvers.buildModel(strapi.components[key], {
|
||||
isComponent: true,
|
||||
schema: componentsSchema,
|
||||
})
|
||||
|
||||
@ -222,8 +222,7 @@ module.exports = {
|
||||
polymorphicResolver: {
|
||||
Morph: {
|
||||
__resolveType(obj) {
|
||||
// eslint-disable-line no-unused-vars
|
||||
return obj.kind || obj._type;
|
||||
return obj.kind || obj.__contentType || null;
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user