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