diff --git a/.agignore b/.agignore new file mode 100644 index 00000000..849ddff3 --- /dev/null +++ b/.agignore @@ -0,0 +1 @@ +dist/ diff --git a/docs/customization/plugin-api.md b/docs/customization/plugin-api.md index db88ca71..245b5a14 100644 --- a/docs/customization/plugin-api.md +++ b/docs/customization/plugin-api.md @@ -293,7 +293,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 +310,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 +324,7 @@ const MyNumberDisplayPlugin = function(system) { } } +// Here's a component wrapper defined as a function. const MyWrapComponentPlugin = function(system) { return { wrapComponents: { @@ -328,6 +332,7 @@ const MyWrapComponentPlugin = function(system) { if(props.number > 10) { return

Warning! Big number ahead.

+
} else { return @@ -336,8 +341,30 @@ 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
+

Warning! Big number ahead.

+ +
+ } else { + return + } + } + } + } + } +} ``` + + ##### fn The fn interface allows you to add helper functions to the system for use elsewhere. diff --git a/docs/usage/installation.md b/docs/usage/installation.md index 63f7d233..e4fd09d2 100644 --- a/docs/usage/installation.md +++ b/docs/usage/installation.md @@ -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 - ``` diff --git a/src/core/components/deep-link.jsx b/src/core/components/deep-link.jsx new file mode 100644 index 00000000..44aa08bb --- /dev/null +++ b/src/core/components/deep-link.jsx @@ -0,0 +1,20 @@ +import React from "react" +import PropTypes from "prop-types" + +export const DeepLink = ({ enabled, path, text }) => { + return ( + e.preventDefault() : null} + href={enabled ? `#/${path}` : null}> + {text} + + ) +} +DeepLink.propTypes = { + enabled: PropTypes.bool, + isShown: PropTypes.bool, + path: PropTypes.string, + text: PropTypes.string +} + +export default DeepLink diff --git a/src/core/components/operation.jsx b/src/core/components/operation.jsx index 37ec4d16..7063cf22 100644 --- a/src/core/components/operation.jsx +++ b/src/core/components/operation.jsx @@ -102,12 +102,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 +121,11 @@ export default class Operation extends PureComponent { and pulled in with getComponent */} {method.toUpperCase()} - e.preventDefault() : null} - href={isDeepLinkingEnabled ? `#/${isShownKey.join("/")}` : null}> - {path} - + {/*TODO: use wrapComponents here, swagger-ui doesn't care about jumpToPath */} diff --git a/src/core/components/operations.jsx b/src/core/components/operations.jsx index 46a04cc9..6500587e 100644 --- a/src/core/components/operations.jsx +++ b/src/core/components/operations.jsx @@ -37,6 +37,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 +80,11 @@ export default class Operations extends React.Component { onClick={() => layoutActions.show(isShownKey, !showTag)} className={!tagDescription ? "opblock-tag no-desc" : "opblock-tag" } id={isShownKey.join("-")}> - e.preventDefault() : null} - href= {isDeepLinkingEnabled ? `#/${tag}` : null}> - {tag} - + { !tagDescription ? null : diff --git a/src/core/components/param-body.jsx b/src/core/components/param-body.jsx index 980ef490..13a4377f 100644 --- a/src/core/components/param-body.jsx +++ b/src/core/components/param-body.jsx @@ -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") diff --git a/src/core/components/response-body.jsx b/src/core/components/response-body.jsx index 1e48f4e5..e239c258 100644 --- a/src/core/components/response-body.jsx +++ b/src/core/components/response-body.jsx @@ -43,7 +43,11 @@ export default class ResponseBody extends React.Component { // Image } else if (/^image\//i.test(contentType)) { - bodyEl = + if(contentType.includes("svg")) { + bodyEl =
{ content }
+ } else { + bodyEl = + } // Audio } else if (/^audio\//i.test(contentType)) { diff --git a/src/core/oauth2-authorize.js b/src/core/oauth2-authorize.js index c5521aaf..0de353d2 100644 --- a/src/core/oauth2-authorize.js +++ b/src/core/oauth2-authorize.js @@ -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 } diff --git a/src/core/plugins/ast/ast.js b/src/core/plugins/ast/ast.js index d067e80f..880cdb38 100644 --- a/src/core/plugins/ast/ast.js +++ b/src/core/plugins/ast/ast.js @@ -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 } } diff --git a/src/core/plugins/spec/actions.js b/src/core/plugins/spec/actions.js index b332f953..6ec1ed9c 100644 --- a/src/core/plugins/spec/actions.js +++ b/src/core/plugins/spec/actions.js @@ -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) { + return specActions.updateJsonSpec(json) + } + return {} } export const resolveSpec = (json, url) => ({specActions, specSelectors, errActions, fn: { fetch, resolve, AST }, getConfigs}) => { @@ -130,18 +125,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, diff --git a/src/core/plugins/spec/selectors.js b/src/core/plugins/spec/selectors.js index 71914c7d..ba35abc2 100644 --- a/src/core/plugins/spec/selectors.js +++ b/src/core/plugins/spec/selectors.js @@ -256,10 +256,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 +273,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 +297,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 +316,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 +333,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 diff --git a/src/core/presets/base.js b/src/core/presets/base.js index 133df134..1521fa28 100644 --- a/src/core/presets/base.js +++ b/src/core/presets/base.js @@ -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 } } diff --git a/test/components/operations.js b/test/components/operations.js index 8a6a7506..bfcbed4f 100644 --- a/test/components/operations.js +++ b/test/components/operations.js @@ -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 }) => }