2018-11-22 16:40:52 +01:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Loaders.js service
|
|
|
|
*
|
|
|
|
* @description: A set of functions similar to controller's actions to avoid code duplication.
|
|
|
|
*/
|
|
|
|
|
|
|
|
const _ = require('lodash');
|
|
|
|
const DataLoader = require('dataloader');
|
|
|
|
|
|
|
|
module.exports = {
|
|
|
|
loaders: {},
|
|
|
|
|
|
|
|
createLoader: function(model) {
|
|
|
|
this.loaders[model] = new DataLoader(keys => {
|
|
|
|
return new Promise(async (resolve, reject) => {
|
|
|
|
try {
|
|
|
|
// Extract queries from keys and merge similar queries.
|
|
|
|
const { queries, map } = this.extractQueries(model, _.cloneDeep(keys));
|
|
|
|
// Run queries in parallel.
|
|
|
|
const results = await Promise.all(queries.map((query) => this.makeQuery(model, query)));
|
|
|
|
// Use to match initial queries order.
|
2018-11-22 17:33:51 +01:00
|
|
|
const data = this.mapData(model, keys, map, results);
|
|
|
|
|
|
|
|
resolve(data);
|
2018-11-22 16:40:52 +01:00
|
|
|
} catch (e) {
|
|
|
|
reject(e);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}, {
|
|
|
|
cacheKeyFn: (key) => {
|
|
|
|
return _.isObjectLike(key) ? JSON.stringify(_.cloneDeep(key)) : key;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
mapData: function(model, 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.
|
|
|
|
const indexResults = map.findIndex(queryMap => queryMap.indexOf(index) !== -1);
|
|
|
|
const data = results[indexResults];
|
|
|
|
|
|
|
|
// Retrieving referring model.
|
|
|
|
const ref = this.retrieveModel(model, query.options.source);
|
2018-11-22 18:32:33 +01:00
|
|
|
|
|
|
|
if (query.single) {
|
|
|
|
// Return object instead of array for one-to-many relationship.
|
2018-11-23 17:46:43 +01:00
|
|
|
return data.find(entry => entry[ref.primaryKey].toString() === (query.params[ref.primaryKey] || '').toString());
|
2018-11-22 18:32:33 +01:00
|
|
|
}
|
|
|
|
|
2018-11-22 16:40:52 +01:00
|
|
|
// Extracting ids from original request to map with query results.
|
2018-11-22 19:57:26 +01:00
|
|
|
const ids = (() => {
|
|
|
|
if ( _.get(query.options, `query.${ref.primaryKey}`)) {
|
|
|
|
return _.get(query.options, `query.${ref.primaryKey}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Single object to retrieve (one-to-many).
|
|
|
|
const alias = _.first(Object.keys(query.options.query));
|
|
|
|
|
|
|
|
return {
|
|
|
|
alias,
|
|
|
|
value: _.get(query.options, `query.${alias}`)
|
|
|
|
};
|
|
|
|
})();
|
2018-11-23 17:46:43 +01:00
|
|
|
|
2018-11-22 19:57:26 +01:00
|
|
|
if (!_.isArray(ids)) {
|
|
|
|
return data.filter(entry => entry[ids.alias].toString() === ids.value.toString());
|
|
|
|
}
|
2018-11-22 16:40:52 +01:00
|
|
|
|
2018-11-22 17:33:51 +01:00
|
|
|
return ids
|
2018-11-22 18:32:33 +01:00
|
|
|
.map(id => data.find(entry => entry[ref.primaryKey].toString() === id.toString()))
|
2018-11-22 17:33:51 +01:00
|
|
|
.filter(entry => entry !== undefined);
|
2018-11-22 16:40:52 +01:00
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
makeQuery: async function(model, query = {}) {
|
2018-11-23 17:33:41 +01:00
|
|
|
// Construct parameters object sent to the Content Manager service.
|
2018-11-22 19:57:26 +01:00
|
|
|
const params = {
|
2018-11-22 16:40:52 +01:00
|
|
|
...query.options,
|
2018-11-22 19:57:26 +01:00
|
|
|
populate: [],
|
|
|
|
query: {}
|
|
|
|
};
|
|
|
|
|
2018-11-23 17:46:43 +01:00
|
|
|
params.query[query.alias] = _.uniq(query.ids.filter(x => !_.isEmpty(x)).map(x => x.toString())) ;
|
2018-11-22 19:57:26 +01:00
|
|
|
|
|
|
|
// Run query and remove duplicated ID.
|
|
|
|
const request = await strapi.plugins['content-manager'].services['contentmanager'].fetchAll({ model }, params);
|
2018-11-22 16:40:52 +01:00
|
|
|
|
|
|
|
return request && request.toJSON ? request.toJSON() : request;
|
|
|
|
},
|
|
|
|
|
|
|
|
retrieveModel: function(model, source) {
|
|
|
|
// Retrieve refering model.
|
|
|
|
return source ?
|
|
|
|
strapi.plugins[source].models[model]:
|
|
|
|
strapi.models[model];
|
|
|
|
},
|
|
|
|
|
|
|
|
extractQueries: function(model, keys) {
|
|
|
|
const queries = [];
|
|
|
|
const map = [];
|
|
|
|
|
|
|
|
keys.forEach((current, index) => {
|
|
|
|
// Extract query options.
|
2018-11-22 19:57:26 +01:00
|
|
|
const { single = false, params = {}, association } = current;
|
2018-11-22 18:32:33 +01:00
|
|
|
const { query = {}, ...options } = current.options;
|
|
|
|
|
2018-11-22 16:40:52 +01:00
|
|
|
// Retrieving referring model.
|
|
|
|
const ref = this.retrieveModel(model, options.source);
|
|
|
|
|
|
|
|
// Find similar query.
|
|
|
|
const indexQueries = queries.findIndex(query => _.isEqual(query.options, options));
|
|
|
|
|
2018-11-22 19:57:26 +01:00
|
|
|
const ids = (() => {
|
|
|
|
if (single) {
|
|
|
|
return [params[ref.primaryKey]];
|
|
|
|
}
|
|
|
|
|
|
|
|
return _.isArray(query[ref.primaryKey]) ? query[ref.primaryKey] : [query[association.via]];
|
|
|
|
})();
|
|
|
|
|
2018-11-22 16:40:52 +01:00
|
|
|
if (indexQueries !== -1) {
|
|
|
|
// Push to the same query the new IDs to fetch.
|
2018-11-22 19:57:26 +01:00
|
|
|
queries[indexQueries].ids.push(...ids);
|
2018-11-22 16:40:52 +01:00
|
|
|
map[indexQueries].push(index);
|
|
|
|
} else {
|
|
|
|
// Create new query in the query.
|
|
|
|
queries.push({
|
2018-11-22 19:57:26 +01:00
|
|
|
ids,
|
|
|
|
options,
|
2018-11-23 17:33:41 +01:00
|
|
|
alias: _.first(Object.keys(query)) || ref.primaryKey
|
2018-11-22 16:40:52 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
map[queries.length - 1 > 0 ? queries.length - 1 : 0] = [];
|
|
|
|
map[queries.length - 1].push(index);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return {
|
|
|
|
queries,
|
|
|
|
map
|
|
|
|
};
|
|
|
|
}
|
|
|
|
};
|