Skip to content

Commit 4052eca

Browse files
authored
Add support for an introduction page (#94)
1 parent f43b46c commit 4052eca

File tree

12 files changed

+573
-837
lines changed

12 files changed

+573
-837
lines changed

cypress/integration/test.spec.ts

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,28 +7,35 @@
77

88
describe("test", () => {
99
before(() => {
10-
cy.visit("/api");
10+
cy.visit("/api/recursive");
1111
});
1212

1313
it("renders query parameters", () => {
1414
cy.findByText(/query parameters/i).should("exist");
1515
});
1616

17-
it("loads when clicking on tabs", () => {
18-
cy.on("uncaught:exception", () => {
19-
// there is an uncaught error trying to load monaco in ci
20-
return false;
21-
});
17+
it("loads issue 21 tab", () => {
18+
checkTab(/issue 21/i, [], /missing summary/i);
19+
});
20+
21+
it("loads cos tab", () => {
22+
checkTab(/cos/i, [], /generating an iam token/i);
23+
});
2224

23-
function checkTab(tab: RegExp, heading: RegExp) {
24-
cy.get(".navbar").findByRole("link", { name: tab }).click();
25-
cy.findByRole("heading", { name: heading, level: 1 }).should("exist");
26-
}
25+
it("loads yaml tab", () => {
26+
checkTab(/yaml/i, [/api/i, /hello world/i], /hello world/i);
27+
});
28+
29+
it("loads petstore tab", () => {
30+
checkTab(
31+
/petstore/i,
32+
[/pet/i, /add a new pet to the store/i],
33+
/add a new pet to the store/i
34+
);
35+
});
2736

28-
checkTab(/issue 21/i, /missing summary/i);
29-
checkTab(/cos/i, /generating an iam token/i);
30-
checkTab(/yaml/i, /hello world/i);
31-
checkTab(/api/i, /recursive/i);
37+
it("loads api tab", () => {
38+
checkTab(/api/i, [/^pet$/i, /recursive/i], /recursive/i);
3239
});
3340

3441
it("loads a page with authentication", () => {
@@ -39,3 +46,20 @@ describe("test", () => {
3946
cy.findByRole("button", { name: /authorize/i }).should("exist");
4047
});
4148
});
49+
50+
function checkTab(tab: RegExp, links: RegExp[], heading: RegExp) {
51+
cy.on("uncaught:exception", () => {
52+
// there is an uncaught error trying to load monaco in ci
53+
return false;
54+
});
55+
56+
cy.get(".navbar").findByRole("link", { name: tab }).click();
57+
58+
for (let link of links) {
59+
cy.get("nav.menu")
60+
.findByRole("link", { name: link })
61+
.click({ force: true }); // sometimes the sidebar items get covered by the navbar in CI.
62+
}
63+
64+
cy.findByRole("heading", { name: heading, level: 1 }).should("exist");
65+
}

packages/docusaurus-plugin-openapi/src/index.ts

Lines changed: 40 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@ import {
2323
import chalk from "chalk";
2424
import { Configuration } from "webpack";
2525

26-
import { createMD } from "./markdown";
27-
import { loadOpenapi } from "./openapi";
26+
import { createApiPageMD, createInfoPageMD } from "./markdown";
27+
import { readOpenapiFiles, processOpenapiFiles } from "./openapi";
28+
import { generateSidebars } from "./sidebars";
2829
import { PluginOptions, LoadedContent } from "./types";
2930

3031
export default function pluginOpenAPI(
@@ -58,11 +59,12 @@ export default function pluginOpenAPI(
5859
const { routeBasePath } = options;
5960

6061
try {
61-
const loadedApi = await loadOpenapi(
62-
contentPath,
62+
const openapiFiles = await readOpenapiFiles(contentPath, {});
63+
const loadedApi = await processOpenapiFiles(openapiFiles, {
6364
baseUrl,
64-
routeBasePath
65-
);
65+
routeBasePath,
66+
siteDir: context.siteDir,
67+
});
6668
return { loadedApi };
6769
} catch (e) {
6870
console.error(chalk.red(`Loading of api failed for "${contentPath}"`));
@@ -72,86 +74,53 @@ export default function pluginOpenAPI(
7274

7375
async contentLoaded({ content, actions }) {
7476
const { loadedApi } = content;
75-
const { routeBasePath, apiLayoutComponent, apiItemComponent } = options;
77+
const {
78+
routeBasePath,
79+
apiLayoutComponent,
80+
apiItemComponent,
81+
sidebarCollapsed,
82+
sidebarCollapsible,
83+
} = options;
7684
const { addRoute, createData } = actions;
7785

7886
const sidebarName = `openapi-sidebar-${pluginId}`;
87+
const sidebar = generateSidebars(loadedApi, {
88+
sidebarCollapsible,
89+
sidebarCollapsed,
90+
});
7991

80-
const sidebar = loadedApi.map((category) => {
92+
const promises = loadedApi.map(async (item) => {
93+
const pageId = `site-${routeBasePath}-${item.id}`;
94+
95+
await createData(
96+
`${docuHash(pageId)}.json`,
97+
JSON.stringify(item, null, 2)
98+
);
99+
100+
// TODO: "-content" should be inside hash to prevent name too long errors.
101+
const markdown = await createData(
102+
`${docuHash(pageId)}-content.mdx`,
103+
item.type === "api" ? createApiPageMD(item) : createInfoPageMD(item)
104+
);
81105
return {
82-
type: "category",
83-
label: category.title,
84-
collapsible: options.sidebarCollapsible,
85-
collapsed: options.sidebarCollapsed,
86-
items: category.items.map((item) => {
87-
return {
88-
href: item.permalink,
89-
label: item.title,
90-
type: "link",
91-
className: item.deprecated
92-
? "menu__list-item--deprecated"
93-
: undefined,
94-
};
95-
}),
106+
path: item.permalink,
107+
component: apiItemComponent,
108+
exact: true,
109+
modules: {
110+
content: markdown,
111+
},
112+
sidebar: sidebarName,
96113
};
97114
});
98115

99-
const promises = loadedApi.flatMap((section) => {
100-
return section.items.map(async (item) => {
101-
const { id, title, description, permalink, previous, next, ...api } =
102-
item;
103-
104-
const pageId = `site-${routeBasePath}-${id}`;
105-
106-
await createData(
107-
`${docuHash(pageId)}.json`,
108-
JSON.stringify(
109-
{
110-
unversionedId: id,
111-
id,
112-
isDocsHomePage: false, // TODO: Where does this come from?
113-
title,
114-
description,
115-
// source: "@site/docs/tutorial-basics/congratulations.md",
116-
// sourceDirName: "tutorial-basics",
117-
slug: "/" + id, // TODO: Should this really be prepended with "/"?
118-
permalink,
119-
frontMatter: {},
120-
sidebar: sidebarName,
121-
previous,
122-
next,
123-
api,
124-
},
125-
null,
126-
2
127-
)
128-
);
129-
130-
// TODO: "-content" should be inside hash to prevent name too long errors.
131-
const markdown = await createData(
132-
`${docuHash(pageId)}-content.mdx`,
133-
createMD(item)
134-
);
135-
return {
136-
path: item.permalink,
137-
component: apiItemComponent,
138-
exact: true,
139-
modules: {
140-
content: markdown,
141-
},
142-
sidebar: sidebarName,
143-
};
144-
});
145-
});
146-
147116
// Important: the layout component should not end with /,
148117
// as it conflicts with the home doc
149118
// Workaround fix for https://github.com/facebook/docusaurus/issues/2917
150119
const apiBaseRoute = normalizeUrl([baseUrl, routeBasePath]);
151120
const basePath = apiBaseRoute === "/" ? "" : apiBaseRoute;
152121

153122
async function rootRoute() {
154-
const item = loadedApi[0].items[0];
123+
const item = loadedApi[0];
155124
const pageId = `site-${routeBasePath}-${item.id}`;
156125

157126
return {
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/* ============================================================================
2+
* Copyright (c) Cloud Annotations
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
* ========================================================================== */
7+
8+
import { create, guard } from "./utils";
9+
10+
export function createVersionBadge(version: string | undefined) {
11+
return guard(version, (version) => [
12+
create("span", {
13+
className: "theme-doc-version-badge badge badge--secondary",
14+
children: `Version: ${escape(version)}`,
15+
}),
16+
`\n\n`,
17+
]);
18+
}

packages/docusaurus-plugin-openapi/src/markdown/index.ts

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,26 @@
77

88
import { escape } from "lodash";
99

10-
import { ApiItem } from "../types";
10+
import { ApiPageMetadata, InfoPageMetadata } from "../types";
1111
import { createDeprecationNotice } from "./createDeprecationNotice";
1212
import { createDescription } from "./createDescription";
1313
import { createParamsTable } from "./createParamsTable";
1414
import { createRequestBodyTable } from "./createRequestBodyTable";
1515
import { createStatusCodesTable } from "./createStatusCodesTable";
16+
import { createVersionBadge } from "./createVersionBadge";
1617
import { render } from "./utils";
1718

18-
export function createMD({
19+
export function createApiPageMD({
1920
title,
20-
deprecated,
21-
"x-deprecated-description": deprecatedDescription,
22-
description,
23-
parameters,
24-
requestBody,
25-
responses,
26-
}: ApiItem) {
21+
api: {
22+
deprecated,
23+
"x-deprecated-description": deprecatedDescription,
24+
description,
25+
parameters,
26+
requestBody,
27+
responses,
28+
},
29+
}: ApiPageMetadata) {
2730
return render([
2831
`# ${escape(title)}\n\n`,
2932
createDeprecationNotice({ deprecated, description: deprecatedDescription }),
@@ -36,3 +39,13 @@ export function createMD({
3639
createStatusCodesTable({ responses }),
3740
]);
3841
}
42+
43+
export function createInfoPageMD({
44+
info: { title, version, description },
45+
}: InfoPageMetadata) {
46+
return render([
47+
createVersionBadge(version),
48+
`# ${escape(title)}\n\n`,
49+
createDescription(description),
50+
]);
51+
}

packages/docusaurus-plugin-openapi/src/openapi/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@
55
* LICENSE file in the root directory of this source tree.
66
* ========================================================================== */
77

8-
export { loadOpenapi } from "./load";
8+
export { readOpenapiFiles, processOpenapiFiles } from "./openapi";

0 commit comments

Comments
 (0)