Compare commits

...
100 Commits
Author SHA1 Message Date
kyle 774a06606d Merge pull request #3699 from shockey/v/3.2.2
v3.2.2
2017-09-23 14:43:44 -07:00
Kyle Shockey 2d10a740af Rebuild with changes from #3702 2017-09-23 13:09:08 -07:00
Kyle Shockey 45e7a54182 Merge branch 'master' of github.com:swagger-api/swagger-ui into v/3.2.2 2017-09-23 12:58:06 -07:00
kyle 9e418ad68b Merge pull request #3702 from owenconti/bug/3511-query-formData-parameters
Fixes #3511 - 2nd attempt - Query & formData parameters together
2017-09-23 12:57:35 -07:00
Kyle Shockey de61dccff0 Revert "Revert "Merge pull request #3516 from owenconti/bug/3511-query-formData-parameters""
This reverts commit beeb6b608e.
2017-09-23 12:54:17 -07:00
Owen Conti fd00a0489a Remove shouldComponentUpdate from parameter-row component. It will be put back in eventually as part of the performance optimizations once the parameter components are converted to the smart/dumb approach. 2017-09-23 13:10:09 -06:00
Kyle Shockey beeb6b608e Revert "Merge pull request #3516 from owenconti/bug/3511-query-formData-parameters"
This reverts commit 6a7e9d2e88, reversing
changes made to eb01beedfa.
2017-09-23 10:51:32 -07:00
Kyle Shockey c8a9ddbf94 v3.2.2 2017-09-23 00:02:10 -07:00
kyle 1de6010629 Merge pull request #3666 from AlexanderVangelov/master
OAuth2 authorizationUrl allow extra query parameters ('&' vs '?')
2017-09-22 20:49:21 -07:00
kyle b6eb23217f Merge branch 'master' into master 2017-09-22 20:45:07 -07:00
kyle a3f923eec7 Merge pull request #3698 from shockey/heldersepu-master
Heldersepu master
2017-09-22 20:16:08 -07:00
Kyle Shockey 6b59707af1 Fix tests 2017-09-22 19:50:36 -07:00
kyle 827bcd2f36 Merge branch 'master' into master 2017-09-22 19:50:19 -07:00
kyle 6a7e9d2e88 Merge pull request #3516 from owenconti/bug/3511-query-formData-parameters
Fixes #3511 - Query & formData parameters together
2017-09-22 19:27:31 -07:00
HelderSepu e08f31d674 Add UnitTests for validateDateTime & validateGuid 2017-09-22 21:37:33 -04:00
HelderSepu 7d9b56255c Correction to the validateDateTime 2017-09-22 17:44:01 -04:00
Helder Sepulveda b2abf769de Merge branch 'master' into master 2017-09-22 13:04:09 -04:00
HelderSepu 11768b16bf Correct IF for validateDateTime & validateGuid 2017-09-22 11:30:07 -04:00
kyle 54a2d402c6 Merge branch 'master' into master 2017-09-21 00:15:41 -07:00
Owen Conti a1758da87c Merge branch 'master' into bug/3511-query-formData-parameters 2017-09-20 22:37:43 -06:00
kyle eb01beedfa Merge pull request #3691 from shockey/bug/3638-61338ref-in-examples
Remove $$ref values from response examples
2017-09-20 17:42:03 -07:00
kyle deebe1f840 Merge branch 'master' into bug/3638-61338ref-in-examples 2017-09-20 17:36:57 -07:00
Kyle Shockey 69941a6d9a Remove $$ref values from response examples 2017-09-20 17:34:23 -07:00
kyle 6a8f821901 Merge pull request #3690 from shockey/bug/3636-tag-description-markdown
Display Tag descriptions as Markdown
2017-09-20 17:01:45 -07:00
Kyle Shockey b0a580bdc1 Display Tag descriptions as Markdown 2017-09-20 16:55:45 -07:00
Alexander Vangelov 696bdcf961 Merge branch 'master' into master 2017-09-20 13:11:53 -04:00
kyle 6d67c6dabe Merge pull request #3676 from owenconti/bug/3633-primitive-titles
Fixes #3633 - PrimitiveModel titles
2017-09-19 20:17:49 -07:00
kyle 69799b0975 Merge branch 'master' into bug/3633-primitive-titles 2017-09-19 20:14:00 -07:00
HelderSepu 85b7b7241d Add validateDateTime & validateGuid
Initial work to tacke some of the missing validation (Issue #993)
2017-09-19 14:31:27 -04:00
HelderSepu bd6e482df2 Merge remote-tracking branch 'refs/remotes/swagger-api/master' 2017-09-19 13:37:04 -04:00
Owen Conti 7ef8a7ddc7 Merge branch 'master' of github.com:swagger-api/swagger-ui into bug/3511-query-formData-parameters
# Conflicts:
#	src/core/components/parameter-row.jsx
#	src/core/plugins/spec/actions.js
2017-09-18 16:59:08 -06:00
Kyle aa9e008f0b Merge pull request #3679 from swagger-api/shockey-readme-updates
README: npm module clarifications
2017-09-18 15:12:25 -07:00
Kyle b43ac6da44 Update README.md 2017-09-18 13:38:11 -07:00
Kyle cb3e1ed78c Update README.md 2017-09-18 13:36:07 -07:00
Owen Conti 44ece46cd3 Fixes #3633
Make sure PrimitiveModel uses the schema's title first and then falls back to the passed-in `name` property. Added enzyme test for functionality.
2017-09-17 09:49:13 -06:00
Owen Conti f63f022e0c Remove .only from utils test 2017-09-17 09:48:26 -06:00
Owen Conti b855c25406 Slightly increase size available for property names. Change units to em and add a margin to separate the property name from the property type. 2017-09-17 09:48:03 -06:00
Owen Conti 08282205aa Fix property key: value alignment in array-model 2017-09-17 09:45:33 -06:00
Alexander Vangelov 90fa7065f8 Merge branch 'master' into master 2017-09-16 11:24:36 -04:00
Kyle aee5e75913 Merge pull request #3673 from shockey/v/3.2.1
v3.2.1
2017-09-15 21:22:37 -07:00
Kyle Shockey a625177c38 v3.2.1 2017-09-15 21:15:21 -07:00
Kyle a777e9ba91 Merge pull request #3669 from shockey/bug/oas3-accept-controls
OAS3: Accept header control
2017-09-15 20:38:05 -07:00
Kyle Shockey ae5ad484d9 More merge oversights... 2017-09-15 20:33:16 -07:00
Kyle Shockey 6010245210 Roll back win import removal
Lost in merge conflict....
2017-09-15 20:29:49 -07:00
Kyle bd5424308f Merge branch 'master' into bug/oas3-accept-controls 2017-09-15 20:21:27 -07:00
Kyle ef5904f3f0 Merge pull request #3668 from owenconti/test/3667-escaping-utils-tests
Deep link path util tests
2017-09-15 20:19:28 -07:00
Kyle Shockey ef8b37a580 LINTING! 2017-09-15 20:17:35 -07:00
Kyle fa312cb70d Merge branch 'master' into bug/oas3-accept-controls 2017-09-15 20:11:20 -07:00
AlexVangelov c1abdfb1c4 follow lowercase naming convention in tests 2017-09-15 21:04:38 -04:00
AlexVangelov 5bf6d2ff0b Merge branches 'master' and 'master' of github.com:AlexanderVangelov/swagger-ui 2017-09-15 20:53:52 -04:00
AlexVangelov 4172e33e04 satisfy project lint rules 2017-09-15 20:52:55 -04:00
Alexander Vangelov 34e8fffb65 Merge branch 'master' into master 2017-09-15 20:40:46 -04:00
Owen Conti 33ee880f36 Add extra check for String types in createDeepLinkPath. Add trim() call on passed-in value in createDeepLinkPath. Added unit tests for new deep link util functions. 2017-09-15 17:17:45 -06:00
Owen Conti acac3ee875 Merge branch 'master' of github.com:swagger-api/swagger-ui 2017-09-15 16:51:10 -06:00
Kyle 11d8a341e7 Merge pull request #3661 from owenconti/bug/3596-permalink-encoding
Fixes #3596 - Deep linking issue with spaces
2017-09-15 14:49:22 -07:00
AlexVangelov 9b1a6141df OAuth2 authorizationUrl with extra query parameters (should include double '?') 2017-09-15 15:57:41 -04:00
Owen Conti 78ed1e60f3 Merge branch 'master' of github.com:swagger-api/swagger-ui 2017-09-14 14:20:10 -06:00
Owen Conti 840912cb56 Handle null value in createDeepLinkPath 2017-09-13 21:38:07 -06:00
Owen Conti a6e070ac76 Replace spaces with underscores when setting the hash value and inserting the ID into the DOM. Escape the deep link path when querying for the DOM element on hash change. 2017-09-13 21:32:46 -06:00
Owen Conti 16364fb81e Merge branch 'master' of github.com:swagger-api/swagger-ui into bug/3596-permalink-encoding 2017-09-13 17:25:38 -06:00
Owen Conti 430e2a970f Fixes #3596
Wrap `isShownKey` values in a function that replaces spaces with underscores. When parsing the hash on route change, replace the spaces in the values with underscores again.
2017-09-13 17:20:37 -06:00
Kyle c7cb902eee Merge pull request #3655 from owenconti/bug/3032-wrong-properties-order
Fixes #3032 - Wrong property order
2017-09-13 12:21:20 -07:00
Owen Conti ef99b7e434 Remove it.only from new test 2017-09-12 23:09:21 -06:00
Owen Conti cade5100e0 Merge branch 'master' of github.com:swagger-api/swagger-ui into bug/3032-wrong-properties-order 2017-09-12 23:07:36 -06:00
Owen Conti cb88cd53b6 Added test for response.jsx to make sure properties are passed to ModelExample component in the correct order 2017-09-12 23:07:11 -06:00
Owen Conti a47cc284f7 Update response.jsx to use already existing, fromJSOrdered function 2017-09-12 23:06:24 -06:00
Kyle Shockey 277cc81a8c OAS3 Accept header control: State-side 2017-09-12 19:39:52 -07:00
Kyle Shockey 26edaa5f0b OAS3 Accept header control: Component-side 2017-09-12 17:22:51 -07:00
Owen Conti 7cd42992f2 Merge branch 'master' of github.com:swagger-api/swagger-ui 2017-09-12 17:14:39 -06:00
Kyle 746e247834 Merge pull request #3650 from owenconti/bug/3490-anyOf-oneOf-not
Fixes #3490 (mostly)
2017-09-11 19:59:00 -07:00
Kyle 8a4d2461d8 Merge branch 'master' into bug/3490-anyOf-oneOf-not 2017-09-11 19:54:59 -07:00
Kyle 72f01b631e Merge branch 'master' into bug/3032-wrong-properties-order 2017-09-11 19:51:02 -07:00
Owen Conti 408e038b2d fromJS does not maintain order of object properties. Use a reviver function with fromJS inside the response.jsx component for the passed down schema prop. 2017-09-11 20:28:58 -06:00
Ron 65ff161c0b Merge pull request #3579 from OpenGov/bug/SWAGGER_JSON-doc-fix
Updated docs for correct usage of SWAGGER_JSON
2017-09-11 05:25:16 -07:00
Ron 93fcf06620 Merge branch 'master' into bug/SWAGGER_JSON-doc-fix 2017-09-11 05:21:10 -07:00
Kyle 23cb5c93e5 Merge pull request #3651 from shockey/v/3.2.0
v3.2.0
2017-09-09 13:33:27 -07:00
Kyle Shockey 560d566d7d v3.2.0 2017-09-09 13:10:34 -07:00
Owen Conti 3f04d2dfc6 Merge branch 'master' into bug/3490-anyOf-oneOf-not 2017-09-09 13:56:22 -06:00
Owen Conti 12c96e3190 Add comment to array-model for to clarify change. Rework logic in Model.render() to fix bug with overriding name and schema from $ref definition. 2017-09-09 13:51:09 -06:00
Kyle 3f9b999e90 Merge pull request #3648 from swagger-api/ft/oas3-tio
OAS3: Try-It-Out
2017-09-08 18:54:25 -07:00
Kyle a2b572872f Merge branch 'master' into ft/oas3-tio 2017-09-08 18:50:14 -07:00
Kyle Shockey 5bc096d32b Fit linter and tests 2017-09-08 18:44:11 -07:00
Owen Conti e9f46e6f5e Working on refactoring of model.jsx 2017-09-08 17:59:41 -06:00
Owen Conti 3b9940567b Fix property names being displayed in array models 2017-09-07 18:28:38 -06:00
Kyle Shockey a58e6eb766 Fix typo in swagger-client dependency 2017-09-06 13:57:11 -07:00
Kyle Shockey e2fcadf6d4 Use Client branch 2017-09-06 13:25:06 -07:00
Kyle Shockey d14ae62aa5 Parse JSON requestBodies so Client can consume them correctly 2017-09-05 15:07:55 -07:00
Kyle f618ed632d Merge pull request #3632 from heldersepu/master
fix #3624
2017-09-05 14:14:06 -07:00
Kyle Shockey 7a2c7d2cdc Squash commit: OAS3 Try-It-Out changes 2017-09-05 13:59:31 -07:00
HelderSepu 947953d087 fix #3624 2017-09-02 19:59:18 -04:00
Kyle de64123d6f Merge pull request #3619 from vanta/patch-1
Typo fix
2017-08-29 12:11:44 -07:00
Krzysztof Wolny 6c03655003 Typo fix 2017-08-28 15:33:54 +02:00
Matthew Seal 54b383a7d2 Updated docs for correct usage of SWAGGER_JSON 2017-08-15 18:02:17 -07:00
Owen Conti 5d7d20ec5e Merge branch 'master' into bug/3511-query-formData-parameters 2017-08-03 19:30:50 -06:00
Owen Conti 7a694e287a Change name format to in.name and update tests 2017-08-03 19:29:49 -06:00
Owen Conti 17e99428c7 Merge branch 'bug/3511-query-formData-parameters' of github.com:owenconti/swagger-ui into bug/3511-query-formData-parameters 2017-08-03 07:14:15 -06:00
Owen Conti ce8adb91a5 Fix failing selector test 2017-08-03 07:14:03 -06:00
Owen Conti 053d032e94 Merge branch 'master' into bug/3511-query-formData-parameters 2017-08-02 22:37:49 -06:00
Owen Conti 51016c33f4 Attach in value to parameters map so parameters with the same name from different in types do not override each other. 2017-08-02 22:37:31 -06:00
Owen Conti fdb0d13089 Fixes #3511 - Update changeParameter calls to accept in value to identify parameters based on name plus in value 2017-08-02 22:07:48 -06:00
52 changed files with 1254 additions and 171 deletions
+1 -1
View File
@@ -25,7 +25,7 @@ or anything that violates the specifications.
| Which Swagger/OpenAPI version? |
| Which Swagger-UI version? |
| How did you install Swagger-UI? |
| Which broswer & version? |
| Which browser & version? |
| Which operating system? |
+5 -5
View File
@@ -10,10 +10,10 @@
As a brand new version, written from the ground up, there are some known issues and unimplemented features. Check out the [Known Issues](#known-issues) section for more details.
This repo publishes to two different NPM packages:
This repository publishes to two different NPM modules:
* [swagger-ui](https://www.npmjs.com/package/swagger-ui) is intended for use as a node module.
* [swagger-ui-dist](https://www.npmjs.com/package/swagger-ui-dist) comes pre-bundled with all dependencies and can be incorporated directly in a webapp.
* [swagger-ui](https://www.npmjs.com/package/swagger-ui) is a traditional npm module intended for use in JavaScript web application projects that are capable of resolving dependencies (via Webpack, Browserify, etc).
* [swagger-ui-dist](https://www.npmjs.com/package/swagger-ui-dist) is a dependency-free module that includes everything you need to serve Swagger-UI in a server-side project, or a web project that can't resolve npm module dependencies.
For the older version of swagger-ui, refer to the [*2.x branch*](https://github.com/swagger-api/swagger-ui/tree/2.x).
@@ -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.1.7 | 2017-08-25 | 2.0, 3.0 | [tag v3.1.7](https://github.com/swagger-api/swagger-ui/tree/v3.1.7)
3.2.2 | 2017-09-22 | 2.0, 3.0 | [tag v3.2.2](https://github.com/swagger-api/swagger-ui/tree/v3.2.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)
@@ -46,7 +46,7 @@ Will start nginx with swagger-ui on port 80.
Or you can provide your own swagger.json on your host
```
docker run -p 80:8080 -e "SWAGGER_JSON=/foo/swagger.json" -v /bar:/foo swaggerapi/swagger-ui
docker run -p 80:8080 -e SWAGGER_JSON=/foo/swagger.json -v /bar:/foo swaggerapi/swagger-ui
```
##### Prerequisites
+5 -1
View File
@@ -11,7 +11,11 @@
var redirectUrl = oauth2.redirectUrl;
var isValid, qp, arr;
qp = (window.location.hash || location.search).substring(1);
if (/code|token|error/.test(window.location.hash)) {
qp = window.location.hash.substring(1);
} else {
qp = location.search.substring(1);
}
arr = qp.split("&")
arr.forEach(function (v,i,_arr) { _arr[i] = '"' + v.replace('=', '":"') + '"';})
+5 -1
View File
@@ -11,7 +11,11 @@
var redirectUrl = oauth2.redirectUrl;
var isValid, qp, arr;
qp = (window.location.hash || location.search).substring(1);
if (/code|token|error/.test(window.location.hash)) {
qp = window.location.hash.substring(1);
} else {
qp = location.search.substring(1);
}
arr = qp.split("&")
arr.forEach(function (v,i,_arr) { _arr[i] = '"' + v.replace('=', '":"') + '"';})
+16 -11
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"swagger-ui-bundle.js","sources":["webpack:///swagger-ui-bundle.js"],"mappings":"AAAA;;;;;AAoyKA;;;;;;AAm/EA;;;;;;;;;;;;;;;;;;;;;;;;;;AAy7TA;;;;;;;;;;;;;;AAs8JA;;;;;;;;;AA2hpBA;;;;;AA41QA;AAm4DA;;;;;;AAo4YA;;;;;;AA8jaA;AAumvBA","sourceRoot":""}
{"version":3,"file":"swagger-ui-bundle.js","sources":["webpack:///swagger-ui-bundle.js"],"mappings":"AAAA;;;;;AA4kMA;;;;;;AA65DA;;;;;;;;;;;;;;;;;;;;;;;;;;AAq9TA;;;;;;;;;;;;;;AAs8JA;;;;;;;;;AAo/pBA;;;;;AAk6QA;;;;;AAynBA;AAi0CA;;;;;;AAq/YA;;;;;;AAojaA;AA8lvBA","sourceRoot":""}
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"swagger-ui-standalone-preset.js","sources":["webpack:///swagger-ui-standalone-preset.js"],"mappings":"AAAA;;;;;AA21CA;;;;;;AAypFA","sourceRoot":""}
{"version":3,"file":"swagger-ui-standalone-preset.js","sources":["webpack:///swagger-ui-standalone-preset.js"],"mappings":"AAAA;;;;;AA80CA;;;;;;AAqpFA","sourceRoot":""}
+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
@@ -1 +1 @@
{"version":3,"file":"swagger-ui.js","sources":["webpack:///swagger-ui.js"],"mappings":"AAAA;;;;;;AA8idA","sourceRoot":""}
{"version":3,"file":"swagger-ui.js","sources":["webpack:///swagger-ui.js"],"mappings":"AAAA;;;;;;AAkzeA","sourceRoot":""}
+4 -2
View File
@@ -1,6 +1,6 @@
{
"name": "swagger-ui",
"version": "3.1.7",
"version": "3.2.2",
"main": "dist/swagger-ui.js",
"repository": "git@github.com:swagger-api/swagger-ui.git",
"contributors": [
@@ -41,6 +41,8 @@
"dependencies": {
"base64-js": "^1.2.0",
"brace": "0.7.0",
"classnames": "^2.2.5",
"css.escape": "1.5.1",
"deep-extend": "0.4.1",
"expect": "1.20.2",
"getbase": "^2.8.2",
@@ -75,7 +77,7 @@
"scroll-to-element": "^2.0.0",
"serialize-error": "2.0.0",
"shallowequal": "0.2.2",
"swagger-client": "3.0.20",
"swagger-client": "^3.1.2",
"url-parse": "^1.1.8",
"whatwg-fetch": "0.11.1",
"worker-loader": "^0.7.1",
+7 -2
View File
@@ -28,15 +28,20 @@ export default class ArrayModel extends Component {
<span className="model-title__text">{ title }</span>
</span>
/*
Note: we set `name={null}` in <Model> below because we don't want
the name of the current Model passed (and displayed) as the name of the array element Model
*/
return <span className="model">
<ModelCollapse title={titleEl} collapsed={ depth > expandDepth } collapsedContent="[...]">
[
<span><Model { ...this.props } schema={ items } required={ false } depth={ depth + 1 } /></span>
<span><Model { ...this.props } name={null} schema={ items } required={ false } depth={ depth + 1 } /></span>
]
{
properties.size ? <span>
{ properties.entrySeq().map( ( [ key, v ] ) => <span key={`${key}-${v}`} style={propStyle}>
<br />{ `${key}:`}{ String(v) }</span>)
<br />{ key }: { String(v) }</span>)
}<br /></span>
: null
}
+28 -1
View File
@@ -8,6 +8,8 @@ export default class BaseLayout extends React.Component {
errActions: PropTypes.object.isRequired,
specActions: PropTypes.object.isRequired,
specSelectors: PropTypes.object.isRequired,
oas3Selectors: PropTypes.object.isRequired,
oas3Actions: PropTypes.object.isRequired,
layoutSelectors: PropTypes.object.isRequired,
layoutActions: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired
@@ -19,7 +21,14 @@ export default class BaseLayout extends React.Component {
}
render() {
let { specSelectors, specActions, getComponent, layoutSelectors } = this.props
let {
specSelectors,
specActions,
getComponent,
layoutSelectors,
oas3Selectors,
oas3Actions
} = this.props
let info = specSelectors.info()
let url = specSelectors.url()
@@ -28,6 +37,7 @@ export default class BaseLayout extends React.Component {
let securityDefinitions = specSelectors.securityDefinitions()
let externalDocs = specSelectors.externalDocs()
let schemes = specSelectors.schemes()
let servers = specSelectors.servers()
let Info = getComponent("info")
let Operations = getComponent("operations", true)
@@ -35,6 +45,7 @@ export default class BaseLayout extends React.Component {
let AuthorizeBtn = getComponent("authorizeBtn", true)
let Row = getComponent("Row")
let Col = getComponent("Col")
let Servers = getComponent("Servers")
let Errors = getComponent("errors", true)
let isLoading = specSelectors.loadingStatus() === "loading"
@@ -82,6 +93,22 @@ export default class BaseLayout extends React.Component {
</div>
) : null }
{ servers && servers.size ? (
<div className="server-container">
<Col className="servers wrapper" mobile={12}>
<Servers
servers={servers}
currentServer={oas3Selectors.selectedServer()}
setSelectedServer={oas3Actions.setSelectedServer}
setServerVariableValue={oas3Actions.setServerVariableValue}
getServerVariable={oas3Selectors.serverVariableValue}
getEffectiveServerValue={oas3Selectors.serverEffectiveValue}
/>
</Col>
</div>
) : null}
{
filter === null || filter === false ? null :
<div className="filter-container">
+26 -26
View File
@@ -30,39 +30,38 @@ export default class Model extends Component {
render () {
let { getComponent, specSelectors, schema, required, name, isRef } = this.props
let ObjectModel = getComponent("ObjectModel")
let ArrayModel = getComponent("ArrayModel")
let PrimitiveModel = getComponent("PrimitiveModel")
const ObjectModel = getComponent("ObjectModel")
const ArrayModel = getComponent("ArrayModel")
const PrimitiveModel = getComponent("PrimitiveModel")
let type = "object"
let $$ref = schema && schema.get("$$ref")
let modelName = $$ref && this.getModelName( $$ref )
let modelSchema, type
// If we weren't passed a `name` but have a ref, grab the name from the ref
if ( !name && $$ref ) {
name = this.getModelName( $$ref )
}
// If we weren't passed a `schema` but have a ref, grab the schema from the ref
if ( !schema && $$ref ) {
schema = this.getRefSchema( name )
}
const deprecated = specSelectors.isOAS3() && schema.get("deprecated")
if ( schema && (schema.get("type") || schema.get("properties")) ) {
modelSchema = schema
} else if ( $$ref ) {
modelSchema = this.getRefSchema( modelName )
}
type = modelSchema && modelSchema.get("type")
if ( !type && modelSchema && modelSchema.get("properties") ) {
type = "object"
}
isRef = isRef !== undefined ? isRef : !!$$ref
type = schema && schema.get("type") || type
switch(type) {
case "object":
return <ObjectModel
className="object" { ...this.props }
schema={ modelSchema }
name={ modelName || name }
schema={ schema }
name={ name }
deprecated={deprecated}
isRef={ isRef!== undefined ? isRef : !!$$ref } />
isRef={ isRef } />
case "array":
return <ArrayModel
className="array" { ...this.props }
schema={ modelSchema }
name={ modelName || name }
schema={ schema }
name={ name }
deprecated={deprecated}
required={ required } />
case "string":
@@ -73,9 +72,10 @@ export default class Model extends Component {
return <PrimitiveModel
{ ...this.props }
getComponent={ getComponent }
schema={ modelSchema }
name={ modelName || name }
schema={ schema }
name={ name }
deprecated={deprecated}
required={ required }/> }
required={ required }/>
}
}
}
-1
View File
@@ -34,7 +34,6 @@ export default class Models extends Component {
return <div className="model-container" key={ `models-section-${name}` }>
<ModelWrapper name={ name }
schema={ model }
isRef={ true }
getComponent={ getComponent }
specSelectors={ specSelectors }/>
</div>
+4 -1
View File
@@ -28,6 +28,7 @@ export default class Operation extends PureComponent {
authSelectors: PropTypes.object,
specActions: PropTypes.object.isRequired,
specSelectors: PropTypes.object.isRequired,
oas3Actions: PropTypes.object.isRequired,
layoutActions: PropTypes.object.isRequired,
layoutSelectors: PropTypes.object.isRequired,
fn: PropTypes.object.isRequired,
@@ -117,7 +118,8 @@ export default class Operation extends PureComponent {
specSelectors,
authActions,
authSelectors,
getConfigs
getConfigs,
oas3Actions
} = this.props
let summary = operation.get("summary")
@@ -265,6 +267,7 @@ export default class Operation extends PureComponent {
getComponent={ getComponent }
getConfigs={ getConfigs }
specSelectors={ specSelectors }
oas3Actions={oas3Actions}
specActions={ specActions }
produces={ produces }
producesValue={ operation.get("produces_value") }
+9 -4
View File
@@ -1,7 +1,7 @@
import React from "react"
import PropTypes from "prop-types"
import { helpers } from "swagger-client"
import { createDeepLinkPath } from "core/utils"
const { opId } = helpers
export default class Operations extends React.Component {
@@ -9,6 +9,7 @@ export default class Operations extends React.Component {
static propTypes = {
specSelectors: PropTypes.object.isRequired,
specActions: PropTypes.object.isRequired,
oas3Actions: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired,
layoutSelectors: PropTypes.object.isRequired,
layoutActions: PropTypes.object.isRequired,
@@ -21,6 +22,7 @@ export default class Operations extends React.Component {
let {
specSelectors,
specActions,
oas3Actions,
getComponent,
layoutSelectors,
layoutActions,
@@ -34,6 +36,7 @@ export default class Operations extends React.Component {
const Operation = getComponent("operation")
const Collapse = getComponent("Collapse")
const Markdown = getComponent("Markdown")
let showSummary = layoutSelectors.showSummary()
let {
@@ -69,7 +72,7 @@ export default class Operations extends React.Component {
let tagExternalDocsDescription = tagObj.getIn(["tagDetails", "externalDocs", "description"])
let tagExternalDocsUrl = tagObj.getIn(["tagDetails", "externalDocs", "url"])
let isShownKey = ["operations-tag", tag]
let isShownKey = ["operations-tag", createDeepLinkPath(tag)]
let showTag = layoutSelectors.isShown(isShownKey, docExpansion === "full" || docExpansion === "list")
return (
@@ -87,7 +90,7 @@ export default class Operations extends React.Component {
</a>
{ !tagDescription ? null :
<small>
{ tagDescription }
<Markdown source={tagDescription} />
</small>
}
@@ -124,7 +127,7 @@ export default class Operations extends React.Component {
const operationId =
op.getIn(["operation", "operationId"]) || op.getIn(["operation", "__originalOperationId"]) || opId(op.get("operation"), path, method) || op.get("id")
const isShownKey = ["operations", tag, operationId]
const isShownKey = ["operations", createDeepLinkPath(tag), createDeepLinkPath(operationId)]
const allowTryItOut = specSelectors.allowTryItOutFor(op.get("path"), op.get("method"))
const response = specSelectors.responseFor(op.get("path"), op.get("method"))
@@ -147,6 +150,8 @@ export default class Operations extends React.Component {
specActions={ specActions }
specSelectors={ specSelectors }
oas3Actions={oas3Actions}
layoutActions={ layoutActions }
layoutSelectors={ layoutSelectors }
+2 -2
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")) : {}
let parameter = specSelectors ? specSelectors.getParameter(pathMethod, param.get("name"), param.get("in")) : {}
let isXml = /xml/i.test(consumesValue)
let isJson = /json/i.test(consumesValue)
let paramValue = isXml ? parameter.get("value_xml") : parameter.get("value")
@@ -107,7 +107,7 @@ export default class ParamBody extends PureComponent {
const HighlightCode = getComponent("highlightCode")
const ContentType = getComponent("contentType")
// for domains where specSelectors not passed
let parameter = specSelectors ? specSelectors.getParameter(pathMethod, param.get("name")) : param
let parameter = specSelectors ? specSelectors.getParameter(pathMethod, param.get("name"), param.get("in")) : param
let errors = parameter.get("errors", List())
let consumesValue = specSelectors.contentTypeValues(pathMethod).get("requestContentType")
let consumes = this.props.consumes && this.props.consumes.size ? this.props.consumes : ParamBody.defaultProp.consumes
+3 -4
View File
@@ -19,7 +19,7 @@ export default class ParameterRow extends Component {
let { specSelectors, pathMethod, param } = props
let defaultValue = param.get("default")
let parameter = specSelectors.getParameter(pathMethod, param.get("name"))
let parameter = specSelectors.getParameter(pathMethod, param.get("name"), param.get("in"))
let value = parameter ? parameter.get("value") : ""
if ( defaultValue !== undefined && value === undefined ) {
this.onChangeWrapper(defaultValue)
@@ -81,13 +81,12 @@ export default class ParameterRow extends Component {
const Markdown = getComponent("Markdown")
let schema = param.get("schema")
let type = isOAS3 && isOAS3() ? param.getIn(["schema", "type"]) : param.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 parameter = specSelectors.getParameter(pathMethod, param.get("name"))
let itemType = param.getIn(isOAS3 && isOAS3() ? ["schema", "items", "type"] : ["items", "type"])
let parameter = specSelectors.getParameter(pathMethod, param.get("name"), param.get("in"))
let value = parameter ? parameter.get("value") : ""
return (
+2 -2
View File
@@ -37,7 +37,7 @@ export default class Parameters extends Component {
onChangeKey,
} = this.props
changeParam( onChangeKey, param.get("name"), value, isXml)
changeParam( onChangeKey, param.get("name"), param.get("in"), value, isXml)
}
onChangeConsumesWrapper = ( val ) => {
@@ -94,7 +94,7 @@ export default class Parameters extends Component {
<ParameterRow fn={ fn }
getComponent={ getComponent }
param={ parameter }
key={ parameter.get( "name" ) }
key={ `${parameter.get( "in" )}.${parameter.get("name")}` }
onChange={ this.onChange }
onChangeConsumes={this.onChangeConsumesWrapper}
specSelectors={ specSelectors }
+2 -1
View File
@@ -23,6 +23,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 description = schema.get("description")
let properties = schema.filter( ( v, key) => ["enum", "type", "format", "description", "$$ref"].indexOf(key) === -1 )
const Markdown = getComponent("Markdown")
@@ -30,7 +31,7 @@ export default class Primitive extends Component {
return <span className="model">
<span className="prop">
{ name && <span className={`${depth === 1 && "model-title"} prop-name`}>{ name }</span> }
{ name && <span className={`${depth === 1 && "model-title"} prop-name`}>{ title }</span> }
<span className="prop-type">{ type }</span>
{ format && <span className="prop-format">(${format})</span>}
{
+38 -10
View File
@@ -1,7 +1,8 @@
import React from "react"
import PropTypes from "prop-types"
import cx from "classnames"
import { fromJS, Seq } from "immutable"
import { getSampleSchema } from "core/utils"
import { getSampleSchema, fromJSOrdered } from "core/utils"
const getExampleComponent = ( sampleResponse, examples, HighlightCode ) => {
if ( examples && examples.size ) {
@@ -46,23 +47,35 @@ export default class Response extends React.Component {
getComponent: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired,
fn: PropTypes.object.isRequired,
contentType: PropTypes.string
contentType: PropTypes.string,
controlsAcceptHeader: PropTypes.bool,
onContentTypeChange: PropTypes.func
}
static defaultProps = {
response: fromJS({}),
onContentTypeChange: () => {}
};
_onContentTypeChange = (value) => {
const { onContentTypeChange, controlsAcceptHeader } = this.props
this.setState({ responseContentType: value })
onContentTypeChange({
value: value,
controlsAcceptHeader
})
}
render() {
let {
code,
response,
className,
fn,
getComponent,
specSelectors,
contentType
contentType,
controlsAcceptHeader
} = this.props
let { inferSchema } = fn
@@ -94,6 +107,14 @@ export default class Response extends React.Component {
includeWriteOnly: true // writeOnly has no filtering effect in swagger 2.0
}) : null
}
if(examples) {
examples = examples.map(example => {
// Remove unwanted properties from examples
return example.set("$$ref", undefined)
})
}
let example = getExampleComponent( sampleResponse, examples, HighlightCode )
return (
@@ -107,17 +128,24 @@ export default class Response extends React.Component {
<Markdown source={ response.get( "description" ) } />
</div>
{ isOAS3 ? <ContentType
value={this.state.responseContentType}
contentTypes={ response.get("content") ? response.get("content").keySeq() : Seq() }
onChange={(val) => this.setState({ responseContentType: val })}
className="response-content-type" /> : null }
{ isOAS3 ?
<div className={cx("response-content-type", {
"controls-accept-header": controlsAcceptHeader
})}>
<ContentType
value={this.state.responseContentType}
contentTypes={ response.get("content") ? response.get("content").keySeq() : Seq() }
onChange={this._onContentTypeChange}
/>
{ controlsAcceptHeader ? <small>Controls <code>Accept</code> header.</small> : null }
</div>
: null }
{ example ? (
<ModelExample
getComponent={ getComponent }
specSelectors={ specSelectors }
schema={ fromJS(schema) }
schema={ fromJSOrdered(schema) }
example={ example }/>
) : null}
+30 -3
View File
@@ -1,7 +1,7 @@
import React from "react"
import PropTypes from "prop-types"
import { fromJS } from "immutable"
import { defaultStatusCode } from "core/utils"
import { defaultStatusCode, getAcceptControllingResponse } from "core/utils"
export default class Responses extends React.Component {
@@ -14,6 +14,7 @@ export default class Responses extends React.Component {
getComponent: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired,
specActions: PropTypes.object.isRequired,
oas3Actions: PropTypes.object.isRequired,
pathMethod: PropTypes.array.isRequired,
displayRequestDuration: PropTypes.bool.isRequired,
fn: PropTypes.object.isRequired,
@@ -29,8 +30,28 @@ export default class Responses extends React.Component {
onChangeProducesWrapper = ( val ) => this.props.specActions.changeProducesValue(this.props.pathMethod, val)
onResponseContentTypeChange = ({ controlsAcceptHeader, value }) => {
const { oas3Actions, pathMethod } = this.props
if(controlsAcceptHeader) {
oas3Actions.setResponseContentType({
value,
pathMethod
})
}
}
render() {
let { responses, request, tryItOutResponse, getComponent, getConfigs, specSelectors, fn, producesValue, displayRequestDuration } = this.props
let {
responses,
request,
tryItOutResponse,
getComponent,
getConfigs,
specSelectors,
fn,
producesValue,
displayRequestDuration
} = this.props
let defaultCode = defaultStatusCode( responses )
const ContentType = getComponent( "contentType" )
@@ -39,6 +60,11 @@ export default class Responses extends React.Component {
let produces = this.props.produces && this.props.produces.size ? this.props.produces : Responses.defaultProps.produces
const isSpecOAS3 = specSelectors.isOAS3()
const acceptControllingResponse = isSpecOAS3 ?
getAcceptControllingResponse(responses) : null
return (
<div className="responses-wrapper">
<div className="opblock-section-header">
@@ -78,7 +104,6 @@ export default class Responses extends React.Component {
<tbody>
{
responses.entrySeq().map( ([code, response]) => {
let className = tryItOutResponse && tryItOutResponse.get("status") == code ? "response_current" : ""
return (
<Response key={ code }
@@ -88,6 +113,8 @@ export default class Responses extends React.Component {
code={ code }
response={ response }
specSelectors={ specSelectors }
controlsAcceptHeader={response === acceptControllingResponse}
onContentTypeChange={this.onResponseContentTypeChange}
contentType={ producesValue }
getComponent={ getComponent }/>
)
+2 -1
View File
@@ -64,7 +64,8 @@ export default function authorize ( { auth, authActions, errActions, configs, au
}
}
let url = [schema.get("authorizationUrl"), query.join("&")].join("?")
let authorizationUrl = schema.get("authorizationUrl")
let url = [authorizationUrl, query.join("&")].join(authorizationUrl.indexOf("?") === -1 ? "?" : "&")
// pass action authorizeOauth2 and authentication data through window
// to authorize with oauth2
@@ -1,4 +1,5 @@
import { setHash } from "./helpers"
import { createDeepLinkPath } from "core/utils"
export const show = (ori, { getConfigs }) => (...args) => {
ori(...args)
@@ -19,12 +20,12 @@ export const show = (ori, { getConfigs }) => (...args) => {
if(type === "operations") {
let [, tag, operationId] = thing
setHash(`/${tag}/${operationId}`)
setHash(`/${createDeepLinkPath(tag)}/${createDeepLinkPath(operationId)}`)
}
if(type === "operations-tag") {
let [, tag] = thing
setHash(`/${tag}`)
setHash(`/${createDeepLinkPath(tag)}`)
}
}
@@ -1,4 +1,5 @@
import scrollTo from "scroll-to-element"
import { escapeDeepLinkPath } from "core/utils"
const SCROLL_OFFSET = -5
let hasHashBeenParsed = false
@@ -34,14 +35,14 @@ export const updateResolved = (ori, { layoutActions, getConfigs }) => (...args)
layoutActions.show(["operations-tag", tag], true)
layoutActions.show(["operations", tag, operationId], true)
scrollTo(`#operations-${tag}-${operationId}`, {
scrollTo(`#operations-${escapeDeepLinkPath(tag)}-${escapeDeepLinkPath(operationId)}`, {
offset: SCROLL_OFFSET
})
} else if(tag) {
// Pre-expand and scroll to the tag
layoutActions.show(["operations-tag", tag], true)
scrollTo(`#operations-tag-${tag}`, {
scrollTo(`#operations-tag-${escapeDeepLinkPath(tag)}`, {
offset: SCROLL_OFFSET
})
}
+43
View File
@@ -0,0 +1,43 @@
// Actions conform to FSA (flux-standard-actions)
// {type: string,payload: Any|Error, meta: obj, error: bool}
export const UPDATE_SELECTED_SERVER = "oas3_set_servers"
export const UPDATE_REQUEST_BODY_VALUE = "oas3_set_request_body_value"
export const UPDATE_REQUEST_CONTENT_TYPE = "oas3_set_request_content_type"
export const UPDATE_RESPONSE_CONTENT_TYPE = "oas3_set_response_content_type"
export const UPDATE_SERVER_VARIABLE_VALUE = "oas3_set_server_variable_value"
export function setSelectedServer (selectedServerUrl) {
return {
type: UPDATE_SELECTED_SERVER,
payload: selectedServerUrl
}
}
export function setRequestBodyValue ({ value, pathMethod }) {
return {
type: UPDATE_REQUEST_BODY_VALUE,
payload: { value, pathMethod }
}
}
export function setRequestContentType ({ value, pathMethod }) {
return {
type: UPDATE_REQUEST_CONTENT_TYPE,
payload: { value, pathMethod }
}
}
export function setResponseContentType ({ value, pathMethod }) {
return {
type: UPDATE_RESPONSE_CONTENT_TYPE,
payload: { value, pathMethod }
}
}
export function setServerVariableValue ({ server, key, val }) {
return {
type: UPDATE_SERVER_VARIABLE_VALUE,
payload: { server, key, val }
}
}
@@ -1,9 +1,13 @@
import Callbacks from "./callbacks"
import RequestBody from "./request-body"
import OperationLink from "./operation-link.jsx"
import Servers from "./servers"
import RequestBodyEditor from "./request-body-editor"
export default {
Callbacks,
RequestBody,
Servers,
RequestBodyEditor,
operationLink: OperationLink
}
@@ -0,0 +1,114 @@
import React, { PureComponent } from "react"
import PropTypes from "prop-types"
import { fromJS } from "immutable"
import { getSampleSchema } from "core/utils"
const NOOP = Function.prototype
export default class RequestBodyEditor extends PureComponent {
static propTypes = {
requestBody: PropTypes.object.isRequired,
mediaType: PropTypes.string.isRequired,
onChange: PropTypes.func,
getComponent: PropTypes.func.isRequired,
isExecute: PropTypes.bool,
specSelectors: PropTypes.object.isRequired,
};
static defaultProps = {
mediaType: "application/json",
requestBody: fromJS({}),
onChange: NOOP,
};
constructor(props, context) {
super(props, context)
this.state = {
isEditBox: false,
value: ""
}
}
componentDidMount() {
this.setValueToSample.call(this)
}
componentWillReceiveProps(nextProps) {
if(this.props.mediaType !== nextProps.mediaType) {
// media type was changed
this.setValueToSample(nextProps.mediaType)
}
if(!this.props.isExecute && nextProps.isExecute) {
// we just entered execute mode,
// so enable editing for convenience
this.setState({ isEditBox: true })
}
}
setValueToSample = (explicitMediaType) => {
this.onChange(this.sample(explicitMediaType))
}
sample = (explicitMediaType) => {
let { requestBody, mediaType } = this.props
let schema = requestBody.getIn(["content", explicitMediaType || mediaType, "schema"]).toJS()
return getSampleSchema(schema, explicitMediaType || mediaType, {
includeWriteOnly: true
})
}
onChange = (value) => {
this.setState({value})
this.props.onChange(value)
}
handleOnChange = e => {
const { mediaType } = this.props
const isJson = /json/i.test(mediaType)
const inputValue = isJson ? e.target.value.trim() : e.target.value
this.onChange(inputValue)
}
toggleIsEditBox = () => this.setState( state => ({isEditBox: !state.isEditBox}))
render() {
let {
isExecute,
getComponent,
} = this.props
const Button = getComponent("Button")
const TextArea = getComponent("TextArea")
const HighlightCode = getComponent("highlightCode")
let { value, isEditBox } = this.state
return (
<div className="body-param">
{
isEditBox && isExecute
? <TextArea className={"body-param__text"} value={value} onChange={ this.handleOnChange }/>
: (value && <HighlightCode className="body-param__example"
value={ value }/>)
}
<div className="body-param-options">
{
!isExecute ? null
: <div className="body-param-edit">
<Button className={isEditBox ? "btn cancel body-param__example-edit" : "btn edit body-param__example-edit"}
onClick={this.toggleIsEditBox}>{ isEditBox ? "Cancel" : "Edit"}
</Button>
</div>
}
</div>
</div>
)
}
}
@@ -2,13 +2,18 @@ import React from "react"
import PropTypes from "prop-types"
import ImPropTypes from "react-immutable-proptypes"
import { OrderedMap } from "immutable"
import { getSampleSchema } from "core/utils"
const RequestBody = ({ requestBody, getComponent, specSelectors, contentType }) => {
const RequestBody = ({
requestBody,
getComponent,
specSelectors,
contentType,
isExecute,
onChange
}) => {
const Markdown = getComponent("Markdown")
const ModelExample = getComponent("modelExample")
const HighlightCode = getComponent("highlightCode")
const RequestBodyEditor = getComponent("RequestBodyEditor")
const requestBodyDescription = (requestBody && requestBody.get("description")) || null
const requestBodyContent = (requestBody && requestBody.get("content")) || new OrderedMap()
@@ -16,10 +21,6 @@ const RequestBody = ({ requestBody, getComponent, specSelectors, contentType })
const mediaTypeValue = requestBodyContent.get(contentType)
const sampleSchema = getSampleSchema(mediaTypeValue.get("schema").toJS(), contentType, {
includeWriteOnly: true
})
return <div>
{ requestBodyDescription &&
<Markdown source={requestBodyDescription} />
@@ -28,8 +29,16 @@ const RequestBody = ({ requestBody, getComponent, specSelectors, contentType })
getComponent={ getComponent }
specSelectors={ specSelectors }
expandDepth={1}
isExecute={isExecute}
schema={mediaTypeValue.get("schema")}
example={<HighlightCode value={sampleSchema} />}
example={<RequestBodyEditor
requestBody={requestBody}
onChange={onChange}
mediaType={contentType}
getComponent={getComponent}
isExecute={isExecute}
specSelectors={specSelectors}
/>}
/>
</div>
}
@@ -38,7 +47,9 @@ RequestBody.propTypes = {
requestBody: ImPropTypes.orderedMap.isRequired,
getComponent: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired,
contentType: PropTypes.string.isRequired
contentType: PropTypes.string.isRequired,
isExecute: PropTypes.bool.isRequired,
onChange: PropTypes.func.isRequired
}
export default RequestBody
@@ -0,0 +1,155 @@
import React from "react"
import { OrderedMap } from "immutable"
import PropTypes from "prop-types"
import ImPropTypes from "react-immutable-proptypes"
export default class Servers extends React.Component {
static propTypes = {
servers: ImPropTypes.list.isRequired,
currentServer: PropTypes.string.isRequired,
setSelectedServer: PropTypes.func.isRequired,
setServerVariableValue: PropTypes.func.isRequired,
getServerVariable: PropTypes.func.isRequired,
getEffectiveServerValue: PropTypes.func.isRequired
}
componentDidMount() {
let { servers } = this.props
//fire 'change' event to set default 'value' of select
this.setServer(servers.first().get("url"))
}
componentWillReceiveProps(nextProps) {
let {
servers,
setServerVariableValue,
getServerVariable
} = this.props
if(this.props.currentServer !== nextProps.currentServer) {
// Server has changed, we may need to set default values
let currentServerDefinition = servers
.find(v => v.get("url") === nextProps.currentServer) || OrderedMap()
let currentServerVariableDefs = currentServerDefinition.get("variables") || OrderedMap()
currentServerVariableDefs.map((val, key) => {
let currentValue = getServerVariable(nextProps.currentServer, key)
// only set the default value if the user hasn't set one yet
if(!currentValue) {
setServerVariableValue({
server: nextProps.currentServer,
key,
val: val.get("default") || ""
})
}
})
}
}
onServerChange =( e ) => {
this.setServer( e.target.value )
// set default variable values
}
onServerVariableValueChange = ( e ) => {
let {
setServerVariableValue,
currentServer
} = this.props
let variableName = e.target.getAttribute("data-variable")
let newVariableValue = e.target.value
if(typeof setServerVariableValue === "function") {
setServerVariableValue({
server: currentServer,
key: variableName,
val: newVariableValue
})
}
}
setServer = ( value ) => {
let { setSelectedServer } = this.props
setSelectedServer(value)
}
render() {
let { servers,
currentServer,
getServerVariable,
getEffectiveServerValue
} = this.props
let currentServerDefinition = servers.find(v => v.get("url") === currentServer) || OrderedMap()
let currentServerVariableDefs = currentServerDefinition.get("variables") || OrderedMap()
let shouldShowVariableUI = currentServerVariableDefs.size !== 0
return (
<div>
<label htmlFor="servers">
<span className="servers-title">Servers</span>
<select onChange={ this.onServerChange }>
{ servers.valueSeq().map(
( server ) =>
<option
value={ server.get("url") }
key={ server.get("url") }>
{ server.get("url") }
</option>
).toArray()}
</select>
</label>
{ shouldShowVariableUI ?
<div>
<h4>Server variables</h4>
<div className={"computed-url"}>
Computed URL:
<code>
{getEffectiveServerValue(currentServer)}
</code>
</div>
<table>
<tbody>
{
currentServerVariableDefs.map((val, name) => {
return <tr key={name}>
<td>{name}</td>
<td>
{ val.get("enum") ?
<select data-variable={name} onChange={this.onServerVariableValueChange}>
{val.get("enum").map(enumValue => {
return <option
selected={enumValue === getServerVariable(currentServer, name)}
key={enumValue}
value={enumValue}>
{enumValue}
</option>
})}
</select> :
<input
type={"text"}
value={getServerVariable(currentServer, name) || ""}
onChange={this.onServerVariableValueChange}
data-variable={name}
></input>
}
</td>
</tr>
})
}
</tbody>
</table>
</div>: null
}
</div>
)
}
}
+12 -2
View File
@@ -1,8 +1,12 @@
// import reducers from "./reducers"
// import * as actions from "./actions"
import * as wrapSelectors from "./wrap-selectors"
import * as specWrapSelectors from "./spec-extensions/wrap-selectors"
import * as specSelectors from "./spec-extensions/selectors"
import components from "./components"
import wrapComponents from "./wrap-components"
import * as oas3Actions from "./actions"
import * as oas3Selectors from "./selectors"
import oas3Reducers from "./reducers"
export default function() {
return {
@@ -10,7 +14,13 @@ export default function() {
wrapComponents,
statePlugins: {
spec: {
wrapSelectors
wrapSelectors: specWrapSelectors,
selectors: specSelectors
},
oas3: {
actions: oas3Actions,
reducers: oas3Reducers,
selectors: oas3Selectors,
}
}
}
+28
View File
@@ -0,0 +1,28 @@
import {
UPDATE_SELECTED_SERVER,
UPDATE_REQUEST_BODY_VALUE,
UPDATE_REQUEST_CONTENT_TYPE,
UPDATE_SERVER_VARIABLE_VALUE,
UPDATE_RESPONSE_CONTENT_TYPE
} from "./actions"
export default {
[UPDATE_SELECTED_SERVER]: (state, { payload: selectedServerUrl } ) =>{
return state.setIn( [ "selectedServer" ], selectedServerUrl)
},
[UPDATE_REQUEST_BODY_VALUE]: (state, { payload: { value, pathMethod } } ) =>{
let [path, method] = pathMethod
return state.setIn( [ "requestData", path, method, "bodyValue" ], value)
},
[UPDATE_REQUEST_CONTENT_TYPE]: (state, { payload: { value, pathMethod } } ) =>{
let [path, method] = pathMethod
return state.setIn( [ "requestData", path, method, "requestContentType" ], value)
},
[UPDATE_RESPONSE_CONTENT_TYPE]: (state, { payload: { value, pathMethod } } ) =>{
let [path, method] = pathMethod
return state.setIn( [ "requestData", path, method, "responseContentType" ], value)
},
[UPDATE_SERVER_VARIABLE_VALUE]: (state, { payload: { server, key, val } } ) =>{
return state.setIn( [ "serverVariableValues", server, key ], val)
},
}
+58
View File
@@ -0,0 +1,58 @@
import { OrderedMap } from "immutable"
import { isOAS3 as isOAS3Helper } from "./helpers"
// Helpers
function onlyOAS3(selector) {
return (...args) => (system) => {
const spec = system.getSystem().specSelectors.specJson()
if(isOAS3Helper(spec)) {
return selector(...args)
} else {
return null
}
}
}
export const selectedServer = onlyOAS3(state => {
return state.getIn(["selectedServer"]) || ""
}
)
export const requestBodyValue = onlyOAS3((state, path, method) => {
return state.getIn(["requestData", path, method, "bodyValue"]) || null
}
)
export const requestContentType = onlyOAS3((state, path, method) => {
return state.getIn(["requestData", path, method, "requestContentType"]) || null
}
)
export const responseContentType = onlyOAS3((state, path, method) => {
return state.getIn(["requestData", path, method, "responseContentType"]) || null
}
)
export const serverVariableValue = onlyOAS3((state, server, key) => {
return state.getIn(["serverVariableValues", server, key]) || null
}
)
export const serverVariables = onlyOAS3((state, server) => {
return state.getIn(["serverVariableValues", server]) || OrderedMap()
}
)
export const serverEffectiveValue = onlyOAS3((state, server) => {
let varValues = state.getIn(["serverVariableValues", server]) || OrderedMap()
let str = server
varValues.map((val, key) => {
str = str.replace(new RegExp(`{${key}}`, "g"), val)
})
return str
}
)
@@ -0,0 +1,50 @@
import { createSelector } from "reselect"
import { Map } from "immutable"
import { isOAS3 as isOAS3Helper, isSwagger2 as isSwagger2Helper } from "../helpers"
// Helpers
function onlyOAS3(selector) {
return () => (system, ...args) => {
const spec = system.getSystem().specSelectors.specJson()
if(isOAS3Helper(spec)) {
return selector(...args)
} else {
return null
}
}
}
const state = state => {
return state || Map()
}
const specJson = createSelector(
state,
spec => spec.get("json", Map())
)
const specResolved = createSelector(
state,
spec => spec.get("resolved", Map())
)
const spec = state => {
let res = specResolved(state)
if(res.count() < 1)
res = specJson(state)
return res
}
// New selectors
export const servers = onlyOAS3(createSelector(
spec,
spec => spec.getIn(["servers"]) || Map()
))
export const isSwagger2 = (ori, system) => () => {
const spec = system.getSystem().specSelectors.specJson()
return isSwagger2Helper(spec)
}
@@ -1,6 +1,6 @@
import { createSelector } from "reselect"
import { Map } from "immutable"
import { isOAS3 as isOAS3Helper, isSwagger2 as isSwagger2Helper } from "./helpers"
import { isOAS3 as isOAS3Helper, isSwagger2 as isSwagger2Helper } from "../helpers"
// Helpers
@@ -56,6 +56,11 @@ export const schemes = OAS3NullSelector
// New selectors
export const servers = onlyOAS3(createSelector(
spec,
spec => spec.getIn(["servers"]) || Map()
))
export const isOAS3 = (ori, system) => () => {
const spec = system.getSystem().specSelectors.specJson()
return isOAS3Helper(spec)
@@ -3,7 +3,6 @@ import parameters from "./parameters"
import VersionStamp from "./version-stamp"
import OnlineValidatorBadge from "./online-validator-badge"
import Model from "./model"
import TryItOutButton from "./try-it-out-button"
export default {
Markdown,
@@ -11,5 +10,4 @@ export default {
VersionStamp,
model: Model,
onlineValidatorBadge: OnlineValidatorBadge,
TryItOutButton
}
@@ -13,8 +13,7 @@ class Parameters extends Component {
super(props)
this.state = {
callbackVisible: false,
parametersVisible: true,
requestBodyContentType: ""
parametersVisible: true
}
}
@@ -24,6 +23,8 @@ class Parameters extends Component {
operation: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired,
oas3Actions: PropTypes.object.isRequired,
oas3Selectors: PropTypes.object.isRequired,
fn: PropTypes.object.isRequired,
tryItOutEnabled: PropTypes.bool,
allowTryItOut: PropTypes.bool,
@@ -48,7 +49,7 @@ class Parameters extends Component {
onChangeKey,
} = this.props
changeParam( onChangeKey, param.get("name"), value, isXml)
changeParam( onChangeKey, param.get("name"), param.get("in"), value, isXml)
}
onChangeConsumesWrapper = ( val ) => {
@@ -86,6 +87,8 @@ class Parameters extends Component {
fn,
getComponent,
specSelectors,
oas3Actions,
oas3Selectors,
pathMethod,
operation
} = this.props
@@ -159,16 +162,22 @@ class Parameters extends Component {
<h4 className={`opblock-title parameter__name ${requestBody.get("required") && "required"}`}>Request body</h4>
<label>
<ContentType
value={this.state.requestBodyContentType}
value={oas3Selectors.requestContentType(...pathMethod)}
contentTypes={ requestBody.get("content").keySeq() }
onChange={(val) => this.setState({ requestBodyContentType: val })}
onChange={(value) => {
oas3Actions.setRequestContentType({ value, pathMethod })
}}
className="body-param-content-type" />
</label>
</div>
<div className="opblock-description-wrapper">
<RequestBody
requestBody={requestBody}
contentType={this.state.requestBodyContentType}/>
isExecute={isExecute}
onChange={(value) => {
oas3Actions.setRequestBodyValue({ value, pathMethod })
}}
contentType={oas3Selectors.requestContentType(...pathMethod)}/>
</div>
</div>
}
@@ -1,5 +0,0 @@
import { OAS3ComponentWrapFactory } from "../helpers"
export default OAS3ComponentWrapFactory(() => {
return null
})
+58 -38
View File
@@ -1,6 +1,7 @@
import YAML from "js-yaml"
import parseUrl from "url-parse"
import serializeError from "serialize-error"
import { isJSONObject } from "core/utils"
// Actions conform to FSA (flux-standard-actions)
// {type: string,payload: Any|Error, meta: obj, error: bool}
@@ -129,10 +130,10 @@ export const formatIntoYaml = () => ({specActions, specSelectors}) => {
}
}
export function changeParam( path, paramName, value, isXml ){
export function changeParam( path, paramName, paramIn, value, isXml ){
return {
type: UPDATE_PARAM,
payload:{ path, value, paramName, isXml }
payload:{ path, value, paramName, paramIn, isXml }
}
}
@@ -195,48 +196,67 @@ export const logRequest = (req) => {
// Actually fire the request via fn.execute
// (For debugging) and ease of testing
export const executeRequest = (req) => ({fn, specActions, specSelectors, getConfigs}) => {
let { pathName, method, operation } = req
let { requestInterceptor, responseInterceptor } = getConfigs()
export const executeRequest = (req) =>
({fn, specActions, specSelectors, getConfigs, oas3Selectors}) => {
let { pathName, method, operation } = req
let { requestInterceptor, responseInterceptor } = getConfigs()
let op = operation.toJS()
let op = operation.toJS()
// if url is relative, parseUrl makes it absolute by inferring from `window.location`
req.contextUrl = parseUrl(specSelectors.url()).toString()
// if url is relative, parseUrl makes it absolute by inferring from `window.location`
req.contextUrl = parseUrl(specSelectors.url()).toString()
if(op && op.operationId) {
req.operationId = op.operationId
} else if(op && pathName && method) {
req.operationId = fn.opId(op, pathName, method)
}
if(op && op.operationId) {
req.operationId = op.operationId
} else if(op && pathName && method) {
req.operationId = fn.opId(op, pathName, method)
if(specSelectors.isOAS3()) {
// OAS3 request feature support
req.server = oas3Selectors.selectedServer()
req.serverVariables = oas3Selectors.serverVariables(req.server).toJS()
req.requestContentType = oas3Selectors.requestContentType(pathName, method)
req.responseContentType = oas3Selectors.responseContentType(pathName, method) || "*/*"
const requestBody = oas3Selectors.requestBodyValue(pathName, method)
if(isJSONObject(requestBody)) {
req.requestBody = JSON.parse(requestBody)
} else {
req.requestBody = requestBody
}
}
let parsedRequest = Object.assign({}, req)
parsedRequest = fn.buildRequest(parsedRequest)
specActions.setRequest(req.pathName, req.method, parsedRequest)
let requestInterceptorWrapper = function(r) {
let mutatedRequest = requestInterceptor.apply(this, [r])
let parsedMutatedRequest = Object.assign({}, mutatedRequest)
specActions.setMutatedRequest(req.pathName, req.method, parsedMutatedRequest)
return mutatedRequest
}
req.requestInterceptor = requestInterceptorWrapper
req.responseInterceptor = responseInterceptor
// track duration of request
const startTime = Date.now()
return fn.execute(req)
.then( res => {
res.duration = Date.now() - startTime
specActions.setResponse(req.pathName, req.method, res)
} )
.catch(
err => specActions.setResponse(req.pathName, req.method, {
error: true, err: serializeError(err)
})
)
}
let parsedRequest = Object.assign({}, req)
parsedRequest = fn.buildRequest(parsedRequest)
specActions.setRequest(req.pathName, req.method, parsedRequest)
let requestInterceptorWrapper = function(r) {
let mutatedRequest = requestInterceptor.apply(this, [r])
let parsedMutatedRequest = Object.assign({}, mutatedRequest)
specActions.setMutatedRequest(req.pathName, req.method, parsedMutatedRequest)
return mutatedRequest
}
req.requestInterceptor = requestInterceptorWrapper
req.responseInterceptor = responseInterceptor
// track duration of request
const startTime = Date.now()
return fn.execute(req)
.then( res => {
res.duration = Date.now() - startTime
specActions.setResponse(req.pathName, req.method, res)
} )
.catch( err => specActions.setResponse(req.pathName, req.method, { error: true, err: serializeError(err) } ) )
}
// I'm using extras as a way to inject properties into the final, `execute` method - It's not great. Anyone have a better idea? @ponelat
export const execute = ( { path, method, ...extras }={} ) => (system) => {
+3 -2
View File
@@ -40,9 +40,10 @@ export default {
},
[UPDATE_PARAM]: ( state, {payload} ) => {
let { path, paramName, value, isXml } = payload
let { path, paramName, paramIn, value, isXml } = payload
return state.updateIn( [ "resolved", "paths", ...path, "parameters" ], fromJS([]), parameters => {
const index = parameters.findIndex(p => p.get( "name" ) === paramName )
const index = parameters.findIndex(p => p.get( "name" ) === paramName && p.get("in") === paramIn )
if (!(value instanceof win.File)) {
value = fromJSOrdered( value )
}
+3 -3
View File
@@ -260,10 +260,10 @@ export const allowTryItOutFor = () => {
}
// Get the parameter value by parameter name
export function getParameter(state, pathMethod, name) {
export function getParameter(state, pathMethod, name, inType) {
let params = spec(state).getIn(["paths", ...pathMethod, "parameters"], fromJS([]))
return params.filter( (p) => {
return Map.isMap(p) && p.get("name") === name
return Map.isMap(p) && p.get("name") === name && p.get("in") === inType
}).first()
}
@@ -280,7 +280,7 @@ export function parameterValues(state, pathMethod, isXml) {
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")
return hash.set(p.get("name"), value)
return hash.set(`${p.get("in")}.${p.get("name")}`, value)
}, fromJS({}))
}
+67 -1
View File
@@ -8,11 +8,31 @@ import some from "lodash/some"
import eq from "lodash/eq"
import { memoizedSampleFromSchema, memoizedCreateXMLExample } from "core/plugins/samples/fn"
import win from "./window"
import cssEscape from "css.escape"
const DEFAULT_REPONSE_KEY = "default"
export const isImmutable = (maybe) => Im.Iterable.isIterable(maybe)
export function isJSONObject (str) {
try {
var o = JSON.parse(str)
// Handle non-exception-throwing cases:
// Neither JSON.parse(false) or JSON.parse(1234) throw errors, hence the type-checking,
// but... JSON.parse(null) returns null, and typeof null === "object",
// so we must check for that, too. Thankfully, null is falsey, so this suffices:
if (o && typeof o === "object") {
return o
}
}
catch (e) {
// do nothing
}
return false
}
export function objectify (thing) {
if(!isObject(thing))
return {}
@@ -480,12 +500,25 @@ export const validateString = ( val ) => {
}
}
export const validateDateTime = (val) => {
if (isNaN(Date.parse(val))) {
return "Value must be a DateTime"
}
}
export const validateGuid = (val) => {
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"
}
}
// validation of parameters before execute
export const validateParam = (param, isXml) => {
let errors = []
let value = isXml && param.get("in") === "body" ? param.get("value_xml") : param.get("value")
let required = param.get("required")
let type = param.get("type")
let format = param.get("format")
/*
If the parameter is required OR the parameter has a value (meaning optional, but filled in)
@@ -508,7 +541,14 @@ export const validateParam = (param, isXml) => {
}
if ( type === "string" ) {
let err = validateString(value)
let err
if (format === "date-time") {
err = validateDateTime(value)
} else if (format === "uuid") {
err = validateGuid(value)
} else {
err = validateString(value)
}
if (!err) return errors
errors.push(err)
} else if ( type === "boolean" ) {
@@ -631,3 +671,29 @@ export const shallowEqualKeys = (a,b, keys) => {
return eq(a[key], b[key])
})
}
export function getAcceptControllingResponse(responses) {
if(!Im.OrderedMap.isOrderedMap(responses)) {
// wrong type!
return null
}
if(!responses.size) {
// responses is empty
return null
}
const suitable2xxResponse = responses.find((res, k) => {
return k.startsWith("2") && Object.keys(res.get("content") || {}).length > 0
})
// try to find a suitable `default` responses
const defaultResponse = responses.get("default") || Im.OrderedMap()
const defaultResponseMediaTypes = (defaultResponse.get("content") || Im.OrderedMap()).keySeq().toJS()
const suitableDefaultResponse = defaultResponseMediaTypes.length ? defaultResponse : null
return suitable2xxResponse || suitableDefaultResponse
}
export const createDeepLinkPath = (str) => typeof str == "string" || str instanceof String ? str.trim().replace(/\s/g, "_") : ""
export const escapeDeepLinkPath = (str) => cssEscape( createDeepLinkPath(str) )
+82
View File
@@ -636,6 +636,77 @@
}
}
.server-container
{
margin: 0 0 20px 0;
padding: 30px 0;
background: #fff;
box-shadow: 0 1px 2px 0 rgba(0,0,0,.15);
.computed-url {
margin: 2em 0;
code {
color: grey;
display: inline-block;
padding: 4px;
font-size: 16px;
margin: 0 1em;
font-style: italic;
}
}
.servers
{
display: flex;
align-items: center;
.servers-title {
margin-right: 1em;
}
> label
{
font-size: 12px;
display: flex;
flex-direction: column;
margin: -20px 15px 0 0;
@include text_headline();
select
{
min-width: 130px;
}
}
table {
tr {
width: 30em;
}
td {
display: inline-block;
max-width: 15em;
vertical-align: middle;
padding-top: 10px;
padding-bottom: 10px;
&:first-of-type {
padding-right: 2em;
}
input {
width: 100%;
height: 100%;
}
}
}
}
}
.loading-container
{
@@ -704,6 +775,17 @@
.response-content-type {
padding-top: 1em;
&.controls-accept-header {
select {
border-color: green;
}
small {
color: green;
font-size: .7em;
}
}
}
@keyframes blinker
+2 -1
View File
@@ -237,7 +237,8 @@ span
.prop-name
{
display: inline-block;
width: 100px;
margin-right: 1em;
width: 8em;
}
.prop-type
+49
View File
@@ -0,0 +1,49 @@
/* eslint-env mocha */
import React from "react"
import expect from "expect"
import { shallow } from "enzyme"
import { fromJS } from "immutable"
import PrimitiveModel from "components/primitive-model"
describe("<PrimitiveModel/>", function() {
describe("Model name", function() {
const dummyComponent = () => null
const components = {
Markdown: dummyComponent,
EnumModel: dummyComponent
}
const props = {
getComponent: c => components[c],
name: "Name from props",
depth: 1,
schema: fromJS({
type: "string",
title: "Custom model title"
})
}
it("renders the schema's title", function() {
// When
const wrapper = shallow(<PrimitiveModel {...props}/>)
const modelTitleEl = wrapper.find("span.model-title")
expect(modelTitleEl.length).toEqual(1)
// Then
expect( modelTitleEl.text() ).toEqual( "Custom model title" )
})
it("falls back to the passed-in `name` prop for the title", function() {
// When
props.schema = fromJS({
type: "string"
})
const wrapper = shallow(<PrimitiveModel {...props}/>)
const modelTitleEl = wrapper.find("span.model-title")
expect(modelTitleEl.length).toEqual(1)
// Then
expect( modelTitleEl.text() ).toEqual( "Name from props" )
})
})
} )
+58
View File
@@ -0,0 +1,58 @@
import React from "react"
import expect from "expect"
import { shallow } from "enzyme"
import { fromJS } from "immutable"
import Response from "components/response"
import ModelExample from "components/model-example"
import { inferSchema } from "corePlugins/samples/fn"
describe("<Response />", function() {
const dummyComponent = () => null
const components = {
headers: dummyComponent,
highlightCode: dummyComponent,
modelExample: ModelExample,
Markdown: dummyComponent,
operationLink: dummyComponent,
contentType: dummyComponent
}
const props = {
getComponent: c => components[c],
specSelectors: {
isOAS3() {
return false
}
},
fn: {
inferSchema
},
contentType: "application/json",
className: "for-test",
response: fromJS({
type: "object",
properties: {
// Note reverse order: c, b, a
"c": {
type: "integer"
},
"b": {
type: "boolean"
},
"a": {
type: "string"
}
}
}),
code: "200"
}
it("renders the model-example schema properties in order", function() {
const wrapper = shallow(<Response {...props}/>)
const renderedModelExample = wrapper.find(ModelExample)
expect(renderedModelExample.length).toEqual(1)
// Assert the schema's properties have maintained their order
const modelExampleSchemaProperties = renderedModelExample.props().schema.toJS().properties
expect( Object.keys(modelExampleSchemaProperties) ).toEqual(["c", "b", "a"])
})
})
+39
View File
@@ -0,0 +1,39 @@
/* eslint-env mocha */
import expect, { createSpy } from "expect"
import { fromJS } from "immutable"
import win from "core/window"
import oauth2Authorize from "core/oauth2-authorize"
describe("oauth2", function () {
let mockSchema = {
flow: "accessCode",
authorizationUrl: "https://testAuthorizationUrl"
}
let authConfig = {
auth: { schema: { get: (key)=> mockSchema[key] } },
authActions: {},
errActions: {},
configs: { oauth2RedirectUrl: "" },
authConfigs: {}
}
describe("authorize redirect", function () {
it("should build authorize url", function() {
win.open = createSpy()
oauth2Authorize(authConfig)
expect(win.open.calls.length).toEqual(1)
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() {
win.open = createSpy()
mockSchema.authorizationUrl = "https://testAuthorizationUrl?param=1"
oauth2Authorize(authConfig)
expect(win.open.calls.length).toEqual(1)
expect(win.open.calls[0].arguments[0]).toMatch("https://testAuthorizationUrl?param=1&response_type=code&redirect_uri=&state=")
})
})
})
+2 -1
View File
@@ -113,7 +113,8 @@ describe("spec plugin - actions", function(){
spec: () => fromJS({}),
parameterValues: () => fromJS({}),
contentTypeValues: () => fromJS({}),
url: () => fromJS({})
url: () => fromJS({}),
isOAS3: () => false
},
getConfigs: () => configs
}
+4 -4
View File
@@ -29,8 +29,8 @@ describe("spec plugin - selectors", function(){
"/one": {
get: {
parameters: [
{ name: "one", value: 1},
{ name: "two", value: "duos"}
{ name: "one", in: "query", value: 1},
{ name: "two", in: "query", value: "duos"}
]
}
}
@@ -43,8 +43,8 @@ describe("spec plugin - selectors", function(){
// Then
expect(paramValues.toJS()).toEqual({
one: 1,
two: "duos"
"query.one": 1,
"query.two": "duos"
})
})
+177 -3
View File
@@ -1,7 +1,7 @@
/* eslint-env mocha */
import expect from "expect"
import { fromJS } from "immutable"
import { mapToList, validateNumber, validateInteger, validateParam, validateFile, fromJSOrdered } from "core/utils"
import { fromJS, OrderedMap } from "immutable"
import { mapToList, validateDateTime, validateGuid, validateNumber, validateInteger, validateParam, validateFile, fromJSOrdered, getAcceptControllingResponse, createDeepLinkPath, escapeDeepLinkPath } from "core/utils"
import win from "core/window"
describe("utils", function() {
@@ -158,7 +158,7 @@ describe("utils", function() {
})
})
describe("validateFile", function() {
describe("validateFile", function() {
let errorMessage = "Value must be a file"
it("validates against objects which are instances of 'File'", function() {
@@ -171,6 +171,33 @@ describe("utils", function() {
})
})
describe("validateDateTime", function() {
let errorMessage = "Value must be a DateTime"
it("doesn't return for valid dates", function() {
expect(validateDateTime("Mon, 25 Dec 1995 13:30:00 +0430")).toBeFalsy()
})
it("returns a message for invalid input'", function() {
expect(validateDateTime(null)).toEqual(errorMessage)
expect(validateDateTime("string")).toEqual(errorMessage)
})
})
describe("validateGuid", function() {
let errorMessage = "Value must be a Guid"
it("doesn't return for valid guid", function() {
expect(validateGuid("8ce4811e-cec5-4a29-891a-15d1917153c1")).toBeFalsy()
expect(validateGuid("{8ce4811e-cec5-4a29-891a-15d1917153c1}")).toBeFalsy()
})
it("returns a message for invalid input'", function() {
expect(validateGuid(1)).toEqual(errorMessage)
expect(validateGuid("string")).toEqual(errorMessage)
})
})
describe("validateParam", function() {
let param = null
let result = null
@@ -581,5 +608,152 @@ describe("utils", function() {
const result = fromJSOrdered(param).toJS()
expect( result ).toEqual( [1, 1, 2, 3, 5, 8] )
})
})
describe("getAcceptControllingResponse", () => {
it("should return the first 2xx response with a media type", () => {
const responses = fromJSOrdered({
"200": {
content: {
"application/json": {
schema: {
type: "object"
}
}
}
},
"201": {
content: {
"application/json": {
schema: {
type: "object"
}
}
}
}
})
expect(getAcceptControllingResponse(responses)).toEqual(responses.get("200"))
})
it("should skip 2xx responses without defined media types", () => {
const responses = fromJSOrdered({
"200": {
content: {
"application/json": {
schema: {
type: "object"
}
}
}
},
"201": {
content: {
"application/json": {
schema: {
type: "object"
}
}
}
}
})
expect(getAcceptControllingResponse(responses)).toEqual(responses.get("201"))
})
it("should default to the `default` response if it has defined media types", () => {
const responses = fromJSOrdered({
"200": {
description: "quite empty"
},
"201": {
description: "quite empty"
},
default: {
content: {
"application/json": {
schema: {
type: "object"
}
}
}
}
})
expect(getAcceptControllingResponse(responses)).toEqual(responses.get("default"))
})
it("should return null if there are no suitable controlling responses", () => {
const responses = fromJSOrdered({
"200": {
description: "quite empty"
},
"201": {
description: "quite empty"
},
"default": {
description: "also empty.."
}
})
expect(getAcceptControllingResponse(responses)).toBe(null)
})
it("should return null if an empty OrderedMap is passed", () => {
const responses = fromJSOrdered()
expect(getAcceptControllingResponse(responses)).toBe(null)
})
it("should return null if anything except an OrderedMap is passed", () => {
const responses = {}
expect(getAcceptControllingResponse(responses)).toBe(null)
})
})
describe("createDeepLinkPath", function() {
it("creates a deep link path replacing spaces with underscores", function() {
const result = createDeepLinkPath("tag id with spaces")
expect(result).toEqual("tag_id_with_spaces")
})
it("trims input when creating a deep link path", function() {
let result = createDeepLinkPath(" spaces before and after ")
expect(result).toEqual("spaces_before_and_after")
result = createDeepLinkPath(" ")
expect(result).toEqual("")
})
it("creates a deep link path with special characters", function() {
const result = createDeepLinkPath("!@#$%^&*(){}[]")
expect(result).toEqual("!@#$%^&*(){}[]")
})
it("returns an empty string for invalid input", function() {
expect( createDeepLinkPath(null) ).toEqual("")
expect( createDeepLinkPath(undefined) ).toEqual("")
expect( createDeepLinkPath(1) ).toEqual("")
expect( createDeepLinkPath([]) ).toEqual("")
expect( createDeepLinkPath({}) ).toEqual("")
})
})
describe("escapeDeepLinkPath", function() {
it("creates and escapes a deep link path", function() {
const result = escapeDeepLinkPath("tag id with spaces?")
expect(result).toEqual("tag_id_with_spaces\\?")
})
it("escapes a deep link path that starts with a number", function() {
const result = escapeDeepLinkPath("123")
expect(result).toEqual("\\31 23")
})
it("escapes a deep link path with a class selector", function() {
const result = escapeDeepLinkPath("hello.world")
expect(result).toEqual("hello\\.world")
})
it("escapes a deep link path with an id selector", function() {
const result = escapeDeepLinkPath("hello#world")
expect(result).toEqual("hello\\#world")
})
})
})