diff --git a/.github/workflows/demo-deploy-s3.yml b/.github/workflows/demo-deploy-s3.yml deleted file mode 100644 index efb2ca19..00000000 --- a/.github/workflows/demo-deploy-s3.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: Redoc demo CI/CD - -on: - push: - tags: - - v[0-9]*.[0-9]*.[0-9]* - -jobs: - build-and-unit: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - run: npm ci - - run: npm run bundle - - run: npm test - deploy: - needs: build-and-unit - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - name: cache node modules - uses: actions/cache@v1 - with: - path: ~/.npm # npm cache files are stored in `~/.npm` on Linux/macOS - key: npm-${{ hashFiles('package-lock.json') }} - restore-keys: | - npm-${{ hashFiles('package-lock.json') }} - npm- - - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v1 - with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - aws-region: us-east-1 - - name: Install dependencies - run: npm ci - - name: Build package - run: npm run build:demo - - name: Deploy to S3 bucket - run: npm run deploy:demo - - name: Invalidate - run: aws cloudfront create-invalidation --distribution-id ${{ secrets.CF_DEMO_DISTRIBUTION_ID }} --paths "/*" diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml new file mode 100644 index 00000000..514a3193 --- /dev/null +++ b/.github/workflows/e2e-tests.yml @@ -0,0 +1,12 @@ +name: Tests e2e + +on: [push] + +jobs: + build-and-e2e: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: npm ci + - run: npm run bundle + - run: npm run e2e diff --git a/.github/workflows/publish-cli.yml b/.github/workflows/publish-cli.yml new file mode 100644 index 00000000..9bfa3fff --- /dev/null +++ b/.github/workflows/publish-cli.yml @@ -0,0 +1,115 @@ +name: Publish cli + +on: + push: + tags: + - v[0-9]*.[0-9]*.[0-9]* + + +jobs: + bundle: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + - name: Cache node modules + uses: actions/cache@v2 + with: + path: ~/.npm # npm cache files are stored in `~/.npm` on Linux/macOS + key: npm-${{ hashFiles('package-lock.json') }} + restore-keys: | + npm-${{ hashFiles('package-lock.json') }} + npm- + - run: npm ci + - run: npm run bundle + - name: Store bundle artifact + uses: actions/upload-artifact@v2 + with: + name: bundles-cli + path: bundles + retention-days: 1 + unit-tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - run: npm ci + - run: npm test + e2e-tests: + needs: [bundle] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - run: npm ci + - name: Download bundled artifact + uses: actions/download-artifact@v2 + with: + name: bundles + path: bundles-cli + - run: npm run e2e + bundle-cli: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + - name: Cache node modules + uses: actions/cache@v2 + with: + path: ~/.npm + key: npm-${{ hashFiles('package-lock.json') }} + restore-keys: | + npm-${{ hashFiles('package-lock.json') }} + npm- + - name: Install dependencies + run: npm ci + - name: Bundle + run: npm run compile:cli + - name: Store bundle artifact + uses: actions/upload-artifact@v2 + with: + name: cli + path: cli + retention-days: 1 + check-version-cli: + name: Check Version + runs-on: ubuntu-latest + needs: [bundle-cli, unit-tests, e2e-tests] + outputs: + changed: ${{ steps.check.outputs.changed }} + steps: + - name: Checkout repository + uses: actions/checkout@v2 + - name: Set up Node.js + uses: actions/setup-node@v2 + - name: Check if version has been updated + id: check + uses: EndBug/version-check@v2.0.1 + with: + file-name: ./cli/package.json + file-url: https://unpkg.com/redoc-cli/package.json + static-checking: localIsNew + publish-cli: + needs: [ check-version-cli ] + if: needs.check-version-cli.outputs.changed == 'true' + runs-on: ubuntu-latest + steps: + - uses: actions/setup-node@v1 + with: + node-version: "14.x" + - uses: actions/checkout@v2 + - name: Download cli bundled artifact + uses: actions/download-artifact@v2 + with: + name: cli + path: cli + - name: Cache node modules + uses: actions/cache@v2 + with: + path: ~/.npm # npm cache files are stored in `~/.npm` on Linux/macOS + key: npm-${{ hashFiles('package-lock.json') }} + restore-keys: | + npm-${{ hashFiles('package-lock.json') }} + npm- + - name: Publish to NPM + run: cd cli/ && npm publish + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 00000000..d1c9ccbf --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,100 @@ +name: Publish + +on: + push: + tags: + - v[0-9]*.[0-9]*.[0-9]* + +jobs: + bundle: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + - name: Cache node modules + uses: actions/cache@v2 + with: + path: ~/.npm # npm cache files are stored in `~/.npm` on Linux/macOS + key: npm-${{ hashFiles('package-lock.json') }} + restore-keys: | + npm-${{ hashFiles('package-lock.json') }} + npm- + - run: npm ci + - run: npm run bundle + - name: Store bundle artifact + uses: actions/upload-artifact@v2 + with: + name: bundles + path: bundles + retention-days: 1 + unit-tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - run: npm ci + - run: npm test + e2e-tests: + needs: [bundle] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - run: npm ci + - name: Download bundled artifact + uses: actions/download-artifact@v2 + with: + name: bundles + path: bundles + - run: npm run e2e + deploy-demo: + needs: [bundle, unit-tests, e2e-tests] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: us-east-1 + - name: Install dependencies + run: npm ci + - name: Download bundled artifacts + uses: actions/download-artifact@v2 + with: + name: bundles + path: bundles + - name: Build package + run: npm run build:demo + - name: Deploy to S3 bucket + run: npm run deploy:demo + - name: Invalidate + run: aws cloudfront create-invalidation --distribution-id ${{ secrets.CF_DEMO_DISTRIBUTION_ID }} --paths "/*" + publish: + needs: [bundle, unit-tests, e2e-tests] + runs-on: ubuntu-latest + steps: + - uses: actions/setup-node@v1 + with: + node-version: "14.x" + - uses: actions/checkout@v2 + - name: Download bundled artifacts + uses: actions/download-artifact@v2 + with: + name: bundles + path: bundles + - name: Cache node modules + uses: actions/cache@v2 + with: + path: ~/.npm # npm cache files are stored in `~/.npm` on Linux/macOS + key: npm-${{ hashFiles('package-lock.json') }} + restore-keys: | + npm-${{ hashFiles('package-lock.json') }} + npm- + - name: Before deploy + run: npm run declarations + - name: Publish to NPM + run: npm publish + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + - name: After script + run: cat ./coverage/lcov.info | coveralls diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 09e54a63..a0a0bbd1 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -9,4 +9,4 @@ jobs: - uses: actions/checkout@v1 - run: npm ci - run: npm run bundle - - run: npm test + - run: npm test \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 03ac1fa4..00000000 --- a/.travis.yml +++ /dev/null @@ -1,32 +0,0 @@ -language: node_js -node_js: -- '12' -cache: - directories: - - "~/.cache" -env: - global: - - GH_REF: github.com/Redocly/redoc.git - - GIT_AUTHOR_EMAIL: redoc-bot@users.noreply.github.com - - GIT_AUTHOR_NAME: RedocBot - - secure: apiavCfCQngL9Een1m7MIXMf3bqO3rY4YY59TMBl/yFKi80CEsHPHhgVUkl6hC+aM5PeBt/vgjh37rHMX31j/pcSZ4Z8SO/4Bwr36iHfhSxSEuAQog8P07qWqH7wYYWGIVmF682stgl0fYF+GN92sx/6edFVzsWVECf2G7imtICKSTbhKGm3Dhn2JwGnhD7eyfgZ33omgiaswumdu0xABoXDfqSZR+16fC4Ap5rhv3fXO9ndvRNy1STn376nT+my6e86UrQL4aS/S+HNHgIe1BUs+5cOp6Jgw6t0ie7phY0EAiECsRxy9K4e3Dctv9m6+Wma4+vy65MS0zGyrqey6oyV4l827sCOjrD1qcqc9bX6FlMSouVoNfE4ZjINNAbgigTaiLSoDSPcf5I5smkkM2ezzFOMSZwZxNdaNL2LKb97vc8m/ZUkv0sKZyT7oqVL7aJweEivsSHj5l2KR8Z7XrVB1y2eI6GvyTSa/d+CL4dSRzjh8+IRN047YBrdTKD5IkdT0upfoBu14WPUfFmLKxX+iMCslXRWb6kwojhrWNYmZvL65KRAzJ6+eIPDG/W5QUOpYyYT77bLlBQjVo6NmVvl9v3HMECq9CHH0ivKFBGPiKMOx7cJkTax3FuyznOW2WCXB9kTb5Zk9toaiNlSp9L6ll/h2Eyxa6n6sWUgmmM= - - secure: vVRg9BKGBwF2MbXQnEccFL+XW0/7RaBmge9k7jbGYScBwkP3XjnQ/Xaj0cvTz2CM2EqXsbpwfvr4Jo+enW/E3MGy5RiEzv5hUe/jIFRR0gfAFbZxSTvg5xiFhTDffqQk0fncO4jXu+wPO5lZ2CMRWzyXz3i1MZhjMcAgoDr1+TRss/EGXLNHxr2RM88tpUW0fV2prIRoyGqhCgnYZtrm7hmr41Ej+itg1MqZLml/Rjkt3KsNgI+z0O5Qn3QSAO8GtPZqeftQxAjevOmxZGcssxY8EJvqbjAujr4y51WncXpEmCRPSY2J9R5+fkgZurqwnJapbQpjwKYemok3ps7EHg2gWkAlmPdQO4LKpbffGkM/o5b+8+HdIuQZugsSWQD9hUSftTAFLcfA1isi7V2lHE1m8bX/vk9zIyDdcPSwIaFe9y+w3PexwFmTjPLq+nia/UY2kARFZMEIFAJby6gkA70DcAJ50QOM86InJu5DSzGbIssgTGAXCn0TPPyGveaurVLw8x61j3yh8LDF46gUHey3rqv6WjpCM9h/vg7X/gq5ve/5Q2KHscUKfs/sA53Mt7qPeqRZY1QCaaRjzqJO/ZraHqWWeKmPKaWhPGR0kYEnkvB+K9GZ+HNSWCltjCO4SJ1xeEl7CRqQxAwdiMATF5SKqyiC+bn5oc35mFgbRF8= - - secure: ela1tn4wkJQZ8O4iv+4pIZi5cebxeCStVF1tEUe6qa6WWgJYVXmS2tEv3QQ36NUBFrP58Y6yl10XguPnvj/2BCqcZI4FUBHh3BfiBoUtXxDCVKI5LtlniNiOFGUwfzEeYka8T51zFlcUXSCCaxHkRZbmBsIzeJ39UwTi5fy0qwLv9GgL0czhwm8I8sZ8gyWdGmqpXNFEsb9JP4ZA3mw2qpWkGpGAqQPD9XSCkU3LmX1/ltwsBMAgGYKLLo7vU8d5KV2c8L1Gnxfl6BvfmqUD/dsas/1rnk08rU2nez5ekuQa2tJRkDLOv8bqvrGRLjHSUa3yPuisC6SsDGSU7/3DcozZyYsz7WQ6WI8tYabyjqqeJTF1N8a5T3IbZaZNV1J4JHOO9Cb/y7gIg4edANg6tbe7MzZpdEPRBnw6OkdTdirpNsWQ/jnfpY1hn6mraQZz/q8yaz3W21NjbBJhVnvfh5gWLKQ3YAAziCBhmmrThFhUu0czz+G920MuFo477TBcxvlrE7CaNJ0Q6yYkDehEPOv3jvEs1QVHPwuRrlaLTbBhrlTICKZ58gdX30O8N4i0Xgp/v6qrC03bplnMQc8E/uC61wcVLJixnlZVp8FODpUvPjsxVFkpuNSOIAaiqcERmoiPXx05Epzmr78hjU5rYCx/1MmVoeB4gs9YO+4guD4= -addons: - chrome: stable - apt: - packages: - - libgconf-2-4 -before_script: npm run bundle -script: npm test && ([ "${TRAVIS_PULL_REQUEST}" = "false" ] && npm run e2e-ci || npm - run e2e) -after_script: cat ./coverage/lcov.info | coveralls -before_deploy: npm run compile:cli && npm run declarations -deploy: -- provider: npm - skip_cleanup: true - email: gotsijroman@gmail.com - tag: next - api_key: "$NPM_TOKEN" - on: - tags: true diff --git a/README.md b/README.md index 2bbdc134..6d3f335a 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@
- ReDoc logo + Redoc logo - **OpenAPI/Swagger-generated API Reference Documentation** + # Generate interactive API documentation from OpenAPI definitions [![Build Status](https://travis-ci.com/Redocly/redoc.svg?branch=master)](https://travis-ci.com/Redocly/redoc) [![Coverage Status](https://coveralls.io/repos/Redocly/redoc/badge.svg?branch=master&service=github)](https://coveralls.io/github/Redocly/redoc?branch=master) [![dependencies Status](https://david-dm.org/Redocly/redoc/status.svg)](https://david-dm.org/Redocly/redoc) [![devDependencies Status](https://david-dm.org/Redocly/redoc/dev-status.svg)](https://david-dm.org/Redocly/redoc#info=devDependencies) [![npm](http://img.shields.io/npm/v/redoc.svg)](https://www.npmjs.com/package/redoc) [![License](https://img.shields.io/npm/l/redoc.svg)](https://github.com/Redocly/redoc/blob/master/LICENSE) @@ -11,30 +11,93 @@ **This is the README for the `2.x` version of Redoc (React-based).** **The README for the `1.x` version is on the [v1.x](https://github.com/Redocly/redoc/tree/v1.x) branch** -![ReDoc demo](https://raw.githubusercontent.com/Redocly/redoc/master/demo/redoc-demo.png) +## About Redoc -## [Live demo](http://redocly.github.io/redoc/) +Redoc is an open-source tool for generating documentation from OpenAPI (fka Swagger) definitions. -[Deploy to Github](https://github.com/Rebilly/generator-openapi-repo#generator-openapi-repo--) [ReDoc as a service](https://redoc.ly) [Customization services](https://redoc.ly/#services) +By default Redoc offers a three-panel, responsive layout: + +- The left panel contains a search bar and navigation menu. +- The central panel contains the documentation. +- The right panel contains request and response examples. + +![Redoc demo](https://raw.githubusercontent.com/Redocly/redoc/master/demo/redoc-demo.png) + +## Live demo + +If you want to see how Redoc will render your OpenAPI definition, +you can try it out online at https://redocly.github.io/redoc/. + +A version of the Swagger Petstore API is displayed by default. +To test it with your own OpenAPI definition, +enter the URL for your definition and select **TRY IT**. + +## Redoc vs. Reference vs. Portals + +Redoc is Redocly's community-edition product. Looking for something more? +Checkout the following feature comparison of Redocly's premium products versus Redoc: + +| Features | Redoc | Reference | Portals | +|------------------------------|:---------:|:---------:|:-----------:| +| **Specs** | | | | +| Swagger 2.0 | √ | √ | √ | +| OpenAPI 3.0 | √ | √ | √ | +| OpenAPI 3.1 | √ (basic) | √ | √ | +| | | | | +| **Theming** | | | | +| Fonts/colors | √ | √ | √ | +| Extra theme options | | √ | √ | +| | | | | +| **Performance** | | | | +| Pagination | | √ | √ | +| Search (enhanced) | | √ | √ | +| Search (server-side) | | | √ | +| | | | | +| **Multiple APIs** | | | | +| Multiple versions | | √ | √ | +| Multiple APIs | | | √ | +| API catalog | | | √ | +| | | | | +| **Additional features** | | | | +| Try-it console | | √ | √ | +| Automated code samples | | √ | √ | +| Deep links | | √ | √ | +| More SEO control | | | √ | +| Contextual docs | | | √ | +| Landing pages | | | √ | +| React hooks for more control | | | √ | +| Personalization | | | √ | +| Analytics integrations | | | √ | +| Feedback | | | Coming Soon | + +Refer to the Redocly's documentation for more information on these products: + +- [Portals](https://redoc.ly/docs/developer-portal/introduction/) +- [Reference](https://redoc.ly/docs/api-reference-docs/getting-started/) +- [Redoc](https://redoc.ly/docs/redoc/quickstart/intro/) ## Features +- Responsive three-panel design with menu/scrolling synchronization - [Multiple deployment options](https://redoc.ly/docs/redoc/quickstart/intro/) - [Server-side rendering (SSR) ready](https://redoc.ly/docs/redoc/quickstart/cli/#redoc-cli-commands) -- [Simple integration with `create-react-app`](https://redoc.ly/docs/redoc/quickstart/react/) - - [See an example](https://github.com/APIs-guru/create-react-app-redoc) -- [Command-line interface to bundle your docs into a **zero-dependency** HTML file](https://redoc.ly/docs/redoc/quickstart/cli/) -- Responsive three-panel design with menu/scrolling synchronization -- Deep linking support - Ability to integrate your API introduction into the side menu -- High-level grouping in side-menu with [`x-tagGroups`](https://redoc.ly/docs/api-reference-docs/specification-extensions/x-tag-groups/) specification extension +- [Simple integration with `create-react-app`](https://redoc.ly/docs/redoc/quickstart/react/) + + [Example repo](https://github.com/APIs-guru/create-react-app-redoc) +- [Command-line interface to bundle your docs into a **zero-dependency** HTML file](https://redoc.ly/docs/redoc/quickstart/cli/) +- Neat **interactive** documentation for nested objects
+ ![](docs/images/nested-demo.gif) + +## Customization options +[Customization services](https://redoc.ly/#services) +- High-level grouping in side-menu with the [`x-tagGroups`](https://redoc.ly/docs/api-reference-docs/specification-extensions/x-tag-groups/) specification extension - Branding/customizations using the [`theme` option](https://redoc.ly/docs/api-reference-docs/configuration/theming/) + +## Support - OpenAPI v3.0 support - Basic OpenAPI v3.1 support - Broad OpenAPI v2.0 feature support (yes, it supports even `discriminator`)
![](docs/images/discriminator-demo.gif) -- Neat **interactive** documentation for nested objects
- ![](docs/images/nested-demo.gif) - Code samples support (via vendor extension)
![](docs/images/code-samples-demo.gif) @@ -44,12 +107,12 @@ - `next` release: https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js Additionally, all the 1.x releases are hosted on our GitHub Pages-based CDN **(deprecated)**: -- particular release, e.g. `v1.2.0`: https://rebilly.github.io/ReDoc/releases/v1.2.0/redoc.min.js +- particular release, for example `v1.2.0`: https://rebilly.github.io/ReDoc/releases/v1.2.0/redoc.min.js - `v1.x.x` release: https://rebilly.github.io/ReDoc/releases/v1.x.x/redoc.min.js - `latest` release: https://rebilly.github.io/ReDoc/releases/latest/redoc.min.js - it will point to latest 1.x.x release since 2.x releases are not hosted on this CDN but on unpkg. ## Version Guidance -| ReDoc Release | OpenAPI Specification | +| Redoc Release | OpenAPI Specification | |:--------------|:----------------------| | 2.0.0-alpha.54| 3.1, 3.0.x, 2.0 | | 2.0.0-alpha.x | 3.0, 2.0 | @@ -66,16 +129,59 @@ Additionally, all the 1.x releases are hosted on our GitHub Pages-based CDN **(d - [APIs.guru](https://apis.guru/api-doc/) - [BoxKnight](https://www.docs.boxknight.com/) +## Lint OpenAPI definitions + +Redocly's OpenAPI CLI is an open source command-line tool that you can use to lint +your OpenAPI definition. Linting helps you to catch errors and inconsistencies in your +OpenAPI definition before publishing. + +Refer to [Lint configuration](https://redoc.ly/docs/cli/guides/lint/) in the OpenAPI documentation for more information. + ## Deployment + +### TL;DR final code example + +To render your OpenAPI definition using Redoc, use the following HTML code sample and +replace the `spec-url` attribute with the url or local file address to your definition. + +```html + + + + Redoc + + + + + + + + + + + + + + +``` + For step-by-step instructions for how to get started using Redoc to render your OpenAPI definition, refer to the -[Redoc quickstart guide](https://redoc.ly/docs/redoc/quickstart/intro/). +[**Redoc quickstart guide**](https://redoc.ly/docs/redoc/quickstart/intro/). -[**IE11 Support Notes**](docs/usage-with-ie11.md) +See [**IE11 Support Notes**](docs/usage-with-ie11.md) for information on +IE support for Redoc. -## ReDoc CLI +## Redoc CLI For more information on Redoc's commmand-line interface, refer to -[Using the Redoc CLI](https://redoc.ly/docs/redoc/quickstart/cli/). +[**Using the Redoc CLI**](https://redoc.ly/docs/redoc/quickstart/cli/). ## Configuration diff --git a/demo/openapi-3-1.yaml b/demo/openapi-3-1.yaml index 31063d4b..ac89619d 100644 --- a/demo/openapi-3-1.yaml +++ b/demo/openapi-3-1.yaml @@ -533,6 +533,20 @@ paths: subscriptionId: type: string example: AAA-123-BBB-456 + '200': + description: Successful operation + content: + application/json: + schema: + type: array + maxItems: 999 + minItems: 0 + items: + type: array + maxItems: 777 + minItems: 111 + items: + type: number callbacks: orderInProgress: '{$request.body#/callbackUrl}?event={$request.body#/eventName}': diff --git a/demo/openapi.yaml b/demo/openapi.yaml index c570cc60..5cf19340 100644 --- a/demo/openapi.yaml +++ b/demo/openapi.yaml @@ -377,7 +377,9 @@ paths: application/xml: schema: type: array + maxItems: 999 items: + maxItems: 111 $ref: '#/components/schemas/Pet' '400': description: Invalid tag value diff --git a/docs/images/redoc.png b/docs/images/redoc.png new file mode 100644 index 00000000..15579760 Binary files /dev/null and b/docs/images/redoc.png differ diff --git a/src/components/Schema/ArraySchema.tsx b/src/components/Schema/ArraySchema.tsx index eab37595..0b555c07 100644 --- a/src/components/Schema/ArraySchema.tsx +++ b/src/components/Schema/ArraySchema.tsx @@ -13,15 +13,12 @@ const PaddedSchema = styled.div` export class ArraySchema extends React.PureComponent { render() { - const itemsSchema = this.props.schema.items!; const schema = this.props.schema; + const itemsSchema = schema.items; - const itemConstraintSchema = ( - min: number | undefined = undefined, - max: number | undefined = undefined, - ) => ({ type: 'array', minItems: min, maxItems: max }); - - const minMaxItems = humanizeConstraints(itemConstraintSchema(itemsSchema?.schema?.minItems, itemsSchema?.schema?.maxItems)); + const minMaxItems = schema.minItems === undefined && schema.maxItems === undefined ? + '' : + `(${humanizeConstraints(schema)})`; if (schema.displayType && !itemsSchema && !minMaxItems.length) { return (
@@ -31,7 +28,7 @@ export class ArraySchema extends React.PureComponent { return (
- Array ({minMaxItems}) + Array {minMaxItems} diff --git a/src/components/__tests__/__snapshots__/DiscriminatorDropdown.test.tsx.snap b/src/components/__tests__/__snapshots__/DiscriminatorDropdown.test.tsx.snap index fe5617ed..7af7b482 100644 --- a/src/components/__tests__/__snapshots__/DiscriminatorDropdown.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/DiscriminatorDropdown.test.tsx.snap @@ -33,6 +33,8 @@ exports[`Components SchemaView discriminator should correctly render discriminat "format": undefined, "isCircular": undefined, "isPrimitive": true, + "maxItems": undefined, + "minItems": undefined, "options": "<<>>", "pattern": undefined, "pointer": "#/components/schemas/Dog/properties/packSize", @@ -86,6 +88,8 @@ exports[`Components SchemaView discriminator should correctly render discriminat "format": undefined, "isCircular": undefined, "isPrimitive": true, + "maxItems": undefined, + "minItems": undefined, "options": "<<>>", "pattern": undefined, "pointer": "#/components/schemas/Dog/properties/type", diff --git a/src/services/OpenAPIParser.ts b/src/services/OpenAPIParser.ts index 11a001aa..4881c99e 100644 --- a/src/services/OpenAPIParser.ts +++ b/src/services/OpenAPIParser.ts @@ -174,6 +174,18 @@ export class OpenAPIParser { return obj; } + shallowDeref(obj: OpenAPIRef | T): T { + if (this.isRef(obj)) { + const schemaName = getDefinitionName(obj.$ref); + if (schemaName && this.options.ignoreNamedSchemas.has(schemaName)) { + return { type: 'object', title: schemaName } as T; + } + const resolved = this.byRef(obj.$ref); + return this.allowMergeRefs ? this.mergeRefs(obj, resolved, false) : (resolved as T); + } + return obj; + } + mergeRefs(ref, resolved, mergeAsAllOf: boolean) { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { $ref, ...rest } = ref; @@ -183,7 +195,7 @@ export class OpenAPIParser { } if (mergeAsAllOf && keys.some((k) => k !== 'description' && k !== 'title' && k !== 'externalDocs')) { return { - allOf: [resolved, rest], + allOf: [rest, resolved], }; } else { // small optimization @@ -194,13 +206,6 @@ export class OpenAPIParser { } } - shalowDeref(obj: OpenAPIRef | T): T { - if (this.isRef(obj)) { - return this.byRef(obj.$ref)!; - } - return obj; - } - /** * Merge allOf constraints. * @param schema schema with allOF diff --git a/src/services/__tests__/OpenAPIParser.test.ts b/src/services/__tests__/OpenAPIParser.test.ts index b79f61b4..b8b211ba 100644 --- a/src/services/__tests__/OpenAPIParser.test.ts +++ b/src/services/__tests__/OpenAPIParser.test.ts @@ -1,5 +1,6 @@ import { OpenAPIParser } from '../OpenAPIParser'; import { RedocNormalizedOptions } from '../RedocNormalizedOptions'; +import { OpenAPIParameter, Referenced } from '../../types'; const opts = new RedocNormalizedOptions({}); @@ -13,5 +14,18 @@ describe('Models', () => { parser = new OpenAPIParser(spec, undefined, opts); expect(parser.mergeAllOf(spec.components.schemas.test)).toMatchSnapshot(); }); + + test('should override description from $ref of the referenced component, when sibling description exists ', () => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const spec = require('./fixtures/siblingRefDescription.json'); + parser = new OpenAPIParser(spec, undefined, opts); + const schemaOrRef: Referenced = { + $ref: '#/components/schemas/Test', + description: 'Overriden description', + }; + + expect(parser.shallowDeref(schemaOrRef)).toMatchSnapshot(); + }); + }); }); diff --git a/src/services/__tests__/__snapshots__/OpenAPIParser.test.ts.snap b/src/services/__tests__/__snapshots__/OpenAPIParser.test.ts.snap index 74a26ed0..ffbcbff5 100644 --- a/src/services/__tests__/__snapshots__/OpenAPIParser.test.ts.snap +++ b/src/services/__tests__/__snapshots__/OpenAPIParser.test.ts.snap @@ -86,3 +86,10 @@ Object { ], } `; + +exports[`Models Schema should override description from $ref of the referenced component, when sibling description exists 1`] = ` +Object { + "description": "Overriden description", + "type": "object", +} +`; diff --git a/src/services/__tests__/fixtures/siblingRefDescription.json b/src/services/__tests__/fixtures/siblingRefDescription.json new file mode 100644 index 00000000..b090a963 --- /dev/null +++ b/src/services/__tests__/fixtures/siblingRefDescription.json @@ -0,0 +1,39 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "AA", + "version": "1.0" + }, + "paths": { + "/test": { + "get": { + "operationId": "test", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "testAttr": { + "description": "Overriden description", + "$ref": "#/components/schemas/Test" + } + } + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Test": { + "type": "object", + "description": "Refed description" + } + } + } +} diff --git a/src/services/models/MediaType.ts b/src/services/models/MediaType.ts index 9e41d89a..25dae807 100644 --- a/src/services/models/MediaType.ts +++ b/src/services/models/MediaType.ts @@ -40,7 +40,7 @@ export class MediaTypeModel { this.examples = { default: new ExampleModel( parser, - { value: parser.shalowDeref(info.example) }, + { value: parser.shallowDeref(info.example) }, name, info.encoding, ), diff --git a/src/services/models/Schema.ts b/src/services/models/Schema.ts index d79d738b..b155d254 100644 --- a/src/services/models/Schema.ts +++ b/src/services/models/Schema.ts @@ -63,6 +63,8 @@ export class SchemaModel { const: any; contentEncoding?: string; contentMediaType?: string; + minItems?: number; + maxItems?: number; /** * @param isChild if schema discriminator Child @@ -128,6 +130,8 @@ export class SchemaModel { this.const = schema.const || ''; this.contentEncoding = schema.contentEncoding; this.contentMediaType = schema.contentMediaType; + this.minItems = schema.minItems; + this.maxItems = schema.maxItems; if (!!schema.nullable || schema['x-nullable']) { if (Array.isArray(this.type) && !this.type.some((value) => value === null || value === 'null')) { @@ -359,7 +363,7 @@ function buildFields( ): FieldModel[] { const props = schema.properties || {}; const additionalProps = schema.additionalProperties; - const defaults = schema.default || {}; + const defaults = schema.default; let fields = Object.keys(props || []).map((fieldName) => { let field = props[fieldName]; @@ -380,7 +384,7 @@ function buildFields( required, schema: { ...field, - default: field.default === undefined ? defaults[fieldName] : field.default, + default: field.default === undefined && defaults ? defaults[fieldName] : field.default, }, }, $ref + '/properties/' + fieldName, diff --git a/src/utils/__tests__/__snapshots__/loadAndBundleSpec.test.ts.snap b/src/utils/__tests__/__snapshots__/loadAndBundleSpec.test.ts.snap index 7807e39e..6cd0a034 100644 --- a/src/utils/__tests__/__snapshots__/loadAndBundleSpec.test.ts.snap +++ b/src/utils/__tests__/__snapshots__/loadAndBundleSpec.test.ts.snap @@ -729,7 +729,9 @@ try { "schema": Object { "items": Object { "$ref": "#/components/schemas/Pet", + "maxItems": 111, }, + "maxItems": 999, "type": "array", }, }, @@ -3245,6 +3247,26 @@ culpa qui officia deserunt mollit anim id est laborum. }, }, "responses": Object { + "200": Object { + "content": Object { + "application/json": Object { + "schema": Object { + "items": Object { + "items": Object { + "type": "number", + }, + "maxItems": 777, + "minItems": 111, + "type": "array", + }, + "maxItems": 999, + "minItems": 0, + "type": "array", + }, + }, + }, + "description": "Successful operation", + }, "201": Object { "content": Object { "application/json": Object { diff --git a/src/utils/__tests__/openapi.test.ts b/src/utils/__tests__/openapi.test.ts index d4f23e02..6f40d974 100644 --- a/src/utils/__tests__/openapi.test.ts +++ b/src/utils/__tests__/openapi.test.ts @@ -412,10 +412,10 @@ describe('Utils', () => { describe('openapi humanizeConstraints', () => { const itemConstraintSchema = ( - min: number | undefined = undefined, - max: number | undefined = undefined, - multipleOf: number | undefined = undefined, - uniqueItems?: boolean, + min?: number, + max?: number, + multipleOf?: number, + uniqueItems?: boolean ) => ({ type: 'array', minItems: min, maxItems: max, multipleOf, uniqueItems }); it('should not have a humanized constraint without schema constraints', () => { diff --git a/src/utils/highlight.ts b/src/utils/highlight.ts index 036b9129..23cdd43d 100644 --- a/src/utils/highlight.ts +++ b/src/utils/highlight.ts @@ -15,6 +15,7 @@ import 'prismjs/components/prism-objectivec.js'; import 'prismjs/components/prism-perl.js'; import 'prismjs/components/prism-php.js'; import 'prismjs/components/prism-python.js'; +import 'prismjs/components/prism-q.js'; import 'prismjs/components/prism-ruby.js'; import 'prismjs/components/prism-scala.js'; import 'prismjs/components/prism-sql.js'; diff --git a/src/utils/openapi.ts b/src/utils/openapi.ts index ad267722..d0b081f5 100644 --- a/src/utils/openapi.ts +++ b/src/utils/openapi.ts @@ -505,13 +505,13 @@ export function mergeParams( ): Array> { const operationParamNames = {}; operationParams.forEach(param => { - param = parser.shalowDeref(param); + param = parser.shallowDeref(param); operationParamNames[param.name + '_' + param.in] = true; }); // filter out path params overridden by operation ones with the same name pathParams = pathParams.filter(param => { - param = parser.shalowDeref(param); + param = parser.shallowDeref(param); return !operationParamNames[param.name + '_' + param.in]; });