Compare commits

...
53 Commits
Author SHA1 Message Date
kyle 9e3a801750 v3.14.2 (#4548)
* v3.14.2

* rebuild dist

* constrict `eslint-plugin-react` version

https://github.com/yannickcr/eslint-plugin-react/issues/1789
2018-05-12 00:05:49 -07:00
kyle 9155eb946e fix: always display locally-available title property as a model's name (#4542)
* fix: always display locally-available `title` property as a model's name
* fix failing unit test
* add e2e test case
2018-05-10 22:15:36 -07:00
maryscar 66e9bd735a fix: model property color contrast (#4540) 2018-05-10 16:19:59 -07:00
kyle ae849c31ea fix: remove outdated Safari check for Blob downloads (#4541) 2018-05-10 16:07:29 -07:00
Helder Sepulveda 1a95b9e9c9 New fixes for 1 vulnerable dependency (deep-extend) (#4507)
* fix: package.json to reduce vulnerabilities

The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/npm:lodash:20180130

Latest report for heldersepu/swagger-ui:
https://snyk.io/test/github/heldersepu/swagger-ui

* fix: package.json to reduce vulnerabilities

The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/npm:deep-extend:20180409

Latest report for heldersepu/swagger-ui:
https://snyk.io/test/github/heldersepu/swagger-ui
2018-05-07 23:00:00 -07:00
Brett Randall 073b1df730 Updated 'urls' (An array of API definition objects) description. (#4525)
* Updated 'urls' (An array of API definition objects) description.

Updated 'urls' (An array of API definition objects) description to show an actual object array containing two (2) urls, clarifying this configuration-option.

* urls is an array, not a string
2018-05-07 21:56:08 -07:00
kyle 11c586309f v3.14.1 (#4521)
* v3.14.1

* transform `const` & `let` to `var` in node_modules, for IE11

* rebuild dist
2018-05-04 19:25:20 -07:00
kyle a37d637061 meta: multiple issue templates (#4519)
* MOAR issue templates

* ...LESS issue templates
2018-05-04 12:58:08 -07:00
kyle 75747424cf improvement: sanitization via DOMPurify (#4513)
* swap `sanitize-html` for `dompurify`

* set up node enzyme tests with jsdom

dompurify, as the name suggests, needs a DOM or it won't work!

* reconcile tests and sanitizer settings

* remove obsolete sanitizeOptions

* add `jsdom` dependency
2018-05-04 10:06:44 -07:00
Helder Sepulveda 8055129dd2 Improve downloadable HighlightCode filename (#4508)
* Update highlight-code.jsx
* improve filename no more response.txt
* use new `fileName` prop for file names
* use template strings for `fileName` prop values
* fall back to old "response.txt" file name if none is provided
2018-05-03 17:33:21 -07:00
kyle 7049de6201 v3.14.0 (#4492)
* v3.14.0

* fix: simplify config fetch interceptor implementation

* add `loadRemoteConfig` flag to requests

* v3.14.0
2018-04-27 23:20:13 -07:00
kyle 8cef3adfe6 fix: path-item $ref operation metadata storage (#4489) 2018-04-27 10:29:33 -07:00
Helder Sepulveda cbff0251ae feat: option to show common query parameters (#4245)
* extend getExtensions

Add optional param to getExtensions that can retrieve more stuff

* Add getCommonExtensions

* Trim trailing spaces

* Remove unused parameter

* Move the format inline with the param type

* correction to UnitTest
2018-04-26 21:18:45 -07:00
kyle 62354568a9 feat: request/response interceptors for remote config fetch (#4484) 2018-04-26 21:04:55 -07:00
kyle d981f0f26e v3.13.6 (#4472) 2018-04-24 00:18:38 -07:00
kyle 44c3f09abe v3.13.5 (#4464)
* v3.13.5

* rebuild dist
2018-04-20 20:34:43 -07:00
kyle 0c50c7e99f enhancement: don't block scrolling if HighlightCode is not scrollable (#4463) 2018-04-20 16:34:22 -07:00
Helder Sepulveda 62ed4db110 fix: http auth component state retention issue (#4394)
* Set the value if set is available

* tests: add failing e2e test case

* use Object.assign to always ensure setState receives a new value object
2018-04-20 16:08:39 -07:00
r-ising 553acee07d fix: redundant Markdown required className prop warning (#4457) 2018-04-19 14:39:19 -07:00
kyle 932cc9838c fix: Callback component $$ref handling (#4454) 2018-04-18 18:05:15 -07:00
Helder Sepulveda d3c9ceceef Change the 5# to 4# (#4447)
The 5# do not show well on the wiki but 4# do and they were not used
2018-04-18 16:29:53 -07:00
kyle 5f855e56bb v3.13.4 (#4438)
* v3.13.4

* rebuild dist
2018-04-13 18:38:21 -07:00
Ville Skyttä 3f6e21e3b1 improve: HTTP auth display logic and messaging (#4430)
* http-auth: Include unsupported scheme name in error message

* http-auth: Treat authentication scheme case insensitively

The spec refers to RFC 7235 for scheme names, and the RFC says names
are matched case insensitively.

* linter fixes
2018-04-13 17:02:36 -07:00
Aleksey Ganzha 26d0ad7684 fix(regression): model rendering for Swagger 2.0 body parameters (#4424)
* Fix model rendering for parameters

* tests(e2e): add test confirming that Pet model is shown in operation
2018-04-13 16:57:32 -07:00
kyle 9a121ea445 fix: attempt to display Blob response bodies as text (#4437)
* fix: attempt to display Blob response bodies as text

... if nothing else works.

* correctly pass `prevProps.content` to updateParsedContent

* remove debugger statement

* caniuse `Blob` && `FileReader`? yes.

Blob: https://caniuse.com/#search=Blob
FileReader: https://caniuse.com/#search=filereader
2018-04-13 16:16:54 -07:00
kyle c55f22575a fix: make Schemes <select> a fully state-controlled input (#4436) 2018-04-13 13:52:01 -07:00
Giancarlo Klemm Camilo 2795518340 enhance: scrollable + downloadable HighlightCode (#4397)
* Auto hidding content that is longer than 600 characters long.

* Added basic downloading

Slightly broken

* Better downloading

now downloads file on button click

* Fix the angry linter

* Fix dist

* Removed collapsing, added scrolling.

* Code clean up.

* CSS fix.

* prevent HighlightCode from scrolling entire document

* center "Download" text in button

* `this.downloadJSON` -> `this.downloadText`

we're always saving as `.txt`, so JSON is a misnomer

* hide Download button behind option `downloadable` prop

* `file-saver` -> `js-file-download`
2018-04-12 18:18:49 -07:00
Chris Ormerod 2bf2167a18 docs: fix typo (#4431)
Operations is lowercase, without this fix the example doesn't work as a Copy/Paste
2018-04-12 16:53:10 -07:00
kyle dc7d7054bf fix: provide svg assets through React app instead of relying on HTML (#4429)
* fix: provide svg assets through React app instead of relying on HTML

* fix stylistic inconsistencies in HTML assets
2018-04-10 16:05:17 -07:00
Ville Skyttä 14473dd51d fix: misc. spelling errors (#4421) 2018-04-09 15:58:52 -07:00
kyle a0b659a96e V/3.13.3 (#4420)
* v3.13.3

* rebuild dist
2018-04-06 22:55:24 -07:00
kyle 3efdf1223e fix: add additionalQueryStringParams to auth requests (#4419)
* tests: add failing unit tests

* fix: add additionalQueryStringParams to auth requests
2018-04-06 20:00:10 -07:00
kyle 911a140e38 docs: add async interceptor documentation (#4418)
* docs: add async interceptor documentation

* Update configuration.md
2018-04-06 15:19:44 -07:00
Luka Žitnik e2d8a4e396 Fix(auth): improper resolution of relative token urls (#4180)
* fix(auth): improper resolution of relative token urls

* revert cc58ba7 for OAS2

In OAS2, relative token URLs are resolved against the host that serves the specs.
2018-04-05 19:20:20 -07:00
David DE CARVALHO 39d34523b9 Improve enum values for Enum Type in Swagger ReadOnly documentation (#4191)
* Adding enum values for Enum Type in Swagger ReadOnly documentation

* Adding enum values for Enum Type in Swagger ReadOnly documentation (optimisation) and also adding default/example value

* Add new display enums, defaults, and examples when not in TIO mode (another way to have enums values in swagger.json)

* Fix npm test result

* review corrections

* fix: don't render parameter description if field is empty

* use cross-version schema variable to access properties

* pass className through Markdown component usage

* add per-field classNames to Markdown for easier styling + testing

* remove parameter Example field (out-of-scope for this PR)

* get default value from schema instead of top-level parameter

* tests: add e2e cases for swagger2 and oas3

* remove `swagger-petstore-enum.json`

the purpose of this file lives on in the e2e test specs folder

* add missing proptypes validation

* use `classnames` to more effectively union class names
2018-04-05 18:48:19 -07:00
kyle d90353228a pass meta error information to JsonSchemaForm correctly (#4416)
* tests: add failing e2e tests

* fix: pass meta error information to JsonSchemaForm correctly
2018-04-05 15:30:31 -07:00
kyle 4480db0390 Use non-normalized operationId for layout management, if it is available (#4410)
* test(e2e): modify e2e hot server to peacefully coexist with dev server

* tests: add failing e2e test case

* fix: use originalOperationId for layout targeting, if available
2018-04-04 18:55:10 -07:00
kyle 2c4327bfd8 v3.13.2 (#4393)
* v3.13.2

* rebuild dist
2018-03-30 21:55:58 -07:00
kyle fd8274b353 Filter $$ref from examples (#4392)
* fix(dev-server): don't open localhost in a browser
* tests: refactor model-example enzyme tests to be more isolated
* tests: add failing sampleFromSchema tests for $$ref keys
* tests: add additional test for user-created $$ref values
* fix: create deeplyStripKey; use it to filter $$refs out of examples
* tests: add cases for deeplyStripKey
2018-03-30 18:02:32 -07:00
Helder Sepulveda 762a32b59b fix: header access for Edge browser (#4383) 2018-03-29 16:24:46 -07:00
kyle 8189fd2473 fix: path item $ref rendering (#4381)
* WIP: trigger resolution of Path Item $ref on spec load

* fix(dev-server): don't open localhost in a browser

* preserve key order when merging specJson with specResolvedSubtrees

* remove stray `debugger`
2018-03-27 17:04:31 -07:00
Jake Reed 71d39af443 fix(docker): MAINTAINER deprecated. Use LABEL. (#4370) 2018-03-27 12:02:22 -07:00
kyle 9c8955edd4 v3.13.1 (#4367)
* fix: don't assign example if an example does not exist

fixes #4319

* v3.13.1

* rebuild dist
2018-03-24 14:20:35 -07:00
kyle 003a2e7584 fix: don't assign example if an example does not exist (#4365)
fixes #4319
2018-03-24 02:04:01 -07:00
kyle 1f9fb0e68d fix: request resolution via OperationContainer on mount (#4358) 2018-03-21 21:06:18 -07:00
kyle 14ef0cc379 v3.13.0 (#4342)
* v3.13.0

* Rebuild dist
2018-03-17 00:27:24 -07:00
kyle 08cfc77fde fix: default to empty ImmutableMap when schema is missing (#4341) 2018-03-16 22:59:46 -07:00
kyle b8cf080e6c docs: fix broken method table (#4340) 2018-03-16 22:34:54 -07:00
kyle 245428e7cd feat: preauthorization (#4339)
* feat: swagger 2.0 preauthorization methods
* tests: add cases for oas3 preauthorization
* docs: add new preauth docs; touch up existing auth docs
* tests: add `rootInject` tests
* docs: remove unfinished sentence
2018-03-16 22:25:04 -07:00
kyle 624a81201f feat: onComplete config option (#4322)
* feat: `onComplete` config option

* tests(e2e): add case for onComplete option
2018-03-16 00:08:39 -07:00
kyle b0be261ef8 fix: remove $$ref key from example if it exists (#4333)
* fix: remove $$ref key from example if it exists

* guard $$ref property access
2018-03-15 16:40:04 -07:00
kyle 6c5a370ef7 create InfoUrl and InfoBasePath (#4330)
* create `InfoUrl` component
* create `InfoBasePath` component
* linter fixes
2018-03-14 16:06:29 -07:00
Scott O'Hara fb7f12551f improve: show possible reasons when url fetch fails (#4295)
* improve: show possible reasons when url fetch fails
* Only check for fail reasons if no HTTP status code
* Check for mixed content errors first
* Harmonise error messages
* Set error source to 'fetch'
* improve: hide loading validator image (#4287)
* hide missing validator image
* Render nothing if validator badge has not loaded/received errors
* Revert style changes
* Allow images with data scheme (#4305)
* Add UnitTest for image
allows image elements with https scheme
* Test images with data scheme
* Add allowedSchemesByTag
* Fix error  Strings must use doublequote  quotes
* Add empty div (#4236)
if there are properties show an empty div
* v3.12.1 (#4311)
* v3.12.1
* Rebuild dist for v3.12.1
* Use window wrapper instead of direct reference
* Add trailing period to error messages
* improve readability
2018-03-12 16:57:51 -07:00
104 changed files with 3552 additions and 557 deletions
+1
View File
@@ -6,6 +6,7 @@
],
"plugins": [
"transform-runtime",
"babel-plugin-transform-es2015-constants",
[
"module-alias",
[
+72
View File
@@ -0,0 +1,72 @@
---
name: Bug report
about: Report an issue you're experiencing
---
<!---
Thanks for filing a bug report 😄 ! Before you submit, please read the following:
If you're here to report a security issue, please STOP writing an issue and
contact us at security@swagger.io instead!
Search open/closed issues before submitting!
Issues on GitHub are only related to problems of Swagger-UI itself. We'll try
to offer support here for your use case, but we can't offer help with projects
that use Swagger-UI indirectly, like Springfox or swagger-node.
Likewise, we can't accept bugs in the Swagger/OpenAPIspecifications themselves,
or anything that violates the specifications.
-->
## Q&A (please complete the following information)
- OS: [e.g. macOS]
- Browser: [e.g. chrome, safari]
- Version: [e.g. 22]
- Method of installation: [e.g. npm, dist assets]
- Swagger-UI version: [e.g. 3.10.0]
- Swagger/OpenAPI version: [e.g. Swagger 2.0, OpenAPI 3.0]
### Content & configuration
<!--
Provide us with a way to see what you're seeing,
so that we can fix your issue.
-->
Example Swagger/OpenAPI definition:
```yaml
# your YAML here
```
Swagger-UI configuration options:
```js
SwaggerUI({
// your config options here
})
```
```
?yourQueryStringConfig
```
### Describe the bug you're encountering
<!-- A clear and concise description of what the bug is. -->
### To reproduce...
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
### Expected behavior
<!-- A clear and concise description of what you expected to happen. -->
### Screenshots
<!-- If applicable, add screenshots to help explain your problem. -->
### Additional context or thoughts
<!-- Add any other context about the problem here. -->
+50
View File
@@ -0,0 +1,50 @@
---
name: Feature request
about: Suggest an new feature or enhancement for this project
---
## Q&A (please complete the following information)
- OS: [e.g. macOS]
- Browser: [e.g. chrome, safari]
- Version: [e.g. 22]
- Method of installation: [e.g. npm, dist assets]
- Swagger-UI version: [e.g. 3.10.0]
- Swagger/OpenAPI version: [e.g. Swagger 2.0, OpenAPI 3.0]
### Content & configuration
Swagger/OpenAPI definition:
```yaml
# your YAML here
```
Swagger-UI configuration options:
```js
SwaggerUI({
// your config options here
})
```
```
?yourQueryStringConfig
```
### Is your feature request related to a problem?
<!--
Please provide a clear and concise description of what the problem is.
"I'm always frustrated when..."
-->
### Describe the solution you'd like
<!-- A clear and concise description of what you want to happen. -->
### Describe alternatives you've considered
<!--
A clear and concise description of any alternative solutions or features
you've considered.
-->
### Additional context
<!-- Add any other context or screenshots about the feature request here. -->
+46
View File
@@ -0,0 +1,46 @@
---
name: Support
about: Ask a question or request help with your implementation.
---
<!--
We can only offer support for Swagger-UI itself.
If you're having a problem with a library that uses Swagger-UI
(for example, Springfox or swagger-node), please open an issue
in that project's repository instead.
-->
### Q&A (please complete the following information)
- OS: [e.g. macOS]
- Browser: [e.g. chrome, safari]
- Version: [e.g. 22]
- Method of installation: [e.g. npm, dist assets]
- Swagger-UI version: [e.g. 3.10.0]
- Swagger/OpenAPI version: [e.g. Swagger 2.0, OpenAPI 3.0]
### Content & configuration
<!-- Provide us with a way to see what you're seeing, so that we can help. -->
Swagger/OpenAPI definition:
```yaml
# your YAML here
```
Swagger-UI configuration options:
```js
SwaggerUI({
// your config options here
})
```
```
?yourQueryStringConfig
```
### Screenshots
<!-- If applicable, add screenshots to help give context to your problem. -->
### How can we help?
<!-- Your question or problem goes here! -->
-70
View File
@@ -1,70 +0,0 @@
<!---
Thanks for filing an issue 😄 ! Before you submit, please read the following:
If you're here to report a security issue, please STOP writing an issue and contact us
at security@swagger.io instead!
Search open/closed issues before submitting since someone might have asked the same thing before!
Issues on GitHub are only related to problems of Swagger-UI itself. We'll try to offer support
here for your use case, but we can't offer help with projects that use Swagger-UI indirectly,
like Springfox or swagger-node.
Likewise, we can't accept features or bugs in the Swagger/OpenAPI specifications themselves,
or anything that violates the specifications.
-->
<!--- Provide a general summary of the issue in the title above -->
<!---
If you aren't sure what Swagger-UI version, see this guide: https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/version-detection.md
--->
| Q | A
| ------------------------------- | -------
| Bug or feature request? |
| Which Swagger/OpenAPI version? |
| Which Swagger-UI version? |
| How did you install Swagger-UI? |
| Which browser & version? |
| Which operating system? |
### Demonstration API definition
<!--- If you're describing a bug, please provide an API definition that reproduces your problem -->
<!--- If you have link to a demo repo please link that! -->
<!--- If your spec is large, please put it into a Gist (https://gist.github.com) instead of pasting it here. -->
```yaml
your: "API definition goes here"
```
### Configuration (browser query string, constructor, config.yaml)
<!--- If describing a bug, tell us what your configuration looks like -->
```js
{
"your": { "constructorConfig": "here" }
}
```
`?yourQueryStringConfig=here`
### Expected Behavior
<!--- If you're describing a bug, tell us what should happen -->
<!--- If you're suggesting a change/improvement, tell us how it should work -->
### Current Behavior
<!--- If describing a bug, tell us what happens instead of the expected behavior -->
<!--- If suggesting a change/improvement, explain the difference from current behavior -->
### Possible Solution
<!--- Not obligatory, but suggest a fix/reason for the bug, -->
<!--- or ideas how to implement the addition or change -->
### Context
<!--- How has this issue affected you? What are you trying to accomplish? -->
<!--- Providing context helps us come up with a solution that is most useful in the real world -->
+1 -1
View File
@@ -1,6 +1,6 @@
FROM alpine:3.5
MAINTAINER fehguy
LABEL maintainer="fehguy"
ENV VERSION "v2.2.10"
ENV FOLDER "swagger-ui-2.2.10"
+1 -1
View File
@@ -22,7 +22,7 @@ The OpenAPI Specification has undergone 5 revisions since initial creation in 20
Swagger UI Version | Release Date | OpenAPI Spec compatibility | Notes
------------------ | ------------ | -------------------------- | -----
3.12.1 | 2018-03-09 | 2.0, 3.0 | [tag v3.12.1](https://github.com/swagger-api/swagger-ui/tree/v3.12.1)
3.14.2 | 2018-05-11 | 2.0, 3.0 | [tag v3.14.2](https://github.com/swagger-api/swagger-ui/tree/v3.14.2)
3.0.21 | 2017-07-26 | 2.0 | [tag v3.0.21](https://github.com/swagger-api/swagger-ui/tree/v3.0.21)
2.2.10 | 2017-01-04 | 1.1, 1.2, 2.0 | [tag v2.2.10](https://github.com/swagger-api/swagger-ui/tree/v2.2.10)
2.1.5 | 2016-07-20 | 1.1, 1.2, 2.0 | [tag v2.1.5](https://github.com/swagger-api/swagger-ui/tree/v2.1.5)
+35 -67
View File
@@ -16,13 +16,16 @@
overflow: -moz-scrollbars-vertical;
overflow-y: scroll;
}
*,
*:before,
*:after
{
box-sizing: inherit;
}
body {
body
{
margin:0;
background: #fafafa;
}
@@ -30,75 +33,40 @@
</head>
<body>
<div id="swagger-ui"></div>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position:absolute;width:0;height:0">
<defs>
<symbol viewBox="0 0 20 20" id="unlocked">
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V6h2v-.801C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8z"></path>
</symbol>
<script src="./swagger-ui-bundle.js"> </script>
<script src="./swagger-ui-standalone-preset.js"> </script>
<script>
window.onload = function() {
window["SwaggerUIBundle"] = window["swagger-ui-bundle"]
window["SwaggerUIStandalonePreset"] = window["swagger-ui-standalone-preset"]
// Build a system
const ui = SwaggerUIBundle({
url: "http://petstore.swagger.io/v2/swagger.json",
dom_id: '#swagger-ui',
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout"
})
<symbol viewBox="0 0 20 20" id="locked">
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8zM12 8H8V5.199C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8z"/>
</symbol>
window.ui = ui
<symbol viewBox="0 0 20 20" id="close">
<path d="M14.348 14.849c-.469.469-1.229.469-1.697 0L10 11.819l-2.651 3.029c-.469.469-1.229.469-1.697 0-.469-.469-.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-.469-.469-.469-1.228 0-1.697.469-.469 1.228-.469 1.697 0L10 8.183l2.651-3.031c.469-.469 1.228-.469 1.697 0 .469.469.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c.469.469.469 1.229 0 1.698z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="large-arrow">
<path d="M13.25 10L6.109 2.58c-.268-.27-.268-.707 0-.979.268-.27.701-.27.969 0l7.83 7.908c.268.271.268.709 0 .979l-7.83 7.908c-.268.271-.701.27-.969 0-.268-.269-.268-.707 0-.979L13.25 10z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="large-arrow-down">
<path d="M17.418 6.109c.272-.268.709-.268.979 0s.271.701 0 .969l-7.908 7.83c-.27.268-.707.268-.979 0l-7.908-7.83c-.27-.268-.27-.701 0-.969.271-.268.709-.268.979 0L10 13.25l7.418-7.141z"/>
</symbol>
<symbol viewBox="0 0 24 24" id="jump-to">
<path d="M19 7v4H5.83l3.58-3.59L8 6l-6 6 6 6 1.41-1.41L5.83 13H21V7z"/>
</symbol>
<symbol viewBox="0 0 24 24" id="expand">
<path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>
</symbol>
</defs>
</svg>
<div id="swagger-ui"></div>
<script src="./swagger-ui-bundle.js"> </script>
<script src="./swagger-ui-standalone-preset.js"> </script>
<script>
window.onload = function() {
window["SwaggerUIBundle"] = window["swagger-ui-bundle"]
window["SwaggerUIStandalonePreset"] = window["swagger-ui-standalone-preset"]
// Build a system
const ui = SwaggerUIBundle({
url: "http://petstore.swagger.io/v2/swagger.json",
dom_id: '#swagger-ui',
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout"
})
window.ui = ui
ui.initOAuth({
clientId: "your-client-id",
clientSecret: "your-client-secret-if-required",
realm: "your-realms",
appName: "your-app-name",
scopeSeparator: " ",
additionalQueryStringParams: {}
})
}
</script>
ui.initOAuth({
clientId: "your-client-id",
clientSecret: "your-client-secret-if-required",
realm: "your-realms",
appName: "your-app-name",
scopeSeparator: " ",
additionalQueryStringParams: {}
})
}
</script>
</body>
</html>
+56 -90
View File
@@ -1,95 +1,61 @@
<!-- HTML for static distribution bundle build -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Swagger UI</title>
<link href="https://fonts.googleapis.com/css?family=Open+Sans:400,700|Source+Code+Pro:300,600|Titillium+Web:400,600,700" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="./swagger-ui.css" >
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
<style>
html
{
box-sizing: border-box;
overflow: -moz-scrollbars-vertical;
overflow-y: scroll;
<head>
<meta charset="UTF-8">
<title>Swagger UI</title>
<link href="https://fonts.googleapis.com/css?family=Open+Sans:400,700|Source+Code+Pro:300,600|Titillium+Web:400,600,700" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="./swagger-ui.css" >
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
<style>
html
{
box-sizing: border-box;
overflow: -moz-scrollbars-vertical;
overflow-y: scroll;
}
*,
*:before,
*:after
{
box-sizing: inherit;
}
body
{
margin:0;
background: #fafafa;
}
</style>
</head>
<body>
<div id="swagger-ui"></div>
<script src="./swagger-ui-bundle.js"> </script>
<script src="./swagger-ui-standalone-preset.js"> </script>
<script>
window.onload = function() {
// Build a system
const ui = SwaggerUIBundle({
url: "http://petstore.swagger.io/v2/swagger.json",
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout"
})
window.ui = ui
}
*,
*:before,
*:after
{
box-sizing: inherit;
}
body {
margin:0;
background: #fafafa;
}
</style>
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position:absolute;width:0;height:0">
<defs>
<symbol viewBox="0 0 20 20" id="unlocked">
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V6h2v-.801C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8z"></path>
</symbol>
<symbol viewBox="0 0 20 20" id="locked">
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8zM12 8H8V5.199C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="close">
<path d="M14.348 14.849c-.469.469-1.229.469-1.697 0L10 11.819l-2.651 3.029c-.469.469-1.229.469-1.697 0-.469-.469-.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-.469-.469-.469-1.228 0-1.697.469-.469 1.228-.469 1.697 0L10 8.183l2.651-3.031c.469-.469 1.228-.469 1.697 0 .469.469.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c.469.469.469 1.229 0 1.698z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="large-arrow">
<path d="M13.25 10L6.109 2.58c-.268-.27-.268-.707 0-.979.268-.27.701-.27.969 0l7.83 7.908c.268.271.268.709 0 .979l-7.83 7.908c-.268.271-.701.27-.969 0-.268-.269-.268-.707 0-.979L13.25 10z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="large-arrow-down">
<path d="M17.418 6.109c.272-.268.709-.268.979 0s.271.701 0 .969l-7.908 7.83c-.27.268-.707.268-.979 0l-7.908-7.83c-.27-.268-.27-.701 0-.969.271-.268.709-.268.979 0L10 13.25l7.418-7.141z"/>
</symbol>
<symbol viewBox="0 0 24 24" id="jump-to">
<path d="M19 7v4H5.83l3.58-3.59L8 6l-6 6 6 6 1.41-1.41L5.83 13H21V7z"/>
</symbol>
<symbol viewBox="0 0 24 24" id="expand">
<path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>
</symbol>
</defs>
</svg>
<div id="swagger-ui"></div>
<script src="./swagger-ui-bundle.js"> </script>
<script src="./swagger-ui-standalone-preset.js"> </script>
<script>
window.onload = function() {
// Build a system
const ui = SwaggerUIBundle({
url: "http://petstore.swagger.io/v2/swagger.json",
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout"
})
window.ui = ui
}
</script>
</body>
</script>
</body>
</html>
+38 -27
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+3 -2
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -18,7 +18,7 @@ class OperationsLayout extends React.Component {
getComponent
} = this.props
const Operations = getComponent("Operations", true)
const Operations = getComponent("operations", true)
return (
<div>
+10 -10
View File
@@ -68,7 +68,7 @@ There is no dependency management built into the plugin system, so if you create
### Interfaces
##### Actions
#### Actions
```javascript
const MyActionPlugin = () => {
@@ -102,7 +102,7 @@ This action creator function will be exposed to container components as `example
For more information about the concept of actions in Redux, see the [Redux Actions documentation](http://redux.js.org/docs/basics/Actions.html).
##### Reducers
#### Reducers
Reducers take a state (which is an [Immutable.js map](https://facebook.github.io/immutable-js/docs/#/Map)) and an action, and return a new state.
@@ -126,7 +126,7 @@ const MyReducerPlugin = function(system) {
}
```
##### Selectors
#### Selectors
Selectors reach into their namespace's state to retrieve or derive data from the state.
@@ -172,7 +172,7 @@ Once a selector has been defined, you can use it anywhere that you can get a sys
system.exampleSelectors.myFavoriteColor() // gets `favColor` in state for you
```
##### Components
#### Components
You can provide a map of components to be integrated into the system.
@@ -214,7 +214,7 @@ const NeverShowInfoPlugin = function(system) {
}
```
##### Wrap-Actions
#### Wrap-Actions
Wrap Actions allow you to override the behavior of an action in the system.
@@ -262,7 +262,7 @@ const MyWrapActionPlugin = function(system) {
}
```
##### Wrap-Selectors
#### Wrap-Selectors
Wrap Selectors allow you to override the behavior of a selector in the system.
@@ -307,7 +307,7 @@ const MyWrapSelectorsPlugin = function(system) {
}
```
##### Wrap-Components
#### Wrap-Components
Wrap Components allow you to override a component registered within the system.
@@ -381,7 +381,7 @@ const MyWrapComponentPlugin = function(system) {
}
```
##### `rootInjects`
#### `rootInjects`
The `rootInjects` interface allows you to inject values at the top level of the system.
@@ -398,7 +398,7 @@ const MyRootInjectsPlugin = function(system) {
}
```
##### `afterLoad`
#### `afterLoad`
The `afterLoad` plugin method allows you to get a reference to the system after your plugin has been registered.
@@ -431,7 +431,7 @@ const MyMethodProvidingPlugin = function() {
}
```
##### fn
#### fn
The fn interface allows you to add helper functions to the system for use elsewhere.
+13 -3
View File
@@ -30,7 +30,7 @@ Parameter Name | Description
`domNode` | `Element`, **REQUIRED** if `dom_id` is not provided. The HTML DOM element inside which SwaggerUi will put the user interface for swagger. Overrides `dom_id`.
`spec` | `Object={}`. A JS object describing the OpenAPI Specification. When used, the `url` parameter will not be parsed. This is useful for testing manually-generated specifications without hosting them.
`url` | `String`. The url pointing to API definition (normally `swagger.json` or `swagger.yaml`). Will be ignored if `urls` or `spec` is used.
`urls` | `String`. An array of API definition objects (`{url: "<url>", name: "<name>"}`) used by Topbar plugin. When used and Topbar plugin is enabled, the `url` parameter will not be parsed. Names and URLs must be unique among all items in this array, since they're used as identifiers.
`urls` | `Array`. An array of API definition objects (`[{url: "<url1>", name: "<name1>"},{url: "<url2>", name: "<name2>"}]`) used by Topbar plugin. When used and Topbar plugin is enabled, the `url` parameter will not be parsed. Names and URLs must be unique among all items in this array, since they're used as identifiers.
`urls.primaryName` | `String`. When using `urls`, you can use this subparameter. If the value matches the name of a spec provided in `urls`, that spec will be displayed when Swagger-UI loads, instead of defaulting to the first spec in `urls`.
##### Plugin system
@@ -58,15 +58,17 @@ Parameter Name | Description
`maxDisplayedTags` | `Number`. If set, limits the number of tagged operations displayed to at most this many. The default is to show all operations.
`operationsSorter` | `Function=(a => a)`. Apply a sort to the operation list of each API. It can be 'alpha' (sort by paths alphanumerically), 'method' (sort by HTTP method) or a function (see Array.prototype.sort() to know how sort function works). Default is the order returned by the server unchanged.
`showExtensions` | `Boolean=false`. Controls the display of vendor extension (`x-`) fields and values for Operations, Parameters, and Schema.
`showCommonExtensions` | `Boolean=false`. Controls the display of extensions (`pattern`, `maxLength`, `minLength`, `maximum`, `minimum`) fields and values for Parameters.
`tagsSorter` | `Function=(a => a)`. Apply a sort to the tag list of each API. It can be 'alpha' (sort by paths alphanumerically) or a function (see [Array.prototype.sort()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) to learn how to write a sort function). Two tag name strings are passed to the sorter for each pass. Default is the order determined by Swagger-UI.
`onComplete` | `Function=NOOP`. Provides a mechanism to be notified when Swagger-UI has finished rendering a newly provided definition.
##### Network
Parameter Name | Description
--- | ---
`oauth2RedirectUrl` | `String`. OAuth redirect URL.
`requestInterceptor` | `Function=(a => a)`. MUST be a function. Function to intercept remote definition, Try-It-Out, and OAuth2 requests. Accepts one argument requestInterceptor(request) and must return the potentially modified request.
`responseInterceptor` |`Function=(a => a)`. MUST be a function. Function to intercept remote definition, Try-It-Out, and OAuth2 responses. Accepts one argument responseInterceptor(response) and must return the potentially modified response.
`requestInterceptor` | `Function=(a => a)`. MUST be a function. Function to intercept remote definition, Try-It-Out, and OAuth2 requests. Accepts one argument requestInterceptor(request) and must return the modified request, or a Promise that resolves to the modified request.
`responseInterceptor` |`Function=(a => a)`. MUST be a function. Function to intercept remote definition, Try-It-Out, and OAuth2 responses. Accepts one argument responseInterceptor(response) and must return the modified response, or a Promise that resolves to the modified response.
`showMutatedRequest` | `Boolean=true`. If set to `true`, uses the mutated request returned from a requestInterceptor to produce the curl command in the UI, otherwise the request before the requestInterceptor was applied is used.
`supportedSubmitMethods` | `Array=["get", "put", "post", "delete", "options", "head", "patch", "trace"]`. List of HTTP methods that have the Try it out feature enabled. An empty array disables Try it out for all operations. This does not filter the operations from the display.
`validatorUrl` | `String="https://online.swagger.io/validator" OR null`. By default, Swagger-UI attempts to validate specs against swagger.io's online validator. You can use this parameter to set a different validator URL, for example for locally deployed validators ([Validator Badge](https://github.com/swagger-api/validator-badge)). Setting it to `null` will disable validation.
@@ -77,3 +79,11 @@ Parameter Name | Description
--- | ---
`modelPropertyMacro` | `Function`. Function to set default values to each property in model. Accepts one argument modelPropertyMacro(property), property is immutable
`parameterMacro` | `Function`. Function to set default value to parameters. Accepts two arguments parameterMacro(operation, parameter). Operation and parameter are objects passed for context, both remain immutable
### Instance methods
Method Name | Description
--- | ---
`initOAuth` | `(configObj) => void`. Provide Swagger-UI with information about your OAuth server - see the OAuth2 documentation for more information.
`preauthorizeBasic` | `(authDefinitionKey, username, password) => action`. Programmatically set values for a Basic authorization scheme.
`preauthorizeApiKey` | `(authDefinitionKey, apiKeyValue) => action`. Programmatically set values for an API key authorization scheme.
+2 -4
View File
@@ -1,7 +1,5 @@
# OAuth2 configuration
You can configure OAuth2 authorization by calling `initOAuth` method with passed configs under the instance of `SwaggerUIBundle`
default `client_id` and `client_secret`, `realm`, an application name `appName`, `scopeSeparator`, `additionalQueryStringParams`,
`useBasicAuthenticationWithAccessCodeGrant`.
You can configure OAuth2 authorization by calling the `initOAuth` method.
Config Name | Description
--- | ---
@@ -11,7 +9,7 @@ realm | realm query parameter (for oauth1) added to `authorizationUrl` and `toke
appName | application name, displayed in authorization popup. MUST be a string
scopeSeparator | scope separator for passing scopes, encoded before calling, default value is a space (encoded value `%20`). MUST be a string
additionalQueryStringParams | Additional query parameters added to `authorizationUrl` and `tokenUrl`. MUST be an object
useBasicAuthenticationWithAccessCodeGrant | Only activated for the `accessCode` flow. During the `authorization_code` request to the `tokenUrl`, pass the [Client Password](https://tools.ietf.org/html/rfc6749#section-2.3.1) using the HTTP Basic Authentication scheme (`Authorization` header with `Basic base64encoded[client_id:client_secret]`). The default is `false`
useBasicAuthenticationWithAccessCodeGrant | Only activated for the `accessCode` flow. During the `authorization_code` request to the `tokenUrl`, pass the [Client Password](https://tools.ietf.org/html/rfc6749#section-2.3.1) using the HTTP Basic Authentication scheme (`Authorization` header with `Basic base64encode(client_id + client_secret)`). The default is `false`
```javascript
const ui = SwaggerUI({...})
+14 -10
View File
@@ -1,10 +1,11 @@
var path = require("path")
var webpack = require('webpack')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
var deepExtend = require('deep-extend')
const {gitDescribeSync} = require('git-describe')
var webpack = require("webpack")
var ExtractTextPlugin = require("extract-text-webpack-plugin")
const CopyWebpackPlugin = require("copy-webpack-plugin")
const UglifyJsPlugin = require("uglifyjs-webpack-plugin")
var deepExtend = require("deep-extend")
const {gitDescribeSync} = require("git-describe")
const os = require("os")
var pkg = require("./package.json")
@@ -28,7 +29,10 @@ var commonRules = [
retainLines: true
}
}],
include: [ path.join(__dirname, "src") ]
include: [
path.join(__dirname, "src"),
path.join(__dirname, "node_modules", "object-assign-deep"),
]
},
{ test: /\.(txt|yaml)(\?.*)?$/,
loader: "raw-loader" },
@@ -61,10 +65,10 @@ module.exports = function(rules, options) {
}))
}
if( specialOptions.minimize ) { // production mode
if( specialOptions.minimize ) { // production mode
plugins.push(
new webpack.optimize.UglifyJsPlugin({
new UglifyJsPlugin({
sourceMap: true,
}),
new webpack.LoaderOptionsPlugin({
@@ -76,8 +80,8 @@ module.exports = function(rules, options) {
plugins.push( new webpack.NoEmitOnErrorsPlugin())
} else { // development mode
plugins.push(new CopyWebpackPlugin([ { from: 'test/e2e/specs', to: 'test-specs' } ]))
} else { // development mode
plugins.push(new CopyWebpackPlugin([ { from: "test/e2e/specs", to: "test-specs" } ]))
}
plugins.push(
+13 -9
View File
@@ -1,6 +1,6 @@
{
"name": "swagger-ui",
"version": "3.12.1",
"version": "3.14.2",
"main": "dist/swagger-ui.js",
"repository": "git@github.com:swagger-api/swagger-ui.git",
"contributors": [
@@ -19,9 +19,8 @@
"build-core": "webpack --config webpack-dist.config.js --colors",
"build-standalone": "webpack --config webpack-dist-standalone.config.js --colors",
"predev": "npm install",
"dev": "npm-run-all --parallel hot-server open-localhost",
"dev": "npm-run-all --parallel hot-server",
"watch": "webpack --config webpack-watch.config.js --watch --progress",
"open-localhost": "node -e 'require(\"open\")(\"http://localhost:3200\")'",
"hot-server": "webpack-dev-server --host 0.0.0.0 --config webpack-hot-dev-server.config.js --inline --hot --progress --content-base dev-helpers/",
"deps-license": "license-checker --production --csv --out $npm_package_config_deps_check_dir/licenses.csv && license-checker --development --csv --out $npm_package_config_deps_check_dir/licenses-dev.csv",
"deps-size": "webpack -p --config webpack.check.js --json | webpack-bundle-size-analyzer >| $npm_package_config_deps_check_dir/sizes.txt",
@@ -32,12 +31,13 @@
"test": "npm run lint-errors && npm run just-test-in-node",
"test-in-node": "npm run lint-errors && npm run just-test-in-node",
"just-test": "karma start --config karma.conf.js",
"just-test-in-node": "mocha --recursive --compilers js:babel-core/register test/core test/components test/bugs test/swagger-ui-dist-package test/xss",
"just-test-in-node": "mocha --require test/setup.js --recursive --compilers js:babel-core/register test/core test/components test/bugs test/swagger-ui-dist-package test/xss",
"just-check-coverage": "nyc npm run just-test-in-node",
"test-e2e": "sleep 3 && nightwatch test/e2e/scenarios/ --config test/e2e/nightwatch.json",
"e2e-initial-render": "nightwatch test/e2e/scenarios/ --config test/e2e/nightwatch.json --group initial-render",
"mock-api": "json-server --watch test/e2e/db.json --port 3204",
"e2e": "npm-run-all --parallel -r hot-server mock-api test-e2e"
"hot-e2e-server": "webpack-dev-server --port 3230 --content-base test/e2e/helpers --host 0.0.0.0 --config webpack-hot-dev-server.config.js --inline --hot --progress",
"e2e": "npm-run-all --parallel -r hot-e2e-server mock-api test-e2e"
},
"dependencies": {
"@braintree/sanitize-url": "^2.0.2",
@@ -47,11 +47,13 @@
"commonmark": "^0.28.1",
"core-js": "^2.5.1",
"css.escape": "1.5.1",
"deep-extend": "0.4.1",
"deep-extend": "0.5.1",
"dompurify": "^1.0.4",
"expect": "1.20.2",
"getbase": "^2.8.2",
"ieee754": "^1.1.8",
"immutable": "^3.x.x",
"js-file-download": "^0.4.1",
"js-yaml": "^3.5.5",
"lodash": "4.17.5",
"matcher": "^0.1.2",
@@ -79,11 +81,10 @@
"redux-logger": "*",
"remarkable": "^1.7.1",
"reselect": "2.5.3",
"sanitize-html": "^1.14.1",
"scroll-to-element": "^2.0.0",
"serialize-error": "2.0.0",
"shallowequal": "0.2.2",
"swagger-client": "^3.5.1",
"swagger-client": "^3.8.1",
"url-parse": "^1.1.8",
"whatwg-fetch": "0.11.1",
"worker-loader": "^0.7.1",
@@ -98,6 +99,7 @@
"babel-eslint": "^7.1.1",
"babel-loader": "^7.1.0",
"babel-plugin-module-alias": "^1.6.0",
"babel-plugin-transform-es2015-constants": "^6.1.4",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-es2015": "^6.22.0",
"babel-preset-es2015-ie": "^6.6.2",
@@ -113,11 +115,12 @@
"eslint": "^4.1.1",
"eslint-plugin-import": "^2.6.0",
"eslint-plugin-mocha": "^4.11.0",
"eslint-plugin-react": "^7.1.0",
"eslint-plugin-react": "~7.7.0",
"extract-text-webpack-plugin": "^2.1.2",
"file-loader": "0.11.2",
"git-describe": "^4.0.1",
"imports-loader": "0.7.1",
"jsdom": "^11.10.0",
"json-loader": "0.5.4",
"json-server": "^0.11.0",
"karma": "^1.7.0",
@@ -145,6 +148,7 @@
"standard-loader": "^6.0.1",
"style-loader": "0.18.2",
"tachyons-sass": "^4.9.2",
"uglifyjs-webpack-plugin": "^1.2.5",
"url-loader": "0.5.9",
"webpack": "^2.6.1",
"webpack-bundle-size-analyzer": "^2.5.0"
+3 -2
View File
@@ -11,6 +11,7 @@ export default class ArrayModel extends Component {
getConfigs: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired,
name: PropTypes.string,
displayName: PropTypes.string,
required: PropTypes.bool,
expandDepth: PropTypes.number,
specPath: ImPropTypes.list.isRequired,
@@ -18,10 +19,10 @@ export default class ArrayModel extends Component {
}
render(){
let { getComponent, getConfigs, schema, depth, expandDepth, name, specPath } = this.props
let { getComponent, getConfigs, schema, depth, expandDepth, name, displayName, specPath } = this.props
let description = schema.get("description")
let items = schema.get("items")
let title = schema.get("title") || name
let title = schema.get("title") || displayName || name
let properties = schema.filter( ( v, key) => ["type", "items", "description", "$$ref"].indexOf(key) === -1 )
const Markdown = getComponent("Markdown")
+43 -3
View File
@@ -1,11 +1,14 @@
import React, { Component } from "react"
import PropTypes from "prop-types"
import { highlight } from "core/utils"
import saveAs from "js-file-download"
export default class HighlightCode extends Component {
static propTypes = {
value: PropTypes.string.isRequired,
className: PropTypes.string
className: PropTypes.string,
downloadable: PropTypes.bool,
fileName: PropTypes.string
}
componentDidMount() {
@@ -20,10 +23,47 @@ export default class HighlightCode extends Component {
this.el = c
}
downloadText = () => {
saveAs(this.props.value, this.props.fileName || "response.txt")
}
preventYScrollingBeyondElement = (e) => {
const target = e.target
var deltaY = e.nativeEvent.deltaY
var contentHeight = target.scrollHeight
var visibleHeight = target.offsetHeight
var scrollTop = target.scrollTop
const scrollOffset = visibleHeight + scrollTop
const isElementScrollable = contentHeight > visibleHeight
const isScrollingPastTop = scrollTop === 0 && deltaY < 0
const isScrollingPastBottom = scrollOffset >= contentHeight && deltaY > 0
if (isElementScrollable && (isScrollingPastTop || isScrollingPastBottom)) {
e.preventDefault()
}
}
render () {
let { value, className } = this.props
let { value, className, downloadable } = this.props
className = className || ""
return <pre ref={this.initializeComponent} className={className + " microlight"}>{ value }</pre>
return (
<div className="highlight-code">
{ !downloadable ? null :
<div className="download-contents" onClick={this.downloadText}>
Download
</div>
}
<pre
ref={this.initializeComponent}
onWheel={this.preventYScrollingBeyondElement}
className={className + " microlight"}>
{value}
</pre>
</div>
)
}
}
+16 -3
View File
@@ -5,7 +5,7 @@ import ImPropTypes from "react-immutable-proptypes"
import { sanitizeUrl } from "core/utils"
class Path extends React.Component {
export class InfoBasePath extends React.Component {
static propTypes = {
host: PropTypes.string,
basePath: PropTypes.string
@@ -68,6 +68,17 @@ class License extends React.Component {
}
}
export class InfoUrl extends React.PureComponent {
static propTypes = {
url: PropTypes.string.isRequired
}
render() {
const { url } = this.props
return <a target="_blank" href={ sanitizeUrl(url) }><span className="url"> { url } </span></a>
}
}
export default class Info extends React.Component {
static propTypes = {
info: PropTypes.object,
@@ -90,6 +101,8 @@ export default class Info extends React.Component {
const Markdown = getComponent("Markdown")
const VersionStamp = getComponent("VersionStamp")
const InfoUrl = getComponent("InfoUrl")
const InfoBasePath = getComponent("InfoBasePath")
return (
<div className="info">
@@ -97,8 +110,8 @@ export default class Info extends React.Component {
<h2 className="title" >{ title }
{ version && <VersionStamp version={version}></VersionStamp> }
</h2>
{ host || basePath ? <Path host={ host } basePath={ basePath } /> : null }
{ url && <a target="_blank" href={ sanitizeUrl(url) }><span className="url"> { url } </span></a> }
{ host || basePath ? <InfoBasePath host={ host } basePath={ basePath } /> : null }
{ url && <InfoUrl url={url} /> }
</hgroup>
<div className="description">
+2
View File
@@ -39,6 +39,7 @@ export default class BaseLayout extends React.Component {
let schemes = specSelectors.schemes()
let servers = specSelectors.servers()
let SvgAssets = getComponent("SvgAssets")
let Info = getComponent("info")
let Operations = getComponent("operations", true)
let Models = getComponent("Models", true)
@@ -78,6 +79,7 @@ export default class BaseLayout extends React.Component {
return (
<div className='swagger-ui'>
<SvgAssets />
<div>
<Errors/>
<Row className="information-container">
+1 -1
View File
@@ -60,7 +60,7 @@ export default class LiveResponse extends React.Component {
const body = response.get("text")
const duration = response.get("duration")
const headersKeys = Object.keys(headers)
const contentType = headers["content-type"]
const contentType = headers["content-type"] || headers["Content-Type"]
const Curl = getComponent("curl")
const ResponseBody = getComponent("responseBody")
+1
View File
@@ -9,6 +9,7 @@ export default class ModelWrapper extends Component {
static propTypes = {
schema: PropTypes.object.isRequired,
name: PropTypes.string,
displayName: PropTypes.string,
getComponent: PropTypes.func.isRequired,
getConfigs: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired,
+3 -2
View File
@@ -10,6 +10,7 @@ export default class Model extends ImmutablePureComponent {
getConfigs: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired,
name: PropTypes.string,
displayName: PropTypes.string,
isRef: PropTypes.bool,
required: PropTypes.bool,
expandDepth: PropTypes.number,
@@ -33,7 +34,7 @@ export default class Model extends ImmutablePureComponent {
}
render () {
let { getComponent, getConfigs, specSelectors, schema, required, name, isRef, specPath } = this.props
let { getComponent, getConfigs, specSelectors, schema, required, name, isRef, specPath, displayName } = this.props
const ObjectModel = getComponent("ObjectModel")
const ArrayModel = getComponent("ArrayModel")
const PrimitiveModel = getComponent("PrimitiveModel")
@@ -51,7 +52,7 @@ export default class Model extends ImmutablePureComponent {
if(!schema) {
return <span className="model model-title">
<span className="model-title__text">{ name }</span>
<span className="model-title__text">{ displayName || name }</span>
<img src={require("core/../img/rolling-load.svg")} height={"20px"} width={"20px"} style={{
marginLeft: "1em",
position: "relative",
+11 -4
View File
@@ -51,9 +51,12 @@ export default class Models extends Component {
</h4>
<Collapse isOpened={showModels}>
{
definitions.entrySeq().map( ( [ name ])=>{
definitions.entrySeq().map(([name])=>{
const schema = specSelectors.specResolvedSubtree([...specPathBase, name])
const fullPath = [...specPathBase, name]
const schema = specSelectors.specResolvedSubtree(fullPath)|| Im.Map()
const rawSchema = specSelectors.specJson().getIn(fullPath, Im.Map())
const displayName = schema.get("title") || rawSchema.get("title") || name
if(layoutSelectors.isShown(["models", name], false) && schema === undefined) {
// Firing an action in a container render is not great,
@@ -63,7 +66,8 @@ export default class Models extends Component {
const content = <ModelWrapper name={ name }
expandDepth={ defaultModelsExpandDepth }
schema={ schema }
schema={ schema || Im.Map() }
displayName={displayName}
specPath={Im.List([...specPathBase, name])}
getComponent={ getComponent }
specSelectors={ specSelectors }
@@ -72,7 +76,9 @@ export default class Models extends Component {
layoutActions = {layoutActions}/>
const title = <span className="model-box">
<span className="model model-title">{name}</span>
<span className="model model-title">
{displayName}
</span>
</span>
return <div id={ `model-${name}` } className="model-container" key={ `models-section-${name}` }>
@@ -81,6 +87,7 @@ export default class Models extends Component {
collapsedContent={this.getCollapsedContent(name)}
onToggle={this.handleToggle}
title={title}
displayName={displayName}
modelName={name}
hideSelfOnExpand={true}
expanded={defaultModelsExpandDepth > 1}
+4 -3
View File
@@ -15,6 +15,7 @@ export default class ObjectModel extends Component {
onToggle: PropTypes.func,
specSelectors: PropTypes.object.isRequired,
name: PropTypes.string,
displayName: PropTypes.string,
isRef: PropTypes.bool,
expandDepth: PropTypes.number,
depth: PropTypes.number,
@@ -22,7 +23,7 @@ export default class ObjectModel extends Component {
}
render(){
let { schema, name, isRef, getComponent, getConfigs, depth, onToggle, expanded, specPath, ...otherProps } = this.props
let { schema, name, displayName, isRef, getComponent, getConfigs, depth, onToggle, expanded, specPath, ...otherProps } = this.props
let { specSelectors,expandDepth } = otherProps
const { isOAS3 } = specSelectors
@@ -35,7 +36,7 @@ export default class ObjectModel extends Component {
let description = schema.get("description")
let properties = schema.get("properties")
let additionalProperties = schema.get("additionalProperties")
let title = schema.get("title") || name
let title = schema.get("title") || displayName || name
let requiredProperties = schema.get("required")
const JumpToPath = getComponent("JumpToPath", true)
@@ -78,7 +79,7 @@ export default class ObjectModel extends Component {
{
<table className="model"><tbody>
{
!description ? null : <tr style={{ color: "#999", fontStyle: "italic" }}>
!description ? null : <tr style={{ color: "#666", fontStyle: "italic" }}>
<td>description:</td>
<td>
<Markdown source={ description } />
+2 -1
View File
@@ -72,6 +72,7 @@ export default class Operation extends PureComponent {
tag,
showSummary,
operationId,
originalOperationId,
allowTryItOut,
displayOperationId,
displayRequestDuration,
@@ -140,7 +141,7 @@ export default class Operation extends PureComponent {
</div>
}
{ displayOperationId && operationId ? <span className="opblock-summary-operation-id">{operationId}</span> : null }
{ displayOperationId && (originalOperationId || operationId) ? <span className="opblock-summary-operation-id">{originalOperationId || operationId}</span> : null }
{
(!security || !security.count()) ? null :
+42 -37
View File
@@ -3,7 +3,7 @@ import { Map } from "immutable"
import PropTypes from "prop-types"
import ImPropTypes from "react-immutable-proptypes"
import win from "core/window"
import { getExtensions } from "core/utils"
import { getExtensions, getCommonExtensions } from "core/utils"
export default class ParameterRow extends Component {
static propTypes = {
@@ -82,7 +82,7 @@ export default class ParameterRow extends Component {
let { isOAS3 } = specSelectors
const { showExtensions } = getConfigs()
const { showExtensions, showCommonExtensions } = getConfigs()
// const onChangeWrapper = (value) => onChange(param, value)
const JsonSchemaForm = getComponent("JsonSchemaForm")
@@ -106,41 +106,46 @@ export default class ParameterRow extends Component {
const ParameterExt = getComponent("ParameterExt")
let paramWithMeta = specSelectors.parameterWithMeta(pathMethod, param.get("name"), param.get("in"))
let schema = param.get("schema")
let type = isOAS3 && isOAS3() ? param.getIn(["schema", "type"]) : param.get("type")
let format = param.get("format")
let schema = isOAS3 && isOAS3() ? param.get("schema") : param
let type = schema.get("type")
let isFormData = inType === "formData"
let isFormDataSupported = "FormData" in win
let required = param.get("required")
let itemType = param.getIn(isOAS3 && isOAS3() ? ["schema", "items", "type"] : ["items", "type"])
let itemType = schema.getIn(["items", "type"])
let value = paramWithMeta ? paramWithMeta.get("value") : ""
let extensions = getExtensions(param)
let commonExt = showCommonExtensions ? getCommonExtensions(param) : null
let extensions = showExtensions ? getExtensions(param) : null
let paramItems // undefined
let paramItemsEnum // undefined
let isDisplayParamItemsEnum = false
let paramEnum // undefined
let paramDefaultValue // undefined
let paramExample // undefined
let isDisplayParamEnum = false
if ( param !== undefined ) {
paramItems = param.get("items")
paramItems = schema.get("items")
}
if ( paramItems !== undefined ) {
paramItemsEnum = param.get("items").get("enum")
if (paramItems !== undefined) {
paramEnum = paramItems.get("enum")
paramDefaultValue = paramItems.get("default")
} else {
paramEnum = schema.get("enum")
}
if ( paramItemsEnum !== undefined ) {
if (paramItemsEnum.size > 0) {
isDisplayParamItemsEnum = true
}
if ( paramEnum !== undefined && paramEnum.size > 0) {
isDisplayParamEnum = true
}
// Default and Example Value for readonly doc
let paramDefaultValue // undefined
let paramExample // undefined
if ( param !== undefined ) {
paramDefaultValue = param.get("default")
paramDefaultValue = schema.get("default")
paramExample = param.get("example")
}
if (isDisplayParamItemsEnum) { // if we have an array, default value is in "items"
paramDefaultValue = paramItems.get("default")
if (paramExample === undefined) {
paramExample = param.get("x-example")
}
}
return (
@@ -150,32 +155,32 @@ export default class ParameterRow extends Component {
{ param.get("name") }
{ !required ? null : <span style={{color: "red"}}>&nbsp;*</span> }
</div>
<div className="parameter__type">{ type } { itemType && `[${itemType}]` }</div>
<div className="parameter__type">
{ type }
{ itemType && `[${itemType}]` }
{ format && <span className="prop-format">(${format})</span>}
</div>
<div className="parameter__deprecated">
{ isOAS3 && isOAS3() && param.get("deprecated") ? "deprecated": null }
</div>
<div className="parameter__in">({ param.get("in") })</div>
{ !showCommonExtensions || !commonExt.size ? null : commonExt.map((v, key) => <ParameterExt key={`${key}-${v}`} xKey={key} xVal={v} /> )}
{ !showExtensions || !extensions.size ? null : extensions.map((v, key) => <ParameterExt key={`${key}-${v}`} xKey={key} xVal={v} /> )}
</td>
<td className="col parameters-col_description">
<Markdown source={ param.get("description") }/>
{ param.get("description") ? <Markdown source={ param.get("description") }/> : null }
{ (bodyParam || !isExecute) && isDisplayParamItemsEnum ?
<Markdown source={
"<i>Available values</i>: " + paramItemsEnum.map(function(item) {
{ (bodyParam || !isExecute) && isDisplayParamEnum ?
<Markdown className="parameter__enum" source={
"<i>Available values</i> : " + paramEnum.map(function(item) {
return item
}).toArray().join(", ")}/>
: null
}
{ (bodyParam || !isExecute) && paramDefaultValue !== undefined ?
<Markdown source={"<i>Default value</i>: " + paramDefaultValue}/>
: null
}
{ (bodyParam || !isExecute) && paramExample !== undefined ?
<Markdown source={"<i>Example</i>: " + paramExample}/>
<Markdown className="parameter__default" source={"<i>Default value</i> : " + paramDefaultValue}/>
: null
}
@@ -188,8 +193,8 @@ export default class ParameterRow extends Component {
required={ required }
description={param.get("description") ? `${param.get("name")} - ${param.get("description")}` : `${param.get("name")}`}
onChange={ this.onChangeWrapper }
errors={ param.get("errors") }
schema={ isOAS3 && isOAS3() ? param.get("schema") : param }/>
errors={ paramWithMeta.get("errors") }
schema={ schema }/>
}
@@ -199,7 +204,7 @@ export default class ParameterRow extends Component {
getConfigs={ getConfigs }
isExecute={ isExecute }
specSelectors={ specSelectors }
schema={ schema }
schema={ param.get("schema") }
example={ bodyParam }/>
: null
}
+3 -2
View File
@@ -10,11 +10,12 @@ export default class Primitive extends Component {
getComponent: PropTypes.func.isRequired,
getConfigs: PropTypes.func.isRequired,
name: PropTypes.string,
displayName: PropTypes.string,
depth: PropTypes.number
}
render(){
let { schema, getComponent, getConfigs, name, depth } = this.props
let { schema, getComponent, getConfigs, name, displayName, depth } = this.props
const { showExtensions } = getConfigs()
@@ -27,7 +28,7 @@ export default class Primitive extends Component {
let format = schema.get("format")
let xml = schema.get("xml")
let enumArray = schema.get("enum")
let title = schema.get("title") || name
let title = schema.get("title") || displayName || name
let description = schema.get("description")
let extensions = getExtensions(schema)
let properties = schema
+9 -19
View File
@@ -1,12 +1,13 @@
import React from "react"
import PropTypes from "prop-types"
import Remarkable from "remarkable"
import sanitize from "sanitize-html"
import DomPurify from "dompurify"
import cx from "classnames"
// eslint-disable-next-line no-useless-escape
const isPlainText = (str) => /^[A-Z\s0-9!?\.]+$/gi.test(str)
function Markdown({ source }) {
function Markdown({ source, className = "" }) {
if(isPlainText(source)) {
// If the source text is not Markdown,
// let's save some time and just render it.
@@ -28,30 +29,19 @@ function Markdown({ source }) {
}
return (
<div className="markdown" dangerouslySetInnerHTML={{ __html: sanitized }}></div>
<div className={cx(className, "markdown")} dangerouslySetInnerHTML={{ __html: sanitized }}></div>
)
}
Markdown.propTypes = {
source: PropTypes.string.isRequired
source: PropTypes.string.isRequired,
className: PropTypes.string
}
export default Markdown
const sanitizeOptions = {
allowedTags: sanitize.defaults.allowedTags.concat([ "h1", "h2", "img", "span" ]),
allowedAttributes: {
...sanitize.defaults.allowedAttributes,
"img": sanitize.defaults.allowedAttributes.img.concat(["title"]),
"td": [ "colspan" ],
"*": [ "class" ]
},
allowedSchemesByTag: { img: [ "http", "https", "data" ] },
textFilter: function(text) {
return text.replace(/&quot;/g, "\"")
}
}
export function sanitizer(str) {
return sanitize(str, sanitizeOptions)
return DomPurify.sanitize(str, {
ADD_ATTR: ["target"]
})
}
+57 -9
View File
@@ -5,7 +5,10 @@ import lowerCase from "lodash/lowerCase"
import { extractFileNameFromContentDispositionHeader } from "core/utils"
import win from "core/window"
export default class ResponseBody extends React.Component {
export default class ResponseBody extends React.PureComponent {
state = {
parsedContent: null
}
static propTypes = {
content: PropTypes.any.isRequired,
@@ -15,9 +18,41 @@ export default class ResponseBody extends React.Component {
url: PropTypes.string
}
updateParsedContent = (prevContent) => {
const { content } = this.props
if(prevContent === content) {
return
}
if(content && content instanceof Blob) {
var reader = new FileReader()
reader.onload = () => {
this.setState({
parsedContent: reader.result
})
}
reader.readAsText(content)
} else {
this.setState({
parsedContent: content.toString()
})
}
}
componentDidMount() {
this.updateParsedContent(null)
}
componentDidUpdate(prevProps) {
this.updateParsedContent(prevProps.content)
}
render() {
let { content, contentType, url, headers={}, getComponent } = this.props
const { parsedContent } = this.state
const HighlightCode = getComponent("highlightCode")
const downloadName = "response_" + new Date().getTime()
let body, bodyEl
url = url || ""
@@ -29,9 +64,7 @@ export default class ResponseBody extends React.Component {
(headers["content-description"] && (/File Transfer/i).test(headers["content-description"]))) {
// Download
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent)
if (!isSafari && "Blob" in window) {
if ("Blob" in window) {
let type = contentType || "text/html"
let blob = (content instanceof Blob) ? content : new Blob([content], {type: type})
let href = window.URL.createObjectURL(blob)
@@ -66,7 +99,7 @@ export default class ResponseBody extends React.Component {
body = "can't parse JSON. Raw result:\n\n" + content
}
bodyEl = <HighlightCode value={ body } />
bodyEl = <HighlightCode downloadable fileName={`${downloadName}.json`} value={ body } />
// XML
} else if (/xml/i.test(contentType)) {
@@ -74,11 +107,11 @@ export default class ResponseBody extends React.Component {
textNodesOnSameLine: true,
indentor: " "
})
bodyEl = <HighlightCode value={ body } />
bodyEl = <HighlightCode downloadable fileName={`${downloadName}.xml`} value={ body } />
// HTML or Plain Text
} else if (lowerCase(contentType) === "text/html" || /text\/plain/.test(contentType)) {
bodyEl = <HighlightCode value={ content } />
bodyEl = <HighlightCode downloadable fileName={`${downloadName}.html`} value={ content } />
// Image
} else if (/^image\//i.test(contentType)) {
@@ -92,10 +125,25 @@ export default class ResponseBody extends React.Component {
} else if (/^audio\//i.test(contentType)) {
bodyEl = <pre><audio controls><source src={ url } type={ contentType } /></audio></pre>
} else if (typeof content === "string") {
bodyEl = <HighlightCode value={ content } />
bodyEl = <HighlightCode downloadable fileName={`${downloadName}.txt`} value={ content } />
} else if ( content.size > 0 ) {
// We don't know the contentType, but there was some content returned
bodyEl = <div>Unknown response type</div>
if(parsedContent) {
// We were able to squeeze something out of content
// in `updateParsedContent`, so let's display it
bodyEl = <div>
<p className="i">
Unrecognized response type; displaying content as text.
</p>
<HighlightCode downloadable fileName={`${downloadName}.txt`} value={ parsedContent } />
</div>
} else {
// Give up
bodyEl = <p className="i">
Unrecognized response type; unable to display.
</p>
}
} else {
// We don't know the contentType and there was no content returned
bodyEl = null
+2 -2
View File
@@ -37,12 +37,12 @@ export default class Schemes extends React.Component {
}
render() {
let { schemes } = this.props
let { schemes, currentScheme } = this.props
return (
<label htmlFor="schemes">
<span className="schemes-title">Schemes</span>
<select onChange={ this.onChange }>
<select onChange={ this.onChange } value={currentScheme}>
{ schemes.valueSeq().map(
( scheme ) => <option value={ scheme } key={ scheme }>{ scheme }</option>
).toArray()}
+43
View File
@@ -0,0 +1,43 @@
import React from "react"
const SvgAssets = () =>
<div>
<svg xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" style={{
position: "absolute",
width: 0,
height: 0
}}>
<defs>
<symbol viewBox="0 0 20 20" id="unlocked">
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V6h2v-.801C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8z"></path>
</symbol>
<symbol viewBox="0 0 20 20" id="locked">
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8zM12 8H8V5.199C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="close">
<path d="M14.348 14.849c-.469.469-1.229.469-1.697 0L10 11.819l-2.651 3.029c-.469.469-1.229.469-1.697 0-.469-.469-.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-.469-.469-.469-1.228 0-1.697.469-.469 1.228-.469 1.697 0L10 8.183l2.651-3.031c.469-.469 1.228-.469 1.697 0 .469.469.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c.469.469.469 1.229 0 1.698z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="large-arrow">
<path d="M13.25 10L6.109 2.58c-.268-.27-.268-.707 0-.979.268-.27.701-.27.969 0l7.83 7.908c.268.271.268.709 0 .979l-7.83 7.908c-.268.271-.701.27-.969 0-.268-.269-.268-.707 0-.979L13.25 10z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="large-arrow-down">
<path d="M17.418 6.109c.272-.268.709-.268.979 0s.271.701 0 .969l-7.908 7.83c-.27.268-.707.268-.979 0l-7.908-7.83c-.27-.268-.27-.701 0-.969.271-.268.709-.268.979 0L10 13.25l7.418-7.141z"/>
</symbol>
<symbol viewBox="0 0 24 24" id="jump-to">
<path d="M19 7v4H5.83l3.58-3.59L8 6l-6 6 6 6 1.41-1.41L5.83 13H21V7z"/>
</symbol>
<symbol viewBox="0 0 24 24" id="expand">
<path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>
</symbol>
</defs>
</svg>
</div>
export default SvgAssets
+13 -2
View File
@@ -58,7 +58,7 @@ export default class OperationContainer extends PureComponent {
const { op, layoutSelectors, getConfigs } = props
const { docExpansion, deepLinking, displayOperationId, displayRequestDuration, supportedSubmitMethods } = getConfigs()
const showSummary = layoutSelectors.showSummary()
const operationId = op.getIn(["operation", "operationId"]) || op.getIn(["operation", "__originalOperationId"]) || opId(op.get("operation"), props.path, props.method) || op.get("id")
const operationId = op.getIn(["operation", "__originalOperationId"]) || op.getIn(["operation", "operationId"]) || opId(op.get("operation"), props.path, props.method) || op.get("id")
const isShownKey = ["operations", props.tag, operationId]
const isDeepLinkingEnabled = deepLinking && deepLinking !== "false"
const allowTryItOut = supportedSubmitMethods.indexOf(props.method) >= 0 && (typeof props.allowTryItOut === "undefined" ?
@@ -81,6 +81,15 @@ export default class OperationContainer extends PureComponent {
}
}
componentDidMount() {
const { isShown } = this.props
const resolvedSubtree = this.getResolvedSubtree()
if(isShown && resolvedSubtree === undefined) {
this.requestResolvedSubtree()
}
}
componentWillReceiveProps(nextProps) {
const { response, isShown } = nextProps
const resolvedSubtree = this.getResolvedSubtree()
@@ -96,7 +105,8 @@ export default class OperationContainer extends PureComponent {
toggleShown =() => {
let { layoutActions, tag, operationId, isShown } = this.props
if(!isShown) {
const resolvedSubtree = this.getResolvedSubtree()
if(!isShown && resolvedSubtree === undefined) {
// transitioning from collapsed to expanded
this.requestResolvedSubtree()
}
@@ -194,6 +204,7 @@ export default class OperationContainer extends PureComponent {
security,
isAuthorized,
operationId,
originalOperationId: resolvedSubtree.getIn(["operation", "__originalOperationId"]),
showSummary,
isShown,
jumpToKey,
+7 -1
View File
@@ -49,6 +49,7 @@ module.exports = function SwaggerUI(opts) {
defaultModelExpandDepth: 1,
defaultModelsExpandDepth: 1,
showExtensions: false,
showCommonExtensions: false,
supportedSubmitMethods: [
"get",
"put",
@@ -172,7 +173,12 @@ module.exports = function SwaggerUI(opts) {
let configUrl = queryConfig.config || constructorConfig.configUrl
if (!configUrl || !system.specActions.getConfigByUrl || system.specActions.getConfigByUrl && !system.specActions.getConfigByUrl(configUrl, downloadSpec)) {
if (!configUrl || !system.specActions.getConfigByUrl || system.specActions.getConfigByUrl && !system.specActions.getConfigByUrl({
url: configUrl,
loadRemoteConfig: true,
requestInterceptor: constructorConfig.requestInterceptor,
responseInterceptor: constructorConfig.responseInterceptor,
}, downloadSpec)) {
return downloadSpec()
}
+2 -2
View File
@@ -81,8 +81,8 @@ export function getLineNumberForPath(yaml, path) {
* YAML or JSON string
* @param {array} path
* an array of stings that constructs a
* JSON Path similiar to JSON Pointers(RFC 6901). The difference is, each
* component of path is an item of the array intead of beinf seperated with
* JSON Path similar to JSON Pointers(RFC 6901). The difference is, each
* component of path is an item of the array instead of being separated with
* slash(/) in a string
*/
export function positionRangeForPath(yaml, path) {
+1 -1
View File
@@ -1,6 +1,6 @@
import React from "react"
// Nothing by default- component can be overriden by another plugin.
// Nothing by default- component can be overridden by another plugin.
export default class JumpToPath extends React.Component {
render() {
+16 -5
View File
@@ -1,3 +1,4 @@
import parseUrl from "url-parse"
import win from "core/window"
import { btoa, buildFormData } from "core/utils"
@@ -139,15 +140,25 @@ export const authorizeAccessCodeWithBasicAuthentication = ( { auth, redirectUrl
return authActions.authorizeRequest({body: buildFormData(form), name, url: schema.get("tokenUrl"), auth, headers})
}
export const authorizeRequest = ( data ) => ( { fn, getConfigs, authActions, errActions, authSelectors } ) => {
export const authorizeRequest = ( data ) => ( { fn, getConfigs, authActions, errActions, oas3Selectors, specSelectors, authSelectors } ) => {
let { body, query={}, headers={}, name, url, auth } = data
let { additionalQueryStringParams } = authSelectors.getConfigs() || {}
let fetchUrl = url
for (let key in additionalQueryStringParams) {
url += "&" + key + "=" + encodeURIComponent(additionalQueryStringParams[key])
let { additionalQueryStringParams } = authSelectors.getConfigs() || {}
let parsedUrl
if (specSelectors.isOAS3()) {
parsedUrl = parseUrl(url, oas3Selectors.selectedServer(), true)
} else {
parsedUrl = parseUrl(url, specSelectors.url(), true)
}
if(typeof additionalQueryStringParams === "object") {
parsedUrl.query = Object.assign({}, parsedUrl.query, additionalQueryStringParams)
}
const fetchUrl = parsedUrl.toString()
let _headers = Object.assign({
"Accept":"application/json, text/plain, */*",
"Content-Type": "application/x-www-form-urlencoded"
+49
View File
@@ -8,6 +8,8 @@ export default function() {
afterLoad(system) {
this.rootInjects = this.rootInjects || {}
this.rootInjects.initOAuth = system.authActions.configureAuth
this.rootInjects.preauthorizeApiKey = preauthorizeApiKey.bind(null, system)
this.rootInjects.preauthorizeBasic = preauthorizeBasic.bind(null, system)
},
statePlugins: {
auth: {
@@ -21,3 +23,50 @@ export default function() {
}
}
}
export function preauthorizeBasic(system, key, username, password) {
const {
authActions: { authorize },
specSelectors: { specJson, isOAS3 }
} = system
const definitionBase = isOAS3() ? ["components", "securitySchemes"] : ["securityDefinitions"]
const schema = specJson().getIn([...definitionBase, key])
if(!schema) {
return null
}
return authorize({
[key]: {
value: {
username,
password,
},
schema: schema.toJS()
}
})
}
export function preauthorizeApiKey(system, key, value) {
const {
authActions: { authorize },
specSelectors: { specJson, isOAS3 }
} = system
const definitionBase = isOAS3() ? ["components", "securitySchemes"] : ["securityDefinitions"]
const schema = specJson().getIn([...definitionBase, key])
if(!schema) {
return null
}
return authorize({
[key]: {
value,
schema: schema.toJS()
}
})
}
+12
View File
@@ -0,0 +1,12 @@
import YAML from "js-yaml"
export const parseYamlConfig = (yaml, system) => {
try {
return YAML.safeLoad(yaml)
} catch(e) {
if (system) {
system.errActions.newThrownErr( new Error(e) )
}
return {}
}
}
+2 -38
View File
@@ -1,46 +1,10 @@
import YAML from "js-yaml"
import yamlConfig from "root/swagger-config.yaml"
import { parseYamlConfig } from "./helpers"
import * as actions from "./actions"
import * as specActions from "./spec-actions"
import * as selectors from "./selectors"
import reducers from "./reducers"
const parseYamlConfig = (yaml, system) => {
try {
return YAML.safeLoad(yaml)
} catch(e) {
if (system) {
system.errActions.newThrownErr( new Error(e) )
}
return {}
}
}
const specActions = {
downloadConfig: (url) => ({fn}) => {
let {fetch} = fn
return fetch(url)
},
getConfigByUrl: (configUrl, cb)=> ({ specActions }) => {
if (configUrl) {
return specActions.downloadConfig(configUrl).then(next, next)
}
function next(res) {
if (res instanceof Error || res.status >= 400) {
specActions.updateLoadingStatus("failedConfig")
specActions.updateLoadingStatus("failedConfig")
specActions.updateUrl("")
console.error(res.statusText + " " + configUrl)
cb(null)
} else {
cb(parseYamlConfig(res.text))
}
}
}
}
const specSelectors = {
getLocalConfig: () => {
return parseYamlConfig(yamlConfig)
+25
View File
@@ -0,0 +1,25 @@
import { parseYamlConfig } from "./helpers"
export const downloadConfig = (req) => (system) => {
const {fn: { fetch }} = system
return fetch(req)
}
export const getConfigByUrl = (req, cb)=> ({ specActions }) => {
if (req) {
return specActions.downloadConfig(req).then(next, next)
}
function next(res) {
if (res instanceof Error || res.status >= 400) {
specActions.updateLoadingStatus("failedConfig")
specActions.updateLoadingStatus("failedConfig")
specActions.updateUrl("")
console.error(res.statusText + " " + req.url)
cb(null)
} else {
cb(parseYamlConfig(res.text))
}
}
}
+38 -1
View File
@@ -2,6 +2,7 @@
import { createSelector } from "reselect"
import { Map } from "immutable"
import win from "../window"
export default function downloadUrlPlugin (toolbox) {
let { fn } = toolbox
@@ -12,6 +13,7 @@ export default function downloadUrlPlugin (toolbox) {
const config = getConfigs()
url = url || specSelectors.url()
specActions.updateLoadingStatus("loading")
errActions.clear({source: "fetch"})
fetch({
url,
loadSpec: true,
@@ -26,7 +28,10 @@ export default function downloadUrlPlugin (toolbox) {
function next(res) {
if(res instanceof Error || res.status >= 400) {
specActions.updateLoadingStatus("failed")
return errActions.newThrownErr( new Error((res.message || res.statusText) + " " + url) )
errActions.newThrownErr(Object.assign( new Error((res.message || res.statusText) + " " + url), {source: "fetch"}))
// Check if the failure was possibly due to CORS or mixed content
if (!res.status && res instanceof Error) checkPossibleFailReasons()
return
}
specActions.updateLoadingStatus("success")
specActions.updateSpec(res.text)
@@ -35,6 +40,38 @@ export default function downloadUrlPlugin (toolbox) {
}
}
function checkPossibleFailReasons() {
try {
let specUrl
if("URL" in win ) {
specUrl = new URL(url)
} else {
// legacy browser, use <a href> to parse the URL
specUrl = document.createElement("a")
specUrl.href = url
}
if(specUrl.protocol !== "https:" && win.location.protocol === "https:") {
const error = Object.assign(
new Error(`Possible mixed-content issue? The page was loaded over https:// but a ${specUrl.protocol}// URL was specified. Check that you are not attempting to load mixed content.`),
{source: "fetch"}
)
errActions.newThrownErr(error)
return
}
if(specUrl.origin !== win.location.origin) {
const error = Object.assign(
new Error(`Possible cross-origin (CORS) issue? The URL origin (${specUrl.origin}) does not match the page (${win.location.origin}). Check the server returns the correct 'Access-Control-Allow-*' headers.`),
{source: "fetch"}
)
errActions.newThrownErr(error)
}
} catch (e) {
return
}
}
},
updateLoadingStatus: (status) => {
@@ -16,8 +16,14 @@ const Callbacks = (props) => {
return <div key={callbackName}>
<h2>{callbackName}</h2>
{ callback.map((pathItem, pathItemName) => {
if(pathItemName === "$$ref") {
return null
}
return <div key={pathItemName}>
{ pathItem.map((operation, method) => {
if(method === "$$ref") {
return null
}
let op = fromJS({
operation
})
@@ -33,7 +33,8 @@ export default class HttpAuth extends React.Component {
let { onChange } = this.props
let { value, name } = e.target
let newValue = this.state.value || {}
let newValue = Object.assign({}, this.state.value)
if(name) {
newValue[name] = value
} else {
@@ -53,7 +54,7 @@ export default class HttpAuth extends React.Component {
const Markdown = getComponent( "Markdown" )
const JumpToPath = getComponent("JumpToPath", true)
const scheme = schema.get("scheme")
const scheme = (schema.get("scheme") || "").toLowerCase()
let value = this.getValue()
let errors = errSelectors.allErrors().filter( err => err.get("authId") === name)
@@ -125,7 +126,7 @@ export default class HttpAuth extends React.Component {
)
}
return <div>
<em><b>{name}</b> HTTP authentication: unsupported or missing scheme</em>
<em><b>{name}</b> HTTP authentication: unsupported scheme {`'${scheme}'`}</em>
</div>
}
}
@@ -1,11 +1,12 @@
import React from "react"
import PropTypes from "prop-types"
import ReactMarkdown from "react-markdown"
import cx from "classnames"
import { Parser, HtmlRenderer } from "commonmark"
import { OAS3ComponentWrapFactory } from "../helpers"
import { sanitizer } from "core/components/providers/markdown"
export const Markdown = ({ source }) => {
export const Markdown = ({ source, className = "" }) => {
if ( source ) {
const parser = new Parser()
const writer = new HtmlRenderer()
@@ -19,14 +20,15 @@ export const Markdown = ({ source }) => {
return (
<ReactMarkdown
source={sanitized}
className={"renderedMarkdown"}
className={cx(className, "renderedMarkdown")}
/>
)
}
return null
}
Markdown.propTypes = {
source: PropTypes.string
source: PropTypes.string,
className: PropTypes.string,
}
export default OAS3ComponentWrapFactory(Markdown)
export default OAS3ComponentWrapFactory(Markdown)
+28
View File
@@ -0,0 +1,28 @@
let engaged = false
export default function() {
return {
statePlugins: {
spec: {
wrapActions: {
updateSpec: (ori) => (...args) => {
engaged = true
return ori(...args)
},
updateJsonSpec: (ori, system) => (...args) => {
const cb = system.getConfigs().onComplete
if(engaged && typeof cb === "function") {
// call `onComplete` on next tick, which allows React to
// reconcile the DOM before we notify the user
setTimeout(cb, 0)
engaged = false
}
return ori(...args)
}
}
}
}
}
}
+18 -11
View File
@@ -1,4 +1,4 @@
import { objectify, isFunc, normalizeArray } from "core/utils"
import { objectify, isFunc, normalizeArray, deeplyStripKey } from "core/utils"
import XML from "xml"
import memoizee from "memoizee"
@@ -29,8 +29,13 @@ export const sampleFromSchema = (schema, config={}) => {
let { type, example, properties, additionalProperties, items } = objectify(schema)
let { includeReadOnly, includeWriteOnly } = config
if(example !== undefined)
return example
if(example !== undefined) {
return deeplyStripKey(example, "$$ref", (val) => {
// do a couple of quick sanity tests to ensure the value
// looks like a $$ref that swagger-client generates.
return typeof val === "string" && val.indexOf("#") > -1
})
}
if(!type) {
if(properties) {
@@ -69,13 +74,13 @@ export const sampleFromSchema = (schema, config={}) => {
}
if(type === "array") {
if(Array.isArray(items.anyOf)) {
return items.anyOf.map(i => sampleFromSchema(i, config))
}
if(Array.isArray(items.oneOf)) {
return items.oneOf.map(i => sampleFromSchema(i, config))
}
if(Array.isArray(items.anyOf)) {
return items.anyOf.map(i => sampleFromSchema(i, config))
}
if(Array.isArray(items.oneOf)) {
return items.oneOf.map(i => sampleFromSchema(i, config))
}
return [ sampleFromSchema(items, config) ]
}
@@ -210,7 +215,9 @@ export const sampleXmlFromSchema = (schema, config={}) => {
|| enumAttrVal || primitive(props[propName])
} else {
props[propName].xml.name = props[propName].xml.name || propName
props[propName].example = props[propName].example !== undefined ? props[propName].example : example[propName]
if(props[propName].example === undefined && example[propName] !== undefined) {
props[propName].example = example[propName]
}
let t = sampleXmlFromSchema(props[propName])
if (Array.isArray(t)) {
res[displayName] = res[displayName].concat(t)
+5 -1
View File
@@ -118,7 +118,11 @@ export default {
let operationPath = ["paths", ...path]
let metaPath = ["meta", "paths", ...path]
if(!state.getIn(["json", ...operationPath]) && !state.getIn(["resolved", ...operationPath])) {
if(
!state.getIn(["json", ...operationPath])
&& !state.getIn(["resolved", ...operationPath])
&& !state.getIn(["resolvedSubtrees", ...operationPath])
) {
// do nothing if the operation does not exist
return state
}
+3 -3
View File
@@ -54,7 +54,7 @@ const mergerFn = (oldVal, newVal) => {
return newVal
}
return Map().mergeWith(
return OrderedMap().mergeWith(
mergerFn,
oldVal,
newVal
@@ -66,7 +66,7 @@ const mergerFn = (oldVal, newVal) => {
export const specJsonWithResolvedSubtrees = createSelector(
state,
spec => Map().mergeWith(
spec => OrderedMap().mergeWith(
mergerFn,
spec.get("json"),
spec.get("resolvedSubtrees")
@@ -109,7 +109,7 @@ export const semver = createSelector(
)
export const paths = createSelector(
spec,
specJsonWithResolvedSubtrees,
spec => spec.get("paths")
)
+16
View File
@@ -1,3 +1,5 @@
import get from "lodash/get"
export const updateSpec = (ori, {specActions}) => (...args) => {
ori(...args)
specActions.parseToJson(...args)
@@ -5,7 +7,21 @@ export const updateSpec = (ori, {specActions}) => (...args) => {
export const updateJsonSpec = (ori, {specActions}) => (...args) => {
ori(...args)
specActions.invalidateResolvedSubtreeCache()
// Trigger resolution of any path-level $refs.
const [json] = args
const pathItems = get(json, ["paths"])
const pathItemKeys = Object.keys(pathItems)
pathItemKeys.forEach(k => {
const val = get(pathItems, [k])
if(val.$ref) {
specActions.requestResolvedSubtree(["paths", k])
}
})
}
// Log the request ( just for debugging, shouldn't affect prod )
+12 -3
View File
@@ -13,6 +13,7 @@ import downloadUrlPlugin from "core/plugins/download-url"
import configsPlugin from "core/plugins/configs"
import deepLinkingPlugin from "core/plugins/deep-linking"
import filter from "core/plugins/filter"
import onComplete from "core/plugins/on-complete"
import OperationContainer from "core/containers/OperationContainer"
@@ -45,7 +46,10 @@ import Headers from "core/components/headers"
import Errors from "core/components/errors"
import ContentType from "core/components/content-type"
import Overview from "core/components/overview"
import Info from "core/components/info"
import Info, {
InfoUrl,
InfoBasePath
} from "core/components/info"
import Footer from "core/components/footer"
import ParamBody from "core/components/param-body"
import Curl from "core/components/curl"
@@ -63,6 +67,7 @@ import Property from "core/components/property"
import TryItOutButton from "core/components/try-it-out-button"
import VersionStamp from "core/components/version-stamp"
import DeepLink from "core/components/deep-link"
import SvgAssets from "core/components/svg-assets"
import Markdown from "core/components/providers/markdown"
@@ -124,7 +129,10 @@ export default function() {
OperationExtRow,
ParameterExt,
OperationContainer,
DeepLink
DeepLink,
InfoUrl,
InfoBasePath,
SvgAssets
}
}
@@ -154,6 +162,7 @@ export default function() {
SplitPaneModePlugin,
downloadUrlPlugin,
deepLinkingPlugin,
filter
filter,
onComplete
]
}
+26 -3
View File
@@ -10,7 +10,7 @@ import { memoizedSampleFromSchema, memoizedCreateXMLExample } from "core/plugins
import win from "./window"
import cssEscape from "css.escape"
const DEFAULT_REPONSE_KEY = "default"
const DEFAULT_RESPONSE_KEY = "default"
export const isImmutable = (maybe) => Im.Iterable.isIterable(maybe)
@@ -128,7 +128,7 @@ export function systemThunkMiddleware(getSystem) {
export function defaultStatusCode ( responses ) {
let codes = responses.keySeq()
return codes.contains(DEFAULT_REPONSE_KEY) ? DEFAULT_REPONSE_KEY : codes.filter( key => (key+"")[0] === "2").sort().first()
return codes.contains(DEFAULT_RESPONSE_KEY) ? DEFAULT_RESPONSE_KEY : codes.filter( key => (key+"")[0] === "2").sort().first()
}
@@ -193,7 +193,7 @@ export function highlight (el) {
// running through characters and highlighting
while (prev2 = prev1,
// escaping if needed (with except for comments)
// pervious character will not be therefore
// previous character will not be therefore
// recognized as a token finalize condition
prev1 = tokenType < 7 && prev1 == "\\" ? 1 : chr
) {
@@ -712,3 +712,26 @@ export const createDeepLinkPath = (str) => typeof str == "string" || str instanc
export const escapeDeepLinkPath = (str) => cssEscape( createDeepLinkPath(str) )
export const getExtensions = (defObj) => defObj.filter((v, k) => /^x-/.test(k))
export const getCommonExtensions = (defObj) => defObj.filter((v, k) => /^pattern|maxLength|minLength|maximum|minimum/.test(k))
// Deeply strips a specific key from an object.
//
// `predicate` can be used to discriminate the stripping further,
// by preserving the key's place in the object based on its value.
export function deeplyStripKey(input, keyToStrip, predicate = () => true) {
if(typeof input !== "object" || Array.isArray(input) || !keyToStrip) {
return input
}
const obj = Object.assign({}, input)
Object.keys(obj).forEach(k => {
if(k === keyToStrip && predicate(obj[k], k)) {
delete obj[k]
return
}
obj[k] = deeplyStripKey(obj[k], keyToStrip, predicate)
})
return obj
}
+1 -1
View File
@@ -6,7 +6,7 @@ Plugins allow you to add
- `statePlugins`
- `selectors` - query the state
- `reducers` - modify the state
- `actions` - fire and forget, that will eventually be handled by a reducer. You *can* rely on the result of async actions. But in general its not recommended
- `actions` - fire and forget, that will eventually be handled by a reducer. You *can* rely on the result of async actions. But in general it's not recommended
- `wrapActions` - replace an action with a wrapped action (useful for hooking into existing `actions`)
- `components` - React components
- `fn` - commons functions
+3 -2
View File
@@ -21,6 +21,7 @@ export default class StandaloneLayout extends React.Component {
let Container = getComponent("Container")
let Row = getComponent("Row")
let Col = getComponent("Col")
let Errors = getComponent("errors", true)
const Topbar = getComponent("Topbar", true)
const BaseLayout = getComponent("BaseLayout", true)
@@ -45,8 +46,8 @@ export default class StandaloneLayout extends React.Component {
<div className="info">
<div className="loading-container">
<h4 className="title">Failed to load API definition.</h4>
<p>{lastErrMsg}</p>
</div>
<Errors/>
</div>
</div>
}
{ loadingStatus === "failedConfig" &&
+27
View File
@@ -626,6 +626,33 @@
}
}
.highlight-code {
position: relative;
> .microlight {
overflow-y: auto;
max-height: 400px;
min-height: 6em;
}
}
.download-contents {
position: absolute;
bottom: 10px;
right: 10px;
cursor: pointer;
background: #7d8293;
text-align: center;
padding: 5px;
border-radius: 4px;
font-family: 'Titillium Web', sans-serif;
font-weight: 600;
color: white;
font-size: 14px;
height: 30px;
width: 75px;
}
.scheme-container
{
margin: 0 0 20px 0;
+7 -7
View File
@@ -16,19 +16,19 @@ describe("Markdown component", function() {
it("allows td elements with colspan attrib", function() {
const str = `<table><tr><td>ABC</td></tr></table>`
const el = render(<Markdown source={str} />)
expect(el.html()).toEqual(`<div class="markdown"><table><tr><td>ABC</td></tr></table></div>`)
expect(el.html()).toEqual(`<div class="markdown"><table><tbody><tr><td>ABC</td></tr></tbody></table></div>`)
})
it("allows image elements", function() {
const str = `![Image alt text](http://image.source "Image title")`
const el = render(<Markdown source={str} />)
expect(el.html()).toEqual(`<div class="markdown"><p><img src="http://image.source" title="Image title"></p>\n</div>`)
expect(el.html()).toEqual(`<div class="markdown"><p><img title="Image title" alt="Image alt text" src="http://image.source"></p>\n</div>`)
})
it("allows image elements with https scheme", function() {
const str = `![Image alt text](https://image.source "Image title")`
const el = render(<Markdown source={str} />)
expect(el.html()).toEqual(`<div class="markdown"><p><img src="https://image.source" title="Image title"></p>\n</div>`)
expect(el.html()).toEqual(`<div class="markdown"><p><img title="Image title" alt="Image alt text" src="https://image.source"></p>\n</div>`)
})
it("allows image elements with data scheme", function() {
@@ -52,7 +52,7 @@ describe("Markdown component", function() {
it("allows links", function() {
const str = `[Link](https://example.com/)`
const el = render(<Markdown source={str} />)
expect(el.html()).toEqual(`<div class="markdown"><p><a href="https://example.com/" target="_blank">Link</a></p>\n</div>`)
expect(el.html()).toEqual(`<div class="markdown"><p><a target="_blank" href="https://example.com/">Link</a></p>\n</div>`)
})
})
@@ -60,13 +60,13 @@ describe("Markdown component", function() {
it("allows image elements", function() {
const str = `![Image alt text](http://image.source "Image title")`
const el = render(<OAS3Markdown source={str} />)
expect(el.html()).toEqual(`<div class="renderedMarkdown"><div><p><img src="http://image.source" title="Image title"></p></div></div>`)
expect(el.html()).toEqual(`<div class="renderedMarkdown"><div><p><img title="Image title" alt="Image alt text" src="http://image.source"></p></div></div>`)
})
it("allows image elements with https scheme", function() {
const str = `![Image alt text](https://image.source "Image title")`
const el = render(<OAS3Markdown source={str} />)
expect(el.html()).toEqual(`<div class="renderedMarkdown"><div><p><img src="https://image.source" title="Image title"></p></div></div>`)
expect(el.html()).toEqual(`<div class="renderedMarkdown"><div><p><img title="Image title" alt="Image alt text" src="https://image.source"></p></div></div>`)
})
it("allows image elements with data scheme", function() {
+23 -17
View File
@@ -6,23 +6,8 @@ import ModelExample from "components/model-example"
import ModelComponent from "components/model-wrapper"
describe("<ModelExample/>", function(){
// Given
let components = {
ModelWrapper: ModelComponent
}
let props = {
getComponent: (c) => {
return components[c]
},
specSelectors: {},
schema: {},
example: "{\"example\": \"value\"}",
isExecute: false,
getConfigs: () => ({
defaultModelRendering: "model",
defaultModelExpandDepth: 1
})
}
let components, props
let exampleSelectedTestInputs = [
{ defaultModelRendering: "model", isExecute: true },
{ defaultModelRendering: "example", isExecute: true },
@@ -30,10 +15,31 @@ describe("<ModelExample/>", function(){
{ defaultModelRendering: "othervalue", isExecute: true },
{ defaultModelRendering: "othervalue", isExecute: false }
]
let modelSelectedTestInputs = [
{ defaultModelRendering: "model", isExecute: false }
]
beforeEach(() => {
components = {
ModelWrapper: ModelComponent
}
props = {
getComponent: (c) => {
return components[c]
},
specSelectors: {},
schema: {},
example: "{\"example\": \"value\"}",
isExecute: false,
getConfigs: () => ({
defaultModelRendering: "model",
defaultModelExpandDepth: 1
})
}
})
it("renders model and example tabs", function(){
// When
+2 -1
View File
@@ -2,7 +2,7 @@
import React from "react"
import expect, { createSpy } from "expect"
import { shallow } from "enzyme"
import { fromJS } from "immutable"
import { fromJS, Map } from "immutable"
import Models from "components/models"
import ModelCollpase from "components/model-collapse"
import ModelComponent from "components/model-wrapper"
@@ -19,6 +19,7 @@ describe("<Models/>", function(){
},
specSelectors: {
isOAS3: () => false,
specJson: () => Map(),
definitions: function() {
return fromJS({
def1: {},
+1 -1
View File
@@ -28,7 +28,7 @@ describe("oauth2", function () {
expect(win.open.calls[0].arguments[0]).toMatch("https://testAuthorizationUrl?response_type=code&redirect_uri=&state=")
})
it("should append query paramters to authorizeUrl with query parameters", function() {
it("should append query parameters to authorizeUrl with query parameters", function() {
win.open = createSpy()
mockSchema.authorizationUrl = "https://testAuthorizationUrl?param=1"
oauth2Authorize(authConfig)
+147
View File
@@ -0,0 +1,147 @@
/* eslint-env mocha */
import expect, { createSpy } from "expect"
import { authorizeRequest } from "corePlugins/auth/actions"
describe("auth plugin - actions", () => {
describe("authorizeRequest", () => {
[
[
{
oas3: true,
server: "https://host/resource",
scheme: "http",
host: null,
url: "http://specs/file",
},
"https://host/authorize"
],
[
{
oas3: false,
server: null,
scheme: "https",
host: undefined,
url: "https://specs/file",
},
"https://specs/authorize"
],
[
{
oas3: false,
server: null,
scheme: "https",
host: "host",
url: "http://specs/file",
},
"http://specs/authorize"
],
].forEach(([{oas3, server, scheme, host, url}, expectedFetchUrl]) => {
it("should resolve authorization endpoint against the server URL", () => {
// Given
const data = {
url: "/authorize"
}
const system = {
fn: {
fetch: createSpy().andReturn(Promise.resolve())
},
getConfigs: () => ({}),
authSelectors: {
getConfigs: () => ({})
},
oas3Selectors: {
selectedServer: () => server
},
specSelectors: {
isOAS3: () => oas3,
operationScheme: () => scheme,
host: () => host,
url: () => url
}
}
// When
authorizeRequest(data)(system)
// Then
expect(system.fn.fetch.calls.length).toEqual(1)
expect(system.fn.fetch.calls[0].arguments[0]).toInclude({url: expectedFetchUrl})
})
})
it("should add additionalQueryStringParams to Swagger 2.0 authorization and token URLs", () => {
// Given
const data = {
url: "/authorize?q=1"
}
const system = {
fn: {
fetch: createSpy().andReturn(Promise.resolve())
},
getConfigs: () => ({}),
authSelectors: {
getConfigs: () => ({
additionalQueryStringParams: {
myCustomParam: "abc123"
}
})
},
specSelectors: {
isOAS3: () => false,
operationScheme: () => "https",
host: () => "http://google.com",
url: () => "http://google.com/swagger.json"
}
}
// When
authorizeRequest(data)(system)
// Then
expect(system.fn.fetch.calls.length).toEqual(1)
expect(system.fn.fetch.calls[0].arguments[0].url)
.toEqual("http://google.com/authorize?q=1&myCustomParam=abc123")
})
it("should add additionalQueryStringParams to OpenAPI 3.0 authorization and token URLs", () => {
// Given
const data = {
url: "/authorize?q=1"
}
const system = {
fn: {
fetch: createSpy().andReturn(Promise.resolve())
},
getConfigs: () => ({}),
authSelectors: {
getConfigs: () => ({
additionalQueryStringParams: {
myCustomParam: "abc123"
}
})
},
oas3Selectors: {
selectedServer: () => "http://google.com"
},
specSelectors: {
isOAS3: () => true,
}
}
// When
authorizeRequest(data)(system)
// Then
expect(system.fn.fetch.calls.length).toEqual(1)
expect(system.fn.fetch.calls[0].arguments[0].url)
.toEqual("http://google.com/authorize?q=1&myCustomParam=abc123")
})
})
})
+154
View File
@@ -0,0 +1,154 @@
/* eslint-env mocha */
import expect from "expect"
import { fromJS } from "immutable"
import { preauthorizeBasic, preauthorizeApiKey } from "corePlugins/auth"
import { authorize } from "corePlugins/auth/actions"
const S2_SYSTEM = {
authActions: {
authorize
},
specSelectors: {
isOAS3: () => false,
specJson: () => {
return fromJS({
swagger: "2.0",
securityDefinitions: {
"APIKeyHeader": {
"type": "apiKey",
"in": "header",
"name": "X-API-Key"
},
"basicAuth": {
"type": "basic"
}
}
})
}
}
}
const OAI3_SYSTEM = {
authActions: {
authorize
},
specSelectors: {
isOAS3: () => true,
specJson: () => {
return fromJS({
openapi: "3.0.0",
components: {
securitySchemes: {
basicAuth: {
type: "http",
scheme: "basic"
},
APIKeyHeader: {
type: "apiKey",
in: "header",
name: "X-API-Key"
}
}
}
})
}
}
}
describe("auth plugin - preauthorizers", () => {
describe("preauthorizeBasic", () => {
it("should return a valid authorize action in Swagger 2", () => {
const res = preauthorizeBasic(S2_SYSTEM, "basicAuth", "user", "pass")
expect(res).toEqual({
type: "authorize",
payload: {
basicAuth: {
schema: {
type: "basic"
},
value: {
username: "user",
password: "pass"
}
}
}
})
})
it("should return a valid authorize action in OpenAPI 3", () => {
const res = preauthorizeBasic(OAI3_SYSTEM, "basicAuth", "user", "pass")
expect(res).toEqual({
type: "authorize",
payload: {
basicAuth: {
schema: {
type: "http",
scheme: "basic"
},
value: {
username: "user",
password: "pass"
}
}
}
})
})
it("should return null when the authorization name is invalid in Swagger 2", () => {
const res = preauthorizeBasic(S2_SYSTEM, "fakeBasicAuth", "user", "pass")
expect(res).toEqual(null)
})
it("should return null when the authorization name is invalid in OpenAPI 3", () => {
const res = preauthorizeBasic(OAI3_SYSTEM, "fakeBasicAuth", "user", "pass")
expect(res).toEqual(null)
})
})
describe("preauthorizeApiKey", () => {
it("should return a valid authorize action in Swagger 2", () => {
const res = preauthorizeApiKey(S2_SYSTEM, "APIKeyHeader", "Asdf1234")
expect(res).toEqual({
type: "authorize",
payload: {
APIKeyHeader: {
schema: {
type: "apiKey",
name: "X-API-Key",
"in": "header"
},
value: "Asdf1234"
}
}
})
})
it("should return a valid authorize action in OpenAPI 3", () => {
const res = preauthorizeApiKey(OAI3_SYSTEM, "APIKeyHeader", "Asdf1234")
expect(res).toEqual({
type: "authorize",
payload: {
APIKeyHeader: {
schema: {
type: "apiKey",
"in": "header",
name: "X-API-Key"
},
value: "Asdf1234"
}
}
})
})
it("should return null when the authorization name is invalid in Swagger 2", () => {
const res = preauthorizeApiKey(S2_SYSTEM, "FakeAPIKeyHeader", "Asdf1234")
expect(res).toEqual(null)
})
it("should return null when the authorization name is invalid in OpenAPI 3", () => {
const res = preauthorizeApiKey(OAI3_SYSTEM, "FakeAPIKeyHeader", "Asdf1234")
expect(res).toEqual(null)
})
})
})
+27
View File
@@ -0,0 +1,27 @@
/* eslint-env mocha */
import expect, { createSpy } from "expect"
import { downloadConfig } from "corePlugins/configs/spec-actions"
describe("configs plugin - actions", () => {
describe("downloadConfig", () => {
it("should call the system fetch helper with a provided request", () => {
const fetchSpy = createSpy(async () => {}).andCallThrough()
const system = {
fn: {
fetch: fetchSpy
}
}
const req = {
url: "http://swagger.io/one",
requestInterceptor: a => a,
responseInterceptor: a => a,
}
downloadConfig(req)(system)
expect(fetchSpy).toHaveBeenCalledWith(req)
})
})
})
+108 -22
View File
@@ -100,6 +100,92 @@ describe("sampleFromSchema", function() {
expect(sampleFromSchema(definition, { includeWriteOnly: true })).toEqual(expected)
})
it("returns object without any $$ref fields at the root schema level", function () {
var definition = {
type: "object",
properties: {
message: {
type: "string"
}
},
example: {
value: {
message: "Hello, World!"
},
$$ref: "#/components/examples/WelcomeExample"
},
$$ref: "#/components/schemas/Welcome"
}
var expected = {
"value": {
"message": "Hello, World!"
}
}
expect(sampleFromSchema(definition, { includeWriteOnly: true })).toEqual(expected)
})
it("returns object without any $$ref fields at nested schema levels", function () {
var definition = {
type: "object",
properties: {
message: {
type: "string"
}
},
example: {
a: {
value: {
message: "Hello, World!"
},
$$ref: "#/components/examples/WelcomeExample"
}
},
$$ref: "#/components/schemas/Welcome"
}
var expected = {
a: {
"value": {
"message": "Hello, World!"
}
}
}
expect(sampleFromSchema(definition, { includeWriteOnly: true })).toEqual(expected)
})
it("returns object with any $$ref fields that appear to be user-created", function () {
var definition = {
type: "object",
properties: {
message: {
type: "string"
}
},
example: {
$$ref: {
value: {
message: "Hello, World!"
},
$$ref: "#/components/examples/WelcomeExample"
}
},
$$ref: "#/components/schemas/Welcome"
}
var expected = {
$$ref: {
"value": {
"message": "Hello, World!"
}
}
}
expect(sampleFromSchema(definition, { includeWriteOnly: true })).toEqual(expected)
})
describe("for array type", function() {
it("returns array with sample of array type", function() {
var definition = {
@@ -108,12 +194,12 @@ describe("sampleFromSchema", function() {
type: "integer"
}
}
var expected = [ 0 ]
expect(sampleFromSchema(definition)).toEqual(expected)
})
it("returns array of examples for array that has example", function() {
var definition = {
type: "array",
@@ -122,9 +208,9 @@ describe("sampleFromSchema", function() {
},
example: "dog"
}
var expected = [ "dog" ]
expect(sampleFromSchema(definition)).toEqual(expected)
})
@@ -132,16 +218,16 @@ describe("sampleFromSchema", function() {
var definition = {
type: "array",
items: {
type: "string",
type: "string",
},
example: [ "dog", "cat" ]
}
var expected = [ "dog", "cat" ]
expect(sampleFromSchema(definition)).toEqual(expected)
})
it("returns array of samples for oneOf type", function() {
var definition = {
type: "array",
@@ -154,9 +240,9 @@ describe("sampleFromSchema", function() {
]
}
}
var expected = [ 0 ]
expect(sampleFromSchema(definition)).toEqual(expected)
})
@@ -175,9 +261,9 @@ describe("sampleFromSchema", function() {
]
}
}
var expected = [ "string", 0 ]
expect(sampleFromSchema(definition)).toEqual(expected)
})
@@ -198,12 +284,12 @@ describe("sampleFromSchema", function() {
]
}
}
var expected = [ "dog", 1 ]
expect(sampleFromSchema(definition)).toEqual(expected)
})
it("returns array of samples for anyOf type", function() {
var definition = {
type: "array",
@@ -216,9 +302,9 @@ describe("sampleFromSchema", function() {
]
}
}
var expected = [ 0 ]
expect(sampleFromSchema(definition)).toEqual(expected)
})
@@ -237,9 +323,9 @@ describe("sampleFromSchema", function() {
]
}
}
var expected = [ "string", 0 ]
expect(sampleFromSchema(definition)).toEqual(expected)
})
@@ -260,9 +346,9 @@ describe("sampleFromSchema", function() {
]
}
}
var expected = [ "dog", 1 ]
expect(sampleFromSchema(definition)).toEqual(expected)
})
})
File diff suppressed because it is too large Load Diff
+91 -1
View File
@@ -1,6 +1,7 @@
/* eslint-env mocha */
import expect from "expect"
import { fromJS } from "immutable"
import { fromJSOrdered } from "core/utils"
import {
parameterValues,
contentTypeValues,
@@ -9,6 +10,8 @@ import {
operationConsumes
} from "corePlugins/spec/selectors"
import Petstore from "./assets/petstore.json"
describe("spec plugin - selectors", function(){
describe("parameterValue", function(){
@@ -311,7 +314,6 @@ describe("spec plugin - selectors", function(){
})
describe("specJsonWithResolvedSubtrees", function(){
it("should return a correctly merged tree", function(){
@@ -360,5 +362,93 @@ describe("spec plugin - selectors", function(){
}
})
})
it("should preserve initial map key ordering", function(){
// Given
let state = fromJSOrdered({
json: Petstore,
resolvedSubtrees: {
paths: {
"/pet/{petId}": {
post: {
tags: [
"pet"
],
summary: "Updates a pet in the store with form data",
description: "",
operationId: "updatePetWithForm",
consumes: [
"application/x-www-form-urlencoded"
],
produces: [
"application/xml",
"application/json"
],
parameters: [
{
name: "petId",
"in": "path",
description: "ID of pet that needs to be updated",
required: true,
type: "integer",
format: "int64"
},
{
name: "name",
"in": "formData",
description: "Updated name of the pet",
required: false,
type: "string"
},
{
name: "status",
"in": "formData",
description: "Updated status of the pet",
required: false,
type: "string"
}
],
responses: {
"405": {
description: "Invalid input"
}
},
security: [
{
petstore_auth: [
"write:pets",
"read:pets"
]
}
],
__originalOperationId: "updatePetWithForm"
}
}
}
}
})
// When
let result = specJsonWithResolvedSubtrees(state)
// Then
const correctOrder = [
"/pet",
"/pet/findByStatus",
"/pet/findByTags",
"/pet/{petId}",
"/pet/{petId}/uploadImage",
"/store/inventory",
"/store/order",
"/store/order/{orderId}",
"/user",
"/user/createWithArray",
"/user/createWithList",
"/user/login",
"/user/logout",
"/user/{username}"
]
expect(state.getIn(["json", "paths"]).keySeq().toJS()).toEqual(correctOrder)
expect(result.getIn(["paths"]).keySeq().toJS()).toEqual(correctOrder)
})
})
})
+30
View File
@@ -701,6 +701,36 @@ describe("bound system", function(){
})
})
describe("rootInjects", function() {
it("should attach a rootInject function as an instance method", function() {
// This is the same thing as the `afterLoad` tests, but is here for posterity
// Given
const system = new System({
plugins: [
{
afterLoad(system) {
this.rootInjects.wow = system.dogeSelectors.wow
},
statePlugins: {
doge: {
selectors: {
wow: () => (system) => {
return "so selective"
}
}
}
}
}
]
})
// When
var res = system.getSystem().wow()
expect(res).toEqual("so selective")
})
})
describe("error catching", function() {
it("should encapsulate thrown errors in an afterLoad method", function() {
// Given
+75 -2
View File
@@ -1,6 +1,6 @@
/* eslint-env mocha */
import expect from "expect"
import { fromJS, OrderedMap } from "immutable"
import { Map, fromJS, OrderedMap } from "immutable"
import {
mapToList,
parseSearch,
@@ -20,8 +20,11 @@ import {
getAcceptControllingResponse,
createDeepLinkPath,
escapeDeepLinkPath,
getExtensions,
getCommonExtensions,
sanitizeUrl,
extractFileNameFromContentDispositionHeader
extractFileNameFromContentDispositionHeader,
deeplyStripKey
} from "core/utils"
import win from "core/window"
@@ -942,6 +945,76 @@ describe("utils", function() {
})
})
describe("getExtensions", function() {
const objTest = Map([[ "x-test", "a"], ["minimum", "b"]])
it("does not error on empty array", function() {
const result1 = getExtensions([])
expect(result1).toEqual([])
const result2 = getCommonExtensions([])
expect(result2).toEqual([])
})
it("gets only the x- keys", function() {
const result = getExtensions(objTest)
expect(result).toEqual(Map([[ "x-test", "a"]]))
})
it("gets the common keys", function() {
const result = getCommonExtensions(objTest, true)
expect(result).toEqual(Map([[ "minimum", "b"]]))
})
})
describe("deeplyStripKey", function() {
it("should filter out a specified key", function() {
const input = {
$$ref: "#/this/is/my/ref",
a: {
$$ref: "#/this/is/my/other/ref",
value: 12345
}
}
const result = deeplyStripKey(input, "$$ref")
expect(result).toEqual({
a: {
value: 12345
}
})
})
it("should filter out a specified key by predicate", function() {
const input = {
$$ref: "#/this/is/my/ref",
a: {
$$ref: "#/keep/this/one",
value: 12345
}
}
const result = deeplyStripKey(input, "$$ref", (v) => v !== "#/keep/this/one")
expect(result).toEqual({
a: {
value: 12345,
$$ref: "#/keep/this/one"
}
})
})
it("should only call the predicate when the key matches", function() {
const input = {
$$ref: "#/this/is/my/ref",
a: {
$$ref: "#/this/is/my/other/ref",
value: 12345
}
}
let count = 0
const result = deeplyStripKey(input, "$$ref", () => {
count++
return true
})
expect(count).toEqual(2)
})
})
describe("parse and serialize search", function() {
afterEach(function() {
win.location.search = ""
+111
View File
@@ -0,0 +1,111 @@
<!-- HTML for dev server -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Swagger UI</title>
<link href="https://fonts.googleapis.com/css?family=Open+Sans:400,700|Source+Code+Pro:300,600|Titillium+Web:400,600,700" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="./swagger-ui.css" >
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
<style>
html
{
box-sizing: border-box;
overflow: -moz-scrollbars-vertical;
overflow-y: scroll;
}
*,
*:before,
*:after
{
box-sizing: inherit;
}
body {
margin:0;
background: #fafafa;
}
</style>
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position:absolute;width:0;height:0">
<defs>
<symbol viewBox="0 0 20 20" id="unlocked">
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V6h2v-.801C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8z"></path>
</symbol>
<symbol viewBox="0 0 20 20" id="locked">
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8zM12 8H8V5.199C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="close">
<path d="M14.348 14.849c-.469.469-1.229.469-1.697 0L10 11.819l-2.651 3.029c-.469.469-1.229.469-1.697 0-.469-.469-.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-.469-.469-.469-1.228 0-1.697.469-.469 1.228-.469 1.697 0L10 8.183l2.651-3.031c.469-.469 1.228-.469 1.697 0 .469.469.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c.469.469.469 1.229 0 1.698z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="large-arrow">
<path d="M13.25 10L6.109 2.58c-.268-.27-.268-.707 0-.979.268-.27.701-.27.969 0l7.83 7.908c.268.271.268.709 0 .979l-7.83 7.908c-.268.271-.701.27-.969 0-.268-.269-.268-.707 0-.979L13.25 10z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="large-arrow-down">
<path d="M17.418 6.109c.272-.268.709-.268.979 0s.271.701 0 .969l-7.908 7.83c-.27.268-.707.268-.979 0l-7.908-7.83c-.27-.268-.27-.701 0-.969.271-.268.709-.268.979 0L10 13.25l7.418-7.141z"/>
</symbol>
<symbol viewBox="0 0 24 24" id="jump-to">
<path d="M19 7v4H5.83l3.58-3.59L8 6l-6 6 6 6 1.41-1.41L5.83 13H21V7z"/>
</symbol>
<symbol viewBox="0 0 24 24" id="expand">
<path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>
</symbol>
</defs>
</svg>
<div id="swagger-ui"></div>
<script src="./swagger-ui-bundle.js"> </script>
<script src="./swagger-ui-standalone-preset.js"> </script>
<script>
window.onload = function() {
window["SwaggerUIBundle"] = window["swagger-ui-bundle"]
window["SwaggerUIStandalonePreset"] = window["swagger-ui-standalone-preset"]
// Build a system
const ui = SwaggerUIBundle({
url: "http://petstore.swagger.io/v2/swagger.json",
dom_id: '#swagger-ui',
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout",
onComplete: () => {
if(window.completeCount) {
window.completeCount++
} else {
window.completeCount = 1
}
}
})
window.ui = ui
ui.initOAuth({
clientId: "your-client-id",
clientSecret: "your-client-secret-if-required",
realm: "your-realms",
appName: "your-app-name",
scopeSeparator: " ",
additionalQueryStringParams: {}
})
}
</script>
</body>
</html>
+44
View File
@@ -0,0 +1,44 @@
describe("bug #4196: HTTP basic auth credential retention", function () {
let mainPage
beforeEach(function (client, done) {
mainPage = client
.url("localhost:3230")
.page.main()
client.waitForElementVisible(".download-url-input", 5000)
.pause(80)
.clearValue(".download-url-input")
.setValue(".download-url-input", "http://localhost:3230/test-specs/bugs/4196.yaml")
.click("button.download-url-button")
.pause(1000)
done()
})
afterEach(function (client, done) {
done()
})
it("should display the most recent auth data across modal close/opens", function (client) {
client.waitForElementVisible(".opblock-tag-section", 10000)
.click("button.btn.authorize") // Open modal
.waitForElementVisible("section>input", 5000)
.setValue("section>input", "aaa") // Set user
.waitForElementVisible(`section>input[type="password"]`, 5000)
.setValue(`section>input[type="password"]`, "aaa") // Set password
.click(".auth-btn-wrapper button:nth-child(1)") // Click Authorize
.assert.containsText("div.wrapper:nth-child(4)>code", "aaa")
.click(".auth-btn-wrapper button:nth-child(2)") // Close modal
.pause(50)
.click("button.btn.authorize") // Open modal
.pause(50)
.click(".auth-btn-wrapper button:nth-child(1)") // Logout
.waitForElementVisible("section>input", 5000)
.setValue("section>input", "bbb") // Set user
.waitForElementVisible(`section>input[type="password"]`, 5000)
.setValue(`section>input[type="password"]`, "bbb") // Set password
.click(".auth-btn-wrapper button:nth-child(1)") // Click Authorize
.pause(5000)
.assert.containsText("div.wrapper:nth-child(4)>code", "bbb")
client.end()
})
})
+32
View File
@@ -0,0 +1,32 @@
describe("bug #4374: OAS3 parameters should be visibly validated in Try-It-Out", function () {
let mainPage
beforeEach(function (client, done) {
mainPage = client
.url("localhost:3230")
.page.main()
client.waitForElementVisible(".download-url-input", 10000)
.pause(1000)
.clearValue(".download-url-input")
.setValue(".download-url-input", "http://localhost:3230/test-specs/bugs/4374.yaml")
.click("button.download-url-button")
.pause(1000)
done()
})
afterEach(function (client, done) {
done()
})
it("indicates an error when a required parameter is not selected", function (client) {
client.waitForElementVisible(".opblock-tag-section", 10000)
.assert.containsText(".opblock-summary-path span", "/pet/findByStatus")
.click(".opblock")
.waitForElementVisible(".opblock.is-open", 5000)
.click(".try-out__btn")
.click(".btn.execute")
.pause(100)
.assert.cssClassPresent(".parameters-col_description select", "invalid")
client.end()
})
})
+29
View File
@@ -0,0 +1,29 @@
describe("bug #4409: operationId normalization and layout tracking", function () {
let mainPage
beforeEach(function (client, done) {
mainPage = client
.url("localhost:3230")
.page.main()
client.waitForElementVisible(".download-url-input", 5000)
.pause(5000)
.clearValue(".download-url-input")
.setValue(".download-url-input", "http://localhost:3230/test-specs/bugs/4409.yaml")
.click("button.download-url-button")
.pause(1000)
done()
})
afterEach(function (client, done) {
done()
})
it("expands an operation that has a normalizable operationId", function (client) {
client.waitForElementVisible(".opblock-tag-section", 10000)
.assert.containsText(".opblock-summary-path span", "/myApi")
.click(".opblock")
.waitForElementVisible(".opblock-body", 5000)
.assert.cssClassPresent(".opblock", "is-open")
client.end()
})
})
+32
View File
@@ -0,0 +1,32 @@
describe("bug #4445: callback-via-$ref rendering", function () {
let mainPage
beforeEach(function (client, done) {
mainPage = client
.url("localhost:3230")
.page.main()
client.waitForElementVisible(".download-url-input", 5000)
.pause(5000)
.clearValue(".download-url-input")
.setValue(".download-url-input", "http://localhost:3230/test-specs/bugs/4445.yaml")
.click("button.download-url-button")
.pause(1000)
done()
})
afterEach(function (client, done) {
done()
})
it("expands an operation that has a visible callback", function (client) {
client.waitForElementVisible(".opblock-tag-section", 10000)
.click(".opblock")
.waitForElementVisible(".opblock-body", 5000)
.click(".opblock-section-header > .tab-header > div.tab-item:nth-of-type(2)")
.waitForElementVisible(".callbacks-container .opblock", 5000)
.click(".callbacks-container .opblock")
.waitForElementVisible(".callbacks-container .opblock-body", 5000)
.assert.containsText(".callbacks-container .opblock-description-wrapper:nth-of-type(2) p", "subscription payload")
client.end()
})
})
+31
View File
@@ -0,0 +1,31 @@
describe("bug #4485: operation metadata storage when referenced via path item $ref", function () {
let mainPage
beforeEach(function (client, done) {
mainPage = client
.url("localhost:3230")
.page.main()
client.waitForElementVisible(".download-url-input", 5000)
.pause(2000)
.clearValue(".download-url-input")
.setValue(".download-url-input", "http://localhost:3230/test-specs/bugs/4485/main.yaml")
.click("button.download-url-button")
.pause(1000)
done()
})
afterEach(function (client, done) {
done()
})
it("sets a consumes value for a body parameter correctly", function (client) {
client.waitForElementVisible(".opblock-tag-section", 10000)
.click(".opblock")
.waitForElementVisible(".opblock-body", 5000)
.click("button.btn.try-out__btn")
.click("select.content-type [value=\"application/xml\"]")
.pause(500)
.assert.value("select.content-type", "application/xml")
client.end()
})
})
+29
View File
@@ -0,0 +1,29 @@
describe("bug #4536: model name consistency", function () {
let mainPage
beforeEach(function (client, done) {
mainPage = client
.url("localhost:3230")
.page.main()
client.waitForElementVisible(".download-url-input", 5000)
.pause(2000)
.clearValue(".download-url-input")
.setValue(".download-url-input", "http://localhost:3230/test-specs/bugs/4536.yaml")
.click("button.download-url-button")
.pause(1000)
done()
})
afterEach(function (client, done) {
done()
})
it("consistently displays a model's name regardless of expansion state", function (client) {
client.waitForElementVisible("span.model.model-title", 10000)
.assert.containsText("span.model.model-title", "TitleName")
.click("span.model.model-title")
.pause(500)
.assert.containsText("span.model-title__text", "TitleName")
client.end()
})
})
@@ -0,0 +1,58 @@
describe("parameter enum rendering", function () {
describe("swagger 2.0", () => {
beforeEach(function (client, done) {
client
.url("localhost:3230")
.waitForElementVisible(".download-url-input", 10000)
.pause(1000)
.clearValue(".download-url-input")
.setValue(".download-url-input", "http://localhost:3230/test-specs/features/parameter-enum-rendering.swagger.yaml")
.click("button.download-url-button")
.pause(1000)
done()
})
afterEach(function (client, done) {
done()
})
it("reveals a string parameter's enums and defaults when viewing that parameter", function (client) {
client.waitForElementVisible(".opblock-tag-section", 10000)
.assert.containsText(".opblock-summary-path span", "/report")
.click(".opblock")
.waitForElementVisible(".opblock.is-open", 5000)
.pause(500)
.assert.containsText("div.parameter__enum", "today, yesterday, lastweek")
.assert.containsText("div.parameter__default", "today")
client.end()
})
})
describe("openapi 3.0", () => {
beforeEach(function (client, done) {
client
.url("localhost:3230")
.waitForElementVisible(".download-url-input", 10000)
.pause(1000)
.clearValue(".download-url-input")
.setValue(".download-url-input", "http://localhost:3230/test-specs/features/parameter-enum-rendering.openapi.yaml")
.click("button.download-url-button")
.pause(1000)
done()
})
afterEach(function (client, done) {
done()
})
it("reveals a string parameter's enums and defaults when viewing that parameter", function (client) {
client.waitForElementVisible(".opblock-tag-section", 10000)
.assert.containsText(".opblock-summary-path span", "/report")
.click(".opblock")
.waitForElementVisible(".opblock.is-open", 5000)
.pause(500)
.assert.containsText("div.parameter__enum", "today, yesterday, lastweek")
.assert.containsText("div.parameter__default", "today")
client.end()
})
})
})
+4 -4
View File
@@ -4,12 +4,12 @@ describe("render informationContainer", function () {
beforeEach(function (client, done) {
mainPage = client
.url("localhost:3200")
.url("localhost:3230")
.page.main()
client.waitForElementVisible(".download-url-input", 5000)
.pause(5000)
.clearValue(".download-url-input")
.setValue(".download-url-input", "http://localhost:3200/test-specs/petstore.json")
.setValue(".download-url-input", "http://localhost:3230/test-specs/petstore.json")
.click("button.download-url-button")
.pause(1000)
@@ -29,8 +29,8 @@ describe("render informationContainer", function () {
.assert.containsText("@title", "Swagger Petstore")
.assert.containsText("@version", "1.0.0")
.assert.containsText("@baseUrl", "[ Base URL: localhost:3204/ ]")
.assert.attributeEquals("@mainUrl", "href", "http://localhost:3200/test-specs/petstore.json")
.assert.containsText("@mainUrlContent", "http://localhost:3200/test-specs/petstore.json")
.assert.attributeEquals("@mainUrl", "href", "http://localhost:3230/test-specs/petstore.json")
.assert.containsText("@mainUrlContent", "http://localhost:3230/test-specs/petstore.json")
.assert.containsText("@description", "This is a sample server Petstore server. You can find out more about Swagger at http://swagger.io or on irc.freenode.net, #swagger. For this sample, you can use the api key special-key to test the authorization filters.")
.assert.attributeEquals("@swaggerUrl", "href", "http://swagger.io/")
.assert.attributeEquals("@swaggerircUrl", "href", "http://swagger.io/irc/")
+2 -2
View File
@@ -3,12 +3,12 @@ describe("Render Model Wrapper", function () {
beforeEach(function (client, done) {
mainPage = client
.url("localhost:3200")
.url("localhost:3230")
.page.main()
client.waitForElementVisible(".download-url-input", 5000)
.pause(5000)
.clearValue(".download-url-input")
.setValue(".download-url-input", "http://localhost:3200/test-specs/petstore.json")
.setValue(".download-url-input", "http://localhost:3230/test-specs/petstore.json")
.click("button.download-url-button")
.pause(1000)
+2 -2
View File
@@ -3,13 +3,13 @@ describe("render pet api container", function () {
let apiWrapper
beforeEach(function (client, done) {
mainPage = client
.url("localhost:3200")
.url("localhost:3230")
.page.main()
client.waitForElementVisible(".download-url-input", 5000)
.pause(5000)
.clearValue(".download-url-input")
.setValue(".download-url-input", "http://localhost:3200/test-specs/callbacks.openapi.yaml")
.setValue(".download-url-input", "http://localhost:3230/test-specs/callbacks.openapi.yaml")
.click("button.download-url-button")
.pause(1000)
+2 -2
View File
@@ -3,13 +3,13 @@ describe("render pet api container", function () {
let apiWrapper
beforeEach(function (client, done) {
mainPage = client
.url("localhost:3200")
.url("localhost:3230")
.page.main()
client.waitForElementVisible(".download-url-input", 5000)
.pause(5000)
.clearValue(".download-url-input")
.setValue(".download-url-input", "http://localhost:3200/test-specs/petstore.openapi.yaml")
.setValue(".download-url-input", "http://localhost:3230/test-specs/petstore.openapi.yaml")
.click("button.download-url-button")
.pause(1000)
+22
View File
@@ -0,0 +1,22 @@
const expect = require("expect")
describe("onComplete option", function () {
let mainPage
beforeEach(function (client, done) {
mainPage = client
.url("localhost:3230")
.page.main()
client.waitForElementVisible(".opblock-tag-section", 5000)
done()
})
it("triggers the page-provided onComplete exactly 1 times", function (client, done) {
client.execute(function() {
return window.completeCount
}, [], (result) => {
expect(result.value).toEqual(1)
client.end()
})
})
})
+21 -11
View File
@@ -3,13 +3,13 @@ describe("render pet api container", function () {
let apiWrapper
beforeEach(function (client, done) {
mainPage = client
.url("localhost:3200")
.url("localhost:3230")
.page.main()
client.waitForElementVisible(".download-url-input", 5000)
.pause(5000)
.clearValue(".download-url-input")
.setValue(".download-url-input", "http://localhost:3200/test-specs/petstore.json")
.setValue(".download-url-input", "http://localhost:3230/test-specs/petstore.json")
.click("button.download-url-button")
.pause(1000)
@@ -71,6 +71,16 @@ describe("render pet api container", function () {
client.end()
})
it("Testing post /pet api model values", function (client) {
apiWrapper.waitForElementVisible("@petOperationPostContainer", 5000)
.click("@petOperationPostCollpase")
.waitForElementVisible("@petOperationPostCollapseContainer", 5000)
.click("a.tablinks[data-name=model]")
.assert.containsText("span.model-title > .model-title__text", `Pet`)
client.end()
})
it("render put /pet api container", function (client) {
apiWrapper.waitForElementVisible("@petOperationPutContainer", 5000)
.assert.containsText("@petOperationPutTitle", "/pet")
@@ -81,10 +91,10 @@ describe("render pet api container", function () {
.waitForElementVisible("@petOperationPutExecuteBtn", 1000)
.click("@petOperationPutTryBtn")
.assert.cssClassNotPresent("@petOperationPutTryBtn", "cancel")
client.end()
})
it("Testing put /pet api Mock data", function (client) {
apiWrapper.waitForElementVisible("@petOperationPutContainer", 5000)
.click("@petOperationPutCollpase")
@@ -115,7 +125,7 @@ describe("render pet api container", function () {
.waitForElementVisible("@petOperationGetByTagExecuteBtn", 1000)
.click("@petOperationGetByTagTryBtn")
.assert.cssClassNotPresent("@petOperationGetByTagTryBtn", "cancel")
client.end()
})
@@ -138,7 +148,7 @@ describe("render pet api container", function () {
client.end()
})
it("render get by ID /pet/{petId} api container", function (client) {
apiWrapper.waitForElementVisible("@petOperationGetByIdContainer", 5000)
.assert.containsText("@petOperationGetByIdTitle", "/pet/{petId}")
@@ -148,7 +158,7 @@ describe("render pet api container", function () {
.waitForElementVisible("@petOperationGetByTagExecuteBtn", 1000)
.click("@petOperationGetByTagTryBtn")
.assert.cssClassNotPresent("@petOperationGetByTagTryBtn", "cancel")
client.end()
})
@@ -167,7 +177,7 @@ describe("render pet api container", function () {
.assert.attributeEquals("@petOperationGetByIdParameter", "title", "Value must be an integer")
.click("@petOperationGetByTagTryBtn")
.assert.cssClassNotPresent("@petOperationGetByTagTryBtn", "cancel")
client.end()
})
@@ -180,10 +190,10 @@ describe("render pet api container", function () {
.waitForElementVisible("@petOperationDeleteExecuteBtn", 1000)
.click("@petOperationDeleteTryBtn")
.assert.cssClassNotPresent("@petOperationDeleteTryBtn", "cancel")
client.end()
})
it("Testing delete /pet api Mock data", function (client) {
apiWrapper.waitForElementVisible("@petOperationDeleteContainer", 3000)
.click("@petOperationDeleteCollpase")
@@ -203,4 +213,4 @@ describe("render pet api container", function () {
client.end()
})
})
})
+2 -2
View File
@@ -3,13 +3,13 @@ describe("render store api container", function(){
let apiWrapper
beforeEach( function(client, done){
mainPage = client
.url("localhost:3200")
.url("localhost:3230")
.page.main()
client.waitForElementVisible(".download-url-input", 5000)
.pause(3000)
.clearValue(".download-url-input")
.setValue(".download-url-input", "http://localhost:3200/test-specs/petstore.json")
.setValue(".download-url-input", "http://localhost:3230/test-specs/petstore.json")
.click("button.download-url-button")
.pause(1000)
+2 -2
View File
@@ -3,13 +3,13 @@ describe("render user api container", function(){
let apiWrapper
beforeEach( function(client, done){
mainPage = client
.url("localhost:3200")
.url("localhost:3230")
.page.main()
client.waitForElementVisible(".download-url-input", 5000)
.pause(5000)
.clearValue(".download-url-input")
.setValue(".download-url-input", "http://localhost:3200/test-specs/petstore.json")
.setValue(".download-url-input", "http://localhost:3230/test-specs/petstore.json")
.click("button.download-url-button")
.pause(1000)
+3 -3
View File
@@ -4,13 +4,13 @@ describe("Remote $ref rendering", function () {
mainPage = client
// expand the models so we don't have to manually do it
.url("localhost:3200?defaultModelsExpandDepth=5")
.url("localhost:3230?defaultModelsExpandDepth=5")
.page.main()
client.waitForElementVisible(".download-url-input", 5000)
.pause(2000)
.clearValue(".download-url-input")
.setValue(".download-url-input", "http://localhost:3200/test-specs/refs/api1.yaml")
.setValue(".download-url-input", "http://localhost:3230/test-specs/refs/api1.yaml")
.click("button.download-url-button")
.pause(1000)
@@ -19,7 +19,7 @@ describe("Remote $ref rendering", function () {
})
it("renders a remote $ref correctly", function (client) {
mainPage.expect.element("#model-TestResponse > span > div > span > span > span.inner-object > table > tbody > tr:nth-child(2) > td:nth-child(2) > span > span > div > div > p").text.to.equal("this is a api2prop")
mainPage.expect.element("#model-TestResponse > span > div > span > span > span.inner-object > table > tbody > tr:nth-child(2) > td:nth-child(2) > span > span > div > div > p").text.to.equal("this is an api2prop")
client.end()
})
+2 -2
View File
@@ -4,7 +4,7 @@ describe("Render scheme", function () {
beforeEach(function (client, done) {
mainPage = client
.url("localhost:3200")
.url("localhost:3230")
.page.main()
schemeContainer = mainPage.section.schemeContainer
@@ -12,7 +12,7 @@ describe("Render scheme", function () {
client.waitForElementVisible(".download-url-input", 5000)
.pause(5000)
.clearValue(".download-url-input")
.setValue(".download-url-input", "http://localhost:3200/test-specs/petstore.json")
.setValue(".download-url-input", "http://localhost:3230/test-specs/petstore.json")
.click("button.download-url-button")
.pause(1000)
+2 -2
View File
@@ -18,7 +18,7 @@ describe("initial render", function () {
beforeEach(function (client, done) {
mainPage = client
.url("localhost:3200")
.url("localhost:3230")
.page.main()
topbar = mainPage.section.topbar
@@ -26,7 +26,7 @@ describe("initial render", function () {
client.waitForElementVisible(".download-url-input", 10000)
.pause(5000)
.clearValue(".download-url-input")
.setValue(".download-url-input", "http://localhost:3200/test-specs/petstore.json")
.setValue(".download-url-input", "http://localhost:3230/test-specs/petstore.json")
.click("button.download-url-button")
.pause(1000)
+85
View File
@@ -0,0 +1,85 @@
openapi: 3.0.0
info:
title: Demo API
description: First test
termsOfService: 'http://demo.io/terms-of-service/'
contact:
name: Demo Support
email: support@demo.io
version: 1.0.0
servers:
- url: '{server}/v1'
variables:
server:
default: https://api.demo.io
description: the API endpoint
paths:
/session:
put:
summary: Returns a new authentication token
tags:
- session
security:
- basicAuth: []
responses:
'201':
description: A session object
content:
application/json:
schema:
allOf:
- type: object
properties:
user_id:
type: string
format: uuid
readOnly: true
example: 110e8400-e29b-11d4-a716-446655440000
- $ref: '#/components/schemas/Session'
'401':
$ref: '#/components/responses/Unauthorized'
components:
securitySchemes:
basicAuth:
type: http
scheme: basic
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
schemas:
Session:
required:
- token
properties:
token:
type: string
readOnly: true
example: >-
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.EkN-DOsnsuRjRO6BxXemmJDm3HbxrbRzXglbN2S4sOkopdU4IsDxTI8jO19W_A4K8ZPJijNLis4EZsHeY559a4DFOd50_OqgHGuERTqYZyuhtF39yxJPAjUESwxk2J5k_4zM3O-vtd1Ghyo4IbqKKSy6J9mTniYJPenn5-HIirE
Error:
required:
- message
properties:
message:
description: a human readable message explaining the error
type: string
reason:
description: a functionnal key about the error
type: string
responses:
Unauthorized:
description: Not authenticated
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
Default:
description: unexpected error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
+228
View File
@@ -0,0 +1,228 @@
---
openapi: 3.0.0
servers:
- url: http://localhost:3204/
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.'
version: 1.0.0
title: Swagger Petstore
termsOfService: http://swagger.io/terms/
contact:
email: apiteam@swagger.io
license:
name: Apache 2.0
url: http://www.apache.org/licenses/LICENSE-2.0.html
tags:
- name: pet
description: Everything about your Pets
externalDocs:
description: Find out more
url: http://swagger.io
- name: store
description: Access to Petstore orders
- name: user
description: Operations about user
externalDocs:
description: Find out more about our store
url: http://swagger.io
paths:
"/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
explode: true
schema:
type: array
items:
type: string
enum:
- available
- pending
- sold
default: available
responses:
'200':
description: successful operation
content:
application/xml:
schema:
type: array
items:
"$ref": "#/components/schemas/Pet"
application/json:
schema:
type: array
items:
"$ref": "#/components/schemas/Pet"
'400':
description: Invalid status value
security:
- petstore_auth:
- write:pets
- read:pets
externalDocs:
description: Find out more about Swagger
url: http://swagger.io
components:
schemas:
Order:
type: object
properties:
id:
type: integer
format: int64
petId:
type: integer
format: int64
quantity:
type: integer
format: int32
shipDate:
type: string
format: date-time
status:
type: string
description: Order Status
enum:
- placed
- approved
- delivered
complete:
type: boolean
default: false
xml:
name: Order
Category:
type: object
properties:
id:
type: integer
format: int64
name:
type: string
xml:
name: Category
User:
type: object
properties:
id:
type: integer
format: int64
username:
type: string
firstName:
type: string
lastName:
type: string
email:
type: string
password:
type: string
phone:
type: string
userStatus:
type: integer
format: int32
description: User Status
xml:
name: User
Tag:
type: object
properties:
id:
type: integer
format: int64
name:
type: string
xml:
name: Tag
Pet:
type: object
required:
- name
- photoUrls
properties:
id:
type: integer
format: int64
category:
"$ref": "#/components/schemas/Category"
name:
type: string
example: doggie
photoUrls:
type: array
xml:
name: photoUrl
wrapped: true
items:
type: string
tags:
type: array
xml:
name: tag
wrapped: true
items:
"$ref": "#/components/schemas/Tag"
status:
type: string
description: pet status in the store
enum:
- available
- pending
- sold
xml:
name: Pet
ApiResponse:
type: object
properties:
code:
type: integer
format: int32
type:
type: string
message:
type: string
requestBodies:
Pet:
content:
application/json:
schema:
"$ref": "#/components/schemas/Pet"
application/xml:
schema:
"$ref": "#/components/schemas/Pet"
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:
type: oauth2
flows:
implicit:
authorizationUrl: http://petstore.swagger.io/oauth/dialog
scopes:
write:pets: modify pets in your account
read:pets: read your pets
api_key:
type: apiKey
name: api_key
in: header
+24
View File
@@ -0,0 +1,24 @@
openapi: 3.0.0
servers:
- url: 'https://__host__/'
info:
description: Test API
version: '1.0'
title: Test API
contact:
name: Sample Person
email: Sample.Person@adp.com
tags:
- name: Test API with Schema
description: Test API with Schema
paths:
/myApi:
get:
operationId: some guid
responses:
'200':
description: Returns documents
description: Returns documents
tags:
- Test API with Schema
summary: Test API with Schema
+64
View File
@@ -0,0 +1,64 @@
openapi: 3.0.0
info:
title: Callback Example
version: 1.0.0
paths:
/streams:
post:
description: subscribes a client to receive out-of-band data
parameters:
- name: callbackUrl
in: query
required: true
description: |
the location where data will be sent. Must be network accessible
by the source server
schema:
type: string
format: uri
example: https://tonys-server.com
responses:
'201':
description: subscription successfully created
content:
application/json:
schema:
description: subscription information
required:
- subscriptionId
properties:
subscriptionId:
description: this unique identifier allows management of the subscription
type: string
example: 2531329f-fb09-4ef7-887e-84e648214436
callbacks:
# the name `onData` is a convenience locator
onData:
$ref: '#/components/callbacks/onData'
components:
callbacks:
onData:
# when data is sent, it will be sent to the `callbackUrl` provided
# when making the subscription PLUS the suffix `/data`
'{$request.query.callbackUrl}/data':
post:
requestBody:
description: subscription payload
content:
application/json:
schema:
properties:
timestamp:
type: string
format: date-time
userData:
type: string
responses:
'202':
description: |
Your server implementation should return this HTTP status code
if the data was received successfully
'204':
description: |
Your server should return this HTTP status code if no longer interested
in further updates
+15
View File
@@ -0,0 +1,15 @@
---
post:
description: Book
operationId: buy
summary: Buy a book
tags:
- Book
consumes:
- application/json
- application/xml
parameters:
- name: requestBody
in: body
description: Buy a Book
required: true
+5
View File
@@ -0,0 +1,5 @@
---
swagger: '2.0'
paths:
"/v1/book":
"$ref": "./book.yaml"
+9
View File
@@ -0,0 +1,9 @@
swagger: '2.0'
info:
version: 0.0.0
title: test
paths: {}
definitions:
ModelName:
title: TitleName
@@ -0,0 +1,21 @@
openapi: 3.0.0
info:
title: test
version: 0.0.0
paths:
/report:
get:
parameters:
- in: query
name: rel_date
required: true
schema:
type: string
default: today
enum:
- today
- yesterday
- lastweek
responses:
'200':
description: OK

Some files were not shown because too many files have changed in this diff Show More