diff --git a/src/core/components/primitive-model.jsx b/src/core/components/primitive-model.jsx
new file mode 100644
index 00000000..96ec3a9d
--- /dev/null
+++ b/src/core/components/primitive-model.jsx
@@ -0,0 +1,55 @@
+import React, { Component } from "react"
+import PropTypes from "prop-types"
+
+const propStyle = { color: "#999", fontStyle: "italic" }
+
+export default class Primitive extends Component {
+ static propTypes = {
+ schema: PropTypes.object.isRequired,
+ getComponent: PropTypes.func.isRequired,
+ required: PropTypes.bool
+ }
+
+ render(){
+ let { schema, getComponent, required } = this.props
+
+ if(!schema || !schema.get) {
+ // don't render if schema isn't correctly formed
+ return
+ }
+
+ let type = schema.get("type")
+ let format = schema.get("format")
+ let xml = schema.get("xml")
+ let enumArray = schema.get("enum")
+ let description = schema.get("description")
+ let properties = schema.filter( ( v, key) => ["enum", "type", "format", "description", "$$ref"].indexOf(key) === -1 )
+ let style = required ? { fontWeight: "bold" } : {}
+ const Markdown = getComponent("Markdown")
+ const EnumModel = getComponent("EnumModel")
+
+ return
+ { type } { required && *}
+ { format && (${format})}
+ {
+ properties.size ? properties.entrySeq().map( ( [ key, v ] ) =>
+
{ key }: { String(v) })
+ : null
+ }
+ {
+ !description ? null :
+
+ }
+ {
+ xml && xml.size ? (
xml:
+ {
+ xml.entrySeq().map( ( [ key, v ] ) =>
{key}: { String(v) }).toArray()
+ }
+ ): null
+ }
+ {
+ enumArray &&
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/src/core/index.js b/src/core/index.js
index ed6768e1..f9e76aac 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -6,7 +6,7 @@ import ApisPreset from "core/presets/apis"
import * as AllPlugins from "core/plugins/all"
import { parseSeach, filterConfigs } from "core/utils"
-const CONFIGS = [ "url", "urls", "urls.primaryName", "spec", "validatorUrl", "onComplete", "onFailure", "authorizations", "docExpansion",
+const CONFIGS = [ "url", "urls", "urls.primaryName", "spec", "validatorUrl", "onComplete", "onFailure", "authorizations", "docExpansion", "maxDisplayedTags", "filter",
"apisSorter", "operationsSorter", "supportedSubmitMethods", "dom_id", "defaultModelRendering", "oauth2RedirectUrl",
"showRequestHeaders", "custom", "modelPropertyMacro", "parameterMacro", "displayOperationId" , "displayRequestDuration"]
@@ -32,6 +32,8 @@ module.exports = function SwaggerUI(opts) {
urls: null,
layout: "BaseLayout",
docExpansion: "list",
+ maxDisplayedTags: null,
+ filter: null,
validatorUrl: "https://online.swagger.io/validator",
configs: {},
custom: {},
@@ -56,7 +58,9 @@ module.exports = function SwaggerUI(opts) {
store: { },
}
- const constructorConfig = deepExtend({}, defaults, opts)
+ let queryConfig = parseSeach()
+
+ const constructorConfig = deepExtend({}, defaults, opts, queryConfig)
const storeConfigs = deepExtend({}, constructorConfig.store, {
system: {
@@ -65,7 +69,8 @@ module.exports = function SwaggerUI(opts) {
plugins: constructorConfig.presets,
state: {
layout: {
- layout: constructorConfig.layout
+ layout: constructorConfig.layout,
+ filter: constructorConfig.filter
},
spec: {
spec: "",
@@ -86,7 +91,6 @@ module.exports = function SwaggerUI(opts) {
store.register([constructorConfig.plugins, inlinePlugin])
var system = store.getSystem()
- let queryConfig = parseSeach()
system.initOAuth = system.authActions.configureAuth
diff --git a/src/core/plugins/layout/actions.js b/src/core/plugins/layout/actions.js
index b8cba718..d65d34e3 100644
--- a/src/core/plugins/layout/actions.js
+++ b/src/core/plugins/layout/actions.js
@@ -1,6 +1,7 @@
import { normalizeArray } from "core/utils"
export const UPDATE_LAYOUT = "layout_update_layout"
+export const UPDATE_FILTER = "layout_update_filter"
export const UPDATE_MODE = "layout_update_mode"
export const SHOW = "layout_show"
@@ -13,6 +14,13 @@ export function updateLayout(layout) {
}
}
+export function updateFilter(filter) {
+ return {
+ type: UPDATE_FILTER,
+ payload: filter
+ }
+}
+
export function show(thing, shown=true) {
thing = normalizeArray(thing)
return {
diff --git a/src/core/plugins/layout/reducers.js b/src/core/plugins/layout/reducers.js
index 44e3c480..4b561015 100644
--- a/src/core/plugins/layout/reducers.js
+++ b/src/core/plugins/layout/reducers.js
@@ -1,5 +1,6 @@
import {
UPDATE_LAYOUT,
+ UPDATE_FILTER,
UPDATE_MODE,
SHOW
} from "./actions"
@@ -8,6 +9,8 @@ export default {
[UPDATE_LAYOUT]: (state, action) => state.set("layout", action.payload),
+ [UPDATE_FILTER]: (state, action) => state.set("filter", action.payload),
+
[SHOW]: (state, action) => {
let thing = action.payload.thing
let shown = action.payload.shown
diff --git a/src/core/plugins/layout/selectors.js b/src/core/plugins/layout/selectors.js
index 0eb0c522..42a3bcbb 100644
--- a/src/core/plugins/layout/selectors.js
+++ b/src/core/plugins/layout/selectors.js
@@ -5,6 +5,8 @@ const state = state => state
export const current = state => state.get("layout")
+export const currentFilter = state => state.get("filter")
+
export const isShown = (state, thing, def) => {
thing = normalizeArray(thing)
return Boolean(state.getIn(["shown", ...thing], def))
diff --git a/src/core/plugins/spec/selectors.js b/src/core/plugins/spec/selectors.js
index a4f94349..cd7e722b 100644
--- a/src/core/plugins/spec/selectors.js
+++ b/src/core/plugins/spec/selectors.js
@@ -277,12 +277,13 @@ export function parametersIncludeType(parameters, typeValue="") {
export function contentTypeValues(state, pathMethod) {
let op = spec(state).getIn(["paths", ...pathMethod], fromJS({}))
const parameters = op.get("parameters") || new List()
- const requestContentType = (
- parametersIncludeType(parameters, "file") ? "multipart/form-data"
- : parametersIncludeIn(parameters, "formData") ? "application/x-www-form-urlencoded"
- : op.get("consumes_value")
- )
+ const requestContentType = (
+ op.get("consumes_value") ? op.get("consumes_value")
+ : parametersIncludeType(parameters, "file") ? "multipart/form-data"
+ : parametersIncludeType(parameters, "formData") ? "application/x-www-form-urlencoded"
+ : undefined
+ )
return fromJS({
requestContentType,
diff --git a/src/core/presets/base.js b/src/core/presets/base.js
index 20ff4659..d5471791 100644
--- a/src/core/presets/base.js
+++ b/src/core/presets/base.js
@@ -41,9 +41,15 @@ import Footer from "core/components/footer"
import ParamBody from "core/components/param-body"
import Curl from "core/components/curl"
import Schemes from "core/components/schemes"
+import ModelCollapse from "core/components/model-collapse"
import ModelExample from "core/components/model-example"
+import ModelWrapper from "core/components/model-wrapper"
import Model from "core/components/model"
import Models from "core/components/models"
+import EnumModel from "core/components/enum-model"
+import ObjectModel from "core/components/object-model"
+import ArrayModel from "core/components/array-model"
+import PrimitiveModel from "core/components/primitive-model"
import TryItOutButton from "core/components/try-it-out-button"
import Markdown from "core/components/providers/markdown"
@@ -88,8 +94,14 @@ export default function() {
curl: Curl,
schemes: Schemes,
modelExample: ModelExample,
- model: Model,
- models: Models,
+ ModelWrapper,
+ ModelCollapse,
+ Model,
+ Models,
+ EnumModel,
+ ObjectModel,
+ ArrayModel,
+ PrimitiveModel,
TryItOutButton,
Markdown,
BaseLayout
diff --git a/src/plugins/topbar/topbar.jsx b/src/plugins/topbar/topbar.jsx
index 11b5c7eb..314e1d4e 100644
--- a/src/plugins/topbar/topbar.jsx
+++ b/src/plugins/topbar/topbar.jsx
@@ -6,6 +6,11 @@ import Logo from "./logo_small.png"
export default class Topbar extends React.Component {
+ static propTypes = {
+ layoutSelectors: PropTypes.object.isRequired,
+ layoutActions: PropTypes.object.isRequired
+ }
+
constructor(props, context) {
super(props, context)
this.state = { url: props.specSelectors.url(), selectedIndex: 0 }
@@ -80,13 +85,19 @@ export default class Topbar extends React.Component {
}
}
+ onFilterChange =(e) => {
+ let {target: {value}} = e
+ this.props.layoutActions.updateFilter(value)
+ }
+
render() {
- let { getComponent, specSelectors, getConfigs } = this.props
+ let { getComponent, specSelectors, getConfigs, layoutSelectors } = this.props
const Button = getComponent("Button")
const Link = getComponent("Link")
let isLoading = specSelectors.loadingStatus() === "loading"
let isFailed = specSelectors.loadingStatus() === "failed"
+ let filter = layoutSelectors.currentFilter()
let inputStyle = {}
if(isFailed) inputStyle.color = "red"
@@ -124,6 +135,10 @@ export default class Topbar extends React.Component {
swagger
+ {
+ filter === null || filter === false ? null :
+
+ }
diff --git a/src/standalone/layout.jsx b/src/standalone/layout.jsx
index 20ca50d1..dc36b46e 100644
--- a/src/standalone/layout.jsx
+++ b/src/standalone/layout.jsx
@@ -29,7 +29,7 @@ export default class StandaloneLayout extends React.Component {
return (
- { Topbar ? : null }
+ { Topbar ? : null }
{ loadingStatus === "loading" &&
Loading...
@@ -45,7 +45,7 @@ export default class StandaloneLayout extends React.Component {
Failed to load config.
}
- { !loadingStatus || loadingStatus === "success" && }
+ { !loadingStatus || loadingStatus === "success" && }
diff --git a/src/style/_layout.scss b/src/style/_layout.scss
index 955e4d54..78e09249 100644
--- a/src/style/_layout.scss
+++ b/src/style/_layout.scss
@@ -45,6 +45,7 @@ body
.opblock-tag
{
display: flex;
+ align-items: center;
padding: 10px 20px 10px 10px;
@@ -53,8 +54,6 @@ body
border-bottom: 1px solid rgba(#3b4151, .3);
- align-items: center;
-
&:hover
{
background: rgba(#000,.02);
@@ -106,9 +105,10 @@ body
font-size: 14px;
font-weight: normal;
+ flex: 1;
+
padding: 0 10px;
- flex: 1;
@include text_body();
}
}
@@ -134,6 +134,8 @@ body
transition: all .5s;
}
+
+
.opblock
{
margin: 0 0 15px 0;
@@ -154,24 +156,23 @@ body
.opblock-section-header
{
display: flex;
+ align-items: center;
padding: 8px 20px;
background: rgba(#fff,.8);
box-shadow: 0 1px 2px rgba(#000,.1);
- align-items: center;
-
label
{
font-size: 12px;
font-weight: bold;
display: flex;
+ align-items: center;
margin: 0;
- align-items: center;
@include text_headline();
span
@@ -184,9 +185,10 @@ body
{
font-size: 14px;
+ flex: 1;
+
margin: 0;
- flex: 1;
@include text_headline();
}
}
@@ -215,11 +217,11 @@ body
font-size: 16px;
display: flex;
+ align-items: center;
padding: 0 10px;
@include text_code();
- align-items: center;
.view-line-link
{
@@ -258,18 +260,18 @@ body
font-size: 13px;
flex: 1;
+
@include text_body();
}
.opblock-summary
{
display: flex;
+ align-items: center;
padding: 5px;
cursor: pointer;
-
- align-items: center;
}
&.opblock-post
@@ -316,12 +318,12 @@ body
.opblock-schemes
{
- padding: 8px 20px;
+ padding: 8px 20px;
- .schemes-title
- {
- padding: 0 10px 0 0;
- }
+ .schemes-title
+ {
+ padding: 0 10px 0 0;
+ }
}
}
@@ -498,13 +500,11 @@ body
margin: 0;
padding: 10px;
-
+ white-space: pre-wrap;
word-wrap: break-word;
word-break: break-all;
word-break: break-word;
hyphens: auto;
- white-space: pre-wrap;
-
border-radius: 4px;
background: #41444e;
@@ -533,10 +533,9 @@ body
.schemes
{
display: flex;
-
align-items: center;
- > label
+ > label
{
font-size: 12px;
font-weight: bold;
@@ -624,3 +623,12 @@ body
opacity: 0;
}
}
+
+
+section
+{
+ h3
+ {
+ @include text_headline();
+ }
+}
diff --git a/src/style/_topbar.scss b/src/style/_topbar.scss
index 4bded694..b1427c78 100644
--- a/src/style/_topbar.scss
+++ b/src/style/_topbar.scss
@@ -29,6 +29,12 @@
padding: 0 10px;
}
}
+ .operation-filter-input
+ {
+ border: 2px solid #547f00;
+ border-right: none;
+ border-radius: 4px 0 0 4px;
+ }
.download-url-wrapper
{
@@ -43,7 +49,7 @@
margin: 0;
border: 2px solid #547f00;
- border-radius: 4px 0 0 4px;
+ border-radius: 0 0 0 0;
outline: none;
}
diff --git a/test/core/plugins/spec/selectors.js b/test/core/plugins/spec/selectors.js
index 93abd025..7519a42e 100644
--- a/test/core/plugins/spec/selectors.js
+++ b/test/core/plugins/spec/selectors.js
@@ -52,7 +52,6 @@ describe("spec plugin - selectors", function(){
})
describe("contentTypeValues", function(){
-
it("should return { requestContentType, responseContentType } from an operation", function(){
// Given
let state = fromJS({
@@ -77,6 +76,73 @@ describe("spec plugin - selectors", function(){
})
})
+ it("should prioritize consumes value first from an operation", function(){
+ // Given
+ let state = fromJS({
+ resolved: {
+ paths: {
+ "/one": {
+ get: {
+ "consumes_value": "one",
+ "parameters": [{
+ "type": "file"
+ }],
+ }
+ }
+ }
+ }
+ })
+
+ // When
+ let contentTypes = contentTypeValues(state, [ "/one", "get" ])
+ // Then
+ expect(contentTypes.toJS().requestContentType).toEqual("one")
+ })
+
+ it("should fallback to multipart/form-data if there is no consumes value but there is a file parameter", function(){
+ // Given
+ let state = fromJS({
+ resolved: {
+ paths: {
+ "/one": {
+ get: {
+ "parameters": [{
+ "type": "file"
+ }],
+ }
+ }
+ }
+ }
+ })
+
+ // When
+ let contentTypes = contentTypeValues(state, [ "/one", "get" ])
+ // Then
+ expect(contentTypes.toJS().requestContentType).toEqual("multipart/form-data")
+ })
+
+ it("should fallback to application/x-www-form-urlencoded if there is no consumes value, no file parameter, but there is a formData parameter", function(){
+ // Given
+ let state = fromJS({
+ resolved: {
+ paths: {
+ "/one": {
+ get: {
+ "parameters": [{
+ "type": "formData"
+ }],
+ }
+ }
+ }
+ }
+ })
+
+ // When
+ let contentTypes = contentTypeValues(state, [ "/one", "get" ])
+ // Then
+ expect(contentTypes.toJS().requestContentType).toEqual("application/x-www-form-urlencoded")
+ })
+
it("should be ok, if no operation found", function(){
// Given
let state = fromJS({ })