/* * Copyright (C) 2018-2019 StApps * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ import {asyncPool} from '@krlwlfrt/async-pool'; import {Logger} from '@openstapps/logger'; import {basename, dirname, join} from 'path'; import {ProjectReflection} from 'typedoc'; import {Type} from 'typedoc/dist/lib/models'; import {NodesWithMetaInformation, NodeWithMetaInformation, RouteWithMetaInformation} from './common'; /** * Gather relevant information of routes * * This gathers the information for all routes that implement the abstract class SCAbstractRoute. * Furthermore it instantiates every route and adds it to the information. * * @param reflection Contents of the JSON representation which Typedoc generates */ export async function gatherRouteInformation(reflection: ProjectReflection): Promise { const routes: RouteWithMetaInformation[] = []; if (!Array.isArray(reflection.children)) { throw new Error('Project reflection doesn\'t contain any modules.'); } // tslint:disable-next-line:no-magic-numbers await asyncPool(2, reflection.children, async (module) => { if (Array.isArray(module.children) && module.children.length > 0) { // tslint:disable-next-line:no-magic-numbers await asyncPool(2, module.children, (async (node) => { if (Array.isArray(node.extendedTypes) && node.extendedTypes.length > 0) { if (node.extendedTypes.some((extendedType) => { // tslint:disable-next-line:completed-docs return (extendedType as (Type & { name: string; })).name === 'SCAbstractRoute'; })) { Logger.info(`Found ${node.name} in ${module.originalName}.`); if (Array.isArray(module.originalName.match(/\.d\.ts$/))) { module.originalName = join(dirname(module.originalName), basename(module.originalName, '.d.ts')); Logger.info(`Using compiled version of module in ${module.originalName}.`); } const importedModule = await import(module.originalName); const route = new importedModule[node.name](); routes.push({description: node.comment!, name: node.name, route}); } } })); } }); if (routes.length === 0) { throw new Error('No route information found.'); } return routes; } /** * Get a linked name for a node * * @param name Name of the node * @param node Node itself * @param humanize Whether to humanize the name or not */ export function getLinkedNameForNode(name: string, node: NodeWithMetaInformation, humanize = false): string { const humanizeString = require('humanize-string'); let printableName = name; if (humanize) { printableName = humanizeString(name.substr('SC'.length)); } let link = `[${printableName}]`; link += `(${getLinkForNode(name, node)})`; return link; } /** * Get link for a node * * @param name Name of the node * @param node Node itself */ export function getLinkForNode(name: string, node: NodeWithMetaInformation): string { let link = 'https://openstapps.gitlab.io/core/'; const module = node.module .toLowerCase() .split('/') .join('_'); if (node.type === 'Type alias') { link += 'modules/'; link += `_${module}_`; link += `.html#${name.toLowerCase()}`; return link; } let type = 'classes'; if (node.type !== 'Class') { type = `${node.type.toLowerCase()}s`; } link += `${type}/`; link += `_${module}_`; link += `.${name.toLowerCase()}.html`; return link; } /** * Generate documentation snippet for one route * * @param routeWithInfo A route instance with its meta information * @param nodes Nodes with meta information */ export function generateDocumentationForRoute(routeWithInfo: RouteWithMetaInformation, nodes: NodesWithMetaInformation): string { let output = ''; const route = routeWithInfo.route; output += `## \`${route.method} ${route.urlFragment}\``; output += ` ${getLinkedNameForNode(routeWithInfo.name, nodes[routeWithInfo.name], true)}\n\n`; if (typeof routeWithInfo.description.shortText === 'string') { output += `**${routeWithInfo.description.shortText}**\n\n`; } if (typeof routeWithInfo.description.text === 'string') { output += `${routeWithInfo.description.text.replace('\n', '
')}\n\n`; } output += `### Definition | parameter | value | | --- | --- | | request | ${getLinkedNameForNode(route.requestBodyName, nodes[route.requestBodyName])} | | response | ${getLinkedNameForNode(route.responseBodyName, nodes[route.responseBodyName])} | | success code | ${route.statusCodeSuccess} | | errors | ${route.errorNames .map((error) => { return getLinkedNameForNode(error.name, nodes[error.name]); }) .join('
')} | `; if (typeof route.obligatoryParameters === 'object' && Object.keys(route.obligatoryParameters).length > 0) { let parameterTable = ''; for (const parameter in route.obligatoryParameters) { if (!route.obligatoryParameters.hasOwnProperty(parameter)) { continue; } let type = route.obligatoryParameters![parameter]; if (typeof nodes[type] !== 'undefined') { type = getLinkedNameForNode(type, nodes[type]); } parameterTable += ``; } parameterTable += '
parametertype
${parameter}${type}
'; output += `| obligatory parameters | ${parameterTable} |`; } output += '\n\n'; return output; } /** * Get a map of nodes with their meta information * * @param projectReflection Reflection to get information from */ export function getNodeMetaInformationMap(projectReflection: ProjectReflection): NodesWithMetaInformation { const nodes: NodesWithMetaInformation = {}; if (typeof projectReflection.children === 'undefined') { throw new Error('Project reflection doesn\'t contain any modules.'); } // iterate over modules projectReflection.children.forEach((module) => { if (Array.isArray(module.children) && module.children.length > 0) { // iterate over types module.children.forEach((node) => { // add node with module and type nodes[node.name] = { module: module.name.substring(1, module.name.length - 1), type: node.kindString!, }; }); } }); return nodes; }