Compare commits

..
128 Commits
Author SHA1 Message Date
kyle 253573200a release: 3.9.1 (#4106)
* fix(core-components): make `onToggle` a non-required prop in ModelCollapse

* release: 3.9.1
2018-01-13 01:30:37 -06:00
diegospd 024b7db81e fix(docs): correct tranditional typo (#4104) 2018-01-12 16:39:13 -06:00
David Muto 5b1dbec9b0 feature(docker-image): allow custom nginx port within container (#4086)
* Use PORT to override nginx listen port when defined
* Add dockerignore to make build context smaller
2018-01-12 13:52:09 -06:00
Lukasr29 64f99356a5 improve(tio): extract file name from Content-Disposition (#4035)
* Added extraction of quoted file name from content disposition header

* Added extraction of quoted file name from content disposition header - PR Fixes

* Added extraction of quoted file name from content disposition header - PR Fixes

* Added extraction of quoted file name from content disposition header - PR Fixes
2018-01-11 19:39:42 -06:00
linelson b3e80cc500 fix(tic-validation): uuid format parameter validation should be case insensitive (#4102)
* fix uuid validation casing (#4099)
* add test
2018-01-11 18:42:37 -06:00
kyle 53ea2003c1 improve(npm-module): include standalone preset in swagger-ui (#4101) 2018-01-11 18:39:13 -06:00
Udit Desai a037c7205b improve(docker-image): symlink swagger.json from app directory to nginx (#4024) 2018-01-11 17:06:56 -06:00
kyle 63d5f164c6 fix(core-components): make onToggle a non-required prop in ModelCollapse (#4100) 2018-01-11 16:56:36 -06:00
Helen Kosova 2e816e0440 fix(docs): broken link in the issue template (#4094) 2018-01-10 18:32:33 -06:00
kyle 0818882a90 v3.9.0 (#4078) 2018-01-06 02:27:11 -06:00
kyle 0751022b77 bug(polyfiller): check for Promise on global/window object 2018-01-06 00:19:29 -06:00
kyle fc070408f6 docs(contributing): add merging pull requests section (#4076) 2018-01-05 22:34:04 -06:00
David DE CARVALHO a47da98b54 feat(render/parameters): display enums, defaults, and examples when not in TIO mode 2018-01-05 22:09:32 -06:00
narutowyh 12a9fbc0ca fix: ignore prototype's keys in a handful of for...in loops 2018-01-05 21:57:17 -06:00
Juan J. Jimenez-Anca bd41b736a8 feat: display last error when definition/config load fails
Contains a breaking internal API change to `errActions.newThrownErr`, which will require a MINOR version bump. Swagger-Editor does not depend on the old usage style.
2018-01-05 21:45:21 -06:00
kyle a8f204181e Merge pull request #4075 from jenglish404/patch-2
Make parameter description styles consistent
2018-01-05 20:51:23 -06:00
kyle 67d472acc7 Merge branch 'master' into patch-2 2018-01-05 20:07:03 -06:00
kyle aebcfe8e5d Merge pull request #4073 from champjss/master
Change nginx configuration in Docker container based on BASE_URL, instead of moving actual files
2018-01-05 19:23:24 -06:00
jenglish404 71c0742735 Make parameter description styles consistent
Remove .swagger-ui .parameters-col_description p formatting from _table.scss. With this in place, the parameter descriptions are bold and a larger font for request parameters. This is inconsistent with how response values are displayed.
2018-01-05 15:17:22 -08:00
Jatesadakarn Saengrat 8e4f94b33f Change nginx configuration based on BASE_URL, instead of moving actual files 2018-01-05 18:56:30 +07:00
kyle a9ea10da2c Merge pull request #4037 from scop/zopflipng
Run *.png through zopflipng -m
2018-01-03 18:58:49 -06:00
kyle 8f3535b5ff Merge branch 'master' into zopflipng 2018-01-03 18:55:00 -06:00
kyle 0ca0b8120f Merge pull request #4067 from shockey/master
Add try/catch protection for statePlugin interfaces
2018-01-03 16:54:27 -06:00
kyle 603c436427 Merge branch 'master' into master 2018-01-03 16:37:15 -06:00
Kyle Shockey c601f9b0ca Add try/catch protection for statePlugin interfaces 2018-01-03 13:59:48 -06:00
kyle c742df9573 Merge pull request #4066 from swagger-api/the-year-is-now-2018
Happy new year 🎉
2018-01-02 15:36:12 -06:00
kyle ecfda531de Happy new year 🎉 2018-01-02 15:33:04 -06:00
kyle b749ea737d Merge pull request #4062 from shockey/master
v3.8.1
2017-12-30 01:39:49 -06:00
Kyle Shockey 248aeca0db v3.8.1 2017-12-30 01:33:23 -06:00
kyle adf03e49ae Merge pull request #4060 from shockey/master
Refactor `specPath` to Immutable.js data structures to avoid waste renders
2017-12-28 18:55:02 -06:00
Kyle Shockey 9bad35ed85 Refactor specPath to Im data structures to avoid waste renders 2017-12-28 18:04:12 -06:00
kyle ccc3b109a7 Merge pull request #4059 from shockey/master
Refactor `afterLoad` interface to expose raw plugin context
2017-12-28 16:49:58 -06:00
Kyle Shockey 56caeec8c8 Add documentation for rootInjects interface 2017-12-28 16:44:20 -06:00
Kyle Shockey 9d48c4751a Refactor afterLoad interface to expose raw plugin context 2017-12-28 16:26:18 -06:00
kyle 1646b270f8 Merge pull request #4052 from shockey/v/3.8.0
v3.8.0
2017-12-23 02:03:30 -06:00
Kyle Shockey 03f80dcc70 v3.8.0 2017-12-23 01:58:57 -06:00
kyle f982eb4894 Merge pull request #4049 from shockey/support/editor-validation-refactor
Supporting changes for Swagger-Editor semantic validation changes
2017-12-23 00:25:30 -06:00
kyle 10b4f5d8a7 Merge branch 'master' into support/editor-validation-refactor 2017-12-23 00:18:26 -06:00
kyle 9c2f9d2059 Merge pull request #4051 from shockey/bug/4040-default-tag
Don't parse non-method path keys as operations
2017-12-22 23:39:38 -06:00
kyle 7469705bef Merge branch 'master' into bug/4040-default-tag 2017-12-22 23:35:54 -06:00
Kyle Shockey 57db23ed1f Don't parse non-method path keys as operations 2017-12-22 23:30:38 -06:00
kyle 135fc34077 Merge pull request #4050 from shockey/bug/editor-1596
Prevent non-object values from being dispatched as new JSON content
2017-12-22 23:25:26 -06:00
Kyle Shockey ca0b248df2 Prevent non-object values from being dispatched as new JSON content 2017-12-22 23:19:10 -06:00
Kyle Shockey 27156bb79d Linter fixes 2017-12-22 22:58:37 -06:00
Kyle Shockey b03bf90664 Allow initialState key deletion 2017-12-22 22:41:53 -06:00
Kyle Shockey a9ebdb8a7a Add layout back to initalState 2017-12-22 22:29:55 -06:00
Kyle Shockey 68c689e491 Type-check errors before iterating over them 2017-12-20 13:59:22 -06:00
Kyle Shockey 150ebf3bc0 Add ability to explicitly skip rendering 2017-12-19 23:46:09 -06:00
Kyle Shockey e07b105b29 Remove layout from default initial state 2017-12-19 23:45:35 -06:00
Kyle Shockey 022b660b8c Merge branch 'master' into support/editor-validation-refactor 2017-12-19 23:29:59 -06:00
Ville Skyttä 8c4b511227 Run *.png through zopflipng -m 2017-12-16 22:42:14 +02:00
kyle 7693dfe5bc Merge pull request #4036 from shockey/v/3.7.0
v3.7.0
2017-12-16 00:36:21 -08:00
Kyle Shockey 7faf79ae38 Fix deepLinking scroll behavior 2017-12-16 00:28:53 -08:00
Kyle Shockey 9b5accc17f v3.7.0 2017-12-16 00:10:01 -08:00
kyle d0ffb2bb9d Merge pull request #4010 from heldersepu/master
Add defaultModelsExpandDepth
2017-12-15 18:31:03 -08:00
Kyle Shockey 87572a856b Remove redundant changes to dist/index.html
This is not necessary, since all of these options are the same
as the defaults
2017-12-15 18:25:37 -08:00
Kyle Shockey 292639de57 Undo changes to dev-helpers/index.html 2017-12-15 18:24:43 -08:00
kyle 15dbc20efd Merge branch 'master' into master 2017-12-15 18:22:07 -08:00
kyle 5e72f68738 Merge pull request #4020 from heldersepu/patch-1
Update response-body.jsx to support SVG images
2017-12-15 17:29:44 -08:00
kyle 2b3a590e29 Merge branch 'master' into patch-1 2017-12-15 17:26:53 -08:00
kyle 0413410a1d Merge pull request #3244 from swagger-api/feature/small-tweaks
Some small tweaks
2017-12-15 17:26:31 -08:00
Kyle Shockey 49333dfadc Merge branch 'master' of github.com:swagger-api/swagger-ui into feature/small-tweaks 2017-12-15 17:22:03 -08:00
Kyle Shockey d65d7b3dd7 Revert /dist to contents of master 2017-12-15 17:21:49 -08:00
kyle e594392920 Merge pull request #3980 from thompsongl/ft/deeplinking-link-component
ft/deeplinking-link-component
2017-12-15 17:08:11 -08:00
kyle 48fd8f7689 Merge branch 'master' into ft/deeplinking-link-component 2017-12-15 17:05:22 -08:00
Kyle Shockey a2f8d289a0 Fix error transformer imports 2017-12-15 14:17:12 -08:00
Kyle Shockey 2dd25abbdd Move initOAuth attacher out of core constructor 2017-12-15 14:16:44 -08:00
Kyle Shockey c1ae4c133d Add afterLoad plugin interface 2017-12-14 21:01:37 -08:00
Kyle Shockey 85c83931c4 Move configs plugin to core 2017-12-14 20:17:31 -08:00
Kyle Shockey b5c713a72b Eliminate require.context usage 2017-12-14 20:17:31 -08:00
Kyle Shockey ca7f7ea78b Stop using require.context in error transformers 2017-12-14 20:17:31 -08:00
kyle bb54eaa127 Merge pull request #4019 from swagger-api/swagger-ui/master
Do not display undocumenetd when there is a default response
2017-12-14 15:43:25 -08:00
Greg Thompson a41cdfd650 Merge branch 'master' into ft/deeplinking-link-component 2017-12-13 08:32:57 -06:00
Gonen Dukas 4320118f17 Merge branch 'master' into master 2017-12-13 14:51:43 +02:00
kyle 7f19d791f8 Merge pull request #4026 from swagger-api/ft/docs
Clarify wrapComponent interface in Plugin API docs
2017-12-12 15:55:23 -08:00
kyle 9147d2eac2 Merge branch 'master' into ft/docs 2017-12-12 15:49:23 -08:00
kyle 1613455bc4 Merge pull request #4006 from MartinNowak/patch-1
fix example integration
2017-12-12 15:49:05 -08:00
Helder Sepulveda 6ba6039eb7 Merge branch 'master' into master 2017-12-12 17:48:40 -05:00
Helder Sepulveda f3cb0d7195 Merge branch 'master' into patch-1 2017-12-12 17:44:50 -05:00
kyle 9034e3fcdf Merge branch 'master' into patch-1 2017-12-12 14:35:06 -08:00
kyle a9ee23413d Merge branch 'master' into ft/docs 2017-12-12 14:34:27 -08:00
kyle 9a7ef85aa4 Clarify wrapComponent interface in Plugin API docs 2017-12-12 14:33:41 -08:00
kyle 1b5c060834 Merge pull request #4013 from heldersepu/bug/loading
Show loading message when we are loading
2017-12-12 13:38:32 -08:00
Kyle Shockey dfa96e66a4 Improve a handful of error surfacing messages
`Spec` => `API definition`, notably.
2017-12-12 13:34:25 -08:00
Kyle Shockey 00f4fb3326 Add margin to loading container 2017-12-12 13:25:36 -08:00
Kyle Shockey 3db4f6c93a Wrap BaseLayout loading messages in swagger-ui+loading-container 2017-12-12 13:24:42 -08:00
Kyle Shockey 0864ca849f Use throbber in Standalone layout; center loading messages and content 2017-12-12 13:07:18 -08:00
kyle faa9ada6b8 class => className 2017-12-12 12:31:55 -08:00
Greg Thompson 0e883d4a7a Merge remote-tracking branch 'upstream/master' into ft/deeplinking-link-component 2017-12-12 09:18:12 -06:00
Helder Sepulveda b2a435afdc Replace text with Loading throbber 2017-12-12 07:41:33 -05:00
kyle 5c17153df1 Merge branch 'master' into master 2017-12-11 20:29:02 -08:00
kyle a967be9438 Merge branch 'master' into patch-1 2017-12-11 19:21:26 -08:00
kyle aaf62a4e60 Merge branch 'master' into patch-1 2017-12-11 19:20:29 -08:00
kyle 91ed3baae7 Merge branch 'master' into bug/loading 2017-12-11 19:18:52 -08:00
kyle 2f32056fae Merge pull request #3812 from swagger-api/feature/deeplink-zenscroll
Add zenscroll instead of scroll-to
2017-12-11 19:18:20 -08:00
kyle af14cc9e78 Add missing comma 2017-12-11 19:09:00 -08:00
kyle a94c6c0e7f Merge branch 'master' into feature/deeplink-zenscroll 2017-12-11 19:05:16 -08:00
Greg Thompson f819612144 Merge branch 'master' into ft/deeplinking-link-component 2017-12-11 14:28:28 -06:00
Greg Thompson d2e931f3e1 DeepLink feedback 2017-12-11 14:27:28 -06:00
Helder Sepulveda 8becc9a9a7 Update response-body.jsx
Add support for svg images, with this change we will show an actual preview of the image
2017-12-11 15:14:58 -05:00
Gonen Dukas a5d1853557 Do not display undocumenetd when there is a default response 2017-12-11 17:49:16 +02:00
Helder Sepulveda fb8c3e46e7 Merge branch 'master' into master 2017-12-11 08:51:02 -05:00
Helder Sepulveda 58b64294c8 Merge branch 'master' into bug/loading 2017-12-11 08:50:34 -05:00
Helder Sepulveda d7857052c7 Merge branch 'master' into master 2017-12-09 11:23:43 -05:00
Helder Sepulveda 040d648e64 Merge branch 'master' into bug/loading 2017-12-09 11:22:49 -05:00
HelderSepu 67b2b716e4 Show loading message when we are loading 2017-12-09 11:12:41 -05:00
HelderSepu 28c706c70a Add defaultModelsExpandDepth
This change separates:
the Model (the one on the model-example section)
from
the Models (the ones at the bottom)
2017-12-08 21:12:41 -05:00
Greg Thompson 05fd5b354d update test 2017-12-08 10:20:55 -06:00
Greg Thompson f21601b0e8 Merge branch 'master' into ft/deeplinking-link-component 2017-12-08 09:54:58 -06:00
Martin Nowak 08b8f9c35a fix example integration 2017-12-08 13:22:25 +01:00
Greg Thompson 5bc8ba3694 Merge branch 'master' into ft/deeplinking-link-component 2017-12-04 14:15:36 -06:00
Greg Thompson 661b60fade add isShown prop 2017-12-04 14:14:28 -06:00
Josh Ponelat 780c5f1b83 Add conditionals to ModelWrapper testing for layout props 2017-12-04 16:11:02 +02:00
Josh Ponelat 41ae8242b6 fix test 2017-12-04 16:10:43 +02:00
Josh Ponelat c260263e4e fix issues with deeplink conflicts 2017-12-04 16:10:43 +02:00
Liam Shaw 80ec610485 add zen scroll 2017-12-04 16:10:43 +02:00
Josh Ponelat 3893cb4396 deep linking for models, operations and tags 2017-12-04 16:10:42 +02:00
Josh Ponelat f6b86d2d33 Refactor layout .show/.isShown to handle branch nodes 2017-12-04 16:09:24 +02:00
Greg Thompson 0b8cd7e32f add presentational component to allow for wrapping deeplinking links 2017-12-01 14:30:37 -06:00
Josh Ponelat 3681e86fad build 2017-10-25 09:49:07 +02:00
Josh Ponelat 5db6a1d0c9 add guards to specSelectors 2017-10-25 09:39:44 +02:00
Josh Ponelat 1c6f8e8327 refactor specSelectors.allowTryItOutFor 2017-10-25 09:39:44 +02:00
Josh Ponelat c6f354ae6c clean up spec actions a bit 2017-10-25 09:39:44 +02:00
Josh Ponelat e8772fbe2a fix typo in error message 2017-10-25 09:39:40 +02:00
Josh Ponelat 22732e8cc5 change : {} to fromJS({}) in param-body.jsx 2017-10-25 09:39:40 +02:00
Josh Ponelat a14b4c8039 Extend AST traversal to include string pointer 2017-10-25 09:39:40 +02:00
Josh Ponelat 84cc9489d4 fix typo with clear_validate_params 2017-10-25 09:39:31 +02:00
Josh Ponelat 2a4c8b9377 add .aignore 2017-10-25 09:39:31 +02:00
83 changed files with 1022 additions and 390 deletions
+1
View File
@@ -0,0 +1 @@
dist/
+8
View File
@@ -9,6 +9,10 @@
[
"module-alias",
[
{
"expose": "root",
"src": "."
},
{
"expose": "components",
"src": "src/core/components"
@@ -17,6 +21,10 @@
"expose": "core",
"src": "src/core"
},
{
"expose": "plugins",
"src": "src/plugins"
},
{
"expose": "img",
"src": "src/img"
+7
View File
@@ -0,0 +1,7 @@
/.git
/.github
/dev-helpers
/docs
/src
/swagger-ui-dist-package
/test
+1 -1
View File
@@ -18,7 +18,7 @@ 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/version-detection.md
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
--->
+2
View File
@@ -4,6 +4,8 @@
!package.json
!dist/swagger-ui.js
!dist/swagger-ui.js.map
!dist/swagger-ui-standalone-preset.js
!dist/swagger-ui-standalone-preset.js.map
!dist/swagger-ui.css
!dist/swagger-ui.css.map
!dist/oauth2-redirect.html
+4
View File
@@ -52,3 +52,7 @@ _Adapted from [How to Write a Git Commit Message](https://chris.beams.io/posts/g
- **Don't** include any changes to files in the `dist/` directory - we update those files only during releases.
- **Don't** mention maintainers in your original PR body - we probably would've seen it anyway, so it just increases the noise in our inboxes. Do feel free to ping maintainers if a week has passed and you've heard nothing from us.
- **Don't** open PRs for custom functionality that only serves a small subset of our users - custom functionality should be implemented outside of our codebase, via a plugin.
#### Merging pull requests
- **Do** use GitHub's `Squash and merge` strategy.
- **Do** follow the [Conventional Commits](https://conventionalcommits.org) standard format for your squash commit.
+1 -1
View File
@@ -12,7 +12,7 @@ ENV OAUTH_REALM "**None**"
ENV OAUTH_APP_NAME "**None**"
ENV OAUTH_ADDITIONAL_PARAMS "**None**"
ENV SWAGGER_JSON "/app/swagger.json"
ENV PORT 80
ENV PORT 8080
ENV BASE_URL ""
RUN apk add --update nginx
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright 2017 SmartBear Software
Copyright 2018 SmartBear Software
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+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.6.1 | 2017-12-01 | 2.0, 3.0 | [tag v3.6.1](https://github.com/swagger-api/swagger-ui/tree/v3.6.1)
3.9.1 | 2018-01-12 | 2.0, 3.0 | [tag v3.9.1](https://github.com/swagger-api/swagger-ui/tree/v3.9.1)
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)
+15 -15
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
+2 -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
+6 -8
View File
@@ -2,6 +2,7 @@
set -e
BASE_URL=${BASE_URL:-/}
NGINX_ROOT=/usr/share/nginx/html
INDEX_FILE=$NGINX_ROOT/index.html
@@ -22,13 +23,7 @@ replace_or_delete_in_index () {
}
if [ "${BASE_URL}" ]; then
NGINX_WITH_BASE_URL="${NGINX_ROOT}${BASE_URL}"
mkdir -p ${NGINX_WITH_BASE_URL}
mv ${NGINX_ROOT}/*.* ${NGINX_WITH_BASE_URL}/
INDEX_FILE=$NGINX_WITH_BASE_URL/index.html
NGINX_ROOT=$NGINX_WITH_BASE_URL
sed -i "s|location .* {|location $BASE_URL {|g" /etc/nginx/nginx.conf
fi
replace_in_index myApiKeyXXXX123456789 $API_KEY
@@ -41,7 +36,7 @@ if [ "$OAUTH_ADDITIONAL_PARAMS" != "**None**" ]; then
fi
if [[ -f $SWAGGER_JSON ]]; then
cp $SWAGGER_JSON $NGINX_ROOT
cp -s $SWAGGER_JSON $NGINX_ROOT
REL_PATH="./$(basename $SWAGGER_JSON)"
sed -i "s|http://petstore.swagger.io/v2/swagger.json|$REL_PATH|g" $INDEX_FILE
sed -i "s|http://example.com/api|$REL_PATH|g" $INDEX_FILE
@@ -58,4 +53,7 @@ if [[ -n "$VALIDATOR_URL" ]]; then
unset TMP_VU
fi
# replace the PORT that nginx listens on if supplied
if [[ -n "${PORT}" ]]; then sed -i "s|8080|${PORT}|g" /etc/nginx/nginx.conf; fi
exec nginx -g 'daemon off;'
+81 -4
View File
@@ -19,7 +19,9 @@ A plugin return value may contain any of these keys, where `myStateKey` is a nam
},
components: {},
wrapComponents: {},
fn: {}
rootInjects: {},
afterLoad: (system) => {},
fn: {},
}
```
@@ -293,7 +295,7 @@ const MyWrapSelectorsPlugin = function(system) {
Wrap Components allow you to override a component registered within the system.
Wrap Components are function factories with the signature `(OriginalComponent, system) => props => ReactElement`.
Wrap Components are function factories with the signature `(OriginalComponent, system) => props => ReactElement`. If you'd prefer to provide a React component class, `(OriginalComponent, system) => ReactClass` works as well.
```javascript
const MyWrapBuiltinComponentPlugin = function(system) {
@@ -310,9 +312,12 @@ const MyWrapBuiltinComponentPlugin = function(system) {
}
```
```javascript
// Overriding a component from a plugin
Here's another example that includes a code sample of a component that will be wrapped:
```javascript
///// Overriding a component from a plugin
// Here's our normal, unmodified component.
const MyNumberDisplayPlugin = function(system) {
return {
components: {
@@ -321,6 +326,7 @@ const MyNumberDisplayPlugin = function(system) {
}
}
// Here's a component wrapper defined as a function.
const MyWrapComponentPlugin = function(system) {
return {
wrapComponents: {
@@ -328,6 +334,7 @@ const MyWrapComponentPlugin = function(system) {
if(props.number > 10) {
return <div>
<h3>Warning! Big number ahead.</h3>
<OriginalComponent {...props} />
</div>
} else {
return <Original {...props} />
@@ -336,6 +343,76 @@ const MyWrapComponentPlugin = function(system) {
}
}
}
// Alternatively, here's the same component wrapper defined as a class.
const MyWrapComponentPlugin = function(system) {
return {
wrapComponents: {
NumberDisplay: (Original, system) => class WrappedNumberDisplay extends React.component {
render() {
if(props.number > 10) {
return <div>
<h3>Warning! Big number ahead.</h3>
<OriginalComponent {...props} />
</div>
} else {
return <Original {...props} />
}
}
}
}
}
}
```
##### `rootInjects`
The `rootInjects` interface allows you to inject values at the top level of the system.
This interface takes an object, which will be merged in with the top-level system object at runtime.
```js
const MyRootInjectsPlugin = function(system) {
return {
rootInjects: {
myConstant: 123,
myMethod: (...params) => console.log(...params)
}
}
}
```
##### `afterLoad`
The `afterLoad` plugin method allows you to get a reference to the system after your plugin has been registered.
This interface is used in the core code to attach methods that are driven by bound selectors or actions. You can also use it to execute logic that requires your plugin to already be ready, for example fetching initial data from a remote endpoint and passing it to an action your plugin creates.
The plugin context, which is bound to `this`, is undocumented, but below is an example of how to attach a bound action as a top-level method:
```javascript
const MyMethodProvidingPlugin = function() {
return {
afterLoad(system) {
// at this point in time, your actions have been bound into the system
// so you can do things with them
this.rootInjects = this.rootInjects || {}
this.rootInjects.myMethod = system.exampleActions.updateFavoriteColor
},
statePlugins: {
example: {
actions: {
updateFavoriteColor: (str) => {
return {
type: "EXAMPLE_SET_FAV_COLOR",
payload: str
}
}
}
}
}
}
}
```
##### fn
Binary file not shown.

Before

Width:  |  Height:  |  Size: 390 KiB

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 297 KiB

After

Width:  |  Height:  |  Size: 126 KiB

+3 -2
View File
@@ -49,8 +49,9 @@ Parameter Name | Description
--- | ---
`deepLinking` | `Boolean=false`. If set to `true`, enables deep linking for tags and operations. See the [Deep Linking documentation](/docs/usage/deep-linking.md) for more information.
`displayOperationId` | `Boolean=false`. Controls the display of operationId in operations list. The default is `false`.
`defaultModelExpandDepth` | `Number=1`. The default expansion depth for models.
`defaultModelRendering` | `String=["example"*, "model"]`. Controls how models are shown when the API is first rendered. (The user can always switch the rendering for a given model by clicking the 'Model' and 'Example Value' links.)
`defaultModelsExpandDepth` | `Number=1`. The default expansion depth for models (set to -1 completely hide the models).
`defaultModelExpandDepth` | `Number=1`. The default expansion depth for the model on the model-example section.
`defaultModelRendering` | `String=["example"*, "model"]`. Controls how the model is shown when the API is first rendered. (The user can always switch the rendering for a given model by clicking the 'Model' and 'Example Value' links.)
`displayRequestDuration` | `Boolean=false`. Controls the display of the request duration (in milliseconds) for Try-It-Out requests.
`docExpansion` | `String=["list"*, "full", "none"]`. Controls the default expansion setting for the operations and tags. It can be 'list' (expands only the tags), 'full' (expands the tags and operations) or 'none' (expands nothing).
`filter` | `Boolean=false OR String`. If set, enables filtering. The top bar will show an edit box that you can use to filter the tagged operations that are shown. Can be Boolean to enable or disable, or a string, in which case filtering will be enabled using that string as the filter expression. Filtering is case sensitive matching the filter expression anywhere inside the tag.
+3 -3
View File
@@ -37,7 +37,7 @@ app.listen(3000)
```
The module also exports `SwaggerUIBundle` and `SwaggerUIStandalonePreset`, so
if you're in a JavaScript project that can't handle a tranditional npm module,
if you're in a JavaScript project that can't handle a traditional npm module,
you could do something like this:
```js
@@ -49,7 +49,7 @@ const ui = SwaggerUIBundle({
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIBundle.SwaggerUIStandalonePreset
]
],
layout: "StandaloneLayout"
})
```
@@ -86,7 +86,7 @@ This will serve Swagger UI at `/swagger` instead of `/`.
You can embed Swagger-UI's code directly in your HTML by using unkpg's interface:
```html
<script src="//unpkg.com/swagger-ui-dist@3/swagger-ui-bundle.js">
<script src="//unpkg.com/swagger-ui-dist@3/swagger-ui-bundle.js"></script>
<!-- `SwaggerUIBundle` is now available on the page -->
```
+3 -2
View File
@@ -15,10 +15,11 @@ http {
server {
listen 8080;
server_name localhost;
index index.html index.htm;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
alias /usr/share/nginx/html/;
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
+5 -3
View File
@@ -1,6 +1,6 @@
{
"name": "swagger-ui",
"version": "3.6.1",
"version": "3.9.1",
"main": "dist/swagger-ui.js",
"repository": "git@github.com:swagger-api/swagger-ui.git",
"contributors": [
@@ -67,6 +67,7 @@
"react-height": "^2.0.0",
"react-hot-loader": "1.3.1",
"react-immutable-proptypes": "2.1.0",
"react-immutable-pure-component": "^1.1.1",
"react-markdown": "^2.5.0",
"react-motion": "^0.5.2",
"react-object-inspector": "0.2.1",
@@ -81,13 +82,14 @@
"scroll-to-element": "^2.0.0",
"serialize-error": "2.0.0",
"shallowequal": "0.2.2",
"swagger-client": "^3.4.1",
"swagger-client": "^3.4.5",
"url-parse": "^1.1.8",
"whatwg-fetch": "0.11.1",
"worker-loader": "^0.7.1",
"xml": "1.0.1",
"xml-but-prettier": "^1.0.1",
"yaml-js": "0.2.0"
"yaml-js": "0.2.0",
"zenscroll": "4.0.0"
},
"devDependencies": {
"autoprefixer": "7.1.1",
+14 -3
View File
@@ -1,5 +1,6 @@
import React, { Component } from "react"
import PropTypes from "prop-types"
import ImPropTypes from "react-immutable-proptypes"
const propStyle = { color: "#999", fontStyle: "italic" }
@@ -12,7 +13,7 @@ export default class ArrayModel extends Component {
name: PropTypes.string,
required: PropTypes.bool,
expandDepth: PropTypes.number,
specPath: PropTypes.array.isRequired,
specPath: ImPropTypes.list.isRequired,
depth: PropTypes.number
}
@@ -39,7 +40,7 @@ export default class ArrayModel extends Component {
*/
return <span className="model">
<ModelCollapse title={titleEl} collapsed={ depth > expandDepth } collapsedContent="[...]">
<ModelCollapse title={titleEl} expanded={ depth <= expandDepth } collapsedContent="[...]">
[
{
properties.size ? properties.entrySeq().map( ( [ key, v ] ) => <Property key={`${key}-${v}`} propKey={ key } propVal={ v } propStyle={ propStyle } />) : null
@@ -48,7 +49,17 @@ export default class ArrayModel extends Component {
!description ? null :
<Markdown source={ description } />
}
<span><Model { ...this.props } getConfigs={ getConfigs } specPath={[...specPath, "items"]} name={null} schema={ items } required={ false } depth={ depth + 1 } /></span>
<span>
<Model
{ ...this.props }
getConfigs={ getConfigs }
specPath={specPath.push("items")}
name={null}
schema={ items }
required={ false }
depth={ depth + 1 }
/>
</span>
]
</ModelCollapse>
</span>
+20
View File
@@ -0,0 +1,20 @@
import React from "react"
import PropTypes from "prop-types"
export const DeepLink = ({ enabled, path, text }) => {
return (
<a className="nostyle"
onClick={enabled ? (e) => e.preventDefault() : null}
href={enabled ? `#/${path}` : null}>
<span>{text}</span>
</a>
)
}
DeepLink.propTypes = {
enabled: PropTypes.bool,
isShown: PropTypes.bool,
path: PropTypes.string,
text: PropTypes.string
}
export default DeepLink
+3
View File
@@ -57,6 +57,9 @@ export class Col extends React.Component {
let classesAr = []
for (let device in DEVICES) {
if (!DEVICES.hasOwnProperty(device)) {
continue
}
let deviceClass = DEVICES[device]
if(device in this.props) {
let val = this.props[device]
+12 -1
View File
@@ -61,7 +61,18 @@ export default class BaseLayout extends React.Component {
const isSpecEmpty = !specSelectors.specStr()
if(isSpecEmpty) {
return <h4>No spec provided.</h4>
let loadingMessage
if(isLoading) {
loadingMessage = <div className="loading"></div>
} else {
loadingMessage = <h4>No API definition provided.</h4>
}
return <div className="swagger-ui">
<div className="loading-container">
{loadingMessage}
</div>
</div>
}
return (
+27 -10
View File
@@ -4,31 +4,48 @@ import PropTypes from "prop-types"
export default class ModelCollapse extends Component {
static propTypes = {
collapsedContent: PropTypes.any,
collapsed: PropTypes.bool,
expanded: PropTypes.bool,
children: PropTypes.any,
title: PropTypes.element
title: PropTypes.element,
modelName: PropTypes.string,
onToggle: PropTypes.func
}
static defaultProps = {
collapsedContent: "{...}",
collapsed: true,
title: null
expanded: false,
title: null,
onToggle: () => {}
}
constructor(props, context) {
super(props, context)
let { collapsed, collapsedContent } = this.props
let { expanded, collapsedContent } = this.props
this.state = {
collapsed: collapsed !== undefined ? collapsed : ModelCollapse.defaultProps.collapsed,
expanded : expanded,
collapsedContent: collapsedContent || ModelCollapse.defaultProps.collapsedContent
}
}
componentWillReceiveProps(nextProps){
if(this.props.expanded!= nextProps.expanded){
this.setState({expanded: nextProps.expanded})
}
}
toggleCollapsed=()=>{
if(this.props.onToggle){
this.props.onToggle(this.props.modelName,!this.state.expanded)
}
this.setState({
collapsed: !this.state.collapsed
expanded: !this.state.expanded
})
}
@@ -38,10 +55,10 @@ export default class ModelCollapse extends Component {
<span>
{ title && <span onClick={this.toggleCollapsed} style={{ "cursor": "pointer" }}>{title}</span> }
<span onClick={ this.toggleCollapsed } style={{ "cursor": "pointer" }}>
<span className={ "model-toggle" + ( this.state.collapsed ? " collapsed" : "" ) }></span>
<span className={ "model-toggle" + ( this.state.expanded ? "" : " collapsed" ) }></span>
</span>
{ this.state.collapsed ? this.state.collapsedContent : this.props.children }
{ this.state.expanded ? this.props.children :this.state.collapsedContent }
</span>
)
}
}
}
+2 -1
View File
@@ -1,5 +1,6 @@
import React from "react"
import PropTypes from "prop-types"
import ImPropTypes from "react-immutable-proptypes"
export default class ModelExample extends React.Component {
static propTypes = {
@@ -9,7 +10,7 @@ export default class ModelExample extends React.Component {
example: PropTypes.any.isRequired,
isExecute: PropTypes.bool,
getConfigs: PropTypes.func.isRequired,
specPath: PropTypes.array.isRequired,
specPath: ImPropTypes.list.isRequired,
}
constructor(props, context) {
+22 -3
View File
@@ -1,22 +1,41 @@
import React, { Component, } from "react"
import PropTypes from "prop-types"
//import layoutActions from "actions/layout"
export default class ModelWrapper extends Component {
export default class ModelComponent extends Component {
static propTypes = {
schema: PropTypes.object.isRequired,
name: PropTypes.string,
getComponent: PropTypes.func.isRequired,
getConfigs: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired,
expandDepth: PropTypes.number
expandDepth: PropTypes.number,
layoutActions: PropTypes.object,
layoutSelectors: PropTypes.object.isRequired
}
onToggle = (name,isShown) => {
// If this prop is present, we'll have deepLinking for it
if(this.props.layoutActions) {
this.props.layoutActions.show(["models", name],isShown)
}
}
render(){
let { getComponent, getConfigs } = this.props
const Model = getComponent("Model")
let expanded
if(this.props.layoutSelectors) {
// If this is prop is present, we'll have deepLinking for it
expanded = this.props.layoutSelectors.isShown(["models",this.props.name])
}
return <div className="model-box">
<Model { ...this.props } getConfigs={ getConfigs } depth={ 1 } expandDepth={ this.props.expandDepth || 0 }/>
<Model { ...this.props } getConfigs={ getConfigs } expanded={expanded} depth={ 1 } onToggle={ this.onToggle } expandDepth={ this.props.expandDepth || 0 }/>
</div>
}
}
+4 -3
View File
@@ -1,8 +1,9 @@
import React, { PureComponent } from "react"
import React from "react"
import ImmutablePureComponent from "react-immutable-pure-component"
import ImPropTypes from "react-immutable-proptypes"
import PropTypes from "prop-types"
export default class Model extends PureComponent {
export default class Model extends ImmutablePureComponent {
static propTypes = {
schema: ImPropTypes.orderedMap.isRequired,
getComponent: PropTypes.func.isRequired,
@@ -13,7 +14,7 @@ export default class Model extends PureComponent {
required: PropTypes.bool,
expandDepth: PropTypes.number,
depth: PropTypes.number,
specPath: PropTypes.array.isRequired,
specPath: ImPropTypes.list.isRequired,
}
getModelName =( ref )=> {
+13 -9
View File
@@ -1,4 +1,5 @@
import React, { Component } from "react"
import Im from "immutable"
import PropTypes from "prop-types"
export default class Models extends Component {
@@ -13,15 +14,15 @@ export default class Models extends Component {
render(){
let { specSelectors, getComponent, layoutSelectors, layoutActions, getConfigs } = this.props
let definitions = specSelectors.definitions()
let { docExpansion, defaultModelExpandDepth } = getConfigs()
let showModels = layoutSelectors.isShown("models", docExpansion === "full" || docExpansion === "list" )
let { docExpansion, defaultModelsExpandDepth } = getConfigs()
if (!definitions.size || defaultModelsExpandDepth < 0) return null
let showModels = layoutSelectors.isShown("models", defaultModelsExpandDepth > 0 && docExpansion !== "none")
const specPathBase = specSelectors.isOAS3() ? ["components", "schemas"] : ["definitions"]
const ModelWrapper = getComponent("ModelWrapper")
const Collapse = getComponent("Collapse")
if (!definitions.size) return null
return <section className={ showModels ? "models is-open" : "models"}>
<h4 onClick={() => layoutActions.show("models", !showModels)}>
<span>Models</span>
@@ -32,14 +33,17 @@ export default class Models extends Component {
<Collapse isOpened={showModels}>
{
definitions.entrySeq().map( ( [ name, model ])=>{
return <div className="model-container" key={ `models-section-${name}` }>
return <div id={ `model-${name}` } className="model-container" key={ `models-section-${name}` }>
<ModelWrapper name={ name }
expandDepth={ defaultModelExpandDepth }
expandDepth={ defaultModelsExpandDepth }
schema={ model }
specPath={[...specPathBase, name]}
specPath={Im.List([...specPathBase, name])}
getComponent={ getComponent }
getConfigs={ getConfigs }
specSelectors={ specSelectors }/>
specSelectors={ specSelectors }
getConfigs = {getConfigs}
layoutSelectors = {layoutSelectors}
layoutActions = {layoutActions}/>
</div>
}).toArray()
}
+18 -10
View File
@@ -1,6 +1,7 @@
import React, { Component, } from "react"
import PropTypes from "prop-types"
import { List } from "immutable"
import ImPropTypes from "react-immutable-proptypes"
const braceOpen = "{"
const braceClose = "}"
@@ -10,18 +11,19 @@ export default class ObjectModel extends Component {
schema: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired,
getConfigs: PropTypes.func.isRequired,
expanded: PropTypes.bool,
onToggle: PropTypes.func,
specSelectors: PropTypes.object.isRequired,
name: PropTypes.string,
isRef: PropTypes.bool,
expandDepth: PropTypes.number,
depth: PropTypes.number,
specPath: PropTypes.object.isRequired
specPath: ImPropTypes.list.isRequired
}
render(){
let { schema, name, isRef, getComponent, getConfigs, depth, specPath, expandDepth, ...otherProps } = this.props
let { specSelectors } = otherProps
let { schema, name, isRef, getComponent, getConfigs, depth, onToggle, expanded, specPath, ...otherProps } = this.props
let { specSelectors,expandDepth } = otherProps
const { isOAS3 } = specSelectors
if(!schema) {
@@ -61,7 +63,13 @@ export default class ObjectModel extends Component {
</span>
return <span className="model">
<ModelCollapse title={titleEl} collapsed={ depth > expandDepth } collapsedContent={ collapsedContent }>
<ModelCollapse
modelName={name}
title={titleEl}
onToggle = {onToggle}
expanded={ expanded ? true : depth <= expandDepth }
collapsedContent={ collapsedContent }>
<span className="brace-open object">{ braceOpen }</span>
{
!isRef ? null : <JumpToPathSection />
@@ -95,7 +103,7 @@ export default class ObjectModel extends Component {
<Model key={ `object-${name}-${key}_${value}` } { ...otherProps }
required={ isRequired }
getComponent={ getComponent }
specPath={[...specPath, "properties", key]}
specPath={specPath.push("properties", key)}
getConfigs={ getConfigs }
schema={ value }
depth={ depth + 1 } />
@@ -134,7 +142,7 @@ export default class ObjectModel extends Component {
<td>
<Model { ...otherProps } required={ false }
getComponent={ getComponent }
specPath={[...specPath, "additionalProperties"]}
specPath={specPath.push("additionalProperties")}
getConfigs={ getConfigs }
schema={ additionalProperties }
depth={ depth + 1 } />
@@ -149,7 +157,7 @@ export default class ObjectModel extends Component {
{anyOf.map((schema, k) => {
return <div key={k}><Model { ...otherProps } required={ false }
getComponent={ getComponent }
specPath={[...specPath, "anyOf", k]}
specPath={specPath.push("anyOf", k)}
getConfigs={ getConfigs }
schema={ schema }
depth={ depth + 1 } /></div>
@@ -165,7 +173,7 @@ export default class ObjectModel extends Component {
{oneOf.map((schema, k) => {
return <div key={k}><Model { ...otherProps } required={ false }
getComponent={ getComponent }
specPath={[...specPath, "oneOf", k]}
specPath={specPath.push("oneOf", k)}
getConfigs={ getConfigs }
schema={ schema }
depth={ depth + 1 } /></div>
@@ -182,7 +190,7 @@ export default class ObjectModel extends Component {
<Model { ...otherProps }
required={ false }
getComponent={ getComponent }
specPath={[...specPath, "not"]}
specPath={specPath.push("not")}
getConfigs={ getConfigs }
schema={ not }
depth={ depth + 1 } />
+13 -12
View File
@@ -2,11 +2,12 @@ import React, { PureComponent } from "react"
import PropTypes from "prop-types"
import { getList } from "core/utils"
import { getExtensions, sanitizeUrl } from "core/utils"
import { Iterable } from "immutable"
import { Iterable, List } from "immutable"
import ImPropTypes from "react-immutable-proptypes"
export default class Operation extends PureComponent {
static propTypes = {
specPath: PropTypes.array.isRequired,
specPath: ImPropTypes.list.isRequired,
operation: PropTypes.instanceOf(Iterable).isRequired,
response: PropTypes.instanceOf(Iterable),
request: PropTypes.instanceOf(Iterable),
@@ -33,7 +34,7 @@ export default class Operation extends PureComponent {
operation: null,
response: null,
request: null,
specPath: []
specPath: List()
}
render() {
@@ -102,12 +103,13 @@ export default class Operation extends PureComponent {
const Schemes = getComponent( "schemes" )
const OperationServers = getComponent( "OperationServers" )
const OperationExt = getComponent( "OperationExt" )
const DeepLink = getComponent( "DeepLink" )
const { showExtensions } = getConfigs()
// Merge in Live Response
if(responses && response && response.size > 0) {
let notDocumented = !responses.get(String(response.get("status")))
let notDocumented = !responses.get(String(response.get("status"))) && !responses.get("default")
response = response.set("notDocumented", notDocumented)
}
@@ -120,12 +122,11 @@ export default class Operation extends PureComponent {
and pulled in with getComponent */}
<span className="opblock-summary-method">{method.toUpperCase()}</span>
<span className={ deprecated ? "opblock-summary-path__deprecated" : "opblock-summary-path" } >
<a
className="nostyle"
onClick={isDeepLinkingEnabled ? (e) => e.preventDefault() : null}
href={isDeepLinkingEnabled ? `#/${isShownKey.join("/")}` : null}>
<span>{path}</span>
</a>
<DeepLink
enabled={isDeepLinkingEnabled}
isShown={isShown}
path={`${isShownKey.join("/")}`}
text={path} />
<JumpToPath path={specPath} /> {/*TODO: use wrapComponents here, swagger-ui doesn't care about jumpToPath */}
</span>
@@ -174,7 +175,7 @@ export default class Operation extends PureComponent {
<Parameters
parameters={parameters}
specPath={[...specPath, "parameters"]}
specPath={specPath.push("parameters")}
operation={operation}
onChangeKey={onChangeKey}
onTryoutClick = { onTryoutClick }
@@ -248,7 +249,7 @@ export default class Operation extends PureComponent {
specActions={ specActions }
produces={ produces }
producesValue={ operation.get("produces_value") }
specPath={[...specPath, "responses"]}
specPath={specPath.push("responses")}
path={ path }
method={ method }
displayRequestDuration={ displayRequestDuration }
+8 -7
View File
@@ -1,5 +1,6 @@
import React from "react"
import PropTypes from "prop-types"
import Im from "immutable"
import { createDeepLinkPath, sanitizeUrl } from "core/utils"
const SWAGGER2_OPERATION_METHODS = [
@@ -37,6 +38,7 @@ export default class Operations extends React.Component {
const OperationContainer = getComponent("OperationContainer", true)
const Collapse = getComponent("Collapse")
const Markdown = getComponent("Markdown")
const DeepLink = getComponent("DeepLink")
let {
docExpansion,
@@ -79,12 +81,11 @@ export default class Operations extends React.Component {
onClick={() => layoutActions.show(isShownKey, !showTag)}
className={!tagDescription ? "opblock-tag no-desc" : "opblock-tag" }
id={isShownKey.join("-")}>
<a
className="nostyle"
onClick={isDeepLinkingEnabled ? (e) => e.preventDefault() : null}
href= {isDeepLinkingEnabled ? `#/${tag}` : null}>
<span>{tag}</span>
</a>
<DeepLink
enabled={isDeepLinkingEnabled}
isShown={showTag}
path={tag}
text={tag} />
{ !tagDescription ? null :
<small>
<Markdown source={tagDescription} />
@@ -119,7 +120,7 @@ export default class Operations extends React.Component {
operations.map( op => {
const path = op.get("path")
const method = op.get("method")
const specPath = ["paths", path, method]
const specPath = Im.List(["paths", path, method])
// FIXME: (someday) this logic should probably be in a selector,
+1 -1
View File
@@ -47,7 +47,7 @@ export default class ParamBody extends PureComponent {
updateValues = (props) => {
let { specSelectors, pathMethod, param, isExecute, consumesValue="" } = props
let parameter = specSelectors ? specSelectors.getParameter(pathMethod, param.get("name"), param.get("in")) : {}
let parameter = specSelectors ? specSelectors.getParameter(pathMethod, param.get("name"), param.get("in")) : fromJS({})
let isXml = /xml/i.test(consumesValue)
let isJson = /json/i.test(consumesValue)
let paramValue = isXml ? parameter.get("value_xml") : parameter.get("value")
+50 -2
View File
@@ -1,6 +1,7 @@
import React, { Component } from "react"
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"
@@ -15,7 +16,7 @@ export default class ParameterRow extends Component {
specSelectors: PropTypes.object.isRequired,
pathMethod: PropTypes.array.isRequired,
getConfigs: PropTypes.func.isRequired,
specPath: PropTypes.array.isRequired,
specPath: ImPropTypes.list.isRequired
}
constructor(props, context) {
@@ -107,6 +108,34 @@ export default class ParameterRow extends Component {
let value = parameter ? parameter.get("value") : ""
let extensions = getExtensions(param)
let paramItems // undefined
let paramItemsEnum // undefined
let isDisplayParamItemsEnum = false
if ( param !== undefined ) {
paramItems = param.get("items")
}
if ( paramItems !== undefined ) {
paramItemsEnum = param.get("items").get("enum")
}
if ( paramItemsEnum !== undefined ) {
if (paramItemsEnum.size > 0) {
isDisplayParamItemsEnum = true
}
}
// Default and Example Value for readonly doc
let paramDefaultValue // undefined
let paramExample // undefined
if ( param !== undefined ) {
paramDefaultValue = param.get("default")
paramExample = param.get("example")
}
if (isDisplayParamItemsEnum) { // if we have an array, default value is in "items"
paramDefaultValue = paramItems.get("default")
}
return (
<tr>
<td className="col parameters-col_name">
@@ -124,6 +153,25 @@ export default class ParameterRow extends Component {
<td className="col parameters-col_description">
<Markdown source={ param.get("description") }/>
{ (bodyParam || !isExecute) && isDisplayParamItemsEnum ?
<Markdown source={
"<i>Available values</i> : " + paramItemsEnum.map(function(item) {
return item
}).toArray()}/>
: null
}
{ (bodyParam || !isExecute) && paramDefaultValue !== undefined ?
<Markdown source={"<i>Default value</i> : " + paramDefaultValue}/>
: null
}
{ (bodyParam || !isExecute) && paramExample !== undefined ?
<Markdown source={"<i>Example</i> : " + paramExample}/>
: null
}
{(isFormData && !isFormDataSupported) && <div>Error: your browser does not support FormData</div>}
{ bodyParam || !isExecute ? null
@@ -139,7 +187,7 @@ export default class ParameterRow extends Component {
{
bodyParam && schema ? <ModelExample getComponent={ getComponent }
specPath={[...specPath, "schema"]}
specPath={specPath.push("schema")}
getConfigs={ getConfigs }
isExecute={ isExecute }
specSelectors={ specSelectors }
+2 -2
View File
@@ -21,7 +21,7 @@ export default class Parameters extends Component {
onChangeKey: PropTypes.array,
pathMethod: PropTypes.array.isRequired,
getConfigs: PropTypes.func.isRequired,
specPath: PropTypes.array.isRequired,
specPath: ImPropTypes.list.isRequired,
}
@@ -98,7 +98,7 @@ export default class Parameters extends Component {
eachMap(parameters, (parameter, i) => (
<ParameterRow
fn={ fn }
specPath={[...specPath, i]}
specPath={specPath.push(i.toString())}
getComponent={ getComponent }
getConfigs={ getConfigs }
param={ parameter }
+11 -5
View File
@@ -2,6 +2,7 @@ import React from "react"
import PropTypes from "prop-types"
import formatXml from "xml-but-prettier"
import lowerCase from "lodash/lowerCase"
import { extractFileNameFromContentDispositionHeader } from "core/utils"
export default class ResponseBody extends React.Component {
@@ -43,7 +44,11 @@ export default class ResponseBody extends React.Component {
// Image
} else if (/^image\//i.test(contentType)) {
bodyEl = <img style={{ maxWidth: "100%" }} src={ window.URL.createObjectURL(content) } />
if(contentType.includes("svg")) {
bodyEl = <div> { content } </div>
} else {
bodyEl = <img style={{ maxWidth: "100%" }} src={ window.URL.createObjectURL(content) } />
}
// Audio
} else if (/^audio\//i.test(contentType)) {
@@ -66,12 +71,13 @@ export default class ResponseBody extends React.Component {
let fileName = url.substr(url.lastIndexOf("/") + 1)
let download = [type, fileName, href].join(":")
// Use filename from response header
// Use filename from response header,
// First check if filename is quoted (e.g. contains space), if no, fallback to not quoted check
let disposition = headers["content-disposition"] || headers["Content-Disposition"]
if (typeof disposition !== "undefined") {
let responseFilename = /filename=([^;]*);?/i.exec(disposition)
if (responseFilename !== null && responseFilename.length > 1) {
download = responseFilename[1]
let responseFilename = extractFileNameFromContentDispositionHeader(disposition)
if (responseFilename !== null) {
download = responseFilename
}
}
+5 -4
View File
@@ -1,7 +1,8 @@
import React from "react"
import PropTypes from "prop-types"
import ImPropTypes from "react-immutable-proptypes"
import cx from "classnames"
import { fromJS, Seq, Iterable } from "immutable"
import { fromJS, Seq, Iterable, List } from "immutable"
import { getSampleSchema, fromJSOrdered } from "core/utils"
const getExampleComponent = ( sampleResponse, examples, HighlightCode ) => {
@@ -47,7 +48,7 @@ export default class Response extends React.Component {
getComponent: PropTypes.func.isRequired,
getConfigs: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired,
specPath: PropTypes.array.isRequired,
specPath: ImPropTypes.list.isRequired,
fn: PropTypes.object.isRequired,
contentType: PropTypes.string,
controlsAcceptHeader: PropTypes.bool,
@@ -99,7 +100,7 @@ export default class Response extends React.Component {
var schema, specPathWithPossibleSchema
if(isOAS3()) {
const schemaPath = ["content", this.state.responseContentType, "schema"]
const schemaPath = List(["content", this.state.responseContentType, "schema"])
const oas3SchemaForContentType = response.getIn(schemaPath)
sampleResponse = oas3SchemaForContentType ? getSampleSchema(oas3SchemaForContentType.toJS(), this.state.responseContentType, {
includeReadOnly: true
@@ -108,7 +109,7 @@ export default class Response extends React.Component {
specPathWithPossibleSchema = oas3SchemaForContentType ? schemaPath : specPath
} else {
schema = inferSchema(response.toJS()) // TODO: don't convert back and forth. Lets just stick with immutable for inferSchema
specPathWithPossibleSchema = response.has("schema") ? [...specPath, "schema"] : specPath
specPathWithPossibleSchema = response.has("schema") ? specPath.push("schema") : specPath
sampleResponse = schema ? getSampleSchema(schema, contentType, {
includeReadOnly: true,
includeWriteOnly: true // writeOnly has no filtering effect in swagger 2.0
+4 -3
View File
@@ -1,6 +1,7 @@
import React from "react"
import PropTypes from "prop-types"
import { fromJS, Iterable } from "immutable"
import PropTypes from "prop-types"
import ImPropTypes from "react-immutable-proptypes"
import { defaultStatusCode, getAcceptControllingResponse } from "core/utils"
export default class Responses extends React.Component {
@@ -17,7 +18,7 @@ export default class Responses extends React.Component {
specSelectors: PropTypes.object.isRequired,
specActions: PropTypes.object.isRequired,
oas3Actions: PropTypes.object.isRequired,
specPath: PropTypes.array.isRequired,
specPath: ImPropTypes.list.isRequired,
fn: PropTypes.object.isRequired
}
@@ -120,7 +121,7 @@ export default class Responses extends React.Component {
let className = tryItOutResponse && tryItOutResponse.get("status") == code ? "response_current" : ""
return (
<Response key={ code }
specPath={[...specPath, code]}
specPath={specPath.push(code)}
isDefault={defaultCode === code}
fn={fn}
className={ className }
+2 -2
View File
@@ -1,5 +1,6 @@
import React, { PureComponent } from "react"
import PropTypes from "prop-types"
import ImPropTypes from "react-immutable-proptypes"
import { helpers } from "swagger-client"
import { Iterable, fromJS } from "immutable"
@@ -31,8 +32,7 @@ export default class OperationContainer extends PureComponent {
request: PropTypes.instanceOf(Iterable),
security: PropTypes.instanceOf(Iterable),
isDeepLinkingEnabled: PropTypes.bool.isRequired,
specPath: PropTypes.array.isRequired,
specPath: ImPropTypes.list.isRequired,
getComponent: PropTypes.func.isRequired,
authActions: PropTypes.object,
oas3Actions: PropTypes.object,
+18 -2
View File
@@ -47,6 +47,7 @@ module.exports = function SwaggerUI(opts) {
showMutatedRequest: true,
defaultModelRendering: "example",
defaultModelExpandDepth: 1,
defaultModelsExpandDepth: 1,
showExtensions: false,
// Initial set of plugins ( TODO rename this, or refactor - we don't need presets _and_ plugins. Its just there for performance.
@@ -91,6 +92,20 @@ module.exports = function SwaggerUI(opts) {
}, constructorConfig.initialState)
}
if(constructorConfig.initialState) {
// if the user sets a key as `undefined`, that signals to us that we
// should delete the key entirely.
// known usage: Swagger-Editor validate plugin tests
for (var key in constructorConfig.initialState) {
if(
constructorConfig.initialState.hasOwnProperty(key)
&& constructorConfig.initialState[key] === undefined
) {
delete storeConfigs.state[key]
}
}
}
let inlinePlugin = ()=> {
return {
fn: constructorConfig.fn,
@@ -104,8 +119,6 @@ module.exports = function SwaggerUI(opts) {
var system = store.getSystem()
system.initOAuth = system.authActions.configureAuth
const downloadSpec = (fetchedConfig) => {
if(typeof constructorConfig !== "object") {
return system
@@ -137,6 +150,9 @@ module.exports = function SwaggerUI(opts) {
} else if(mergedConfig.dom_id) {
let domNode = document.querySelector(mergedConfig.dom_id)
system.render(domNode, "App")
} else if(mergedConfig.dom_id === null || mergedConfig.domNode === null) {
// do nothing
// this is useful for testing that does not need to do any rendering
} else {
console.error("Skipped rendering: no `dom_id` or `domNode` was specified")
}
+1 -1
View File
@@ -46,7 +46,7 @@ export default function authorize ( { auth, authActions, errActions, configs, au
authId: name,
source: "validation",
level: "error",
message: "oauth2RedirectUri configuration is not passed. Oauth2 authorization cannot be performed."
message: "oauth2RedirectUrl configuration is not passed. Oauth2 authorization cannot be performed."
})
return
}
+33 -15
View File
@@ -103,30 +103,30 @@ export function positionRangeForPath(yaml, path) {
let ast = cachedCompose(yaml)
// simply walks the tree using current path recursively to the point that
// simply walks the tree using astValue path recursively to the point that
// path is empty.
return find(ast)
function find(current) {
if (current.tag === MAP_TAG) {
for (i = 0; i < current.value.length; i++) {
var pair = current.value[i]
function find(astValue, astKeyValue) {
if (astValue.tag === MAP_TAG) {
for (i = 0; i < astValue.value.length; i++) {
var pair = astValue.value[i]
var key = pair[0]
var value = pair[1]
if (key.value === path[0]) {
path.shift()
return find(value)
return find(value, key)
}
}
}
if (current.tag === SEQ_TAG) {
var item = current.value[path[0]]
if (astValue.tag === SEQ_TAG) {
var item = astValue.value[path[0]]
if (item && item.tag) {
path.shift()
return find(item)
return find(item, astKeyValue)
}
}
@@ -135,17 +135,35 @@ export function positionRangeForPath(yaml, path) {
return invalidRange
}
return {
/* jshint camelcase: false */
const range = {
start: {
line: current.start_mark.line,
column: current.start_mark.column
line: astValue.start_mark.line,
column: astValue.start_mark.column,
pointer: astValue.start_mark.pointer,
},
end: {
line: current.end_mark.line,
column: current.end_mark.column
line: astValue.end_mark.line,
column: astValue.end_mark.column,
pointer: astValue.end_mark.pointer,
}
}
if(astKeyValue) {
// eslint-disable-next-line camelcase
range.key_start = {
line: astKeyValue.start_mark.line,
column: astKeyValue.start_mark.column,
pointer: astKeyValue.start_mark.pointer,
}
// eslint-disable-next-line camelcase
range.key_end = {
line: astKeyValue.end_mark.line,
column: astKeyValue.end_mark.column,
pointer: astKeyValue.end_mark.pointer,
}
}
return range
}
}
+4
View File
@@ -5,6 +5,10 @@ import * as specWrapActionReplacements from "./spec-wrap-actions"
export default function() {
return {
afterLoad(system) {
this.rootInjects = this.rootInjects || {}
this.rootInjects.initOAuth = system.authActions.configureAuth
},
statePlugins: {
auth: {
reducers,
@@ -1,5 +1,5 @@
import YAML from "js-yaml"
import yamlConfig from "../../../swagger-config.yaml"
import yamlConfig from "root/swagger-config.yaml"
import * as actions from "./actions"
import * as selectors from "./selectors"
import reducers from "./reducers"
@@ -1,8 +1,7 @@
import scrollTo from "scroll-to-element"
import zenscroll from "zenscroll"
import { escapeDeepLinkPath } from "core/utils"
const SCROLL_OFFSET = -5
let hasHashBeenParsed = false
let hasHashBeenParsed = false //TODO this forces code to only run once which may prevent scrolling if page not refreshed
export const updateResolved = (ori, { layoutActions, getConfigs }) => (...args) => {
@@ -12,7 +11,6 @@ export const updateResolved = (ori, { layoutActions, getConfigs }) => (...args)
if(!isDeepLinkingEnabled || isDeepLinkingEnabled === "false") {
return
}
if(window.location.hash && !hasHashBeenParsed ) {
let hash = window.location.hash.slice(1) // # is first character
@@ -30,21 +28,35 @@ export const updateResolved = (ori, { layoutActions, getConfigs }) => (...args)
let [tag, operationId] = hash.split("/")
let swaggerUI = document.querySelector(".swagger-ui")
let myScroller = zenscroll.createScroller(swaggerUI)
let target
if(tag && operationId) {
// Pre-expand and scroll to the operation
layoutActions.show(["operations-tag", tag], true)
layoutActions.show(["operations", tag, operationId], true)
scrollTo(`#operations-${escapeDeepLinkPath(tag)}-${escapeDeepLinkPath(operationId)}`, {
offset: SCROLL_OFFSET
})
target = document
.getElementById(`operations-${escapeDeepLinkPath(tag)}-${escapeDeepLinkPath(operationId)}`)
} else if(tag) {
// Pre-expand and scroll to the tag
layoutActions.show(["operations-tag", tag], true)
scrollTo(`#operations-tag-${escapeDeepLinkPath(tag)}`, {
offset: SCROLL_OFFSET
})
target = document.getElementById(`operations-tag-${escapeDeepLinkPath(tag)}`)
}
if(target) {
myScroller.to(target)
setTimeout(() => {
// Backup functionality: if we're still at the top of the document,
// scroll on the entire page (not within the Swagger-UI container)
if(zenscroll.getY() === 0) {
zenscroll.to(target)
}
}, 50)
}
}
+1 -1
View File
@@ -26,7 +26,7 @@ export default function downloadUrlPlugin (toolbox) {
function next(res) {
if(res instanceof Error || res.status >= 400) {
specActions.updateLoadingStatus("failed")
return errActions.newThrownErr( new Error(res.statusText + " " + url) )
return errActions.newThrownErr( new Error((res.message || res.statusText) + " " + url) )
}
specActions.updateLoadingStatus("success")
specActions.updateSpec(res.text)
+2 -2
View File
@@ -7,10 +7,10 @@ export const NEW_SPEC_ERR_BATCH = "err_new_spec_err_batch"
export const NEW_AUTH_ERR = "err_new_auth_err"
export const CLEAR = "err_clear"
export function newThrownErr(err, action) {
export function newThrownErr(err) {
return {
type: NEW_THROWN_ERR,
payload: { action, error: serializeError(err) }
payload: serializeError(err)
}
}
@@ -1,26 +1,13 @@
import reduce from "lodash/reduce"
let request = require.context("./transformers/", true, /\.js$/)
let errorTransformers = []
import * as NotOfType from "./transformers/not-of-type"
import * as ParameterOneOf from "./transformers/parameter-oneof"
import * as StripInstance from "./transformers/strip-instance"
request.keys().forEach( function( key ){
if( key === "./hook.js" ) {
return
}
if( !key.match(/js$/) ) {
return
}
if( key.slice(2).indexOf("/") > -1) {
// skip files in subdirs
return
}
errorTransformers.push({
name: toTitleCase(key).replace(".js", "").replace("./", ""),
transform: request(key).transform
})
})
const errorTransformers = [
NotOfType,
ParameterOneOf,
StripInstance
]
export default function transformErrors (errors, system) {
let inputs = {
@@ -47,10 +34,3 @@ export default function transformErrors (errors, system) {
})
}
function toTitleCase(str) {
return str
.split("-")
.map(substr => substr[0].toUpperCase() + substr.slice(1))
.join("")
}
-14
View File
@@ -37,17 +37,3 @@ export function changeMode(thing, mode="") {
payload: {thing, mode}
}
}
// export function onlyShow(thing, shown=true) {
// thing = normalizeArray(thing)
// if(thing.length < 2)
// throw new Error("layoutActions.onlyShow only works, when `thing` is an array with length > 1")
// return {
// type: ONLY_SHOW,
// payload: {thing, shown}
// }
// }
+9 -4
View File
@@ -1,3 +1,4 @@
import { fromJS } from "immutable"
import {
UPDATE_LAYOUT,
UPDATE_FILTER,
@@ -12,9 +13,14 @@ export default {
[UPDATE_FILTER]: (state, action) => state.set("filter", action.payload),
[SHOW]: (state, action) => {
let thing = action.payload.thing
let shown = action.payload.shown
return state.setIn(["shown"].concat(thing), shown)
const isShown = action.payload.shown
// This is one way to serialize an array, another (preferred) is to convert to json-pointer
// TODO: use json-pointer serilization instead of fromJS(...), for performance
const thingToShow = fromJS(action.payload.thing)
// This is a map of paths to bools
// eg: [one, two] => true
// eg: [one] => false
return state.update("shown", fromJS({}), a => a.set(thingToShow, isShown))
},
[UPDATE_MODE]: (state, action) => {
@@ -24,4 +30,3 @@ export default {
}
}
+2 -2
View File
@@ -1,5 +1,6 @@
import { createSelector } from "reselect"
import { normalizeArray } from "core/utils"
import { fromJS } from "immutable"
const state = state => state
@@ -9,7 +10,7 @@ export const currentFilter = state => state.get("filter")
export const isShown = (state, thing, def) => {
thing = normalizeArray(thing)
return Boolean(state.getIn(["shown", ...thing], def))
return state.get("shown", fromJS({})).get(fromJS(thing), def)
}
export const whatMode = (state, thing, def="") => {
@@ -21,4 +22,3 @@ export const showSummary = createSelector(
state,
state => !isShown(state, "editor")
)
@@ -38,7 +38,7 @@ const RequestBody = ({
expandDepth={1}
isExecute={isExecute}
schema={mediaTypeValue.get("schema")}
specPath={[...specPath, "content", contentType]}
specPath={specPath.push("content", contentType)}
example={<RequestBodyEditor
requestBody={requestBody}
onChange={onChange}
@@ -72,10 +72,10 @@ export const servers = onlyOAS3(createSelector(
export const isOAS3 = (ori, system) => () => {
const spec = system.getSystem().specSelectors.specJson()
return isOAS3Helper(spec)
return isOAS3Helper(Map.isMap(spec) ? spec : Map())
}
export const isSwagger2 = (ori, system) => () => {
const spec = system.getSystem().specSelectors.specJson()
return isSwagger2Helper(spec)
return isSwagger2Helper(Map.isMap(spec) ? spec : Map())
}
@@ -29,7 +29,7 @@ class Parameters extends Component {
fn: PropTypes.object.isRequired,
tryItOutEnabled: PropTypes.bool,
allowTryItOut: PropTypes.bool,
specPath: PropTypes.array.isRequired,
specPath: ImPropTypes.list.isRequired,
onTryoutClick: PropTypes.func,
onCancelClick: PropTypes.func,
onChangeKey: PropTypes.array,
@@ -107,7 +107,7 @@ class Parameters extends Component {
const { isOAS3 } = specSelectors
const requestBody = operation.get("requestBody")
const requestBodySpecPath = [...specPath.slice(0, -1), "requestBody"] // remove the "parameters" part
const requestBodySpecPath = specPath.slice(0, -1).push("requestBody") // remove the "parameters" part
return (
<div className="opblock-section">
@@ -143,7 +143,7 @@ class Parameters extends Component {
eachMap(parameters, (parameter, i) => (
<ParameterRow fn={ fn }
getComponent={ getComponent }
specPath={[...specPath, i]}
specPath={specPath.push(i)}
getConfigs={ getConfigs }
param={ parameter }
key={ parameter.get( "name" ) }
+3
View File
@@ -181,6 +181,9 @@ export const sampleXmlFromSchema = (schema, config={}) => {
example = example || {}
for (let propName in props) {
if (!props.hasOwnProperty(propName)) {
continue
}
if ( props[propName].readOnly && !includeReadOnly ) {
continue
}
+10 -28
View File
@@ -1,6 +1,7 @@
import YAML from "js-yaml"
import parseUrl from "url-parse"
import serializeError from "serialize-error"
import isString from "lodash/isString"
import { isJSONObject } from "core/utils"
// Actions conform to FSA (flux-standard-actions)
@@ -22,22 +23,16 @@ export const UPDATE_OPERATION_VALUE = "spec_update_operation_value"
export const UPDATE_RESOLVED = "spec_update_resolved"
export const SET_SCHEME = "set_scheme"
export function updateSpec(spec) {
if(spec instanceof Error) {
return {type: UPDATE_SPEC, error: true, payload: spec}
}
const toStr = (str) => isString(str) ? str : ""
export function updateSpec(spec) {
const cleanSpec = (toStr(spec)).replace(/\t/g, " ")
if(typeof spec === "string") {
return {
type: UPDATE_SPEC,
payload: spec.replace(/\t/g, " ") || ""
payload: cleanSpec
}
}
return {
type: UPDATE_SPEC,
payload: ""
}
}
export function updateResolved(spec) {
@@ -52,9 +47,6 @@ export function updateUrl(url) {
}
export function updateJsonSpec(json) {
if(!json || typeof json !== "object") {
throw new Error("updateJson must only accept a simple JSON object")
}
return {type: UPDATE_JSON, payload: json}
}
@@ -76,7 +68,10 @@ export const parseToJson = (str) => ({specActions, specSelectors, errActions}) =
line: e.mark && e.mark.line ? e.mark.line + 1 : undefined
})
}
return specActions.updateJsonSpec(json)
if(json && typeof json === "object") {
return specActions.updateJsonSpec(json)
}
return {}
}
export const resolveSpec = (json, url) => ({specActions, specSelectors, errActions, fn: { fetch, resolve, AST }, getConfigs}) => {
@@ -110,8 +105,7 @@ export const resolveSpec = (json, url) => ({specActions, specSelectors, errActio
errActions.clear({
type: "thrown"
})
if(errors.length > 0) {
if(Array.isArray(errors) && errors.length > 0) {
let preparedErrors = errors
.map(err => {
console.error(err)
@@ -130,18 +124,6 @@ export const resolveSpec = (json, url) => ({specActions, specSelectors, errActio
})
}
export const formatIntoYaml = () => ({specActions, specSelectors}) => {
let { specStr } = specSelectors
let { updateSpec } = specActions
try {
let yaml = YAML.safeDump(YAML.safeLoad(specStr()), {indent: 2})
updateSpec(yaml)
} catch(e) {
updateSpec(e)
}
}
export function changeParam( path, paramName, paramIn, value, isXml ){
return {
type: UPDATE_PARAM,
+14 -2
View File
@@ -4,6 +4,10 @@ import { fromJS, Set, Map, OrderedMap, List } from "immutable"
const DEFAULT_TAG = "default"
const OPERATION_METHODS = [
"get", "put", "post", "delete", "options", "head", "patch", "trace"
]
const state = state => {
return state || Map()
}
@@ -95,6 +99,9 @@ export const operations = createSelector(
return {}
}
path.forEach((operation, method) => {
if(OPERATION_METHODS.indexOf(method) < 0) {
return
}
list = list.push(fromJS({
path: pathName,
method,
@@ -256,10 +263,11 @@ export const allowTryItOutFor = () => {
// Get the parameter value by parameter name
export function getParameter(state, pathMethod, name, inType) {
pathMethod = pathMethod || []
let params = spec(state).getIn(["paths", ...pathMethod, "parameters"], fromJS([]))
return params.filter( (p) => {
return params.find( (p) => {
return Map.isMap(p) && p.get("name") === name && p.get("in") === inType
}).first()
}) || Map() // Always return a map
}
export const hasHost = createSelector(
@@ -272,6 +280,7 @@ export const hasHost = createSelector(
// Get the parameter values, that the user filled out
export function parameterValues(state, pathMethod, isXml) {
pathMethod = pathMethod || []
let params = spec(state).getIn(["paths", ...pathMethod, "parameters"], fromJS([]))
return params.reduce( (hash, p) => {
let value = isXml && p.get("in") === "body" ? p.get("value_xml") : p.get("value")
@@ -295,6 +304,7 @@ export function parametersIncludeType(parameters, typeValue="") {
// Get the consumes/produces value that the user selected
export function contentTypeValues(state, pathMethod) {
pathMethod = pathMethod || []
let op = spec(state).getIn(["paths", ...pathMethod], fromJS({}))
const parameters = op.get("parameters") || new List()
@@ -313,6 +323,7 @@ export function contentTypeValues(state, pathMethod) {
// Get the consumes/produces by path
export function operationConsumes(state, pathMethod) {
pathMethod = pathMethod || []
return spec(state).getIn(["paths", ...pathMethod, "consumes"], fromJS({}))
}
@@ -329,6 +340,7 @@ export const canExecuteScheme = ( state, path, method ) => {
}
export const validateBeforeExecute = ( state, pathMethod ) => {
pathMethod = pathMethod || []
let params = spec(state).getIn(["paths", ...pathMethod, "parameters"], fromJS([]))
let isValid = true
@@ -1,17 +0,0 @@
import { pascalCaseFilename } from "core/utils"
const request = require.context(".", true, /\.jsx?$/)
request.keys().forEach( function( key ){
if( key === "./index.js" ) {
return
}
// if( key.slice(2).indexOf("/") > -1) {
// // skip files in subdirs
// return
// }
let mod = request(key)
module.exports[pascalCaseFilename(key)] = mod.default ? mod.default : mod
})
+4 -3
View File
@@ -1,5 +1,4 @@
import * as components from "./components"
import SplitPaneMode from "./components/split-pane-mode"
export default function SplitPaneModePlugin() {
return {
// statePlugins: {
@@ -9,6 +8,8 @@ export default function SplitPaneModePlugin() {
// }
// },
components,
components: {
SplitPaneMode
}
}
}
+4 -2
View File
@@ -10,7 +10,7 @@ import auth from "core/plugins/auth"
import util from "core/plugins/util"
import SplitPaneModePlugin from "core/plugins/split-pane-mode"
import downloadUrlPlugin from "core/plugins/download-url"
import configsPlugin from "plugins/configs"
import configsPlugin from "core/plugins/configs"
import deepLinkingPlugin from "core/plugins/deep-linking"
import OperationContainer from "core/containers/OperationContainer"
@@ -61,6 +61,7 @@ import PrimitiveModel from "core/components/primitive-model"
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 Markdown from "core/components/providers/markdown"
@@ -121,7 +122,8 @@ export default function() {
OperationExt,
OperationExtRow,
ParameterExt,
OperationContainer
OperationContainer,
DeepLink
}
}
+52 -6
View File
@@ -18,7 +18,6 @@ function createStoreWithMiddleware(rootReducer, initialState, getSystem) {
// createLogger( {
// stateTransformer: state => state && state.toJS()
// } ),
// errorLog(getSystem), Need to properly handle errors that occur during a render. Ie: let them be...
systemThunkMiddleware( getSystem )
]
@@ -68,6 +67,12 @@ export default class Store {
if(rebuild) {
this.buildSystem()
}
const needAnotherRebuild = callAfterLoad.call(this.system, plugins, this.getSystem())
if(needAnotherRebuild) {
this.buildSystem()
}
}
buildSystem(buildReducer=true) {
@@ -170,7 +175,7 @@ export default class Store {
if(!isFn(newAction)) {
throw new TypeError("wrapActions needs to return a function that returns a new function (ie the wrapped action)")
}
return newAction
return wrapWithTryCatch(newAction)
}, action || Function.prototype)
})
}
@@ -250,11 +255,11 @@ export default class Store {
return objMap(obj, (fn) => {
return (...args) => {
let res = fn.apply(null, [getNestedState(), ...args])
let res = wrapWithTryCatch(fn).apply(null, [getNestedState(), ...args])
// If a selector returns a function, give it the system - for advanced usage
if(typeof(res) === "function")
res = res(getSystem())
res = wrapWithTryCatch(res)(getSystem())
return res
}
@@ -320,6 +325,25 @@ function combinePlugins(plugins, toolbox) {
return {}
}
function callAfterLoad(plugins, system, { hasLoaded } = {}) {
let calledSomething = hasLoaded
if(isObject(plugins) && !isArray(plugins)) {
if(typeof plugins.afterLoad === "function") {
calledSomething = true
wrapWithTryCatch(plugins.afterLoad).call(this, system)
}
}
if(isFunc(plugins))
return callAfterLoad.call(this, plugins(system), system, { hasLoaded: calledSomething })
if(isArray(plugins)) {
return plugins.map(plugin => callAfterLoad.call(this, plugin, system, { hasLoaded: calledSomething }))
}
return calledSomething
}
// Wraps deepExtend, to account for certain fields, being wrappers.
// Ie: we need to convert some fields into arrays, and append to them.
// Rather than overwrite
@@ -411,14 +435,36 @@ function makeReducer(reducerObj) {
if(!reducerObj)
return state
let redFn = reducerObj[action.type]
let redFn = (reducerObj[action.type])
if(redFn) {
return redFn(state, action)
const res = wrapWithTryCatch(redFn)(state, action)
// If the try/catch wrapper kicks in, we'll get null back...
// in that case, we want to avoid making any changes to state
return res === null ? state : res
}
return state
}
}
function wrapWithTryCatch(fn, {
logErrors = true
} = {}) {
if(typeof fn !== "function") {
return fn
}
return function(...args) {
try {
return fn.call(this, ...args)
} catch(e) {
if(logErrors) {
console.error(e)
}
return null
}
}
}
function configureStore(rootReducer, initialState, getSystem) {
const store = createStoreWithMiddleware(rootReducer, initialState, getSystem)
+15 -9
View File
@@ -126,15 +126,6 @@ export function systemThunkMiddleware(getSystem) {
}
}
export const errorLog = getSystem => () => next => action => {
try{
next( action )
}
catch( e ) {
getSystem().errActions.newThrownErr( e, action )
}
}
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()
@@ -351,6 +342,17 @@ export function mapToList(map, keyNames="key", collectedKeys=Im.Map()) {
return list
}
export function extractFileNameFromContentDispositionHeader(value){
let responseFilename = /filename="([^;]*);?"/i.exec(value)
if (responseFilename === null) {
responseFilename = /filename=([^;]*);?/i.exec(value)
}
if (responseFilename !== null && responseFilename.length > 1) {
return responseFilename[1]
}
return null
}
// PascalCase, aka UpperCamelCase
export function pascalCase(str) {
return upperFirst(camelCase(str))
@@ -442,6 +444,7 @@ export const validateDateTime = (val) => {
}
export const validateGuid = (val) => {
val = val.toString().toLowerCase()
if (!/^[{(]?[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}[)}]?$/.test(val)) {
return "Value must be a Guid"
}
@@ -616,6 +619,9 @@ export const parseSearch = () => {
let params = search.substr(1).split("&")
for (let i in params) {
if (!params.hasOwnProperty(i)) {
continue
}
i = params[i].split("=")
map[decodeURIComponent(i[0])] = decodeURIComponent(i[1])
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 770 B

After

Width:  |  Height:  |  Size: 458 B

+6 -16
View File
@@ -1,17 +1,7 @@
import { pascalCaseFilename } from "js/utils"
import Configs from "./configs"
import Topbar from "./topbar"
const request = require.context(".", true, /\.jsx?$/)
request.keys().forEach( function( key ){
if( key === "./index.js" ) {
return
}
// if( key.slice(2).indexOf("/") > -1) {
// // skip files in subdirs
// return
// }
let mod = request(key)
module.exports[pascalCaseFilename(key)] = mod.default ? mod.default : mod
})
export default {
Configs,
Topbar
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 770 B

After

Width:  |  Height:  |  Size: 458 B

+3 -1
View File
@@ -1,6 +1,8 @@
// Promise global, Used ( at least ) by 'whatwg-fetch'. And required by IE 11
if(typeof Promise === "undefined") {
import win from "core/window"
if(typeof win.Promise === "undefined") {
require("core-js/fn/promise")
}
+1 -1
View File
@@ -1,6 +1,6 @@
import StandaloneLayout from "./layout"
import TopbarPlugin from "plugins/topbar"
import ConfigsPlugin from "plugins/configs"
import ConfigsPlugin from "corePlugins/configs"
// the Standalone preset
+16 -4
View File
@@ -1,3 +1,5 @@
import React from "react"
import PropTypes from "prop-types"
@@ -14,7 +16,7 @@ export default class StandaloneLayout extends React.Component {
}
render() {
let { getComponent, specSelectors } = this.props
let { getComponent, specSelectors, errSelectors } = this.props
let Container = getComponent("Container")
let Row = getComponent("Row")
@@ -25,6 +27,8 @@ export default class StandaloneLayout extends React.Component {
const OnlineValidatorBadge = getComponent("onlineValidatorBadge", true)
const loadingStatus = specSelectors.loadingStatus()
const lastErr = errSelectors.lastError()
const lastErrMsg = lastErr ? lastErr.get("message") : ""
return (
@@ -32,17 +36,25 @@ export default class StandaloneLayout extends React.Component {
{ Topbar ? <Topbar /> : null }
{ loadingStatus === "loading" &&
<div className="info">
<h4 className="title">Loading...</h4>
<div className="loading-container">
<div className="loading"></div>
</div>
</div>
}
{ loadingStatus === "failed" &&
<div className="info">
<h4 className="title">Failed to load spec.</h4>
<div className="loading-container">
<h4 className="title">Failed to load API definition.</h4>
<p>{lastErrMsg}</p>
</div>
</div>
}
{ loadingStatus === "failedConfig" &&
<div className="info" style={{ maxWidth: "880px", marginLeft: "auto", marginRight: "auto", textAlign: "center" }}>
<h4 className="title">Failed to load config.</h4>
<div className="loading-container">
<h4 className="title">Failed to load remote configuration.</h4>
<p>{lastErrMsg}</p>
</div>
</div>
}
{ !loadingStatus || loadingStatus === "success" && <BaseLayout /> }
+7
View File
@@ -653,6 +653,13 @@
.loading-container
{
padding: 40px 0 60px;
margin-top: 1em;
min-height: 1px;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
.loading
{
position: relative;
-9
View File
@@ -84,15 +84,6 @@ table
.parameters-col_description
{
p
{
font-size: 14px;
margin: 0;
@include text_body();
}
input[type=text]
{
width: 100%;
+3 -3
View File
@@ -32,18 +32,18 @@ describe("<Models/>", function(){
layoutActions: {},
getConfigs: () => ({
docExpansion: "list",
defaultModelExpandDepth: 0
defaultModelsExpandDepth: 0
})
}
it("passes defaultModelExpandDepth to ModelWrapper", function(){
it("passes defaultModelsExpandDepth to ModelWrapper", function(){
// When
let wrapper = shallow(<Models {...props}/>)
// Then should render tabs
expect(wrapper.find("ModelCollapse").length).toEqual(1)
expect(wrapper.find("ModelComponent").length).toBeGreaterThan(0)
expect(wrapper.find("ModelWrapper").length).toBeGreaterThan(0)
wrapper.find("ModelComponent").forEach((modelWrapper) => {
expect(modelWrapper.props().expandDepth).toBe(0)
})
+2 -2
View File
@@ -1,7 +1,7 @@
import React from "react"
import expect from "expect"
import { shallow } from "enzyme"
import { fromJS } from "immutable"
import { fromJS, List } from "immutable"
import ObjectModel from "components/object-model"
import ModelExample from "components/model-example"
import Immutable from "immutable"
@@ -25,7 +25,7 @@ describe("<ObjectModel />", function() {
}
},
isRef : false,
specPath: [],
specPath: List(),
schema: Immutable.fromJS(
{
"properties": {
+2
View File
@@ -3,11 +3,13 @@ import React from "react"
import expect, { createSpy } from "expect"
import { render } from "enzyme"
import { fromJS } from "immutable"
import DeepLink from "components/deep-link"
import Operations from "components/operations"
import {Collapse} from "components/layout-utils"
const components = {
Collapse,
DeepLink,
OperationContainer: ({ path, method }) => <span className="mocked-op" id={`${path}-${method}`} />
}
+367 -72
View File
@@ -571,116 +571,411 @@ describe("bound system", function(){
// Then
expect(renderedComponent.text()).toEqual("This came from mapStateToProps and this came from the system and this came from my own props")
})
})
it("should catch errors thrown inside of React Component Class render methods", function() {
describe("afterLoad", function() {
it("should call a plugin's `afterLoad` method after the plugin is loaded", function() {
// Given
// eslint-disable-next-line react/require-render-return
class BrokenComponent extends React.Component {
render() {
throw new Error("This component is broken")
}
}
const system = new System({
plugins: [
ViewPlugin,
{
components: {
BrokenComponent
afterLoad(system) {
this.rootInjects.wow = system.dogeSelectors.wow
},
statePlugins: {
doge: {
selectors: {
wow: () => (system) => {
return "so selective"
}
}
}
}
}
]
})
// When
var Component = system.getSystem().getComponent("BrokenComponent")
const renderedComponent = render(<Component />)
// Then
expect(renderedComponent.text()).toEqual("😱 Could not render BrokenComponent, see the console.")
var res = system.getSystem().wow()
expect(res).toEqual("so selective")
})
it("should catch errors thrown inside of pure component render methods", function() {
it("should call a preset plugin's `afterLoad` method after the plugin is loaded", function() {
// Given
// eslint-disable-next-line react/require-render-return
class BrokenComponent extends PureComponent {
render() {
throw new Error("This component is broken")
const MyPlugin = {
afterLoad(system) {
this.rootInjects.wow = system.dogeSelectors.wow
},
statePlugins: {
doge: {
selectors: {
wow: () => (system) => {
return "so selective"
}
}
}
}
}
const system = new System({
plugins: [
ViewPlugin,
{
components: {
BrokenComponent
}
}
[MyPlugin]
]
})
// When
var Component = system.getSystem().getComponent("BrokenComponent")
const renderedComponent = render(<Component />)
// Then
expect(renderedComponent.text()).toEqual("😱 Could not render BrokenComponent, see the console.")
var res = system.getSystem().wow()
expect(res).toEqual("so selective")
})
it("should catch errors thrown inside of stateless component functions", function() {
it("should call a function preset plugin's `afterLoad` method after the plugin is loaded", function() {
// Given
// eslint-disable-next-line react/require-render-return
let BrokenComponent = function BrokenComponent() { throw new Error("This component is broken") }
const system = new System({
plugins: [
ViewPlugin,
{
components: {
BrokenComponent
const MyPlugin = {
afterLoad(system) {
this.rootInjects.wow = system.dogeSelectors.wow
},
statePlugins: {
doge: {
selectors: {
wow: () => (system) => {
return "so selective"
}
}
}
]
})
// When
var Component = system.getSystem().getComponent("BrokenComponent")
const renderedComponent = render(<Component />)
// Then
expect(renderedComponent.text().startsWith("😱 Could not render")).toEqual(true)
})
it("should catch errors thrown inside of container components", function() {
// Given
// eslint-disable-next-line react/require-render-return
class BrokenComponent extends React.Component {
render() {
throw new Error("This component is broken")
}
}
const system = new System({
plugins: [
ViewPlugin,
{
components: {
BrokenComponent
}
() => {
return [MyPlugin]
}
]
})
// When
var Component = system.getSystem().getComponent("BrokenComponent", true)
const renderedComponent = render(
<Provider store={system.getStore()}>
<Component />
</Provider>
)
var res = system.getSystem().wow()
expect(res).toEqual("so selective")
})
it("should call a registered plugin's `afterLoad` method after the plugin is loaded", function() {
// Given
const MyPlugin = {
afterLoad(system) {
this.rootInjects.wow = system.dogeSelectors.wow
},
statePlugins: {
doge: {
selectors: {
wow: () => (system) => {
return "so selective"
}
}
}
}
}
// Then
expect(renderedComponent.text()).toEqual("😱 Could not render BrokenComponent, see the console.")
const system = new System({
plugins: []
})
system.register([MyPlugin])
// 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
const ThrowyPlugin = {
afterLoad(system) {
throw new Error("afterLoad BREAKS STUFF!")
},
statePlugins: {
doge: {
selectors: {
wow: () => (system) => {
return "so selective"
}
}
}
}
}
const system = new System({
plugins: []
})
// When
expect(function() {
system.register([ThrowyPlugin])
// var resSystem = system.getSystem()
}).toNotThrow()
})
it("should encapsulate thrown errors in an action creator", function(){
// Given
const system = new System({
plugins: {
statePlugins: {
throw: {
actions: {
func() {
throw new Error("this action creator THROWS!")
}
}
}
}
}
})
expect(function() {
// TODO: fix existing action error catcher that creates THROWN ERR actions
system.getSystem().throwActions.func()
}).toNotThrow()
})
it("should encapsulate thrown errors in a reducer", function(){
// Given
const system = new System({
plugins: {
statePlugins: {
throw: {
actions: {
func: () => {
return {
type: "THROW_FUNC",
payload: "BOOM!"
}
}
},
reducers: {
"THROW_FUNC": (state, action) => {
throw new Error("this reducer EXPLODES!")
}
}
}
}
}
})
expect(function() {
system.getSystem().throwActions.func()
}).toNotThrow()
})
it("should encapsulate thrown errors in a selector", function(){
// Given
const system = new System({
plugins: {
statePlugins: {
throw: {
selectors: {
func: (state, arg1) => {
throw new Error("this selector THROWS!")
}
}
}
}
}
})
expect(system.getSystem().throwSelectors.func).toNotThrow()
})
it("should encapsulate thrown errors in a complex selector", function(){
// Given
const system = new System({
plugins: {
statePlugins: {
throw: {
selectors: {
func: (state, arg1) => system => {
throw new Error("this selector THROWS!")
}
}
}
}
}
})
expect(system.getSystem().throwSelectors.func).toNotThrow()
})
it("should encapsulate thrown errors in a wrapAction", function(){
// Given
const system = new System({
plugins: {
statePlugins: {
throw: {
actions: {
func: () => {
return {
type: "THROW_FUNC",
payload: "this original action does NOT throw"
}
}
},
wrapActions: {
func: (ori) => (...args) => {
throw new Error("this wrapAction UNRAVELS EVERYTHING!")
}
}
}
}
}
})
expect(system.getSystem().throwActions.func).toNotThrow()
})
it("should encapsulate thrown errors in a wrapSelector", function(){
// Given
const system = new System({
plugins: {
statePlugins: {
throw: {
selectors: {
func: (state, arg1) => {
return 123
}
},
wrapSelectors: {
func: (ori) => (...props) => {
return ori(...props)
}
}
}
}
}
})
expect(system.getSystem().throwSelectors.func).toNotThrow()
})
describe("components", function() {
it("should catch errors thrown inside of React Component Class render methods", function() {
// Given
// eslint-disable-next-line react/require-render-return
class BrokenComponent extends React.Component {
render() {
throw new Error("This component is broken")
}
}
const system = new System({
plugins: [
ViewPlugin,
{
components: {
BrokenComponent
}
}
]
})
// When
var Component = system.getSystem().getComponent("BrokenComponent")
const renderedComponent = render(<Component />)
// Then
expect(renderedComponent.text()).toEqual("😱 Could not render BrokenComponent, see the console.")
})
it("should catch errors thrown inside of pure component render methods", function() {
// Given
// eslint-disable-next-line react/require-render-return
class BrokenComponent extends PureComponent {
render() {
throw new Error("This component is broken")
}
}
const system = new System({
plugins: [
ViewPlugin,
{
components: {
BrokenComponent
}
}
]
})
// When
var Component = system.getSystem().getComponent("BrokenComponent")
const renderedComponent = render(<Component />)
// Then
expect(renderedComponent.text()).toEqual("😱 Could not render BrokenComponent, see the console.")
})
it("should catch errors thrown inside of stateless component functions", function() {
// Given
// eslint-disable-next-line react/require-render-return
let BrokenComponent = function BrokenComponent() { throw new Error("This component is broken") }
const system = new System({
plugins: [
ViewPlugin,
{
components: {
BrokenComponent
}
}
]
})
// When
var Component = system.getSystem().getComponent("BrokenComponent")
const renderedComponent = render(<Component />)
// Then
expect(renderedComponent.text().startsWith("😱 Could not render")).toEqual(true)
})
it("should catch errors thrown inside of container components", function() {
// Given
// eslint-disable-next-line react/require-render-return
class BrokenComponent extends React.Component {
render() {
throw new Error("This component is broken")
}
}
const system = new System({
plugins: [
ViewPlugin,
{
components: {
BrokenComponent
}
}
]
})
// When
var Component = system.getSystem().getComponent("BrokenComponent", true)
const renderedComponent = render(
<Provider store={system.getStore()}>
<Component />
</Provider>
)
// Then
expect(renderedComponent.text()).toEqual("😱 Could not render BrokenComponent, see the console.")
})
})
})
})
+23 -1
View File
@@ -18,7 +18,8 @@ import {
getAcceptControllingResponse,
createDeepLinkPath,
escapeDeepLinkPath,
sanitizeUrl
sanitizeUrl,
extractFileNameFromContentDispositionHeader
} from "core/utils"
import win from "core/window"
@@ -90,6 +91,26 @@ describe("utils", function() {
})
describe("extractFileNameFromContentDispositionHeader", function(){
it("should extract quoted filename", function(){
let cdHeader = "attachment; filename=\"file name.jpg\""
let expectedResult = "file name.jpg"
expect(extractFileNameFromContentDispositionHeader(cdHeader)).toEqual(expectedResult)
})
it("should extract filename", function(){
let cdHeader = "attachment; filename=filename.jpg"
let expectedResult = "filename.jpg"
expect(extractFileNameFromContentDispositionHeader(cdHeader)).toEqual(expectedResult)
})
it("should not extract filename and return null", function(){
let cdHeader = "attachment; no file name provided"
let expectedResult = null
expect(extractFileNameFromContentDispositionHeader(cdHeader)).toEqual(expectedResult)
})
})
describe("validateMaximum", function() {
let errorMessage = "Value must be less than Maximum"
@@ -238,6 +259,7 @@ describe("utils", function() {
it("doesn't return for valid guid", function() {
expect(validateGuid("8ce4811e-cec5-4a29-891a-15d1917153c1")).toBeFalsy()
expect(validateGuid("{8ce4811e-cec5-4a29-891a-15d1917153c1}")).toBeFalsy()
expect(validateGuid("8CE4811E-CEC5-4A29-891A-15D1917153C1")).toBeFalsy()
})
it("returns a message for invalid input'", function() {
+1
View File
@@ -67,6 +67,7 @@ module.exports = require("./make-webpack-config")(rules, {
publicPath: "/",
noInfo: true,
hot: true,
disableHostCheck: true, // for development within VMs
stats: {
colors: true
},