diff --git a/.github/workflows/canary-beta-release.yml b/.github/workflows/canary-beta-release.yml
index 6762f3e25..abac23ecd 100644
--- a/.github/workflows/canary-beta-release.yml
+++ b/.github/workflows/canary-beta-release.yml
@@ -3,7 +3,7 @@ name: Canary Beta Release
on:
push:
branches:
- - v2.0.0
+ - v3.0.0
paths:
- packages/**
@@ -14,7 +14,7 @@ jobs:
publish-canary:
name: Publish Canary Beta
runs-on: ubuntu-latest
- if: ${{ github.repository == 'PaloAltoNetworks/docusaurus-openapi-docs' && github.ref == 'refs/heads/v2.0.0' && github.event_name == 'push' }}
+ if: ${{ github.repository == 'PaloAltoNetworks/docusaurus-openapi-docs' && github.ref == 'refs/heads/v3.0.0' && github.event_name == 'push' }}
steps:
- name: Checkout
uses: actions/checkout@v3
@@ -30,7 +30,7 @@ jobs:
git config --global user.name "Steven Serrata"
git config --global user.email "sserrata@paloaltonetworks.com"
git fetch
- git checkout v2.0.0
+ git checkout v3.0.0
echo "//registry.npmjs.org/:_authToken=${NPM_AUTH_TOKEN}" >> .npmrc
env:
NPM_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 8a42c4cf0..34cd3b01a 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -2,7 +2,7 @@ name: "CodeQL"
on:
push:
- branches: [main, v2.0.0]
+ branches: [main, v3.0.0]
jobs:
analyze:
diff --git a/.github/workflows/deploy-preview.yml b/.github/workflows/deploy-preview.yml
index b0454c33e..569c57acb 100644
--- a/.github/workflows/deploy-preview.yml
+++ b/.github/workflows/deploy-preview.yml
@@ -2,7 +2,7 @@ name: "Deploy Preview"
on:
pull_request_target:
- branches: [main, v2.0.0]
+ branches: [main, v3.0.0]
jobs:
precheck:
diff --git a/.github/workflows/pr-title-check.yaml b/.github/workflows/pr-title-check.yaml
index 8c647198a..a7fbb042b 100644
--- a/.github/workflows/pr-title-check.yaml
+++ b/.github/workflows/pr-title-check.yaml
@@ -4,7 +4,7 @@ on:
pull_request:
branches:
- main
- - v2.0.0
+ - v3.0.0
types:
- opened
- synchronize
diff --git a/.github/workflows/release-beta.yaml b/.github/workflows/release-beta.yaml
index 5903a8d48..68084aa56 100644
--- a/.github/workflows/release-beta.yaml
+++ b/.github/workflows/release-beta.yaml
@@ -3,7 +3,7 @@ name: Release Beta
on:
push:
branches:
- - v2.0.0
+ - v3.0.0
env:
FORCE_COLOR: true
diff --git a/.gitignore b/.gitignore
index bc9d2e278..f7c58c98b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -137,6 +137,8 @@ dist
demo/**/*.api.mdx
demo/**/*.info.mdx
demo/**/*.tag.mdx
+demo/**/*.schema.mdx
demo/**/sidebar.js
demo/**/versions.json
+.idea
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 92660fde6..77cb6d20d 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -4,21 +4,21 @@
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
- "source.fixAll.eslint": true
+ "source.fixAll.eslint": "explicit"
}
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
- "source.fixAll.eslint": true
+ "source.fixAll.eslint": "explicit"
}
},
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
- "source.fixAll.eslint": true
+ "source.fixAll.eslint": "explicit"
}
}
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9ea0eebba..8ce96e91b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,136 @@
+## 2.2.0 (Jul 3, 2024)
+
+High level enhancements
+
+- Improved support for customizing code snippets
+- Switched back to canonical postman depdendencies
+- Improved support for OpenAPI readOnly/writeOnly
+- Added support for OpenAPI `x-tags`
+
+Other enhancements and bug fixes
+
+- fix typo in attribute ([#864](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/pull/864))
+- uncomment version dropdown styles ([#863](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/pull/863))
+- revert to canonical postman libraries ([#861](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/pull/861))
+- Fix tagGroup display when showSchemas is configured ([#851](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/pull/851))
+- check to avoid tagGroup config before concat operation, api, schemas ([#854](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/pull/854))
+- support empty object schema type ([#849](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/pull/849))
+- ensure readOnly/writeOnly are evaluated first ([#848](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/pull/848))
+- fix: markdown table within the description attribute cannot be rendered correctly ([#831](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/pull/831))
+- Implement the `x-tags` extension for schema objects ([#837](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/pull/837))
+- fix col row padding footer&pagination ([#810](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/pull/810))
+- Update index.tsx ([#839](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/pull/839))
+- Fix clean-api-docs not deleting sidebar.ts ([#829](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/pull/829))
+- Add option to disable frontmatter api prop compression ([#800](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/pull/800))
+- preventing to send form onClick left/right arrows in SchemaTabs component ([#796](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/pull/796))
+- changed theme and plugin to headings ([#786](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/pull/786))
+- Allow custom plugin to render ([#784](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/pull/784))
+- Remove scrollbar width for Tab components ([#785](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/pull/785))
+
+## 2.1.3 (Mar 22, 2024)
+
+High level enhancements
+
+- bugfix
+
+Other enhancements and bug fixes
+
+- ensure correct eval of required properties with allOf ([#771](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/pull/771))
+
+## 2.1.2 (Mar 21, 2024)
+
+High level enhancements
+
+- Various bug fixes
+
+Other enhancements and bug fixes
+
+- Fix allOf schema qualifier and type ([#766](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/pull/766))
+- Ensure qualifiers are rendered for polymorphic/primitive properties ([#765](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/pull/765))
+- uncomment line preventing grouping by operation tags ([#764](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/pull/764))
+- ensure resize observer is calculated once per frame to avoid loops ([#763](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/pull/763))
+
+## 2.1.1 (Mar 20, 2024)
+
+High level enhancements
+
+- Improved support for OpenAPI 3.1
+
+Other enhancements and bug fixes
+
+- [bugfix] Ensure 0 and false are guarded correctly and add deprecated support to params ([#754](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/pull/754))
+- upgrade openapi parsers ([#748](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/pull/748))
+
+## 2.1.0 (Mar 15, 2024)
+
+High level enhancements
+
+- Adds support for generating sidebar using x-tagGroup
+- Adds support for generating schemas
+- Improved x-codeSamples support
+
+Other enhancements and bug fixes
+
+- Upgrade dependencies ([#741](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/pull/741))
+- update compatibility matrix ([#739](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/pull/739))
+- feat: implement x-tagGroup feature ([#737](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/pull/737))
+- feat: Implement schema pages behind a config option `showSchemas` ([#736](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/pull/736))
+- fix: Guard only undefined and empty strings ([#725](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/pull/725))
+- Conditional display of header Request ([#719](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/pull/719))
+- Fix x-codeSamples load when switching language tabs ([#707](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/pull/707))
+
+## 2.0.4 (Jan 18, 2024)
+
+High level enhancements
+
+- Add x-codeSamples support
+- Add callbacks support
+
+Other enhancements and bug fixes
+
+- Add support to x-codeSamples ([#697](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/pull/697))
+- Remove deprecated node packages ([#699](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/pull/699))
+- Add Callbacks support ([#691](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/pull/691))
+
+## 2.0.3 (Jan 9, 2024)
+
+High level enhancements
+
+- Add callbacks support
+- Add markdown support to example/examples summary
+
+Other enhancements and bug fixes
+
+- Add markdown support to example summary ([#690](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/pull/690))
+- Add Callbacks support ([#691](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/pull/691))
+
+## 2.0.2 (Dec 5, 2023)
+
+High level enhancements
+
+- Adds security schemes to API Explorer panel
+
+Other enhancements and bug fixes
+
+- Add missing security schemes component ([#673](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/pull/673))
+
+## 2.0.1 (Dec 1, 2023)
+
+High level enhancements
+
+- Improve support for handling multi-part form data
+
+Other enhancements and bug fixes
+
+- Improve support for handling multipart form data ([#666](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/pull/666))
+- use SCHEMA_TYPE to distinguish request/response to support readOnly/writeOnly properties ([#665](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/pull/665))
+- add negative look behind to exclude colon delimited path segments ([#663](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/pull/663))
+- Update deploy-preview.yml
+- update v2.0.0 refs to v3.0.0
+- Update pr-title-check.yaml
+- Update canary-beta-release.yml
+- Update README.md
+
## 2.0.0 (Nov 13, 2023)
High level enhancements
diff --git a/README.md b/README.md
index 8e70c5744..c7b689d66 100644
--- a/README.md
+++ b/README.md
@@ -41,6 +41,7 @@ Key Features:
| Docusaurus OpenAPI Docs | Docusaurus |
| ----------------------- | --------------- |
+| 3.0.0-beta.x (beta) | `3.0.1 - 3.1.1` |
| 2.0.x (current) | `2.4.1 - 2.4.3` |
| 1.7.3 (legacy) | `2.0.1 - 2.2.0` |
@@ -145,10 +146,11 @@ Here is an example of properly configuring `docusaurus.config.js` file for `docu
The `docusaurus-plugin-openapi-docs` plugin can be configured with the following options:
-| Name | Type | Default | Description |
-| -------------- | -------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `id` | `string` | `null` | A unique plugin ID. |
-| `docsPluginId` | `string` | `null` | The ID associated with the `plugin-content-docs` or `preset` instance used to render the OpenAPI docs (e.g. "your-plugin-id", "classic", "default"). |
+| Name | Type | Default | Description |
+| -------------- | -------- | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `id` | `string` | `null` | A unique plugin ID. |
+| `docsPlugin` | `string` | `@docusaurus/plugin-content-docs` | The plugin used to render the OpenAPI docs (ignored if the plugin instance referenced by `docsPluginId` is a `preset`). |
+| `docsPluginId` | `string` | `null` | The plugin ID associated with the `preset` or configured `docsPlugin` instance used to render the OpenAPI docs (e.g. "your-plugin-id", "classic", "default"). |
### config
@@ -169,12 +171,13 @@ The `docusaurus-plugin-openapi-docs` plugin can be configured with the following
| `baseUrl` | `string` | `null` | _Optional:_ Version base URL used when generating version selector dropdown menu. |
| `versions` | `object` | `null` | _Optional:_ Set of options for versioning configuration. See below for a list of supported options. |
| `markdownGenerators` | `object` | `null` | _Optional:_ Customize MDX content with a set of options for specifying markdown generator functions. See below for a list of supported options. |
+| `showSchemas` | `boolean` | `null` | _Optional:_ If set to `true`, generates schema pages and adds them to the sidebar. |
`sidebarOptions` can be configured with the following options:
| Name | Type | Default | Description |
| -------------------- | --------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `groupPathsBy` | `string` | `null` | Organize and group sidebar slice by specified option. Note: Currently, `groupPathsBy` only contains support for grouping by `tag`. |
+| `groupPathsBy` | `string` | `null` | Organize and group sidebar slice by specified option. Note: Currently, `groupPathsBy` only contains support for grouping by `tag` and `tagGroup`. |
| `categoryLinkSource` | `string` | `null` | Defines what source to use for rendering category link pages when grouping paths by tag.
The supported options are as follows:
`tag`: Sets the category link config type to `generated-index` and uses the tag description as the link config description.
`info`: Sets the category link config type to `doc` and renders the `info` section as the category link (recommended only for multi/micro-spec scenarios).
`none`: Does not create pages for categories, only groups that can be expanded/collapsed. |
| `sidebarCollapsible` | `boolean` | `true` | Whether sidebar categories are collapsible by default. |
| `sidebarCollapsed` | `boolean` | `true` | Whether sidebar categories are collapsed by default. |
@@ -197,11 +200,12 @@ The `docusaurus-plugin-openapi-docs` plugin can be configured with the following
`markdownGenerators` can be configured with the following options:
-| Name | Type | Default | Description |
-| ------------------ | ---------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
-| `createApiPageMD` | `function` | `null` | _Optional:_ Returns a string of the raw markdown body for API pages.
**Function type:** `(pageData: ApiPageMetadata) => string` |
-| `createInfoPageMD` | `function` | `null` | _Optional:_ Returns a string of the raw markdown body for info pages.
**Function type:** `(pageData: InfoPageMetadata) => string` |
-| `createTagPageMD` | `function` | `null` | _Optional:_ Returns a string of the raw markdown body for tag pages.
**Function type:** `(pageData: TagPageMetadata) => string` |
+| Name | Type | Default | Description |
+| -------------------- | ---------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
+| `createApiPageMD` | `function` | `null` | _Optional:_ Returns a string of the raw markdown body for API pages.
**Function type:** `(pageData: ApiPageMetadata) => string` |
+| `createInfoPageMD` | `function` | `null` | _Optional:_ Returns a string of the raw markdown body for info pages.
**Function type:** `(pageData: InfoPageMetadata) => string` |
+| `createTagPageMD` | `function` | `null` | _Optional:_ Returns a string of the raw markdown body for tag pages.
**Function type:** `(pageData: TagPageMetadata) => string` |
+| `createSchemaPageMD` | `function` | `null` | _Optional:_ Returns a string of the raw markdown body for schema pages.
**Function type:** `(pageData: SchemaPageMetadata) => string` |
## CLI Usage
diff --git a/demo/docs/customization/languagetabs.md b/demo/docs/customization/languagetabs.md
index 0d9de3d36..b40c8cdca 100644
--- a/demo/docs/customization/languagetabs.md
+++ b/demo/docs/customization/languagetabs.md
@@ -15,7 +15,7 @@ The Docusaurus OpenAPI docs plugin comes with support for 8 languages which you
| `curl` | bash | `curl`\* |
| `python` | python | `requests`\*, `http.client` |
| `go` | go | `native`\* |
-| `nodejs` | javascript | `axios`\*, `native`, `requests`, `unirest` |
+| `nodejs` | javascript | `axios`\*, `native` |
| `ruby` | ruby | `net::http`\* |
| `csharp` | csharp | `restsharp`\*, `httpclient` |
| `php` | php | `curl`\*, `guzzle`, `pecl_http`, `http_request2` |
diff --git a/demo/docs/customization/styling.md b/demo/docs/customization/styling.md
index 7a3c34c60..6a43a5e22 100644
--- a/demo/docs/customization/styling.md
+++ b/demo/docs/customization/styling.md
@@ -18,13 +18,15 @@ The demo site uses the following CSS to add coloured labels to each request incl
```css
/* API Menu Items */
-.api-method > .menu__link {
+.api-method > .menu__link,
+.schema > .menu__link {
align-items: center;
justify-content: start;
}
-.api-method > .menu__link::before {
- width: 50px;
+.api-method > .menu__link::before,
+.schema > .menu__link::before {
+ width: 55px;
height: 20px;
font-size: 12px;
line-height: 20px;
@@ -68,6 +70,16 @@ The demo site uses the following CSS to add coloured labels to each request incl
content: "head";
background-color: var(--ifm-color-secondary-darkest);
}
+
+.event > .menu__link::before {
+ content: "event";
+ background-color: var(--ifm-color-secondary-darkest);
+}
+
+.schema > .menu__link::before {
+ content: "schema";
+ background-color: var(--ifm-color-secondary-darkest);
+}
```
## Alternative Styling
@@ -76,13 +88,15 @@ In [this issue](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/issu
```css
/* Sidebar Method labels */
-.api-method > .menu__link {
+.api-method > .menu__link,
+.schema > .menu__link {
align-items: center;
justify-content: start;
}
-.api-method > .menu__link::before {
- width: 50px;
+.api-method > .menu__link::before,
+.schema > .menu__link::before {
+ width: 55px;
height: 20px;
font-size: 12px;
line-height: 20px;
@@ -137,4 +151,18 @@ In [this issue](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/issu
color: var(--ifm-color-secondary-contrast-foreground);
border-color: var(--ifm-color-secondary-dark);
}
+
+.event > .menu__link::before {
+ content: "event";
+ background-color: var(--ifm-color-secondary-contrast-background);
+ color: var(--ifm-color-secondary-contrast-foreground);
+ border-color: var(--ifm-color-secondary-dark);
+}
+
+.schema > .menu__link::before {
+ content: "schema";
+ background-color: var(--ifm-color-secondary-contrast-background);
+ color: var(--ifm-color-secondary-contrast-foreground);
+ border-color: var(--ifm-color-secondary-dark);
+}
```
diff --git a/demo/docs/intro.mdx b/demo/docs/intro.mdx
index e5d43e55d..1143ae386 100644
--- a/demo/docs/intro.mdx
+++ b/demo/docs/intro.mdx
@@ -58,6 +58,7 @@ Key Features:
| Docusaurus OpenAPI Docs | Docusaurus |
| ----------------------- | --------------- |
+| 3.0.0-beta.x (beta) | `3.0.1 - 3.1.1` |
| 2.0.x (current) | `2.4.1 - 2.4.3` |
| 1.7.3 (legacy) | `2.0.1 - 2.2.0` |
@@ -95,13 +96,13 @@ yarn start
Both the plugin and theme are currently designed to pair with a specific Docusaurus release. The Docusaurus badge in the `README.md` and at the top of this page will always reflect the current compatible versions.
:::
-Plugin:
+### Plugin
```bash
yarn add docusaurus-plugin-openapi-docs
```
-Theme:
+### Theme
```bash
yarn add docusaurus-theme-openapi-docs
@@ -336,6 +337,7 @@ The `docusaurus-plugin-openapi-docs` plugin can be configured with the following
| `baseUrl` | `string` | `null` | _Optional:_ Version base URL used when generating version selector dropdown menu. |
| `versions` | `object` | `null` | _Optional:_ Set of options for versioning configuration. See below for a list of supported options. |
| `markdownGenerators` | `object` | `null` | _Optional:_ Customize MDX content with a set of options for specifying markdown generator functions. See below for a list of supported options. |
+| `showSchemas` | `boolean` | `null` | _Optional:_ If set to `true`, generates schema pages and adds them to the sidebar. |
### sidebarOptions
@@ -373,11 +375,12 @@ All versions will automatically inherit `sidebarOptions` from the parent/base co
`markdownGenerators` can be configured with the following options:
-| Name | Type | Default | Description |
-| ------------------ | ---------- | ------- | --------------------------------------------------------------------------------------------------------------------------------|
-| `createApiPageMD` | `function` | `null` | _Optional:_ Returns a string of the raw markdown body for API pages.
**Function type:** `(pageData: ApiPageMetadata) => string` |
-| `createInfoPageMD` | `function` | `null` | _Optional:_ Returns a string of the raw markdown body for info pages.
**Function type:** `(pageData: InfoPageMetadata) => string` |
-| `createTagPageMD` | `function` | `null` | _Optional:_ Returns a string of the raw markdown body for tag pages.
**Function type:** `(pageData: TagPageMetadata) => string` |
+| Name | Type | Default | Description |
+| ------------------- | ---------- | ------- | --------------------------------------------------------------------------------------------------------------------------------|
+| `createApiPageMD` | `function` | `null` | _Optional:_ Returns a string of the raw markdown body for API pages.
**Function type:** `(pageData: ApiPageMetadata) => string` |
+| `createInfoPageMD` | `function` | `null` | _Optional:_ Returns a string of the raw markdown body for info pages.
**Function type:** `(pageData: InfoPageMetadata) => string` |
+| `createTagPageMD` | `function` | `null` | _Optional:_ Returns a string of the raw markdown body for tag pages.
**Function type:** `(pageData: TagPageMetadata) => string` |
+| `createSchemaPageMD`| `function` | `null` | _Optional:_ Returns a string of the raw markdown body for schema pages.
**Function type:** `(pageData: SchemaPageMetadata) => string` |
:::info NOTE
If a config option is not provided, its default markdown generator will be used.
diff --git a/demo/docs/sidebars.md b/demo/docs/sidebars.md
index 73245e9e0..30bcb00d7 100644
--- a/demo/docs/sidebars.md
+++ b/demo/docs/sidebars.md
@@ -51,6 +51,14 @@ For greater control, you also have the option of constructing the sidebar object
The OpenAPI docs plugin provides out-of-the-box support for grouping paths by "tag". What this means is that the plugin will automatically generate a sidebar slice using the first path tag as the "group by" value and the path itself as one of the `items` under that category.
+### Grouping by TagGroup
+
+The OpenAPI docs plugin provides out-of-the-box support for grouping paths by "tagGroup".
+What this means is that the plugin will automatically generate a sidebar slice using the first path group as the "group by" value and the path itself as one of the `tags` under that category.
+Use `x-tagGroups` to group tags in the [Reference](https://redocly.com/docs/api-reference-docs/specification-extensions/x-tag-groups/) docs navigation sidebar. Add it to the root OpenAPI object.
+
+If `x-tagGroups` is used for grouping API paths, and you've also configured `showSchemas: true` for your OpenAPI Docs plugin, an additional "sibling" category labelled `Schemas` will be created and placed at the end of sidebar, after all the `tagGroups` categories.
+
### Category Links
Docusaurus now supports the ability to designate or customize what page gets displayed when a category is clicked.
@@ -60,3 +68,9 @@ The OpenAPI Docs plugin can leverage this feature in a number of ways, including
- Using the `generated-index` feature to create an index of all paths/endpoints available under a tag.
- Setting the `tag` description of an OpenAPI specification as the content that displays when a category is clicked.
- Setting the `info` section of an OpenAPI specification as the page that displays when a category is clicked (reserved primarily for micro-specs).
+
+### Grouping Schemas by `x-tags`
+
+The OpenAPI plugin provides out-of-the-box support for grouping schema objects into tags alongside path objects grouped by that same tag.
+
+What this means is that when the `groupPathsBy` sidebar option is set to `tag`, any `x-tag`ged schema objects will be gathered together with the tagged paths in that sidebar category. In the event that `showSchemas` is not configured, and `x-tags` is found on a schema object, the schema **will be included** in the relevant tag's category sidebar.
diff --git a/demo/docusaurus.config.js b/demo/docusaurus.config.js
index 409581e4e..c7355ece9 100644
--- a/demo/docusaurus.config.js
+++ b/demo/docusaurus.config.js
@@ -139,16 +139,20 @@ const config = {
additionalLanguages: ["ruby", "csharp", "php", "java", "powershell"],
},
languageTabs: [
+ {
+ highlight: "python",
+ language: "python",
+ logoClass: "python",
+ },
{
highlight: "bash",
language: "curl",
logoClass: "bash",
},
{
- highlight: "python",
- language: "python",
- logoClass: "python",
- variant: "requests",
+ highlight: "csharp",
+ language: "csharp",
+ logoClass: "csharp",
},
{
highlight: "go",
@@ -159,19 +163,12 @@ const config = {
highlight: "javascript",
language: "nodejs",
logoClass: "nodejs",
- variant: "axios",
},
{
highlight: "ruby",
language: "ruby",
logoClass: "ruby",
},
- {
- highlight: "csharp",
- language: "csharp",
- logoClass: "csharp",
- variant: "httpclient",
- },
{
highlight: "php",
language: "php",
@@ -218,12 +215,16 @@ const config = {
version: "2.0.0", // Current version
label: "v2.0.0", // Current version label
baseUrl: "/petstore_versioned/swagger-petstore-yaml", // Leading slash is important
+ downloadUrl:
+ "https://raw.githubusercontent.com/PaloAltoNetworks/docusaurus-openapi-docs/main/demo/examples/petstore.yaml",
versions: {
"1.0.0": {
specPath: "examples/petstore-1.0.0.yaml",
outputDir: "docs/petstore_versioned/1.0.0", // No trailing slash
label: "v1.0.0",
baseUrl: "/petstore_versioned/1.0.0/swagger-petstore-yaml", // Leading slash is important
+ downloadUrl:
+ "https://redocly.com/_spec/docs/openapi/petstore.json",
},
},
},
@@ -236,9 +237,10 @@ const config = {
categoryLinkSource: "tag",
},
template: "api.mustache", // Customize API MDX with mustache template
- downloadUrl:
- "https://raw.githubusercontent.com/PaloAltoNetworks/docusaurus-openapi-docs/main/demo/examples/petstore.yaml",
+ downloadUrl: "/petstore.yaml",
hideSendButton: false,
+ showSchemas: true,
+ disableCompression: true,
},
cos: {
specPath: "examples/openapi-cos.json",
@@ -255,6 +257,14 @@ const config = {
specPath: "examples/food/yogurtstore/openapi.yaml",
outputDir: "docs/food/yogurtstore",
},
+ restaurant: {
+ specPath: "examples/food/restaurant/openapi.yaml",
+ outputDir: "docs/restaurant",
+ sidebarOptions: {
+ groupPathsBy: "tagGroup",
+ },
+ showSchemas: true,
+ },
},
},
],
diff --git a/demo/examples/food/restaurant/_category_.json b/demo/examples/food/restaurant/_category_.json
new file mode 100644
index 000000000..54240eaac
--- /dev/null
+++ b/demo/examples/food/restaurant/_category_.json
@@ -0,0 +1,4 @@
+{
+ "label": "Restaurant",
+ "collapsed": true
+}
diff --git a/demo/examples/food/restaurant/openapi.yaml b/demo/examples/food/restaurant/openapi.yaml
new file mode 100644
index 000000000..be9eb022b
--- /dev/null
+++ b/demo/examples/food/restaurant/openapi.yaml
@@ -0,0 +1,81 @@
+openapi: 3.0.3
+info:
+ title: Restaurant Example
+ version: 1.0.0
+ description: Sample description.
+paths:
+ /menu:
+ get:
+ tags:
+ - tag1
+ summary: Get Menu
+ description: Froyo's the best!
+ responses:
+ 200:
+ description: OK
+ /products:
+ get:
+ tags:
+ - tag1
+ - tag2
+ summary: List All Products
+ description: Froyo's the best!
+ responses:
+ 200:
+ description: OK
+ /drinks:
+ get:
+ tags:
+ - tag1
+ - tag2
+ summary: List All Drinks
+ description: Froyo's the best!
+ responses:
+ 200:
+ description: OK
+ /pay:
+ post:
+ tags:
+ - tag3
+ summary: Make Payment
+ description: Froyo's the best!
+ responses:
+ 200:
+ description: OK
+
+components:
+ schemas:
+ Payment:
+ type: object
+ properties:
+ amount:
+ type: number
+ method:
+ type: string
+ enum: [cash, card, check]
+
+tags:
+ - name: tag1
+ description: Everything about your restaurant
+ x-displayName: Tag 1
+ - name: tag2
+ description: Tag 2 description
+ x-displayName: Tag 2
+ - name: tag3
+ description: Tag 3 description
+ x-displayName: Tag 3
+
+x-tagGroups:
+ - name: Tag 1 & 2
+ tags:
+ - tag1
+ - tag2
+ - name: Trinity
+ tags:
+ - tag1
+ - tag2
+ - tag3
+ - name: Last Two
+ tags:
+ - tag2
+ - tag3
diff --git a/demo/examples/petstore.yaml b/demo/examples/petstore.yaml
index c27f0d903..c31ac04ae 100644
--- a/demo/examples/petstore.yaml
+++ b/demo/examples/petstore.yaml
@@ -37,7 +37,7 @@ info:
Petstore offers two forms of authentication:
- API Key
- OAuth2
-
+
OAuth2 - an open protocol to allow secure authorization in a simple
and standard method from web, mobile and desktop applications.
@@ -147,6 +147,7 @@ paths:
Console.WriteLine(response.getRawResponse());
}
- lang: PHP
+ label: Custom
source: |
$form = new \PetStore\Entities\Pet();
$form->setPetType("Dog");
@@ -933,6 +934,8 @@ components:
message:
type: string
Cat:
+ x-tags:
+ - pet
description: A representation of a cat
allOf:
- $ref: "#/components/schemas/Pet"
@@ -995,6 +998,7 @@ components:
description: Average amount of honey produced per day in ounces
example: 3.14
multipleOf: .01
+ default: 0
required:
- honeyPerDay
Id:
diff --git a/demo/package.json b/demo/package.json
index e3f779d18..aad62d64d 100644
--- a/demo/package.json
+++ b/demo/package.json
@@ -1,6 +1,6 @@
{
"name": "demo",
- "version": "2.0.0",
+ "version": "2.2.0",
"private": true,
"scripts": {
"docusaurus": "docusaurus",
@@ -26,8 +26,8 @@
"@docusaurus/preset-classic": "2.4.3",
"@mdx-js/react": "^1.6.22",
"clsx": "^1.1.1",
- "docusaurus-plugin-openapi-docs": "^2.0.0",
- "docusaurus-theme-openapi-docs": "^2.0.0",
+ "docusaurus-plugin-openapi-docs": "^2.2.0",
+ "docusaurus-theme-openapi-docs": "^2.2.0",
"prism-react-renderer": "^1.3.1",
"react": "^18.2.0",
"react-dom": "^18.2.0"
diff --git a/demo/sidebars.js b/demo/sidebars.js
index c9755b4ec..0ec8de6b1 100644
--- a/demo/sidebars.js
+++ b/demo/sidebars.js
@@ -100,6 +100,16 @@ const sidebars = {
},
],
},
+ {
+ type: "category",
+ label: "Restaurant",
+ link: {
+ type: "generated-index",
+ title: "Restaurant API",
+ slug: "/category/restaurant-api",
+ },
+ items: require("./docs/restaurant/sidebar.js"),
+ },
],
"petstore-2.0.0": [
{
diff --git a/demo/src/css/custom.css b/demo/src/css/custom.css
index 6dc5ea86d..4ca833446 100644
--- a/demo/src/css/custom.css
+++ b/demo/src/css/custom.css
@@ -37,13 +37,15 @@ a:any-link:hover {
}
/* Sidebar Method labels */
-.api-method > .menu__link {
+.api-method > .menu__link,
+.schema > .menu__link {
align-items: center;
justify-content: start;
}
-.api-method > .menu__link::before {
- width: 50px;
+.api-method > .menu__link::before,
+.schema > .menu__link::before {
+ width: 55px;
height: 20px;
font-size: 12px;
line-height: 20px;
@@ -93,6 +95,11 @@ a:any-link:hover {
background-color: var(--ifm-color-secondary-darkest);
}
+.schema > .menu__link::before {
+ content: "schema";
+ background-color: var(--ifm-color-secondary-darkest);
+}
+
/* GitHub Header Link */
.header-github-link:hover {
opacity: 0.6;
diff --git a/demo/static/petstore.yaml b/demo/static/petstore.yaml
new file mode 100644
index 000000000..c31ac04ae
--- /dev/null
+++ b/demo/static/petstore.yaml
@@ -0,0 +1,1271 @@
+openapi: 3.0.0
+servers:
+ - url: https://petstore.swagger.io/v2
+ description: Default server
+ - url: https://petstore.swagger.io/sandbox
+ description: Sandbox server
+ - url: http://127.0.0.1:4010
+ description: Prism Mock API (local)
+info:
+ description: |
+ This is a sample server Petstore server.
+ You can find out more about Swagger at
+ [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/).
+ For this sample, you can use the api key `special-key` to test the authorization filters.
+
+ ## Introduction
+ This API is documented in **OpenAPI format** and is based on
+ [Petstore sample](http://petstore.swagger.io/) provided by [swagger.io](http://swagger.io) team.
+ It was **extended** to illustrate features of [generator-openapi-repo](https://github.com/Rebilly/generator-openapi-repo)
+ tool and [ReDoc](https://github.com/Redocly/redoc) documentation. In addition to standard
+ OpenAPI syntax we use a few [vendor extensions](https://github.com/Redocly/redoc/blob/master/docs/redoc-vendor-extensions.md).
+
+ ## OpenAPI Specification
+ This API is documented in **OpenAPI format** and is based on
+ [Petstore sample](http://petstore.swagger.io/) provided by [swagger.io](http://swagger.io) team.
+ It was **extended** to illustrate features of [generator-openapi-repo](https://github.com/Rebilly/generator-openapi-repo)
+ tool and [ReDoc](https://github.com/Redocly/redoc) documentation. In addition to standard
+ OpenAPI syntax we use a few [vendor extensions](https://github.com/Redocly/redoc/blob/master/docs/redoc-vendor-extensions.md).
+
+ ## Cross-Origin Resource Sharing
+ This API features Cross-Origin Resource Sharing (CORS) implemented in compliance with [W3C spec](https://www.w3.org/TR/cors/).
+ And that allows cross-domain communication from the browser.
+ All responses have a wildcard same-origin which makes them completely public and accessible to everyone, including any code on any site.
+
+ ## Authentication
+
+ Petstore offers two forms of authentication:
+ - API Key
+ - OAuth2
+
+ OAuth2 - an open protocol to allow secure authorization in a simple
+ and standard method from web, mobile and desktop applications.
+
+ version: 2.0.0
+ title: Swagger Petstore YAML
+ termsOfService: "http://swagger.io/terms/"
+ contact:
+ name: API Support
+ email: apiteam@swagger.io
+ url: https://github.com/Redocly/redoc
+ x-logo:
+ url: "https://redocly.github.io/redoc/petstore-logo.png"
+ altText: Petstore logo
+ x-dark-logo:
+ url: "/img/petstore-logo-dark.png"
+ altText: "Petstore dark logo"
+ license:
+ name: Apache 2.0
+ url: "http://www.apache.org/licenses/LICENSE-2.0.html"
+externalDocs:
+ description: Find out how to create Github repo for your OpenAPI spec.
+ url: "https://github.com/Rebilly/generator-openapi-repo"
+tags:
+ - name: pet
+ description: Everything about your Pets
+ x-displayName: Pets
+ - name: store
+ description: Access to Petstore orders
+ x-displayName: Petstore Orders
+ - name: user
+ description: Operations about user
+ x-displayName: Users
+ - name: pet_model
+ x-displayName: The Pet Model
+ description: |
+
+ - name: store_model
+ x-displayName: The Order Model
+ description: |
+
+x-tagGroups:
+ - name: General
+ tags:
+ - pet
+ - store
+ - name: User Management
+ tags:
+ - user
+ - name: Models
+ tags:
+ - pet_model
+ - store_model
+paths:
+ /pet:
+ parameters:
+ - name: Accept-Language
+ in: header
+ description: "The language you prefer for messages. Supported values are en-AU, en-CA, en-GB, en-US"
+ example: en-US
+ required: false
+ schema:
+ type: string
+ default: en-AU
+ - name: cookieParam
+ in: cookie
+ description: Some cookie
+ required: true
+ schema:
+ type: integer
+ format: int64
+ post:
+ tags:
+ - pet
+ summary: Add a new pet to the store
+ description: Add new pet to the store inventory.
+ operationId: addPet
+ responses:
+ "405":
+ description: Invalid input
+ security:
+ - petstore_auth:
+ - "write:pets"
+ - "read:pets"
+ - api_key: []
+ - ApiKeyAuth: []
+ - BasicAuth: []
+ - BearerAuth: []
+ - OAuth2: []
+ - OpenID: []
+
+ x-codeSamples:
+ - lang: "C#"
+ source: |
+ PetStore.v1.Pet pet = new PetStore.v1.Pet();
+ pet.setApiKey("your api key");
+ pet.petType = PetStore.v1.Pet.TYPE_DOG;
+ pet.name = "Rex";
+ // set other fields
+ PetStoreResponse response = pet.create();
+ if (response.statusCode == HttpStatusCode.Created)
+ {
+ // Successfully created
+ }
+ else
+ {
+ // Something wrong -- check response for errors
+ Console.WriteLine(response.getRawResponse());
+ }
+ - lang: PHP
+ label: Custom
+ source: |
+ $form = new \PetStore\Entities\Pet();
+ $form->setPetType("Dog");
+ $form->setName("Rex");
+ // set other fields
+ try {
+ $pet = $client->pets()->create($form);
+ } catch (UnprocessableEntityException $e) {
+ var_dump($e->getErrors());
+ }
+ requestBody:
+ $ref: "#/components/requestBodies/Pet"
+ put:
+ tags:
+ - pet
+ summary: Update an existing pet
+ description: ""
+ operationId: updatePet
+ responses:
+ "400":
+ description: Invalid ID supplied
+ "404":
+ description: Pet not found
+ "405":
+ description: Validation exception
+ security:
+ - petstore_auth:
+ - "write:pets"
+ - "read:pets"
+ x-codeSamples:
+ - lang: PHP
+ source: |
+ $form = new \PetStore\Entities\Pet();
+ $form->setPetId(1);
+ $form->setPetType("Dog");
+ $form->setName("Rex");
+ // set other fields
+ try {
+ $pet = $client->pets()->update($form);
+ } catch (UnprocessableEntityException $e) {
+ var_dump($e->getErrors());
+ }
+ requestBody:
+ $ref: "#/components/requestBodies/Pet"
+ "/pet/{petId}":
+ get:
+ tags:
+ - pet
+ summary: Find pet by ID
+ description: Returns a single pet
+ operationId: getPetById
+ parameters:
+ - name: petId
+ in: path
+ description: ID of pet to return
+ required: true
+ deprecated: true
+ schema:
+ type: integer
+ format: int64
+ responses:
+ "200":
+ description: successful operation
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Pet"
+ application/xml:
+ schema:
+ $ref: "#/components/schemas/Pet"
+
+ "400":
+ description: Invalid ID supplied
+ "404":
+ description: Pet not found
+ security:
+ - api_key: []
+ post:
+ tags:
+ - pet
+ summary: Updates a pet in the store with form data
+ description: ""
+ operationId: updatePetWithForm
+ parameters:
+ - name: petId
+ in: path
+ description: ID of pet that needs to be updated
+ required: true
+ schema:
+ type: integer
+ format: int64
+ responses:
+ "405":
+ description: Invalid input
+ security:
+ - petstore_auth:
+ - "write:pets"
+ - "read:pets"
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ properties:
+ name:
+ description: Updated name of the pet
+ type: string
+ status:
+ description: Updated status of the pet
+ type: string
+ delete:
+ tags:
+ - pet
+ summary: Deletes a pet
+ description: ""
+ operationId: deletePet
+ parameters:
+ - name: api_key
+ in: header
+ required: false
+ schema:
+ type: string
+ example: "Bearer "
+ - name: petId
+ in: path
+ description: Pet id to delete
+ required: true
+ schema:
+ type: integer
+ format: int64
+ responses:
+ "400":
+ description: Invalid pet value
+ security:
+ - petstore_auth:
+ - "write:pets"
+ - "read:pets"
+ "/pet/{petId}/uploadImage":
+ post:
+ tags:
+ - pet
+ summary: uploads an image
+ description: ""
+ operationId: uploadFile
+ parameters:
+ - name: petId
+ in: path
+ description: ID of pet to update
+ required: true
+ schema:
+ type: integer
+ format: int64
+ responses:
+ "200":
+ description: successful operation
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/ApiResponse"
+ security:
+ - petstore_auth:
+ - "write:pets"
+ - "read:pets"
+ requestBody:
+ content:
+ application/octet-stream:
+ schema:
+ type: string
+ format: binary
+ /pet/findByStatus:
+ get:
+ tags:
+ - pet
+ summary: Finds Pets by status
+ description: Multiple status values can be provided with comma separated strings
+ operationId: findPetsByStatus
+ parameters:
+ - name: status
+ in: query
+ description: Status values that need to be considered for filter
+ required: true
+ style: form
+ schema:
+ type: array
+ minItems: 1
+ maxItems: 3
+ items:
+ type: string
+ enum:
+ - available
+ - pending
+ - sold
+ default: available
+ responses:
+ "200":
+ description: successful operation
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: "#/components/schemas/Pet"
+ application/xml:
+ schema:
+ type: array
+ items:
+ $ref: "#/components/schemas/Pet"
+ "400":
+ description: Invalid status value
+ security:
+ - api_key: []
+ /pet/findByTags:
+ get:
+ tags:
+ - pet
+ summary: Finds Pets by tags
+ description: >-
+ Multiple tags can be provided with comma separated strings. Use tag1,
+ tag2, tag3 for testing.
+ operationId: findPetsByTags
+ deprecated: true
+ parameters:
+ - name: tags
+ in: query
+ description: Tags to filter by
+ required: true
+ style: form
+ schema:
+ type: array
+ items:
+ type: string
+ responses:
+ "200":
+ description: successful operation
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: "#/components/schemas/Pet"
+ application/xml:
+ schema:
+ type: array
+ items:
+ $ref: "#/components/schemas/Pet"
+ "400":
+ description: Invalid tag value
+ security:
+ - petstore_auth:
+ - "write:pets"
+ - "read:pets"
+ /store/inventory:
+ get:
+ tags:
+ - store
+ summary: Returns pet inventories by status
+ description: Returns a map of status codes to quantities
+ operationId: getInventory
+ responses:
+ "200":
+ description: successful operation
+ content:
+ application/json:
+ schema:
+ type: object
+ additionalProperties:
+ type: integer
+ format: int32
+ security:
+ - api_key: []
+ /store/order:
+ post:
+ tags:
+ - store
+ summary: Place an order for a pet
+ description: ""
+ operationId: placeOrder
+ responses:
+ "200":
+ description: successful operation
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Order"
+ application/xml:
+ schema:
+ $ref: "#/components/schemas/Order"
+ "400":
+ description: Invalid Order
+ content:
+ application/json:
+ example:
+ status: 400
+ message: "Invalid Order"
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Order"
+ examples:
+ OrderDelivered:
+ summary: Order delivered
+ value:
+ quantity: 4
+ shipDate: 2022-10-12
+ status: delivered
+ requestId: 444-4444-444-4444
+ OrderPlaced:
+ summary: Order placed
+ value:
+ quantity: 10
+ shipDate: 2022-10-01
+ status: placed
+ requestId: 111-222-333-444
+ OrderApproved:
+ summary: Order approved
+ value:
+ quantity: 1000
+ shipDate: 2022-09-01
+ status: approved
+ requestId: 000-111-222-333
+ description: order placed for purchasing the pet
+ required: true
+ "/store/order/{orderId}":
+ get:
+ tags:
+ - store
+ summary: Find purchase order by ID
+ description: >-
+ For valid response try integer IDs with value <= 5 or > 10. Other values
+ will generated exceptions
+ operationId: getOrderById
+ parameters:
+ - name: orderId
+ in: path
+ description: ID of pet that needs to be fetched
+ required: true
+ schema:
+ type: integer
+ format: int64
+ minimum: 1
+ maximum: 5
+ responses:
+ "200":
+ description: successful operation
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Order"
+ application/xml:
+ schema:
+ $ref: "#/components/schemas/Order"
+ "400":
+ description: Invalid ID supplied
+ "404":
+ description: Order not found
+ delete:
+ tags:
+ - store
+ summary: Delete purchase order by ID
+ description: >-
+ For valid response try integer IDs with value < 1000. Anything above
+ 1000 or nonintegers will generate API errors
+ operationId: deleteOrder
+ parameters:
+ - name: orderId
+ in: path
+ description: ID of the order that needs to be deleted
+ required: true
+ schema:
+ type: string
+ minimum: 1
+ responses:
+ "400":
+ description: Invalid ID supplied
+ "404":
+ description: Order not found
+ /store/subscribe:
+ post:
+ tags:
+ - store
+ summary: Subscribe to the Store events
+ description: Add subscription for a store events
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ callbackUrl:
+ type: string
+ format: uri
+ description: This URL will be called by the server when the desired event will occur
+ example: https://myserver.com/send/callback/here
+ eventName:
+ type: string
+ description: Event name for the subscription
+ enum:
+ - orderInProgress
+ - orderShipped
+ - orderDelivered
+ example: orderInProgress
+ required:
+ - callbackUrl
+ - eventName
+ responses:
+ "201":
+ description: Subscription added
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ subscriptionId:
+ type: string
+ example: AAA-123-BBB-456
+ callbacks:
+ orderInProgress:
+ "{$request.body#/callbackUrl}?event={$request.body#/eventName}":
+ servers:
+ - url: //callback-url.path-level/v1
+ description: Path level server 1
+ - url: //callback-url.path-level/v2
+ description: Path level server 2
+ post:
+ summary: Order in Progress (Summary)
+ description: A callback triggered every time an Order is updated status to "inProgress" (Description)
+ externalDocs:
+ description: Find out more
+ url: "https://more-details.com/demo"
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ orderId:
+ type: string
+ example: "123"
+ timestamp:
+ type: string
+ format: date-time
+ example: "2018-10-19T16:46:45Z"
+ status:
+ type: string
+ example: "inProgress"
+ application/xml:
+ schema:
+ type: object
+ properties:
+ orderId:
+ type: string
+ example: "123"
+ example: |
+
+
+ 123
+ inProgress
+ 2018-10-19T16:46:45Z
+
+ responses:
+ "200":
+ description: Callback successfully processed and no retries will be performed
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ someProp:
+ type: string
+ example: "123"
+ "299":
+ description: Response for cancelling subscription
+ "500":
+ description: Callback processing failed and retries will be performed
+ x-codeSamples:
+ - lang: "C#"
+ source: |
+ PetStore.v1.Pet pet = new PetStore.v1.Pet();
+ pet.setApiKey("your api key");
+ pet.petType = PetStore.v1.Pet.TYPE_DOG;
+ pet.name = "Rex";
+ // set other fields
+ PetStoreResponse response = pet.create();
+ if (response.statusCode == HttpStatusCode.Created)
+ {
+ // Successfully created
+ }
+ else
+ {
+ // Something wrong -- check response for errors
+ Console.WriteLine(response.getRawResponse());
+ }
+ - lang: PHP
+ source: |
+ $form = new \PetStore\Entities\Pet();
+ $form->setPetType("Dog");
+ $form->setName("Rex");
+ // set other fields
+ try {
+ $pet = $client->pets()->create($form);
+ } catch (UnprocessableEntityException $e) {
+ var_dump($e->getErrors());
+ }
+ put:
+ description: Order in Progress (Only Description)
+ servers:
+ - url: //callback-url.operation-level/v1
+ description: Operation level server 1 (Operation override)
+ - url: //callback-url.operation-level/v2
+ description: Operation level server 2 (Operation override)
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ orderId:
+ type: string
+ example: "123"
+ timestamp:
+ type: string
+ format: date-time
+ example: "2018-10-19T16:46:45Z"
+ status:
+ type: string
+ example: "inProgress"
+ application/xml:
+ schema:
+ type: object
+ properties:
+ orderId:
+ type: string
+ example: "123"
+ example: |
+
+
+ 123
+ inProgress
+ 2018-10-19T16:46:45Z
+
+ responses:
+ "200":
+ description: Callback successfully processed and no retries will be performed
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ someProp:
+ type: string
+ example: "123"
+ orderShipped:
+ "{$request.body#/callbackUrl}?event={$request.body#/eventName}":
+ post:
+ description: A callback triggered every time an Order is shipped to the recipient
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ orderId:
+ type: string
+ example: "123"
+ timestamp:
+ type: string
+ format: date-time
+ example: "2018-10-19T16:46:45Z"
+ estimatedDeliveryDate:
+ type: string
+ format: date-time
+ example: "2018-11-11T16:00:00Z"
+ responses:
+ "200":
+ description: Callback successfully processed and no retries will be performed
+ orderDelivered:
+ "http://notificationServer.com?url={$request.body#/callbackUrl}&event={$request.body#/eventName}":
+ post:
+ deprecated: true
+ summary: Order delivered
+ description: A callback triggered every time an Order is delivered to the recipient
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ orderId:
+ type: string
+ example: "123"
+ timestamp:
+ type: string
+ format: date-time
+ example: "2018-10-19T16:46:45Z"
+ responses:
+ "200":
+ description: Callback successfully processed and no retries will be performed
+ /user:
+ post:
+ tags:
+ - user
+ summary: Create user
+ description: This can only be done by the logged in user.
+ operationId: createUser
+ responses:
+ default:
+ description: successful operation
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/User"
+ description: Created user object
+ required: true
+ "/user/{username}":
+ get:
+ tags:
+ - user
+ summary: Get user by user name
+ description: ""
+ operationId: getUserByName
+ parameters:
+ - name: username
+ in: path
+ description: "The name that needs to be fetched. Use user1 for testing. "
+ required: true
+ schema:
+ type: string
+ responses:
+ "200":
+ description: successful operation
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/User"
+ application/xml:
+ schema:
+ $ref: "#/components/schemas/User"
+ "400":
+ description: Invalid username supplied
+ "404":
+ description: User not found
+ put:
+ tags:
+ - user
+ summary: Updated user
+ description: This can only be done by the logged in user.
+ operationId: updateUser
+ parameters:
+ - name: username
+ in: path
+ description: name that need to be deleted
+ required: true
+ schema:
+ type: string
+ responses:
+ "400":
+ description: Invalid user supplied
+ "404":
+ description: User not found
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/User"
+ description: Updated user object
+ required: true
+ delete:
+ tags:
+ - user
+ summary: Delete user
+ description: This can only be done by the logged in user.
+ operationId: deleteUser
+ parameters:
+ - name: username
+ in: path
+ description: The name that needs to be deleted
+ required: true
+ schema:
+ type: string
+ responses:
+ "400":
+ description: Invalid username supplied
+ "404":
+ description: User not found
+ /user/createWithArray:
+ post:
+ tags:
+ - user
+ summary: Creates list of users with given input array
+ description: ""
+ operationId: createUsersWithArrayInput
+ responses:
+ default:
+ description: successful operation
+ requestBody:
+ $ref: "#/components/requestBodies/UserArray"
+ /user/createWithList:
+ post:
+ tags:
+ - user
+ summary: Creates list of users with given input list
+ description: ""
+ operationId: createUsersWithListInput
+ responses:
+ default:
+ description: successful operation
+ requestBody:
+ $ref: "#/components/requestBodies/UserArray"
+ /user/login:
+ get:
+ tags:
+ - user
+ summary: Logs user into the system
+ description: ""
+ operationId: loginUser
+ parameters:
+ - name: username
+ in: query
+ description: The user name for login
+ required: true
+ schema:
+ type: string
+ - name: password
+ in: query
+ description: The password for login in clear text
+ required: true
+ schema:
+ type: string
+ responses:
+ "200":
+ description: successful operation
+ headers:
+ X-Rate-Limit:
+ description: calls per hour allowed by the user
+ schema:
+ type: integer
+ format: int32
+ X-Expires-After:
+ description: date in UTC when token expires
+ schema:
+ type: string
+ format: date-time
+ content:
+ application/json:
+ schema:
+ type: string
+ examples:
+ response:
+ value: OK
+ application/xml:
+ schema:
+ type: string
+ examples:
+ response:
+ value: OK
+ text/plain:
+ examples:
+ response:
+ value: OK
+ "400":
+ description: Invalid username/password supplied
+ /user/logout:
+ get:
+ tags:
+ - user
+ summary: Logs out current logged in user session
+ description: ""
+ operationId: logoutUser
+ responses:
+ default:
+ description: successful operation
+components:
+ schemas:
+ ApiResponse:
+ type: object
+ properties:
+ code:
+ type: integer
+ format: int32
+ type:
+ type: string
+ message:
+ type: string
+ Cat:
+ x-tags:
+ - pet
+ description: A representation of a cat
+ allOf:
+ - $ref: "#/components/schemas/Pet"
+ - type: object
+ properties:
+ huntingSkill:
+ type: string
+ description: The measured skill for hunting
+ default: lazy
+ example: adventurous
+ enum:
+ - clueless
+ - lazy
+ - adventurous
+ - aggressive
+ required:
+ - huntingSkill
+ Category:
+ type: object
+ properties:
+ id:
+ description: Category ID
+ allOf:
+ - $ref: "#/components/schemas/Id"
+ name:
+ description: Category name
+ type: string
+ minLength: 1
+ sub:
+ description: Test Sub Category
+ type: object
+ properties:
+ prop1:
+ type: string
+ description: Dumb Property
+ xml:
+ name: Category
+ Dog:
+ description: A representation of a dog
+ allOf:
+ - $ref: "#/components/schemas/Pet"
+ - type: object
+ properties:
+ packSize:
+ type: integer
+ format: int32
+ description: The size of the pack the dog is from
+ default: 1
+ minimum: 1
+ required:
+ - packSize
+ HoneyBee:
+ description: A representation of a honey bee
+ allOf:
+ - $ref: "#/components/schemas/Pet"
+ - type: object
+ properties:
+ honeyPerDay:
+ type: number
+ description: Average amount of honey produced per day in ounces
+ example: 3.14
+ multipleOf: .01
+ default: 0
+ required:
+ - honeyPerDay
+ Id:
+ type: integer
+ format: int64
+ readOnly: true
+ Order:
+ type: object
+ properties:
+ id:
+ description: Order ID
+ allOf:
+ - $ref: "#/components/schemas/Id"
+ petId:
+ description: Pet ID
+ allOf:
+ - $ref: "#/components/schemas/Id"
+ quantity:
+ type: integer
+ format: int32
+ minimum: 1
+ default: 1
+ shipDate:
+ description: Estimated ship date
+ type: string
+ format: date-time
+ status:
+ type: string
+ description: Order Status
+ enum:
+ - placed
+ - approved
+ - delivered
+ complete:
+ description: Indicates whenever order was completed or not
+ type: boolean
+ default: false
+ readOnly: true
+ requestId:
+ description: Unique Request Id
+ type: string
+ writeOnly: true
+ xml:
+ name: Order
+ Pet:
+ type: object
+ required:
+ - name
+ - photoUrls
+ - tags
+ discriminator:
+ propertyName: petType
+ mapping:
+ cat: "#/components/schemas/Cat"
+ dog: "#/components/schemas/Dog"
+ bee: "#/components/schemas/HoneyBee"
+ properties:
+ id:
+ externalDocs:
+ description: "Find more info here"
+ url: "https://example.com"
+ description: Pet ID
+ allOf:
+ - $ref: "#/components/schemas/Id"
+ category:
+ description: Categories this pet belongs to
+ allOf:
+ - $ref: "#/components/schemas/Category"
+ name:
+ description: The name given to a pet
+ type: string
+ example: Guru
+ photoUrls:
+ description: The list of URL to a cute photos featuring pet
+ type: array
+ maxItems: 20
+ xml:
+ name: photoUrl
+ wrapped: true
+ items:
+ type: string
+ format: url
+ friend:
+ allOf:
+ - $ref: "#/components/schemas/Pet"
+ tags:
+ description: Tags attached to the pet
+ type: array
+ minItems: 1
+ xml:
+ name: tag
+ wrapped: true
+ items:
+ $ref: "#/components/schemas/Tag"
+ status:
+ type: string
+ description: Pet status in the store
+ enum:
+ - available
+ - pending
+ - sold
+ petType:
+ description: Type of a pet
+ type: string
+ oneOf:
+ - $ref: "#/components/schemas/Cat"
+ - $ref: "#/components/schemas/Dog"
+ - $ref: "#/components/schemas/HoneyBee"
+ xml:
+ name: Pet
+ Tag:
+ type: object
+ properties:
+ id:
+ description: Tag ID
+ allOf:
+ - $ref: "#/components/schemas/Id"
+ name:
+ description: Tag name
+ type: string
+ minLength: 1
+ xml:
+ name: Tag
+ User:
+ type: object
+ properties:
+ id:
+ $ref: "#/components/schemas/Id"
+ pet:
+ oneOf:
+ - $ref: "#/components/schemas/Pet"
+ - $ref: "#/components/schemas/Tag"
+ username:
+ description: User supplied username
+ type: string
+ minLength: 4
+ example: John78
+ firstName:
+ description: User first name
+ type: string
+ minLength: 1
+ example: John
+ lastName:
+ description: User last name
+ type: string
+ minLength: 1
+ example: Smith
+ email:
+ description: User email address
+ type: string
+ format: email
+ example: john.smith@example.com
+ password:
+ type: string
+ description: >-
+ User password, MUST contain a mix of upper and lower case letters,
+ as well as digits
+ format: password
+ minLength: 8
+ pattern: "/(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])/"
+ example: drowssaP123
+ phone:
+ description: User phone number in international format
+ type: string
+ pattern: '/^\+(?:[0-9]-?){6,14}[0-9]$/'
+ example: +1-202-555-0192
+ userStatus:
+ description: User status
+ type: integer
+ format: int32
+ xml:
+ name: User
+ requestBodies:
+ Pet:
+ content:
+ application/json:
+ schema:
+ allOf:
+ - description: My Pet
+ title: Pettie
+ - $ref: "#/components/schemas/Pet"
+ example:
+ category:
+ name: Great Dane
+ sub:
+ prop1: Just a test property
+ name: Pepper
+ photoUrls:
+ - https://assets.orvis.com/is/image/orvisprd/great-dane
+ tags:
+ - name: Great Danes
+ status: pending
+ petType:
+ huntingSkill: lazy
+ application/xml:
+ schema:
+ type: "object"
+ properties:
+ name:
+ type: string
+ description: hooray
+ description: Pet object that needs to be added to the store
+ required: true
+ UserArray:
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: "#/components/schemas/User"
+ description: List of user object
+ required: true
+ securitySchemes:
+ petstore_auth:
+ description: |
+ Get access to data while protecting your account credentials.
+ OAuth2 is also a safer and more secure way to give you access.
+ type: oauth2
+ flows:
+ implicit:
+ authorizationUrl: "http://petstore.swagger.io/api/oauth/dialog"
+ scopes:
+ "write:pets": modify pets in your account
+ "read:pets": read your pets
+ api_key:
+ description: >
+ For this sample, you can use the api key `special-key` to test the
+ authorization filters.
+ type: apiKey
+ name: api_key
+ in: header
+ BasicAuth:
+ type: http
+ scheme: basic
+ BearerAuth:
+ type: http
+ scheme: bearer
+ ApiKeyAuth:
+ type: apiKey
+ in: header
+ name: X-API-Key
+ OpenID:
+ type: openIdConnect
+ openIdConnectUrl: https://example.com/.well-known/openid-configuration
+ OAuth2:
+ type: oauth2
+ flows:
+ authorizationCode:
+ authorizationUrl: https://example.com/oauth/authorize
+ tokenUrl: https://example.com/oauth/token
+ scopes:
+ read: Grants read access
+ write: Grants write access
+ admin: Grants access to admin operations
+x-webhooks:
+ newPet:
+ post:
+ summary: New pet
+ description: Information about a new pet in the systems
+ operationId: newPet
+ tags:
+ - pet
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Pet"
+ responses:
+ "200":
+ description: Return a 200 status to indicate that the data was received successfully
diff --git a/lerna.json b/lerna.json
index db66e79ea..91d66593d 100644
--- a/lerna.json
+++ b/lerna.json
@@ -1,5 +1,5 @@
{
- "version": "2.0.0",
+ "version": "2.2.0",
"npmClient": "yarn",
"useWorkspaces": true
}
diff --git a/package.json b/package.json
index 0e7c3a1c8..c61cdd9a5 100644
--- a/package.json
+++ b/package.json
@@ -41,6 +41,7 @@
},
"devDependencies": {
"@babel/core": "^7.16.0",
+ "@eslint-community/eslint-utils": "^4.4.0",
"@testing-library/cypress": "^8.0.1",
"@types/jest": "^27.0.2",
"@types/node": "^17.0.2",
@@ -76,5 +77,6 @@
},
"engines": {
"node": ">=14"
- }
+ },
+ "packageManager": "yarn@1.22.1"
}
diff --git a/packages/docusaurus-plugin-openapi-docs/README.md b/packages/docusaurus-plugin-openapi-docs/README.md
index 5be5e9751..a775eeccb 100644
--- a/packages/docusaurus-plugin-openapi-docs/README.md
+++ b/packages/docusaurus-plugin-openapi-docs/README.md
@@ -31,6 +31,7 @@ Key Features:
| Docusaurus OpenAPI Docs | Docusaurus |
| ----------------------- | --------------- |
+| 3.0.0-beta.x (beta) | `3.0.1 - 3.1.1` |
| 2.0.x (current) | `2.4.1 - 2.4.3` |
| 1.7.3 (legacy) | `2.0.1 - 2.2.0` |
@@ -159,6 +160,7 @@ The `docusaurus-plugin-openapi-docs` plugin can be configured with the following
| `baseUrl` | `string` | `null` | _Optional:_ Version base URL used when generating version selector dropdown menu. |
| `versions` | `object` | `null` | _Optional:_ Set of options for versioning configuration. See below for a list of supported options. |
| `markdownGenerators` | `object` | `null` | _Optional:_ Customize MDX content with a set of options for specifying markdown generator functions. See below for a list of supported options. |
+| `showSchemas` | `boolean` | `null` | _Optional:_ If set to `true`, generates schema pages and adds them to the sidebar. |
`sidebarOptions` can be configured with the following options:
diff --git a/packages/docusaurus-plugin-openapi-docs/package.json b/packages/docusaurus-plugin-openapi-docs/package.json
index 0fe385bc3..599116f1d 100644
--- a/packages/docusaurus-plugin-openapi-docs/package.json
+++ b/packages/docusaurus-plugin-openapi-docs/package.json
@@ -1,7 +1,7 @@
{
"name": "docusaurus-plugin-openapi-docs",
"description": "OpenAPI plugin for Docusaurus.",
- "version": "2.0.0",
+ "version": "2.2.0",
"license": "MIT",
"keywords": [
"openapi",
@@ -36,13 +36,11 @@
"@types/mustache": "^4.1.2"
},
"dependencies": {
- "@apidevtools/json-schema-ref-parser": "^10.1.0",
+ "@apidevtools/json-schema-ref-parser": "^11.5.4",
"@docusaurus/plugin-content-docs": ">=2.4.1 <=2.4.3",
"@docusaurus/utils": ">=2.4.1 <=2.4.3",
"@docusaurus/utils-validation": ">=2.4.1 <=2.4.3",
- "@paloaltonetworks/openapi-to-postmanv2": "3.1.0-hotfix.1",
- "@paloaltonetworks/postman-collection": "^4.1.0",
- "@redocly/openapi-core": "^1.0.0-beta.125",
+ "@redocly/openapi-core": "^1.10.5",
"chalk": "^4.1.2",
"clsx": "^1.1.1",
"fs-extra": "^9.0.1",
@@ -50,6 +48,8 @@
"json-schema-merge-allof": "^0.8.1",
"lodash": "^4.17.20",
"mustache": "^4.2.0",
+ "openapi-to-postmanv2": "^4.21.0",
+ "postman-collection": "^4.4.0",
"slugify": "^1.6.5",
"swagger2openapi": "^7.0.8",
"xml-formatter": "^2.6.1"
diff --git a/packages/docusaurus-plugin-openapi-docs/src/index.ts b/packages/docusaurus-plugin-openapi-docs/src/index.ts
index ea679f25b..730d1de45 100644
--- a/packages/docusaurus-plugin-openapi-docs/src/index.ts
+++ b/packages/docusaurus-plugin-openapi-docs/src/index.ts
@@ -14,11 +14,25 @@ import { Globby, posixPath } from "@docusaurus/utils";
import chalk from "chalk";
import { render } from "mustache";
-import { createApiPageMD, createInfoPageMD, createTagPageMD } from "./markdown";
+import {
+ createApiPageMD,
+ createInfoPageMD,
+ createSchemaPageMD,
+ createTagPageMD,
+} from "./markdown";
import { readOpenapiFiles, processOpenapiFiles } from "./openapi";
import { OptionsSchema } from "./options";
import generateSidebarSlice from "./sidebars";
-import type { PluginOptions, LoadedContent, APIOptions } from "./types";
+import type {
+ PluginOptions,
+ LoadedContent,
+ APIOptions,
+ ApiMetadata,
+ ApiPageMetadata,
+ InfoPageMetadata,
+ TagPageMetadata,
+ SchemaPageMetadata,
+} from "./types";
export function isURL(str: string): boolean {
return /^(https?:)\/\//m.test(str);
@@ -26,6 +40,7 @@ export function isURL(str: string): boolean {
export function getDocsPluginConfig(
presetsPlugins: any[],
+ plugin: string,
pluginId: string
): Object | undefined {
// eslint-disable-next-line array-callback-return
@@ -37,10 +52,7 @@ export function getDocsPluginConfig(
}
// Search plugin-content-docs instances
- if (
- typeof data[0] === "string" &&
- data[0] === "@docusaurus/plugin-content-docs"
- ) {
+ if (typeof data[0] === "string" && data[0] === plugin) {
const configPluginId = data[1].id ? data[1].id : "default";
if (configPluginId === pluginId) {
return data[1];
@@ -56,7 +68,7 @@ export function getDocsPluginConfig(
}
// Search plugin-content-docs instances
- if (filteredConfig[0] === "@docusaurus/plugin-content-docs") {
+ if (filteredConfig[0] === plugin) {
const configPluginId = filteredConfig[1].id
? filteredConfig[1].id
: "default";
@@ -80,14 +92,22 @@ export default function pluginOpenAPIDocs(
context: LoadContext,
options: PluginOptions
): Plugin {
- const { config, docsPluginId } = options;
+ const {
+ config,
+ docsPlugin = "@docusaurus/plugin-content-docs",
+ docsPluginId,
+ } = options;
const { siteDir, siteConfig } = context;
// Get routeBasePath and path from plugin-content-docs or preset
const presets: any = siteConfig.presets;
const plugins: any = siteConfig.plugins;
const presetsPlugins = presets.concat(plugins);
- let docData: any = getDocsPluginConfig(presetsPlugins, docsPluginId);
+ let docData: any = getDocsPluginConfig(
+ presetsPlugins,
+ docsPlugin,
+ docsPluginId
+ );
let docRouteBasePath = docData ? docData.routeBasePath : undefined;
let docPath = docData ? (docData.path ? docData.path : "docs") : undefined;
@@ -99,6 +119,7 @@ export default function pluginOpenAPIDocs(
markdownGenerators,
downloadUrl,
sidebarOptions,
+ disableCompression,
} = options;
// Remove trailing slash before proceeding
@@ -106,7 +127,7 @@ export default function pluginOpenAPIDocs(
// Override docPath if pluginId provided
if (pluginId) {
- docData = getDocsPluginConfig(presetsPlugins, pluginId);
+ docData = getDocsPluginConfig(presetsPlugins, docsPlugin, pluginId);
docRouteBasePath = docData ? docData.routeBasePath : undefined;
docPath = docData ? (docData.path ? docData.path : "docs") : undefined;
}
@@ -117,7 +138,7 @@ export default function pluginOpenAPIDocs(
try {
const openapiFiles = await readOpenapiFiles(contentPath);
- const [loadedApi, tags] = await processOpenapiFiles(
+ const [loadedApi, tags, tagGroups] = await processOpenapiFiles(
openapiFiles,
options,
sidebarOptions!
@@ -141,7 +162,8 @@ export default function pluginOpenAPIDocs(
options,
loadedApi,
tags,
- docPath
+ docPath,
+ tagGroups
);
const sidebarSliceTemplate = `module.exports = {{{slice}}};`;
@@ -244,25 +266,51 @@ import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
\`\`\`
`;
+ const schemaMdTemplate = `---
+id: {{{id}}}
+title: "{{{title}}}"
+description: "{{{frontMatter.description}}}"
+sidebar_label: "{{{title}}}"
+hide_title: true
+schema: true
+custom_edit_url: null
+---
+
+{{{markdown}}}
+ `;
+
const apiPageGenerator =
markdownGenerators?.createApiPageMD ?? createApiPageMD;
const infoPageGenerator =
markdownGenerators?.createInfoPageMD ?? createInfoPageMD;
const tagPageGenerator =
markdownGenerators?.createTagPageMD ?? createTagPageMD;
+ const schemaPageGenerator =
+ markdownGenerators?.createSchemaPageMD ?? createSchemaPageMD;
+
+ const pageGeneratorByType: {
+ [key in ApiMetadata["type"]]: (
+ pageData: {
+ api: ApiPageMetadata;
+ info: InfoPageMetadata;
+ tag: TagPageMetadata;
+ schema: SchemaPageMetadata;
+ }[key]
+ ) => string;
+ } = {
+ api: apiPageGenerator,
+ info: infoPageGenerator,
+ tag: tagPageGenerator,
+ schema: schemaPageGenerator,
+ };
loadedApi.map(async (item) => {
if (item.type === "info") {
- if (downloadUrl && isURL(downloadUrl)) {
+ if (downloadUrl) {
item.downloadUrl = downloadUrl;
}
}
- const markdown =
- item.type === "api"
- ? apiPageGenerator(item)
- : item.type === "info"
- ? infoPageGenerator(item)
- : tagPageGenerator(item);
+ const markdown = pageGeneratorByType[item.type](item as any);
item.markdown = markdown;
if (item.type === "api") {
// opportunity to compress JSON
@@ -272,9 +320,11 @@ import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
// const deserialize = (s: any) => {
// return zlib.inflateSync(Buffer.from(s, "base64")).toString();
// };
- item.json = zlib
- .deflateSync(JSON.stringify(item.api))
- .toString("base64");
+ disableCompression === true
+ ? (item.json = JSON.stringify(item.api))
+ : (item.json = zlib
+ .deflateSync(JSON.stringify(item.api))
+ .toString("base64"));
let infoBasePath = `${outputDir}/${item.infoId}`;
if (docRouteBasePath) {
infoBasePath = `${docRouteBasePath}/${outputDir
@@ -363,6 +413,49 @@ import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
}
}
}
+
+ if (item.type === "schema") {
+ if (!fs.existsSync(`${outputDir}/schemas/${item.id}.schema.mdx`)) {
+ if (!fs.existsSync(`${outputDir}/schemas`)) {
+ try {
+ fs.mkdirSync(`${outputDir}/schemas`, { recursive: true });
+ console.log(
+ chalk.green(`Successfully created "${outputDir}/schemas"`)
+ );
+ } catch (err) {
+ console.error(
+ chalk.red(`Failed to create "${outputDir}/schemas"`),
+ chalk.yellow(err)
+ );
+ }
+ }
+ try {
+ // kebabCase(arg) returns 0-length string when arg is undefined
+ if (item.id.length === 0) {
+ throw Error("Schema must have title defined");
+ }
+ // eslint-disable-next-line testing-library/render-result-naming-convention
+ const schemaView = render(schemaMdTemplate, item);
+ fs.writeFileSync(
+ `${outputDir}/schemas/${item.id}.schema.mdx`,
+ schemaView,
+ "utf8"
+ );
+ console.log(
+ chalk.green(
+ `Successfully created "${outputDir}/${item.id}.schema.mdx"`
+ )
+ );
+ } catch (err) {
+ console.error(
+ chalk.red(
+ `Failed to write "${outputDir}/${item.id}.schema.mdx"`
+ ),
+ chalk.yellow(err)
+ );
+ }
+ }
+ }
return;
});
@@ -380,7 +473,11 @@ import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
cwd: path.resolve(apiDir),
deep: 1,
});
- const sidebarFile = await Globby(["sidebar.js"], {
+ const schemaMdxFiles = await Globby(["*.schema.mdx"], {
+ cwd: path.resolve(apiDir, "schemas"),
+ deep: 1,
+ });
+ const sidebarFile = await Globby(["sidebar.{js,ts}"], {
cwd: path.resolve(apiDir),
deep: 1,
});
@@ -397,6 +494,21 @@ import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
})
);
+ schemaMdxFiles.map((mdx) =>
+ fs.unlink(`${apiDir}/schemas/${mdx}`, (err) => {
+ if (err) {
+ console.error(
+ chalk.red(`Cleanup failed for "${apiDir}/schemas/${mdx}"`),
+ chalk.yellow(err)
+ );
+ } else {
+ console.log(
+ chalk.green(`Cleanup succeeded for "${apiDir}/schemas/${mdx}"`)
+ );
+ }
+ })
+ );
+
sidebarFile.map((sidebar) =>
fs.unlink(`${apiDir}/${sidebar}`, (err) => {
if (err) {
@@ -420,6 +532,7 @@ import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
version: version,
label: metadata.label,
baseUrl: metadata.baseUrl,
+ downloadUrl: metadata.downloadUrl,
});
}
@@ -570,6 +683,7 @@ import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
delete parentConfig.version;
delete parentConfig.label;
delete parentConfig.baseUrl;
+ delete parentConfig.downloadUrl;
// TODO: handle when no versions are defined by version command is passed
if (versionId === "all") {
diff --git a/packages/docusaurus-plugin-openapi-docs/src/markdown/__snapshots__/createSchema.test.ts.snap b/packages/docusaurus-plugin-openapi-docs/src/markdown/__snapshots__/createSchema.test.ts.snap
index 8a1b9c507..9bf878b02 100644
--- a/packages/docusaurus-plugin-openapi-docs/src/markdown/__snapshots__/createSchema.test.ts.snap
+++ b/packages/docusaurus-plugin-openapi-docs/src/markdown/__snapshots__/createSchema.test.ts.snap
@@ -35,13 +35,7 @@ Array [
Array [
-
+
string
@@ -57,35 +51,17 @@ Array [
-
+
boolean
-
+
number
-
+
string
diff --git a/packages/docusaurus-plugin-openapi-docs/src/markdown/createAuthentication.ts b/packages/docusaurus-plugin-openapi-docs/src/markdown/createAuthentication.ts
index 013764016..fe8012ad9 100644
--- a/packages/docusaurus-plugin-openapi-docs/src/markdown/createAuthentication.ts
+++ b/packages/docusaurus-plugin-openapi-docs/src/markdown/createAuthentication.ts
@@ -5,9 +5,9 @@
* LICENSE file in the root directory of this source tree.
* ========================================================================== */
-import { OAuthFlowObject, SecuritySchemeObject } from "../openapi/types";
import { createDescription } from "./createDescription";
import { create, guard } from "./utils";
+import { OAuthFlowObject, SecuritySchemeObject } from "../openapi/types";
export function createAuthentication(securitySchemes: SecuritySchemeObject) {
if (!securitySchemes || !Object.keys(securitySchemes).length) return "";
diff --git a/packages/docusaurus-plugin-openapi-docs/src/markdown/createCallbacks.ts b/packages/docusaurus-plugin-openapi-docs/src/markdown/createCallbacks.ts
new file mode 100644
index 000000000..ec32760e6
--- /dev/null
+++ b/packages/docusaurus-plugin-openapi-docs/src/markdown/createCallbacks.ts
@@ -0,0 +1,95 @@
+/* ============================================================================
+ * Copyright (c) Palo Alto Networks
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ * ========================================================================== */
+
+import { createDescription } from "./createDescription";
+import { createMethodEndpoint } from "./createMethodEndpoint";
+import { createRequestBodyDetails } from "./createRequestBodyDetails";
+import { createStatusCodes } from "./createStatusCodes";
+import { create } from "./utils";
+import { MediaTypeObject } from "../openapi/types";
+import { ApiItem } from "../types";
+
+interface Props {
+ callbacks: ApiItem["callbacks"];
+}
+
+interface RequestBodyProps {
+ title: string;
+ body: {
+ content?: {
+ [key: string]: MediaTypeObject;
+ };
+ description?: string;
+ required?: boolean;
+ };
+}
+
+export function createCallbacks({ callbacks }: Props) {
+ if (callbacks === undefined) {
+ return undefined;
+ }
+
+ const callbacksNames = Object.keys(callbacks);
+ if (callbacksNames.length === 0) {
+ return undefined;
+ }
+
+ return create("div", {
+ children: [
+ create("div", {
+ className: "openapi__divider",
+ }),
+ create("h2", {
+ children: "Callbacks",
+ id: "callbacks",
+ }),
+ create("OperationTabs", {
+ className: "openapi-tabs__operation",
+ children: callbacksNames.flatMap((name) => {
+ const path = Object.keys(callbacks[name])[0];
+ const methods = new Map([
+ ["delete", callbacks[name][path].delete],
+ ["get", callbacks[name][path].get],
+ ["head", callbacks[name][path].head],
+ ["options", callbacks[name][path].options],
+ ["patch", callbacks[name][path].patch],
+ ["post", callbacks[name][path].post],
+ ["put", callbacks[name][path].put],
+ ["trace", callbacks[name][path].trace],
+ ]);
+
+ return Array.from(methods).flatMap(([method, operationObject]) => {
+ if (!operationObject) return [];
+
+ const { description, requestBody, responses } = operationObject;
+
+ return [
+ create("TabItem", {
+ label: `${method.toUpperCase()} ${name}`,
+ value: `${method}-${name}`,
+ children: [
+ createMethodEndpoint(method, path),
+ // TODO: add `deprecation notice` when markdown support is added
+ createDescription(description),
+ createRequestBodyDetails({
+ title: "Body",
+ body: requestBody,
+ } as RequestBodyProps),
+ createStatusCodes({
+ id: "callbacks-responses",
+ label: "Callbacks Responses",
+ responses,
+ }),
+ ],
+ }),
+ ];
+ });
+ }),
+ }),
+ ],
+ });
+}
diff --git a/packages/docusaurus-plugin-openapi-docs/src/markdown/createContactInfo.ts b/packages/docusaurus-plugin-openapi-docs/src/markdown/createContactInfo.ts
index 1a6d772db..fd927200c 100644
--- a/packages/docusaurus-plugin-openapi-docs/src/markdown/createContactInfo.ts
+++ b/packages/docusaurus-plugin-openapi-docs/src/markdown/createContactInfo.ts
@@ -5,8 +5,8 @@
* LICENSE file in the root directory of this source tree.
* ========================================================================== */
-import { ContactObject } from "../openapi/types";
import { create, guard } from "./utils";
+import { ContactObject } from "../openapi/types";
export function createContactInfo(contact: ContactObject) {
if (!contact || !Object.keys(contact).length) return "";
diff --git a/packages/docusaurus-plugin-openapi-docs/src/markdown/createLicense.ts b/packages/docusaurus-plugin-openapi-docs/src/markdown/createLicense.ts
index c1d981dbc..86cc17780 100644
--- a/packages/docusaurus-plugin-openapi-docs/src/markdown/createLicense.ts
+++ b/packages/docusaurus-plugin-openapi-docs/src/markdown/createLicense.ts
@@ -5,8 +5,8 @@
* LICENSE file in the root directory of this source tree.
* ========================================================================== */
-import { LicenseObject } from "../openapi/types";
import { create, guard } from "./utils";
+import { LicenseObject } from "../openapi/types";
export function createLicense(license: LicenseObject) {
if (!license || !Object.keys(license).length) return "";
diff --git a/packages/docusaurus-plugin-openapi-docs/src/markdown/createLogo.ts b/packages/docusaurus-plugin-openapi-docs/src/markdown/createLogo.ts
index 8e9006bb0..090255985 100644
--- a/packages/docusaurus-plugin-openapi-docs/src/markdown/createLogo.ts
+++ b/packages/docusaurus-plugin-openapi-docs/src/markdown/createLogo.ts
@@ -5,8 +5,8 @@
* LICENSE file in the root directory of this source tree.
* ========================================================================== */
-import { LogoObject } from "../openapi/types";
import { create, guard } from "./utils";
+import { LogoObject } from "../openapi/types";
export function createLogo(
logo: LogoObject | undefined,
diff --git a/packages/docusaurus-plugin-openapi-docs/src/markdown/createParamsDetails.ts b/packages/docusaurus-plugin-openapi-docs/src/markdown/createParamsDetails.ts
index 9c5b1db49..f3415e286 100644
--- a/packages/docusaurus-plugin-openapi-docs/src/markdown/createParamsDetails.ts
+++ b/packages/docusaurus-plugin-openapi-docs/src/markdown/createParamsDetails.ts
@@ -5,10 +5,10 @@
* LICENSE file in the root directory of this source tree.
* ========================================================================== */
-import { ApiItem } from "../types";
import { createDetails } from "./createDetails";
import { createDetailsSummary } from "./createDetailsSummary";
import { create } from "./utils";
+import { ApiItem } from "../types";
interface Props {
parameters: ApiItem["parameters"];
diff --git a/packages/docusaurus-plugin-openapi-docs/src/markdown/createRequestBodyDetails.ts b/packages/docusaurus-plugin-openapi-docs/src/markdown/createRequestBodyDetails.ts
index 47aa0b861..19cae0633 100644
--- a/packages/docusaurus-plugin-openapi-docs/src/markdown/createRequestBodyDetails.ts
+++ b/packages/docusaurus-plugin-openapi-docs/src/markdown/createRequestBodyDetails.ts
@@ -5,8 +5,8 @@
* LICENSE file in the root directory of this source tree.
* ========================================================================== */
-import { MediaTypeObject } from "../openapi/types";
import { createRequestSchema } from "./createRequestSchema";
+import { MediaTypeObject } from "../openapi/types";
interface Props {
title: string;
@@ -19,6 +19,6 @@ interface Props {
};
}
-export function createRequestBodyDetails({ title, body }: Props): any {
+export function createRequestBodyDetails({ title, body }: Props) {
return createRequestSchema({ title, body });
}
diff --git a/packages/docusaurus-plugin-openapi-docs/src/markdown/createRequestSchema.ts b/packages/docusaurus-plugin-openapi-docs/src/markdown/createRequestSchema.ts
index f22726264..6b7805a90 100644
--- a/packages/docusaurus-plugin-openapi-docs/src/markdown/createRequestSchema.ts
+++ b/packages/docusaurus-plugin-openapi-docs/src/markdown/createRequestSchema.ts
@@ -5,12 +5,12 @@
* LICENSE file in the root directory of this source tree.
* ========================================================================== */
-import { MediaTypeObject } from "../openapi/types";
import { createDescription } from "./createDescription";
import { createDetails } from "./createDetails";
import { createDetailsSummary } from "./createDetailsSummary";
import { createNodes } from "./createSchema";
import { create, guard } from "./utils";
+import { MediaTypeObject } from "../openapi/types";
interface Props {
style?: any;
@@ -90,7 +90,7 @@ export function createRequestSchema({ title, body, ...rest }: Props) {
}),
create("ul", {
style: { marginLeft: "1rem" },
- children: createNodes(firstBody),
+ children: createNodes(firstBody, "request"),
}),
],
}),
@@ -108,12 +108,6 @@ export function createRequestSchema({ title, body, ...rest }: Props) {
return undefined;
}
- // we don't show the table if there is no properties to show
- if (firstBody.properties !== undefined) {
- if (Object.keys(firstBody.properties).length === 0) {
- return undefined;
- }
- }
return create("MimeTabs", {
className: "openapi-tabs__mime",
children: [
@@ -161,7 +155,7 @@ export function createRequestSchema({ title, body, ...rest }: Props) {
}),
create("ul", {
style: { marginLeft: "1rem" },
- children: createNodes(firstBody),
+ children: createNodes(firstBody, "request"),
}),
],
}),
diff --git a/packages/docusaurus-plugin-openapi-docs/src/markdown/createResponseSchema.ts b/packages/docusaurus-plugin-openapi-docs/src/markdown/createResponseSchema.ts
index 378ba5159..7ce159f2a 100644
--- a/packages/docusaurus-plugin-openapi-docs/src/markdown/createResponseSchema.ts
+++ b/packages/docusaurus-plugin-openapi-docs/src/markdown/createResponseSchema.ts
@@ -5,7 +5,6 @@
* LICENSE file in the root directory of this source tree.
* ========================================================================== */
-import { MediaTypeObject } from "../openapi/types";
import { createDescription } from "./createDescription";
import { createDetails } from "./createDetails";
import { createDetailsSummary } from "./createDetailsSummary";
@@ -16,6 +15,7 @@ import {
createResponseExamples,
} from "./createStatusCodes";
import { create, guard } from "./utils";
+import { MediaTypeObject } from "../openapi/types";
interface Props {
style?: any;
@@ -60,12 +60,6 @@ export function createResponseSchema({ title, body, ...rest }: Props) {
return undefined;
}
- if (firstBody?.properties !== undefined) {
- if (Object.keys(firstBody?.properties).length === 0) {
- return undefined;
- }
- }
-
return create("TabItem", {
label: `${mimeType}`,
value: `${mimeType}`,
@@ -118,7 +112,7 @@ export function createResponseSchema({ title, body, ...rest }: Props) {
}),
create("ul", {
style: { marginLeft: "1rem" },
- children: createNodes(firstBody!),
+ children: createNodes(firstBody!, "response"),
}),
],
}),
diff --git a/packages/docusaurus-plugin-openapi-docs/src/markdown/createSchema.test.ts b/packages/docusaurus-plugin-openapi-docs/src/markdown/createSchema.test.ts
index 0232ff4ce..5299210e9 100644
--- a/packages/docusaurus-plugin-openapi-docs/src/markdown/createSchema.test.ts
+++ b/packages/docusaurus-plugin-openapi-docs/src/markdown/createSchema.test.ts
@@ -7,12 +7,13 @@
import * as prettier from "prettier";
-import { SchemaObject } from "../openapi/types";
import { createNodes } from "./createSchema";
+import { SchemaObject } from "../openapi/types";
describe("createNodes", () => {
it("should create readable MODs for oneOf primitive properties", () => {
const schema: SchemaObject = {
+ "x-tags": ["clown"],
type: "object",
properties: {
oneOfProperty: {
@@ -48,7 +49,7 @@ describe("createNodes", () => {
},
};
expect(
- createNodes(schema).map((md: any) =>
+ createNodes(schema, "request").map((md: any) =>
prettier.format(md, { parser: "babel" })
)
).toMatchSnapshot();
diff --git a/packages/docusaurus-plugin-openapi-docs/src/markdown/createSchema.ts b/packages/docusaurus-plugin-openapi-docs/src/markdown/createSchema.ts
index 1c6d34f4b..7331ed3c2 100644
--- a/packages/docusaurus-plugin-openapi-docs/src/markdown/createSchema.ts
+++ b/packages/docusaurus-plugin-openapi-docs/src/markdown/createSchema.ts
@@ -7,7 +7,6 @@
import clsx from "clsx";
-import { SchemaObject } from "../openapi/types";
import {
createClosingArrayBracket,
createOpeningArrayBracket,
@@ -17,9 +16,12 @@ import { createDetails } from "./createDetails";
import { createDetailsSummary } from "./createDetailsSummary";
import { getQualifierMessage, getSchemaName } from "./schema";
import { create, guard } from "./utils";
+import { SchemaObject } from "../openapi/types";
const jsonSchemaMergeAllOf = require("json-schema-merge-allof");
+let SCHEMA_TYPE: "request" | "response";
+
/**
* Returns a merged representation of allOf array of schemas.
*/
@@ -29,6 +31,9 @@ export function mergeAllOf(allOf: SchemaObject[]) {
readOnly: function () {
return true;
},
+ writeOnly: function () {
+ return true;
+ },
example: function () {
return true;
},
@@ -39,7 +44,7 @@ export function mergeAllOf(allOf: SchemaObject[]) {
ignoreAdditionalProperties: true,
});
- const required = allOf.reduce((acc, cur) => {
+ const mergedRequired = allOf.reduce((acc, cur) => {
if (Array.isArray(cur.required)) {
const next = [...acc, ...cur.required];
return next;
@@ -47,7 +52,7 @@ export function mergeAllOf(allOf: SchemaObject[]) {
return acc;
}, [] as any);
- return { mergedSchemas, required };
+ return { mergedSchemas, mergedRequired };
}
/**
@@ -74,7 +79,7 @@ function createAnyOneOf(schema: SchemaObject): any {
}
if (anyOneSchema.allOf !== undefined) {
- anyOneChildren.push(createNodes(anyOneSchema));
+ anyOneChildren.push(createNodes(anyOneSchema, SCHEMA_TYPE));
delete anyOneSchema.allOf;
}
@@ -89,7 +94,7 @@ function createAnyOneOf(schema: SchemaObject): any {
anyOneSchema.type === "integer" ||
anyOneSchema.type === "boolean"
) {
- anyOneChildren.push(createNodes(anyOneSchema));
+ anyOneChildren.push(createNodes(anyOneSchema, SCHEMA_TYPE));
}
if (anyOneChildren.length) {
if (schema.type === "array") {
@@ -124,6 +129,16 @@ function createAnyOneOf(schema: SchemaObject): any {
*/
function createProperties(schema: SchemaObject) {
const discriminator = schema.discriminator;
+ if (Object.keys(schema.properties!).length === 0) {
+ return create("SchemaItem", {
+ collapsible: false,
+ name: "",
+ required: false,
+ schemaName: "object",
+ qualifierMessage: undefined,
+ schema: {},
+ });
+ }
return Object.entries(schema.properties!).map(([key, val]) => {
return createEdges({
name: key,
@@ -255,9 +270,8 @@ function createItems(schema: SchemaObject) {
// TODO: figure out if and how we should pass merged required array
const {
mergedSchemas,
- }: { mergedSchemas: SchemaObject; required: string[] } = mergeAllOf(
- schema.items?.allOf
- );
+ }: { mergedSchemas: SchemaObject; mergedRequired: string[] | boolean } =
+ mergeAllOf(schema.items?.allOf);
// Handles combo anyOf/oneOf + properties
if (
@@ -304,7 +318,7 @@ function createItems(schema: SchemaObject) {
) {
return [
createOpeningArrayBracket(),
- createNodes(schema.items),
+ createNodes(schema.items, SCHEMA_TYPE),
createClosingArrayBracket(),
].flat();
}
@@ -411,7 +425,7 @@ function createDetailsNode(
children: createDescription(description),
})
),
- createNodes(schema),
+ createNodes(schema, SCHEMA_TYPE),
],
}),
],
@@ -565,7 +579,7 @@ function createPropertyDiscriminator(
// className: "openapi-tabs__discriminator-item",
label: label,
value: `${index}-item-discriminator`,
- children: [createNodes(discriminator?.mapping[key])],
+ children: [createNodes(discriminator?.mapping[key], SCHEMA_TYPE)],
});
}),
}),
@@ -590,8 +604,19 @@ function createEdges({
required,
discriminator,
}: EdgeProps): any {
- const schemaName = getSchemaName(schema);
+ if (SCHEMA_TYPE === "request") {
+ if (schema.readOnly && schema.readOnly === true) {
+ return undefined;
+ }
+ }
+ if (SCHEMA_TYPE === "response") {
+ if (schema.writeOnly && schema.writeOnly === true) {
+ return undefined;
+ }
+ }
+
+ const schemaName = getSchemaName(schema);
if (discriminator !== undefined && discriminator.propertyName === name) {
return createPropertyDiscriminator(
name,
@@ -613,13 +638,23 @@ function createEdges({
}
if (schema.allOf !== undefined) {
- const {
- mergedSchemas,
- required,
- }: { mergedSchemas: SchemaObject; required: string[] | boolean } =
- mergeAllOf(schema.allOf);
- const mergedSchemaName = getSchemaName(mergedSchemas);
+ const { mergedSchemas }: { mergedSchemas: SchemaObject } = mergeAllOf(
+ schema.allOf
+ );
+ if (SCHEMA_TYPE === "request") {
+ if (mergedSchemas.readOnly && mergedSchemas.readOnly === true) {
+ return undefined;
+ }
+ }
+
+ if (SCHEMA_TYPE === "response") {
+ if (mergedSchemas.writeOnly && mergedSchemas.writeOnly === true) {
+ return undefined;
+ }
+ }
+
+ const mergedSchemaName = getSchemaName(mergedSchemas);
if (
mergedSchemas.oneOf !== undefined ||
mergedSchemas.anyOf !== undefined
@@ -664,16 +699,12 @@ function createEdges({
);
}
- if (mergedSchemas.readOnly && mergedSchemas.readOnly === true) {
- return undefined;
- }
-
return create("SchemaItem", {
collapsible: false,
name,
required: Array.isArray(required) ? required.includes(name) : required,
- schemaName: schemaName,
- qualifierMessage: getQualifierMessage(schema),
+ schemaName: mergedSchemaName,
+ qualifierMessage: getQualifierMessage(mergedSchemas),
schema: mergedSchemas,
});
}
@@ -719,10 +750,6 @@ function createEdges({
);
}
- if (schema.readOnly && schema.readOnly === true) {
- return undefined;
- }
-
// primitives and array of non-objects
return create("SchemaItem", {
collapsible: false,
@@ -737,7 +764,22 @@ function createEdges({
/**
* Creates a hierarchical level of a schema tree. Nodes produce edges that can branch into sub-nodes with edges, recursively.
*/
-export function createNodes(schema: SchemaObject): any {
+export function createNodes(
+ schema: SchemaObject,
+ schemaType: "request" | "response"
+): any {
+ SCHEMA_TYPE = schemaType;
+ if (SCHEMA_TYPE === "request") {
+ if (schema.readOnly && schema.readOnly === true) {
+ return undefined;
+ }
+ }
+
+ if (SCHEMA_TYPE === "response") {
+ if (schema.writeOnly && schema.writeOnly === true) {
+ return undefined;
+ }
+ }
const nodes = [];
// if (schema.discriminator !== undefined) {
// return createDiscriminator(schema);
@@ -792,9 +834,18 @@ export function createNodes(schema: SchemaObject): any {
style: {
marginTop: ".5rem",
marginBottom: ".5rem",
- marginLeft: "1rem",
},
- children: createDescription(schema.type),
+ children: [
+ createDescription(schema.type),
+ guard(getQualifierMessage(schema), (message) =>
+ create("div", {
+ style: {
+ paddingTop: "1rem",
+ },
+ children: createDescription(message),
+ })
+ ),
+ ],
});
}
@@ -804,9 +855,18 @@ export function createNodes(schema: SchemaObject): any {
style: {
marginTop: ".5rem",
marginBottom: ".5rem",
- marginLeft: "1rem",
},
- children: [createDescription(schema)],
+ children: [
+ createDescription(schema),
+ guard(getQualifierMessage(schema), (message) =>
+ create("div", {
+ style: {
+ paddingTop: "1rem",
+ },
+ children: createDescription(message),
+ })
+ ),
+ ],
});
}
diff --git a/packages/docusaurus-plugin-openapi-docs/src/markdown/createStatusCodes.ts b/packages/docusaurus-plugin-openapi-docs/src/markdown/createStatusCodes.ts
index c7376f712..2a99c1272 100644
--- a/packages/docusaurus-plugin-openapi-docs/src/markdown/createStatusCodes.ts
+++ b/packages/docusaurus-plugin-openapi-docs/src/markdown/createStatusCodes.ts
@@ -7,14 +7,14 @@
import format from "xml-formatter";
-import { sampleResponseFromSchema } from "../openapi/createResponseExample";
-import { ApiItem } from "../types";
import { createDescription } from "./createDescription";
import { createDetails } from "./createDetails";
import { createDetailsSummary } from "./createDetailsSummary";
import { createResponseSchema } from "./createResponseSchema";
import { create } from "./utils";
import { guard } from "./utils";
+import { sampleResponseFromSchema } from "../openapi/createResponseExample";
+import { ApiItem } from "../types";
export default function json2xml(o: any, tab: any) {
var toXml = function (v: any, name: string, ind: any) {
@@ -54,6 +54,8 @@ export default function json2xml(o: any, tab: any) {
}
interface Props {
+ id?: string;
+ label?: string;
responses: ApiItem["responses"];
}
@@ -125,7 +127,7 @@ export function createResponseExamples(
value: `${exampleName}`,
children: [
guard(exampleValue.summary, (summary) => [
- create("p", {
+ create("Markdown", {
children: ` ${summary}`,
}),
]),
@@ -141,7 +143,7 @@ export function createResponseExamples(
value: `${exampleName}`,
children: [
guard(exampleValue.summary, (summary) => [
- create("p", {
+ create("Markdown", {
children: ` ${summary}`,
}),
]),
@@ -169,7 +171,7 @@ export function createResponseExample(responseExample: any, mimeType: string) {
value: `Example`,
children: [
guard(responseExample.summary, (summary) => [
- create("p", {
+ create("Markdown", {
children: ` ${summary}`,
}),
]),
@@ -185,7 +187,7 @@ export function createResponseExample(responseExample: any, mimeType: string) {
value: `Example`,
children: [
guard(responseExample.summary, (summary) => [
- create("p", {
+ create("Markdown", {
children: ` ${summary}`,
}),
]),
@@ -254,7 +256,7 @@ export function createExampleFromSchema(schema: any, mimeType: string) {
return undefined;
}
-export function createStatusCodes({ responses }: Props) {
+export function createStatusCodes({ label, id, responses }: Props) {
if (responses === undefined) {
return undefined;
}
@@ -269,6 +271,8 @@ export function createStatusCodes({ responses }: Props) {
create("div", {
children: [
create("ApiTabs", {
+ label,
+ id,
children: codes.map((code) => {
const responseHeaders: any = responses[code].headers;
return create("TabItem", {
@@ -281,7 +285,7 @@ export function createStatusCodes({ responses }: Props) {
responseHeaders &&
createDetails({
className: "openapi-markdown__details",
- "data-collaposed": true,
+ "data-collapsed": true,
open: false,
style: { textAlign: "left", marginBottom: "1rem" },
children: [
diff --git a/packages/docusaurus-plugin-openapi-docs/src/markdown/index.ts b/packages/docusaurus-plugin-openapi-docs/src/markdown/index.ts
index 4b6d4bd59..533af429c 100644
--- a/packages/docusaurus-plugin-openapi-docs/src/markdown/index.ts
+++ b/packages/docusaurus-plugin-openapi-docs/src/markdown/index.ts
@@ -5,15 +5,9 @@
* LICENSE file in the root directory of this source tree.
* ========================================================================== */
-import {
- ContactObject,
- LicenseObject,
- MediaTypeObject,
- SecuritySchemeObject,
-} from "../openapi/types";
-import { ApiPageMetadata, InfoPageMetadata, TagPageMetadata } from "../types";
import { createAuthentication } from "./createAuthentication";
import { createAuthorization } from "./createAuthorization";
+import { createCallbacks } from "./createCallbacks";
import { createContactInfo } from "./createContactInfo";
import { createDeprecationNotice } from "./createDeprecationNotice";
import { createDescription } from "./createDescription";
@@ -25,13 +19,26 @@ import { createMethodEndpoint } from "./createMethodEndpoint";
import { createParamsDetails } from "./createParamsDetails";
import { createRequestBodyDetails } from "./createRequestBodyDetails";
import { createRequestHeader } from "./createRequestHeader";
+import { createNodes } from "./createSchema";
import { createStatusCodes } from "./createStatusCodes";
import { createTermsOfService } from "./createTermsOfService";
import { createVendorExtensions } from "./createVendorExtensions";
import { createVersionBadge } from "./createVersionBadge";
-import { greaterThan, lessThan, render } from "./utils";
+import { create, greaterThan, lessThan, render } from "./utils";
+import {
+ ContactObject,
+ LicenseObject,
+ MediaTypeObject,
+ SecuritySchemeObject,
+} from "../openapi/types";
+import {
+ ApiPageMetadata,
+ InfoPageMetadata,
+ SchemaPageMetadata,
+ TagPageMetadata,
+} from "../types";
-interface Props {
+interface RequestBodyProps {
title: string;
body: {
content?: {
@@ -54,6 +61,7 @@ export function createApiPageMD({
parameters,
requestBody,
responses,
+ callbacks,
},
infoPath,
frontMatter,
@@ -68,14 +76,18 @@ export function createApiPageMD({
`import ResponseSamples from "@theme/ResponseSamples";\n`,
`import SchemaItem from "@theme/SchemaItem";\n`,
`import SchemaTabs from "@theme/SchemaTabs";\n`,
+ `import Markdown from "@theme/Markdown";\n`,
+ `import OperationTabs from "@theme/OperationTabs";\n`,
`import TabItem from "@theme/TabItem";\n\n`,
createHeading(title.replace(lessThan, "<").replace(greaterThan, ">")),
createMethodEndpoint(method, path),
infoPath && createAuthorization(infoPath),
- frontMatter.show_extensions && createVendorExtensions(extensions),
+ frontMatter.show_extensions
+ ? createVendorExtensions(extensions)
+ : undefined,
createDeprecationNotice({ deprecated, description: deprecatedDescription }),
createDescription(description),
- createRequestHeader("Request"),
+ requestBody || parameters ? createRequestHeader("Request") : undefined,
createParamsDetails({ parameters, type: "path" }),
createParamsDetails({ parameters, type: "query" }),
createParamsDetails({ parameters, type: "header" }),
@@ -83,8 +95,9 @@ export function createApiPageMD({
createRequestBodyDetails({
title: "Body",
body: requestBody,
- } as Props),
+ } as RequestBodyProps),
createStatusCodes({ responses }),
+ createCallbacks({ callbacks }),
]);
}
@@ -123,3 +136,18 @@ export function createInfoPageMD({
export function createTagPageMD({ tag: { description } }: TagPageMetadata) {
return render([createDescription(description)]);
}
+
+export function createSchemaPageMD({ schema }: SchemaPageMetadata) {
+ const { title = "", description } = schema;
+ return render([
+ `import DiscriminatorTabs from "@theme/DiscriminatorTabs";\n`,
+ `import SchemaItem from "@theme/SchemaItem";\n`,
+ `import SchemaTabs from "@theme/SchemaTabs";\n`,
+ `import TabItem from "@theme/TabItem";\n\n`,
+ createHeading(title.replace(lessThan, "<").replace(greaterThan, ">")),
+ createDescription(description),
+ create("ul", {
+ children: createNodes(schema, "response"),
+ }),
+ ]);
+}
diff --git a/packages/docusaurus-plugin-openapi-docs/src/markdown/schema.ts b/packages/docusaurus-plugin-openapi-docs/src/markdown/schema.ts
index be09c11cd..5ac9c92b9 100644
--- a/packages/docusaurus-plugin-openapi-docs/src/markdown/schema.ts
+++ b/packages/docusaurus-plugin-openapi-docs/src/markdown/schema.ts
@@ -40,6 +40,10 @@ function prettyName(schema: SchemaObject, circular?: boolean) {
// return schema.type;
}
+ if (schema.title && schema.type) {
+ return `${schema.title} (${schema.type})`;
+ }
+
return schema.title ?? schema.type;
}
diff --git a/packages/docusaurus-plugin-openapi-docs/src/markdown/utils.ts b/packages/docusaurus-plugin-openapi-docs/src/markdown/utils.ts
index bc8b209a8..ea37fb69b 100644
--- a/packages/docusaurus-plugin-openapi-docs/src/markdown/utils.ts
+++ b/packages/docusaurus-plugin-openapi-docs/src/markdown/utils.ts
@@ -24,7 +24,7 @@ export function guard(
value: T | undefined,
cb: (value: T) => Children
): string {
- if (!!value) {
+ if (!!value || value === 0) {
const children = cb(value);
return render(children);
}
@@ -47,3 +47,25 @@ export const lessThan =
export const greaterThan =
/(?/gu;
export const codeFence = /`{1,3}[\s\S]*?`{1,3}/g;
+export const curlyBrackets = /([{}])/g;
+export const codeBlock = /(^```.*[\s\S]*?```$|`[^`].+?`)/gm;
+
+export function clean(value: string | undefined): string {
+ if (!value) {
+ return "";
+ }
+
+ let sections = value.split(codeBlock);
+ for (let sectionIndex in sections) {
+ if (!sections[sectionIndex].startsWith("`")) {
+ sections[sectionIndex] = sections[sectionIndex]
+ .replace(lessThan, "<")
+ .replace(greaterThan, ">")
+ .replace(codeFence, function (match) {
+ return match.replace(/\\>/g, ">");
+ })
+ .replace(curlyBrackets, "\\$1");
+ }
+ }
+ return sections.join("");
+}
diff --git a/packages/docusaurus-plugin-openapi-docs/src/openapi-to-postmanv2.d.ts b/packages/docusaurus-plugin-openapi-docs/src/openapi-to-postmanv2.d.ts
index f39042f25..a5bad23e0 100644
--- a/packages/docusaurus-plugin-openapi-docs/src/openapi-to-postmanv2.d.ts
+++ b/packages/docusaurus-plugin-openapi-docs/src/openapi-to-postmanv2.d.ts
@@ -5,6 +5,6 @@
* LICENSE file in the root directory of this source tree.
* ========================================================================== */
-declare module "@paloaltonetworks/openapi-to-postmanv2" {
+declare module "openapi-to-postmanv2" {
export default any;
}
diff --git a/packages/docusaurus-plugin-openapi-docs/src/openapi/__fixtures__/examples/openapi.yaml b/packages/docusaurus-plugin-openapi-docs/src/openapi/__fixtures__/examples/openapi.yaml
index 03a7005ec..6c9fa2f8d 100644
--- a/packages/docusaurus-plugin-openapi-docs/src/openapi/__fixtures__/examples/openapi.yaml
+++ b/packages/docusaurus-plugin-openapi-docs/src/openapi/__fixtures__/examples/openapi.yaml
@@ -11,3 +11,39 @@ paths:
responses:
200:
description: OK
+
+tags:
+ - name: tag1
+ description: Everything about your Pets
+ x-displayName: Tag 1
+ - name: tag2
+ description: Tag 2 description
+ x-displayName: Tag 2
+ - name: tag3
+ description: Tag 3 description
+ x-displayName: Tag 3
+ - name: tag4
+ description: Tag 4 description
+ x-displayName: Tag 4
+
+x-tagGroups:
+ - name: Tag 1 & 2
+ tags:
+ - tag1
+ - tag2
+ - name: Trinity
+ tags:
+ - tag1
+ - tag2
+ - tag3
+ - name: Last Two
+ tags:
+ - tag3
+ - tag4
+
+components:
+ schemas:
+ HelloString:
+ x-tags:
+ - tag1
+ type: string
diff --git a/packages/docusaurus-plugin-openapi-docs/src/openapi/createRequestExample.ts b/packages/docusaurus-plugin-openapi-docs/src/openapi/createRequestExample.ts
index 8413cb8ea..4ec427383 100644
--- a/packages/docusaurus-plugin-openapi-docs/src/openapi/createRequestExample.ts
+++ b/packages/docusaurus-plugin-openapi-docs/src/openapi/createRequestExample.ts
@@ -8,8 +8,8 @@
import chalk from "chalk";
import merge from "lodash/merge";
-import { mergeAllOf } from "../markdown/createSchema";
import { SchemaObject } from "./types";
+import { mergeAllOf } from "../markdown/createSchema";
interface OASTypeToTypeMap {
string: string;
diff --git a/packages/docusaurus-plugin-openapi-docs/src/openapi/createResponseExample.ts b/packages/docusaurus-plugin-openapi-docs/src/openapi/createResponseExample.ts
index 11a287e8b..7b0492dab 100644
--- a/packages/docusaurus-plugin-openapi-docs/src/openapi/createResponseExample.ts
+++ b/packages/docusaurus-plugin-openapi-docs/src/openapi/createResponseExample.ts
@@ -8,8 +8,8 @@
import chalk from "chalk";
import merge from "lodash/merge";
-import { mergeAllOf } from "../markdown/createSchema";
import { SchemaObject } from "./types";
+import { mergeAllOf } from "../markdown/createSchema";
interface OASTypeToTypeMap {
string: string;
diff --git a/packages/docusaurus-plugin-openapi-docs/src/openapi/openapi.test.ts b/packages/docusaurus-plugin-openapi-docs/src/openapi/openapi.test.ts
index c1c412f0b..678a003ff 100644
--- a/packages/docusaurus-plugin-openapi-docs/src/openapi/openapi.test.ts
+++ b/packages/docusaurus-plugin-openapi-docs/src/openapi/openapi.test.ts
@@ -28,6 +28,13 @@ describe("openapi", () => {
const yaml = results.find((x) => x.source.endsWith("openapi.yaml"));
expect(yaml).toBeTruthy();
expect(yaml?.sourceDirName).toBe(".");
+
+ expect(yaml?.data.tags).toBeDefined();
+ expect(yaml?.data["x-tagGroups"]).toBeDefined();
+
+ expect(
+ yaml?.data.components?.schemas?.HelloString["x-tags"]
+ ).toBeDefined();
});
});
});
diff --git a/packages/docusaurus-plugin-openapi-docs/src/openapi/openapi.ts b/packages/docusaurus-plugin-openapi-docs/src/openapi/openapi.ts
index 99bc85a35..abba4b17b 100644
--- a/packages/docusaurus-plugin-openapi-docs/src/openapi/openapi.ts
+++ b/packages/docusaurus-plugin-openapi-docs/src/openapi/openapi.ts
@@ -8,28 +8,29 @@
import path from "path";
import { Globby, GlobExcludeDefault, posixPath } from "@docusaurus/utils";
-import Converter from "@paloaltonetworks/openapi-to-postmanv2";
-import sdk from "@paloaltonetworks/postman-collection";
-import Collection from "@paloaltonetworks/postman-collection";
import chalk from "chalk";
import fs from "fs-extra";
import cloneDeep from "lodash/cloneDeep";
import kebabCase from "lodash/kebabCase";
import unionBy from "lodash/unionBy";
import uniq from "lodash/uniq";
+import Converter from "openapi-to-postmanv2";
+import Collection from "postman-collection";
+import sdk from "postman-collection";
+import { sampleRequestFromSchema } from "./createRequestExample";
+import { OpenApiObject, TagGroupObject, TagObject } from "./types";
+import { loadAndResolveSpec } from "./utils/loadAndResolveSpec";
import { isURL } from "../index";
import {
ApiMetadata,
APIOptions,
ApiPageMetadata,
InfoPageMetadata,
+ SchemaPageMetadata,
SidebarOptions,
TagPageMetadata,
} from "../types";
-import { sampleRequestFromSchema } from "./createRequestExample";
-import { OpenApiObject, TagObject } from "./types";
-import { loadAndResolveSpec } from "./utils/loadAndResolveSpec";
/**
* Convenience function for converting raw JSON to a Postman Collection object.
@@ -409,6 +410,56 @@ function createItems(
}
}
+ if (
+ options?.showSchemas === true ||
+ Object.entries(openapiData?.components?.schemas ?? {})
+ .flatMap(([_, s]) => s["x-tags"])
+ .filter((item) => !!item).length > 0
+ ) {
+ // Gather schemas
+ for (let [schema, schemaObject] of Object.entries(
+ openapiData?.components?.schemas ?? {}
+ )) {
+ if (options?.showSchemas === true || schemaObject["x-tags"]) {
+ const baseIdSpaces =
+ schemaObject?.title?.replace(" ", "-").toLowerCase() ?? "";
+ const baseId = kebabCase(baseIdSpaces);
+
+ const schemaDescription = schemaObject.description;
+ let splitDescription: any;
+ if (schemaDescription) {
+ splitDescription = schemaDescription.match(/[^\r\n]+/g);
+ }
+
+ const schemaPage: PartialPage = {
+ type: "schema",
+ id: baseId,
+ infoId: infoId ?? "",
+ unversionedId: baseId,
+ title: schemaObject.title
+ ? schemaObject.title.replace(/((?:^|[^\\])(?:\\{2})*)"/g, "$1'")
+ : schema,
+ description: schemaObject.description
+ ? schemaObject.description.replace(
+ /((?:^|[^\\])(?:\\{2})*)"/g,
+ "$1'"
+ )
+ : "",
+ frontMatter: {
+ description: splitDescription
+ ? splitDescription[0]
+ .replace(/((?:^|[^\\])(?:\\{2})*)"/g, "$1'")
+ .replace(/\s+$/, "")
+ : "",
+ },
+ schema: schemaObject,
+ };
+
+ items.push(schemaPage);
+ }
+ }
+ }
+
if (sidebarOptions?.categoryLinkSource === "tag") {
// Get global tags
const tags: TagObject[] = openapiData.tags ?? [];
@@ -469,10 +520,13 @@ function bindCollectionToApiItems(
const method = item.request.method.toLowerCase();
const path = item.request.url
.getPath({ unresolved: true }) // unresolved returns "/:variableName" instead of "/"
- .replace(/:([a-z0-9-_]+)/gi, "{$1}"); // replace "/:variableName" with "/{variableName}"
-
+ .replace(/(? {
- if (item.type === "info" || item.type === "tag") {
+ if (
+ item.type === "info" ||
+ item.type === "tag" ||
+ item.type === "schema"
+ ) {
return false;
}
return item.api.path === path && item.api.method === method;
@@ -535,7 +589,7 @@ export async function processOpenapiFiles(
files: OpenApiFiles[],
options: APIOptions,
sidebarOptions: SidebarOptions
-): Promise<[ApiMetadata[], TagObject[][]]> {
+): Promise<[ApiMetadata[], TagObject[][], TagGroupObject[]]> {
const promises = files.map(async (file) => {
if (file.data !== undefined) {
const processedFile = await processOpenapiFile(
@@ -547,7 +601,8 @@ export async function processOpenapiFiles(
...item,
}));
const tags = processedFile[1];
- return [itemsObjectsArray, tags];
+ const tagGroups = processedFile[2];
+ return [itemsObjectsArray, tags, tagGroups];
}
console.warn(
chalk.yellow(
@@ -566,6 +621,7 @@ export async function processOpenapiFiles(
// Remove undefined items due to transient parsing errors
return x !== undefined;
});
+
const tags = metadata
.map(function (x) {
return x[1];
@@ -574,14 +630,29 @@ export async function processOpenapiFiles(
// Remove undefined tags due to transient parsing errors
return x !== undefined;
});
- return [items as ApiMetadata[], tags as TagObject[][]];
+
+ const tagGroups = metadata
+ .map(function (x) {
+ return x[2];
+ })
+ .flat()
+ .filter(function (x) {
+ // Remove undefined tags due to transient parsing errors
+ return x !== undefined;
+ });
+
+ return [
+ items as ApiMetadata[],
+ tags as TagObject[][],
+ tagGroups as TagGroupObject[],
+ ];
}
export async function processOpenapiFile(
openapiData: OpenApiObject,
options: APIOptions,
sidebarOptions: SidebarOptions
-): Promise<[ApiMetadata[], TagObject[]]> {
+): Promise<[ApiMetadata[], TagObject[], TagGroupObject[]]> {
const postmanCollection = await createPostmanCollection(openapiData);
const items = createItems(openapiData, options, sidebarOptions);
@@ -591,7 +662,13 @@ export async function processOpenapiFile(
if (openapiData.tags !== undefined) {
tags = openapiData.tags;
}
- return [items, tags];
+
+ let tagGroups: TagGroupObject[] = [];
+ if (openapiData["x-tagGroups"] !== undefined) {
+ tagGroups = openapiData["x-tagGroups"];
+ }
+
+ return [items, tags, tagGroups];
}
// order for picking items as a display name of tags
diff --git a/packages/docusaurus-plugin-openapi-docs/src/openapi/types.ts b/packages/docusaurus-plugin-openapi-docs/src/openapi/types.ts
index 72e6468d4..b9e45c48a 100644
--- a/packages/docusaurus-plugin-openapi-docs/src/openapi/types.ts
+++ b/packages/docusaurus-plugin-openapi-docs/src/openapi/types.ts
@@ -22,6 +22,7 @@ export interface OpenApiObject {
externalDocs?: ExternalDocumentationObject;
swagger?: string;
"x-webhooks"?: PathsObject;
+ "x-tagGroups"?: TagGroupObject[];
}
export interface OpenApiObjectWithRef {
@@ -311,6 +312,11 @@ export interface TagObject {
"x-displayName"?: string;
}
+export interface TagGroupObject {
+ name: string;
+ tags: string[];
+}
+
export interface ReferenceObject {
$ref: string;
}
@@ -346,6 +352,7 @@ export type SchemaObject = Omit<
externalDocs?: ExternalDocumentationObject;
example?: any;
deprecated?: boolean;
+ "x-tags"?: string[];
};
export type SchemaObjectWithRef = Omit<
diff --git a/packages/docusaurus-plugin-openapi-docs/src/openapi/utils/loadAndResolveSpec.ts b/packages/docusaurus-plugin-openapi-docs/src/openapi/utils/loadAndResolveSpec.ts
index ee59054b2..2b75c3844 100644
--- a/packages/docusaurus-plugin-openapi-docs/src/openapi/utils/loadAndResolveSpec.ts
+++ b/packages/docusaurus-plugin-openapi-docs/src/openapi/utils/loadAndResolveSpec.ts
@@ -13,8 +13,8 @@ import chalk from "chalk";
// @ts-ignore
import { convertObj } from "swagger2openapi";
-import { OpenApiObject } from "../types";
import { OpenAPIParser } from "./services/OpenAPIParser";
+import { OpenApiObject } from "../types";
function serializer(replacer: any, cycleReplacer: any) {
var stack: any = [],
diff --git a/packages/docusaurus-plugin-openapi-docs/src/openapi/utils/services/OpenAPIParser.ts b/packages/docusaurus-plugin-openapi-docs/src/openapi/utils/services/OpenAPIParser.ts
index 8f18c8b30..3e90a75af 100644
--- a/packages/docusaurus-plugin-openapi-docs/src/openapi/utils/services/OpenAPIParser.ts
+++ b/packages/docusaurus-plugin-openapi-docs/src/openapi/utils/services/OpenAPIParser.ts
@@ -7,11 +7,11 @@
// @ts-nocheck
+import { RedocNormalizedOptions } from "./RedocNormalizedOptions";
import { OpenAPIRef, OpenAPISchema, OpenAPISpec, Referenced } from "../types";
import { isArray, isBoolean } from "../utils/helpers";
import { JsonPointer } from "../utils/JsonPointer";
import { getDefinitionName, isNamedDefinition } from "../utils/openapi";
-import { RedocNormalizedOptions } from "./RedocNormalizedOptions";
export type MergedOpenAPISchema = OpenAPISchema & { parentRefs?: string[] };
diff --git a/packages/docusaurus-plugin-openapi-docs/src/openapi/utils/utils/openapi.ts b/packages/docusaurus-plugin-openapi-docs/src/openapi/utils/utils/openapi.ts
index 2fbd16d00..241581485 100644
--- a/packages/docusaurus-plugin-openapi-docs/src/openapi/utils/utils/openapi.ts
+++ b/packages/docusaurus-plugin-openapi-docs/src/openapi/utils/utils/openapi.ts
@@ -9,6 +9,13 @@
import { dirname } from "path";
+import {
+ isNumeric,
+ removeQueryString,
+ resolveUrl,
+ isArray,
+ isBoolean,
+} from "./helpers";
import { OpenAPIParser } from "../services/OpenAPIParser";
import {
OpenAPIEncoding,
@@ -21,13 +28,6 @@ import {
OpenAPIServer,
Referenced,
} from "../types";
-import {
- isNumeric,
- removeQueryString,
- resolveUrl,
- isArray,
- isBoolean,
-} from "./helpers";
function isWildcardStatusCode(
statusCode: string | number
diff --git a/packages/docusaurus-plugin-openapi-docs/src/options.ts b/packages/docusaurus-plugin-openapi-docs/src/options.ts
index d2a56805d..e3f11ddec 100644
--- a/packages/docusaurus-plugin-openapi-docs/src/options.ts
+++ b/packages/docusaurus-plugin-openapi-docs/src/options.ts
@@ -8,7 +8,7 @@
import { Joi } from "@docusaurus/utils-validation";
const sidebarOptions = Joi.object({
- groupPathsBy: Joi.string().valid("tag"),
+ groupPathsBy: Joi.string().valid("tag", "tagGroup"),
categoryLinkSource: Joi.string().valid("tag", "info", "auto"),
customProps: Joi.object(),
sidebarCollapsible: Joi.boolean(),
@@ -23,6 +23,7 @@ const markdownGenerators = Joi.object({
export const OptionsSchema = Joi.object({
id: Joi.string().required(),
+ docsPlugin: Joi.string(),
docsPluginId: Joi.string().required(),
config: Joi.object()
.pattern(
@@ -37,6 +38,8 @@ export const OptionsSchema = Joi.object({
showExtensions: Joi.boolean(),
sidebarOptions: sidebarOptions,
markdownGenerators: markdownGenerators,
+ showSchemas: Joi.boolean(),
+ disableCompression: Joi.boolean(),
version: Joi.string().when("versions", {
is: Joi.exist(),
then: Joi.required(),
@@ -56,6 +59,7 @@ export const OptionsSchema = Joi.object({
outputDir: Joi.string().required(),
label: Joi.string().required(),
baseUrl: Joi.string().required(),
+ downloadUrl: Joi.string(),
})
),
})
diff --git a/packages/docusaurus-plugin-openapi-docs/src/postman-collection.d.ts b/packages/docusaurus-plugin-openapi-docs/src/postman-collection.d.ts
index 9084c02e9..1e002ccd4 100644
--- a/packages/docusaurus-plugin-openapi-docs/src/postman-collection.d.ts
+++ b/packages/docusaurus-plugin-openapi-docs/src/postman-collection.d.ts
@@ -5,6 +5,6 @@
* LICENSE file in the root directory of this source tree.
* ========================================================================== */
-declare module "@paloaltonetworks/postman-collection" {
+declare module "postman-collection" {
export default any;
}
diff --git a/packages/docusaurus-plugin-openapi-docs/src/sidebars/index.ts b/packages/docusaurus-plugin-openapi-docs/src/sidebars/index.ts
index 285415ead..def167105 100644
--- a/packages/docusaurus-plugin-openapi-docs/src/sidebars/index.ts
+++ b/packages/docusaurus-plugin-openapi-docs/src/sidebars/index.ts
@@ -7,6 +7,7 @@
import path from "path";
+import { ProcessedSidebarItem } from "@docusaurus/plugin-content-docs/lib/sidebars/types";
import {
ProcessedSidebar,
SidebarItemCategory,
@@ -18,12 +19,14 @@ import clsx from "clsx";
import { kebabCase } from "lodash";
import uniq from "lodash/uniq";
-import { TagObject } from "../openapi/types";
+import { TagGroupObject, TagObject } from "../openapi/types";
import type {
SidebarOptions,
APIOptions,
ApiPageMetadata,
ApiMetadata,
+ InfoPageMetadata,
+ SchemaPageMetadata,
} from "../types";
function isApiItem(item: ApiMetadata): item is ApiMetadata {
@@ -34,14 +37,18 @@ function isInfoItem(item: ApiMetadata): item is ApiMetadata {
return item.type === "info";
}
+function isSchemaItem(item: ApiMetadata): item is ApiMetadata {
+ return item.type === "schema";
+}
+
function groupByTags(
- items: ApiPageMetadata[],
+ items: ApiMetadata[],
sidebarOptions: SidebarOptions,
options: APIOptions,
tags: TagObject[][],
docPath: string
): ProcessedSidebar {
- let { outputDir, label } = options;
+ let { outputDir, label, showSchemas } = options;
// Remove trailing slash before proceeding
outputDir = outputDir.replace(/\/$/, "");
@@ -53,8 +60,9 @@ function groupByTags(
categoryLinkSource,
} = sidebarOptions;
- const apiItems = items.filter(isApiItem);
- const infoItems = items.filter(isInfoItem);
+ const apiItems = items.filter(isApiItem) as ApiPageMetadata[];
+ const infoItems = items.filter(isInfoItem) as InfoPageMetadata[];
+ const schemaItems = items.filter(isSchemaItem) as SchemaPageMetadata[];
const intros = infoItems.map((item: any) => {
return {
id: item.id,
@@ -70,38 +78,56 @@ function groupByTags(
.flatMap((item) => item.api.tags)
.filter((item): item is string => !!item)
);
+ const schemaTags = uniq(
+ schemaItems
+ .flatMap((item) => item.schema["x-tags"])
+ .filter((item): item is string => !!item)
+ );
- // Combine globally defined tags with operation tags
- // Only include global tag if referenced in operation tags
+ // Combine globally defined tags with operation and schema tags
+ // Only include global tag if referenced in operation/schema tags
let apiTags: string[] = [];
tags.flat().forEach((tag) => {
// Should we also check x-displayName?
- if (operationTags.includes(tag.name!)) {
+ if (operationTags.includes(tag.name!) || schemaTags.includes(tag.name!)) {
apiTags.push(tag.name!);
}
});
- apiTags = uniq(apiTags.concat(operationTags));
+
+ if (sidebarOptions.groupPathsBy !== "tagGroup") {
+ apiTags = uniq(apiTags.concat(operationTags, schemaTags));
+ }
const basePath = docPath
? outputDir.split(docPath!)[1].replace(/^\/+/g, "")
: outputDir.slice(outputDir.indexOf("/", 1)).replace(/^\/+/g, "");
- function createDocItem(item: ApiPageMetadata): SidebarItemDoc {
+ function createDocItem(
+ item: ApiPageMetadata | SchemaPageMetadata
+ ): SidebarItemDoc {
const sidebar_label = item.frontMatter.sidebar_label;
const title = item.title;
- const id = item.id;
+ const id = item.type === "schema" ? `schemas/${item.id}` : item.id;
+ const className =
+ item.type === "api"
+ ? clsx(
+ {
+ "menu__list-item--deprecated": item.api.deprecated,
+ "api-method": !!item.api.method,
+ },
+ item.api.method
+ )
+ : clsx(
+ {
+ "menu__list-item--deprecated": item.schema.deprecated,
+ },
+ "schema"
+ );
return {
type: "doc" as const,
- id:
- basePath === "" || undefined ? `${item.id}` : `${basePath}/${item.id}`,
+ id: basePath === "" || undefined ? `${id}` : `${basePath}/${id}`,
label: (sidebar_label as string) ?? title ?? id,
customProps: customProps,
- className: clsx(
- {
- "menu__list-item--deprecated": item.api.deprecated,
- "api-method": !!item.api.method,
- },
- item.api.method
- ),
+ className: className ? className : undefined,
};
}
@@ -169,15 +195,20 @@ function groupByTags(
} as SidebarItemCategoryLinkConfig;
}
+ const taggedApiItems = apiItems.filter(
+ (item) => !!item.api.tags?.includes(tag)
+ );
+ const taggedSchemaItems = schemaItems.filter(
+ (item) => !!item.schema["x-tags"]?.includes(tag)
+ );
+
return {
type: "category" as const,
label: tagObject?.["x-displayName"] ?? tag,
link: linkConfig,
collapsible: sidebarCollapsible,
collapsed: sidebarCollapsed,
- items: apiItems
- .filter((item) => !!item.api.tags?.includes(tag))
- .map(createDocItem),
+ items: [...taggedSchemaItems, ...taggedApiItems].map(createDocItem),
};
})
.filter((item) => item.items.length > 0); // Filter out any categories with no items.
@@ -201,13 +232,28 @@ function groupByTags(
];
}
+ let schemas: SidebarItemCategory[] = [];
+ if (showSchemas && schemaItems.length > 0) {
+ schemas = [
+ {
+ type: "category" as const,
+ label: "Schemas",
+ collapsible: sidebarCollapsible!,
+ collapsed: sidebarCollapsed!,
+ items: schemaItems
+ .filter(({ schema }) => !schema["x-tags"])
+ .map(createDocItem),
+ },
+ ];
+ }
+
// Shift root intro doc to top of sidebar
// TODO: Add input validation for categoryLinkSource options
if (rootIntroDoc && categoryLinkSource !== "info") {
tagged.unshift(rootIntroDoc as any);
}
- return [...tagged, ...untagged];
+ return [...tagged, ...untagged, ...schemas];
}
export default function generateSidebarSlice(
@@ -215,18 +261,55 @@ export default function generateSidebarSlice(
options: APIOptions,
api: ApiMetadata[],
tags: TagObject[][],
- docPath: string
+ docPath: string,
+ tagGroups?: TagGroupObject[]
) {
let sidebarSlice: ProcessedSidebar = [];
- if (sidebarOptions.groupPathsBy === "tag") {
- sidebarSlice = groupByTags(
- api as ApiPageMetadata[],
- sidebarOptions,
- options,
- tags,
- docPath
- );
+ if (sidebarOptions.groupPathsBy === "tagGroup") {
+ let schemasGroup: ProcessedSidebar = [];
+ tagGroups?.forEach((tagGroup) => {
+ //filter tags only included in group
+ const filteredTags: TagObject[] = [];
+ tags[0].forEach((tag) => {
+ if (tagGroup.tags.includes(tag.name as string)) {
+ filteredTags.push(tag);
+ }
+ });
+
+ const groupCategory = {
+ type: "category" as const,
+ label: tagGroup.name,
+ collapsible: true,
+ collapsed: true,
+ items: groupByTags(
+ api,
+ sidebarOptions,
+ options,
+ [filteredTags],
+ docPath
+ ),
+ };
+
+ if (options.showSchemas) {
+ // For the first tagGroup, save the generated "Schemas" category for later.
+ if (schemasGroup.length === 0) {
+ schemasGroup = groupCategory.items?.filter(
+ (item) => item.type === "category" && item.label === "Schemas"
+ );
+ }
+ // Remove the "Schemas" category from every `groupCategory`.
+ groupCategory.items = groupCategory.items.filter((item) =>
+ "label" in item ? item.label !== "Schemas" : true
+ );
+ }
+ sidebarSlice.push(groupCategory as ProcessedSidebarItem);
+ });
+ // Add `schemasGroup` to the end of the sidebar.
+ sidebarSlice.push(...schemasGroup);
+ } else if (sidebarOptions.groupPathsBy === "tag") {
+ sidebarSlice = groupByTags(api, sidebarOptions, options, tags, docPath);
}
+
return sidebarSlice;
}
diff --git a/packages/docusaurus-plugin-openapi-docs/src/types.ts b/packages/docusaurus-plugin-openapi-docs/src/types.ts
index 7c12d0805..8eee4a0e1 100644
--- a/packages/docusaurus-plugin-openapi-docs/src/types.ts
+++ b/packages/docusaurus-plugin-openapi-docs/src/types.ts
@@ -5,11 +5,12 @@
* LICENSE file in the root directory of this source tree.
* ========================================================================== */
-import type Request from "@paloaltonetworks/postman-collection";
+import type Request from "postman-collection";
import {
InfoObject,
OperationObject,
+ SchemaObject,
SecuritySchemeObject,
TagObject,
} from "./openapi/types";
@@ -22,6 +23,7 @@ export type {
} from "@docusaurus/plugin-content-docs-types";
export interface PluginOptions {
id?: string;
+ docsPlugin?: string;
docsPluginId: string;
config: {
[key: string]: APIOptions;
@@ -44,12 +46,15 @@ export interface APIOptions {
};
proxy?: string;
markdownGenerators?: MarkdownGenerator;
+ showSchemas?: boolean;
+ disableCompression?: boolean;
}
export interface MarkdownGenerator {
createApiPageMD?: (pageData: ApiPageMetadata) => string;
createInfoPageMD?: (pageData: InfoPageMetadata) => string;
createTagPageMD?: (pageData: TagPageMetadata) => string;
+ createSchemaPageMD?: (pageData: SchemaPageMetadata) => string;
}
export interface SidebarOptions {
@@ -65,6 +70,7 @@ export interface APIVersionOptions {
outputDir: string;
label: string;
baseUrl: string;
+ downloadUrl?: string;
}
export interface LoadedContent {
@@ -72,7 +78,11 @@ export interface LoadedContent {
// loadedDocs: DocPageMetadata[]; TODO: cleanup
}
-export type ApiMetadata = ApiPageMetadata | InfoPageMetadata | TagPageMetadata;
+export type ApiMetadata =
+ | ApiPageMetadata
+ | InfoPageMetadata
+ | TagPageMetadata
+ | SchemaPageMetadata;
export interface ApiMetadataBase {
sidebar?: string;
@@ -131,6 +141,19 @@ export interface TagPageMetadata extends ApiMetadataBase {
markdown?: string;
}
+export interface SchemaPageMetadata extends ApiMetadataBase {
+ type: "schema";
+ schema: SchemaObject;
+ markdown?: string;
+}
+
+export interface TagGroupPageMetadata extends ApiMetadataBase {
+ type: "tagGroup";
+ name: string;
+ tags: TagObject[];
+ markdown?: string;
+}
+
export type ApiInfo = InfoObject;
export interface ApiNavLink {
diff --git a/packages/docusaurus-theme-openapi-docs/package.json b/packages/docusaurus-theme-openapi-docs/package.json
index f2004b4b6..5fca1c869 100644
--- a/packages/docusaurus-theme-openapi-docs/package.json
+++ b/packages/docusaurus-theme-openapi-docs/package.json
@@ -1,7 +1,7 @@
{
"name": "docusaurus-theme-openapi-docs",
"description": "OpenAPI theme for Docusaurus.",
- "version": "2.0.0",
+ "version": "2.2.0",
"license": "MIT",
"keywords": [
"openapi",
@@ -37,17 +37,17 @@
"dependencies": {
"@docusaurus/theme-common": ">=2.4.1 <=2.4.3",
"@hookform/error-message": "^2.0.1",
- "@paloaltonetworks/postman-code-generators": "1.1.15-patch.2",
- "@paloaltonetworks/postman-collection": "^4.1.0",
"@reduxjs/toolkit": "^1.7.1",
"clsx": "^1.1.1",
"copy-text-to-clipboard": "^3.1.0",
"crypto-js": "^4.1.1",
- "docusaurus-plugin-openapi-docs": "^2.0.0",
+ "docusaurus-plugin-openapi-docs": "^2.2.0",
"docusaurus-plugin-sass": "^0.2.3",
"file-saver": "^2.0.5",
"lodash": "^4.17.20",
"node-polyfill-webpack-plugin": "^2.0.1",
+ "postman-code-generators": "^1.10.1",
+ "postman-collection": "^4.4.0",
"prism-react-renderer": "^1.3.5",
"react-hook-form": "^7.43.8",
"react-live": "^4.0.0",
diff --git a/packages/docusaurus-theme-openapi-docs/src/markdown/utils.test.ts b/packages/docusaurus-theme-openapi-docs/src/markdown/utils.test.ts
new file mode 100644
index 000000000..999b929db
--- /dev/null
+++ b/packages/docusaurus-theme-openapi-docs/src/markdown/utils.test.ts
@@ -0,0 +1,49 @@
+/* ============================================================================
+ * Copyright (c) Palo Alto Networks
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ * ========================================================================== */
+
+import { guard } from "./utils";
+
+describe("guard", () => {
+ it("should guard empty strings", () => {
+ const actual = guard("", (_) => {
+ throw new Error("Should not be called");
+ });
+ expect(actual).toBe("");
+ });
+
+ it("should guard undefined", () => {
+ const actual = guard(undefined, (value) => {
+ throw new Error("Should not be called");
+ });
+ expect(actual).toBe("");
+ });
+
+ it("should guard false booleans", () => {
+ const actual = guard(false, (value) => `${value}`);
+ expect(actual).toBe("");
+ });
+
+ it("should not guard strings", () => {
+ const actual = guard("hello", (value) => value);
+ expect(actual).toBe("hello");
+ });
+
+ it("should not guard numbers", () => {
+ const actual = guard(1, (value) => `${value}`);
+ expect(actual).toBe("1");
+ });
+
+ it("should not guard numbers equals to 0", () => {
+ const actual = guard(0, (value) => `${value}`);
+ expect(actual).toBe("0");
+ });
+
+ it("should not guard true booleans", () => {
+ const actual = guard(true, (value) => `${value}`);
+ expect(actual).toBe("true");
+ });
+});
diff --git a/packages/docusaurus-theme-openapi-docs/src/markdown/utils.ts b/packages/docusaurus-theme-openapi-docs/src/markdown/utils.ts
index b18701e07..7221cb92c 100644
--- a/packages/docusaurus-theme-openapi-docs/src/markdown/utils.ts
+++ b/packages/docusaurus-theme-openapi-docs/src/markdown/utils.ts
@@ -24,7 +24,7 @@ export function guard(
value: T | undefined | string,
cb: (value: T) => Children
): string {
- if (!!value) {
+ if (!!value || value === 0) {
const children = cb(value as T);
return render(children);
}
diff --git a/packages/docusaurus-theme-openapi-docs/src/postman-code-generators.d.ts b/packages/docusaurus-theme-openapi-docs/src/postman-code-generators.d.ts
index 712697695..a6a3d4152 100644
--- a/packages/docusaurus-theme-openapi-docs/src/postman-code-generators.d.ts
+++ b/packages/docusaurus-theme-openapi-docs/src/postman-code-generators.d.ts
@@ -6,4 +6,4 @@
* ========================================================================== */
// TODO: Remove this when https://github.com/facebook/docusaurus/issues/6087 is resolved.
-declare module "@paloaltonetworks/postman-code-generators";
+declare module "postman-code-generators";
diff --git a/packages/docusaurus-theme-openapi-docs/src/theme-openapi.d.ts b/packages/docusaurus-theme-openapi-docs/src/theme-openapi.d.ts
index 8961e7d49..2456db3d1 100644
--- a/packages/docusaurus-theme-openapi-docs/src/theme-openapi.d.ts
+++ b/packages/docusaurus-theme-openapi-docs/src/theme-openapi.d.ts
@@ -63,6 +63,10 @@ declare module "@theme/SchemaTabs" {
export default function SchemaTabs(props: any): JSX.Element;
}
+declare module "@theme/Markdown" {
+ export default function Markdown(props: any): JSX.Element;
+}
+
declare module "@theme/ApiExplorer/Accept" {
export default function Accept(): JSX.Element;
}
diff --git a/packages/docusaurus-theme-openapi-docs/src/theme/ApiDemoPanel/ApiCodeBlock/ExpandButton/index.js b/packages/docusaurus-theme-openapi-docs/src/theme/ApiDemoPanel/ApiCodeBlock/ExpandButton/index.js
deleted file mode 100644
index cb71c3299..000000000
--- a/packages/docusaurus-theme-openapi-docs/src/theme/ApiDemoPanel/ApiCodeBlock/ExpandButton/index.js
+++ /dev/null
@@ -1,165 +0,0 @@
-/* ============================================================================
- * Copyright (c) Palo Alto Networks
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- * ========================================================================== */
-
-import React, { useEffect } from "react";
-
-import { usePrismTheme } from "@docusaurus/theme-common";
-import { translate } from "@docusaurus/Translate";
-import Container from "@theme/ApiDemoPanel/ApiCodeBlock/Container";
-import CopyButton from "@theme/ApiDemoPanel/ApiCodeBlock/CopyButton";
-import ExitButton from "@theme/ApiDemoPanel/ApiCodeBlock/ExitButton";
-import Line from "@theme/ApiDemoPanel/ApiCodeBlock/Line";
-import clsx from "clsx";
-import Highlight, { defaultProps } from "prism-react-renderer";
-import Modal from "react-modal";
-
-export default function ExpandButton({
- code,
- className,
- language,
- showLineNumbers,
- blockClassName,
- title,
- lineClassNames,
-}) {
- const prismTheme = usePrismTheme();
-
- function openModal() {
- setIsOpen(true);
- }
-
- function closeModal() {
- setIsOpen(false);
- }
-
- const [modalIsOpen, setIsOpen] = React.useState(false);
-
- useEffect(() => {
- Modal.setAppElement("body");
- });
-
- return (
- <>
-
-
-
- {title && (
-