@whook/whook
Version:
Build strong and efficient REST web services.
179 lines • 7.85 kB
JavaScript
import { autoService, location, name } from 'knifecycle';
import { noop } from '../libs/utils.js';
import { ROUTE_ASIDE_COMPONENTS_PROPERTY_MAP, DEFAULT_ROUTE_CONFIG, } from '../types/routes.js';
import { DEFAULT_COMMAND_CONFIG, } from '../types/commands.js';
import { DEFAULT_CRON_CONFIG, } from '../types/crons.js';
import { DEFAULT_TRANSFORMER_CONFIG, } from '../types/transformers.js';
import { DEFAULT_CONSUMER_CONFIG, } from '../types/consumers.js';
/* Architecture Note #2.9.2.1: Definitions loader
The `DEFINITIONS` service provide a convenient way to
gather your various definitions from the routes, crons
or commands you created in the `src/(routes|cron|commands)`
folder.
*/
export const DEFAULT_SECURITY_DEFINITIONS = {
securitySchemes: {},
security: [],
};
export default location(name('DEFINITIONS', autoService(initDefinitions)), import.meta.url);
/**
* Initialize the DEFINITIONS service.
* @param {Object} services
* The services DEFINITIONS depends on
* @param {Object} [services.ROUTES_DEFINITIONS]
* The API routes modules
* @param {Object} [services.COMMANDS_DEFINITIONS]
* The commands modules
* @param {Object} [services.CRONS_DEFINITIONS]
* The crons modules
* @param {Object} [services.CONSUMERS_DEFINITIONS]
* The consumers modules
* @param {Object} [services.TRANSFORMERS_DEFINITIONS]
* The transformers modules
* @param {Object} [services.log=noop]
* An optional logging service
* @return {Promise<String>}
* A promise of a containing the actual host.
*/
async function initDefinitions({ ROUTES_DEFINITIONS, COMMANDS_DEFINITIONS, CRONS_DEFINITIONS, CONSUMERS_DEFINITIONS, TRANSFORMERS_DEFINITIONS, SECURITY_DEFINITIONS = DEFAULT_SECURITY_DEFINITIONS, log = noop, }) {
log('debug', `🈁 - Generating the DEFINITIONS`);
const routesModules = Object.keys(ROUTES_DEFINITIONS).map((key) => ({
file: ROUTES_DEFINITIONS[key].url,
module: ROUTES_DEFINITIONS[key].module,
}));
const commandsModules = Object.keys(COMMANDS_DEFINITIONS).map((key) => ({
file: COMMANDS_DEFINITIONS[key].url,
module: COMMANDS_DEFINITIONS[key].module,
}));
const cronsModules = Object.keys(CRONS_DEFINITIONS).map((key) => ({
file: CRONS_DEFINITIONS[key].url,
module: CRONS_DEFINITIONS[key].module,
}));
const consumersModules = Object.keys(CONSUMERS_DEFINITIONS).map((key) => ({
file: CONSUMERS_DEFINITIONS[key].url,
module: CONSUMERS_DEFINITIONS[key].module,
}));
const transformersModules = Object.keys(TRANSFORMERS_DEFINITIONS).map((key) => ({
file: TRANSFORMERS_DEFINITIONS[key].url,
module: TRANSFORMERS_DEFINITIONS[key].module,
}));
const DEFINITIONS = {
paths: routesModules.reduce((paths, { file, module }) => {
const definition = module.definition;
if (!definition) {
log('debug', `🈁 - Handler module at "${file}" exports no definition!`);
return paths;
}
if (paths[definition.path]?.[definition.method]) {
log('warning', `⚠️ - Overriding an existing definition ("${definition.method}" "${definition.path}").`);
}
return {
...paths,
...(definition
? {
[definition.path]: {
...(paths[definition.path] || {}),
[definition.method]: definition.operation,
},
}
: {}),
};
}, {}),
components: {
schemas: combineComponents({ log }, 'Schema', routesModules
.concat(commandsModules)
.concat(cronsModules)
.concat(transformersModules)
.concat(consumersModules)),
parameters: combineComponents({ log }, 'Parameter', routesModules),
headers: combineComponents({ log }, 'Header', routesModules),
requestBodies: combineComponents({ log }, 'RequestBody', routesModules),
responses: combineComponents({ log }, 'Response', routesModules),
callbacks: combineComponents({ log }, 'Callback', routesModules),
securitySchemes: SECURITY_DEFINITIONS.securitySchemes,
},
security: SECURITY_DEFINITIONS.security,
configs: {},
};
for (const routeModule of routesModules) {
if (!routeModule.module.definition) {
continue;
}
const operationId = routeModule.module.definition.operation.operationId;
if (DEFINITIONS.configs[operationId]) {
log('error', `💥 - Cannot override an existing definition (${operationId}).`);
}
DEFINITIONS.configs[operationId] = {
type: 'route',
config: routeModule.module.definition.config || DEFAULT_ROUTE_CONFIG,
path: routeModule.module.definition.path,
method: routeModule.module.definition.method,
operation: routeModule.module.definition.operation,
};
}
for (const cronModule of cronsModules) {
const name = cronModule.module.definition.name;
if (DEFINITIONS.configs[name]) {
log('error', `💥 - Cannot override an existing definition (${name}).`);
}
DEFINITIONS.configs[name] = {
type: 'cron',
...cronModule.module.definition,
config: cronModule.module.definition.config || DEFAULT_CRON_CONFIG,
schedules: cronModule.module.definition.schedules || [],
};
}
for (const consumerModule of consumersModules) {
const name = consumerModule.module.definition.name;
if (DEFINITIONS.configs[name]) {
log('error', `💥 - Cannot override an existing definition (${name}).`);
}
DEFINITIONS.configs[name] = {
type: 'consumer',
...consumerModule.module.definition,
config: consumerModule.module.definition.config || DEFAULT_CONSUMER_CONFIG,
};
}
for (const transformerModule of transformersModules) {
const name = transformerModule.module.definition.name;
if (DEFINITIONS.configs[name]) {
log('error', `💥 - Cannot override an existing definition (${name}).`);
}
DEFINITIONS.configs[name] = {
type: 'transformer',
...transformerModule.module.definition,
config: transformerModule.module.definition.config ||
DEFAULT_TRANSFORMER_CONFIG,
};
}
for (const commandModule of commandsModules) {
const name = commandModule.module.definition.name;
if (DEFINITIONS.configs[name]) {
log('error', `💥 - Cannot override an existing definition (${name}).`);
}
DEFINITIONS.configs[name] = {
type: 'command',
...commandModule.module.definition,
config: commandModule.module.definition.config || DEFAULT_COMMAND_CONFIG,
};
}
return DEFINITIONS;
}
export function combineComponents({ log }, type, modules) {
return modules.reduce((components, { module }) => ({
...components,
...Object.keys(module)
.filter((key) => key.endsWith(type))
.reduce((addedComponents, key) => {
const component = module[key];
if (addedComponents[component.name]) {
log('warning', `⚠️ - Overriding an existing aside component (type: "${type}", name: "${component.name}").`);
}
return {
...addedComponents,
[component.name]: component[ROUTE_ASIDE_COMPONENTS_PROPERTY_MAP[type]],
};
}, {}),
}), {});
}
//# sourceMappingURL=DEFINITIONS.js.map