'use strict'; /** * Module dependencies */ // Node.js core. const path = require('path'); // Public node modules. const _ = require('lodash'); const async = require('async'); // Local utilities. const dictionary = require('../../../../util/dictionary'); /** * Async module loader to create a * dictionary of the user APIs. */ module.exports = function (strapi) { const hook = { /** * Initialize the hook */ initialize: function (cb) { _.forEach(strapi.api, function (definition, api) { async.auto({ // Expose the `name` of the API for the callback. 'name': function (cb) { cb(null, api); }, // Load API controllers from `./api/*/controllers/*.js`. 'controllers/*': function (cb) { dictionary.optional({ dirname: path.resolve(strapi.config.appPath, strapi.config.paths.api, api, strapi.config.paths.controllers), filter: /(.+)\.(js)$/, depth: 1 }, cb); }, // Load API models from `./api/*/models/*.js` and `./api/*/models/*.settings.json`. 'models/*': function (cb) { async.parallel({ settings: function (cb) { dictionary.optional({ dirname: path.resolve(strapi.config.appPath, strapi.config.paths.api, api, strapi.config.paths.models), filter: /(.+)\.settings.json$/, depth: 1 }, cb); }, functions: function (cb) { dictionary.optional({ dirname: path.resolve(strapi.config.appPath, strapi.config.paths.api, api, strapi.config.paths.models), filter: /(.+)\.js$/, depth: 1 }, cb); } }, function (err, models) { if (err) { return cb(err); } return cb(null, _.merge(models.settings, models.functions)); }); }, // Load API templates from `./api/*/templates/**/*.template.json`. 'templates/**': function (cb) { dictionary.aggregate({ dirname: path.resolve(strapi.config.appPath, strapi.config.paths.api, api, strapi.config.paths.templates), filter: /(.+)\.(template.json)$/, depth: 2 }, cb); }, // Load API services from `./api/*/services/*.js`. 'services/*': function (cb) { dictionary.optional({ dirname: path.resolve(strapi.config.appPath, strapi.config.paths.api, api, strapi.config.paths.services), filter: /(.+)\.(js)$/, depth: 1 }, cb); }, // Load API policies from `./api/*/policies/*.js`. 'policies/*': function (cb) { dictionary.aggregate({ dirname: path.resolve(strapi.config.appPath, strapi.config.paths.api, api, strapi.config.paths.policies), filter: /(.+)\.(js)$/, depth: 1 }, cb); }, // Load API config from `./api/*/config/*.js|json` and `./api/*/config/environments/**/*.js|json`. 'config/**': function (cb) { async.parallel({ common: function (cb) { dictionary.aggregate({ dirname: path.resolve(strapi.config.appPath, strapi.config.paths.api, api, strapi.config.paths.config), filter: /(.+)\.(js|json)$/, depth: 2 }, cb); }, specific: function (cb) { dictionary.aggregate({ dirname: path.resolve(strapi.config.appPath, strapi.config.paths.api, api, strapi.config.paths.config, 'environments', strapi.config.environment), filter: /(.+)\.(js|json)$/, depth: 2 }, cb); } }, function (err, config) { if (err) { return cb(err); } return cb(null, _.merge(config.common, config.specific)); }); } }, // Callback. function (err, api) { // Just in case there is an error. if (err) { return cb(err); } // Expose the API dictionary. strapi.api[api.name] = { controllers: api['controllers/*'], models: api['models/*'], services: api['services/*'], policies: api['policies/*'], config: api['config/**'], templates: _.reduce(api['templates/**'], function (result, attributes) { return _.merge(result, attributes); }) || {} }; // Delete the definition if it's empty. _.forEach(strapi.api[api.name], function (dictionary, entry) { if (_.isEmpty(strapi.api[api.name][entry])) { delete strapi.api[api.name][entry]; } }); // If the module doesn't have a definition at all // just remove it completely from the dictionary. if (_.isEmpty(strapi.api[api.name])) { delete strapi.api[api.name]; } // Merge API controllers with the main ones. strapi.controllers = _.merge(strapi.controllers, strapi.api[api.name].controllers); // Merge API models with the main ones. strapi.models = _.merge(strapi.models, strapi.api[api.name].models); // Merge API policies with the main ones. strapi.policies = _.merge(strapi.policies, strapi.api[api.name].policies); // Merge API routes with the main ones. strapi.config.routes = _.merge(strapi.config.routes, strapi.api[api.name].config.routes); }); }); cb(); }, /** * Reload the hook */ reload: function () { hook.initialize(function (err) { if (err) { strapi.log.error('Failed to reinitialize the API hook.'); strapi.stop(); } else { strapi.emit('hook:_api:reloaded'); } }); } }; return hook; };