Skip to content

Commit

Permalink
feat(includers/openapi): toc, content, leading pages
Browse files Browse the repository at this point in the history
Documentation generation from openapi specification

includes generation of:
  * table of content
  * content (main page, sections/endpoints page, endpoint page)
  * leading pages
  • Loading branch information
moki committed Sep 1, 2022
1 parent 8e63577 commit f3bb43b
Show file tree
Hide file tree
Showing 15 changed files with 928 additions and 18 deletions.
250 changes: 235 additions & 15 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@
"prepublishOnly": "npm run lint && npm run build"
},
"dependencies": {
"@apidevtools/swagger-parser": "^10.1.0",
"@doc-tools/components": "2.0.0",
"@doc-tools/transform": "^2.8.2",
"@octokit/core": "3.5.1",
"ajv": "^8.11.0",
"aws-sdk": "2.1054.0",
"chalk": "4.1.2",
"highlight.js": "10.7.3",
Expand All @@ -36,6 +38,7 @@
"react-dom": "16.14.0",
"shelljs": "0.8.5",
"simple-git": "2.48.0",
"slugify": "^1.6.5",
"threads": "1.7.0",
"threads-plugin": "1.4.0",
"walk-sync": "2.2.0",
Expand Down
2 changes: 1 addition & 1 deletion src/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export interface YfmTocInclude {
includer?: IncluderName;
}

export const includersNames = ['sourcedocs'] as const;
export const includersNames = ['sourcedocs', 'openapi'] as const;

export type IncluderName = typeof includersNames[number];

Expand Down
1 change: 1 addition & 0 deletions src/services/includers/batteries/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * as sourcedocs from './sourcedocs';
export * as openapi from './openapi';
14 changes: 14 additions & 0 deletions src/services/includers/batteries/openapi/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const EOL = '\n';
const TAG_NAMES_FIELD = 'x-navtitle';
const BLOCK = EOL.repeat(2);
const DESCRIPTION_SECTION_NAME = 'Description';
const CONTACTS_SECTION_NAME = 'Contacts';
const TAGS_SECTION_NAME = 'Sections';
const ENDPOINTS_SECTION_NAME = 'Endpoints';
const REQUEST_SECTION_NAME = 'Request';
const PARAMETERS_SECTION_NAME = 'Parameters';
const RESPONSES_SECTION_NAME = 'Responses';

export {TAG_NAMES_FIELD, BLOCK, DESCRIPTION_SECTION_NAME, CONTACTS_SECTION_NAME, TAGS_SECTION_NAME, ENDPOINTS_SECTION_NAME, REQUEST_SECTION_NAME, PARAMETERS_SECTION_NAME, RESPONSES_SECTION_NAME, EOL};

export default {TAG_NAMES_FIELD, BLOCK, DESCRIPTION_SECTION_NAME, CONTACTS_SECTION_NAME, TAGS_SECTION_NAME, ENDPOINTS_SECTION_NAME, REQUEST_SECTION_NAME, PARAMETERS_SECTION_NAME, RESPONSES_SECTION_NAME, EOL};
47 changes: 47 additions & 0 deletions src/services/includers/batteries/openapi/generators/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import {EOL, BLOCK} from '../constants';

import {TitleDepth} from '../types';

function list(items: string[]) {
return items.map((item) => `- ${item}`).join(EOL) + EOL;
}

function link(text: string, src: string) {
return `[${text}](${src})`;
}

function title(depth: TitleDepth) {
return (content?: string) => content?.length && '#'.repeat(depth) + ` ${content}`;
}

function body(text?: string) {
return text?.length && text;
}

function mono(text: string) {
return `##${text}##`;
}

function code(text: string) {
return EOL + ['```', text, '```'].join(EOL) + EOL;
}

function table(data: any[][]) {
const colgen = (col: any) => (Array.isArray(col) ? `${EOL}${table(col)}${EOL}` : ` ${col} `);
const rowgen = (row: any) => `||${row.map(colgen).join('|')}||`;

return `#|${block(data.map(rowgen))}|#`;
}

function cut(text: string, heading = '') {
return block([`{% cut "${heading}" %}`, text, '{% endcut %}']) + EOL;
}

function block(elements: any[]) {
return elements.filter(Boolean).join(BLOCK);
}

export {list, link, title, body, mono, table, code, cut, block};

export default {list, link, title, body, mono, table, code, cut, block};
74 changes: 74 additions & 0 deletions src/services/includers/batteries/openapi/generators/endpoint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import {block, title, body, table, code, cut} from './common';
import {
DESCRIPTION_SECTION_NAME,
REQUEST_SECTION_NAME,
PARAMETERS_SECTION_NAME,
RESPONSES_SECTION_NAME,
} from '../constants';

import {Endpoint, Parameters, Parameter, Responses, Response, Schema, Method} from '../types';

function endpoint(data: Endpoint) {
const page = [
title(1)(data.summary ?? data.id),
description(data.description),
request(data.path, data.method, data.servers),
parameters(data.parameters),
responses(data.responses),
];

return block(page);
}

function description(text?: string) {
return text?.length && block([
title(2)(DESCRIPTION_SECTION_NAME),
body(text),
]);
}

function request(path: string, method: Method, servers: string[]) {
const requestsTable = table([
['method', 'url'],
[method, block(servers.map((href) => code(href + '/' + path)))],
]);

return block([title(2)(REQUEST_SECTION_NAME), requestsTable]);
}

function parameters(params?: Parameters) {
const parametersTable = params?.length && table([
['name', 'type', 'required', 'description'],
...params.map((parameter: Parameter) =>
[parameter.name, parameter.in, parameter.required, (parameter.description ?? '')]),
]);

return parameters?.length && block([
title(3)(PARAMETERS_SECTION_NAME),
parametersTable,
]);
}

function responses(responses?: Responses) {
return responses?.length && block([
title(2)(RESPONSES_SECTION_NAME),
block(responses.map(response)),
]);
}

function response(response: Response) {
return block([
title(3)(response.code),
title(4)(DESCRIPTION_SECTION_NAME),
body(response.description),
response.schemas?.length && block(response.schemas.map(schema)),
]);
}

function schema({type, schema}: Schema) {
return cut(code(JSON.stringify(schema, null, 4)), type);
}

export {endpoint};

export default {endpoint};
7 changes: 7 additions & 0 deletions src/services/includers/batteries/openapi/generators/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import {main} from './main';
import {section} from './section';
import {endpoint} from './endpoint';

export {main, section, endpoint};

export default {main, section, endpoint};
54 changes: 54 additions & 0 deletions src/services/includers/batteries/openapi/generators/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import {sep} from 'path';

import {block, title, body, mono, link, list} from './common';
import {
DESCRIPTION_SECTION_NAME,
CONTACTS_SECTION_NAME,
TAGS_SECTION_NAME,
} from '../constants';

import {Info, Contact, ContactSource, Tag} from '../types';

function main(info: Info, tags: Map<string, Tag>) {
const license = info.license?.url ? link : body;

const page = [
title(1)(info.name),
info.version?.length && body(mono(`version: ${info.version}`)),
info.terms?.length && link('Terms of service', info.terms),
info.license && license(info.license.name, info.license.url as string),
description(info.description),
contact(info.contact),
sections(tags),
];

return block(page);
}

function contact(data?: Contact) {
return data?.name.length &&
data?.sources.length &&
block([
title(2)(CONTACTS_SECTION_NAME),
list(data.sources.map(contactSource(data))),
]);
}

function contactSource(data: Contact) {
return (src: ContactSource) => link(data.name + ` ${src.type}`, src.url);
}

function description(text?: string) {
return text?.length && block([title(2)(DESCRIPTION_SECTION_NAME), body(text)]);
}

function sections(tags: Map<string, Tag>) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
const links = Array.from(tags).map(([_, {name, id}]: [any, Tag]) => link(name, id + sep + 'index.md'));

return tags?.size && block([title(2)(TAGS_SECTION_NAME), list(links)]);
}

export {main};

export default {main};
29 changes: 29 additions & 0 deletions src/services/includers/batteries/openapi/generators/section.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import {block, title, body, link, list} from './common';
import {DESCRIPTION_SECTION_NAME, ENDPOINTS_SECTION_NAME} from '../constants';

import {Tag, Endpoint, Endpoints} from '../types';

function section(tag: Tag) {
const page = [
title(1)(tag.name),
description(tag.description),
endpoints(tag.endpoints),
];

return block(page);
}

function description(text?: string) {
return text?.length && block([title(2)(DESCRIPTION_SECTION_NAME), body(text)]);
}

function endpoints(data?: Endpoints) {
const links = (endpoints: Endpoints) =>
endpoints.map(({id, summary}: Endpoint) => link(summary ?? id, id + '.md'));

return data?.length && block([title(2)(ENDPOINTS_SECTION_NAME), list(links(data))]);
}

export {section};

export default {section};
Loading

0 comments on commit f3bb43b

Please sign in to comment.