diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..3acb80b0 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,54 @@ +## Contributing to Swagger-UI + +We love contributions from our community of users! This document explains our guidelines and workflows. Please take care to follow them, as it helps us keep things moving smoothly. + +#### Environment setup + +0. Install Node.js (4 or newer) and npm (3 or newer). +1. Make a fork of Swagger-UI on GitHub, then clone your fork to your machine. +2. Run `npm install` in your Swagger-UI directory. +3. Run `npm run dev`. `localhost:3200` should open automatically. +4. You're ready to go! + +#### Branching model + +Feature branches should be prefixed with `ft/`. + +Bugfix branches should be prefixed with `bug/`. + +Version branches should be prefixed with `v/`. + +After the forward slash, include a short description of what you're fixing. For example: `bug/fix-everything-that-was-broken`. For versions, add the version that will be released via the branch, for example: `v/1.2.3`. + +If there's an issue filed that you're addressing in your branch, include the issue number directly after the forward slash. For example: `bug/1234-fix-all-the-other-things`. + +#### Filing issues + +- **Do** include the Swagger-UI build you're using - you can find this by opening your console and checking `window.versions.swaggerUi` +- **Do** include a spec that demonstrates the issue you're experiencing. +- **Do** include screenshots, if needed. GIFs are even better! +- **Do** place code inside of a pre-formatted container by surrounding the code with triple backticks. +- **Don't** open tickets discussing issues with the Swagger/OpenAPI specification itself, or for issues with projects that use Swagger-UI. +- **Don't** open an issue without searching the issue tracker for duplicates first. + +#### Committing + +- Break your commits into logical atomic units. Well-segmented commits make it _much_ easier for others to step through your changes. +- Limit your subject (first) line to 50 characters (GitHub truncates more than 70). +- Provide a body if you'd like to explain your commit in detail. +- Capitalize the beginning of your subject line, and do not end the subject line with a period. +- Your subject line should complete this sentence: `If applied, this commit will [your subject line].` +- Don't use [magic GitHub words](https://help.github.com/articles/closing-issues-using-keywords/) in your commits to close issues - do that in the pull request for your code instead. + +_Adapted from [How to Write a Git Commit Message](https://chris.beams.io/posts/git-commit/#seven-rules)._ + +#### Making pull requests + +- **Do** summarize your changes in the PR body. If in doubt, write a bullet-point list titled `This PR does the following:`. +- **Do** include references to issues that your PR solves, and use [magic GitHub words](https://help.github.com/articles/closing-issues-using-keywords/) to close those issues automatically when your PR is merged. +- **Do** include tests that cover new or changed functionality. +- **Do** be careful to follow our ESLint style rules. We recommend installing an ESLint plugin if you use a graphical code editor. +- **Do** make sure that tests and the linter are passing by running `npm test` locally, otherwise we can't merge your pull request. +- **Don't** include any changes to files in the `dist/` directory - we update those files only during releases. +- **Don't** mention maintainers in your original PR body - we probably would've seen it anyway, so it just increases the noise in our inboxes. Do feel free to ping maintainers if a week has passed and you've heard nothing from us. +- **Don't** open PRs for custom functionality that only serves a small subset of our users - custom functionality should be implemented outside of our codebase, via a plugin. diff --git a/package.json b/package.json index 1a2ce9d1..6402639c 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "promise-worker": "^1.1.1", "prop-types": "^15.5.10", "react": "^15.4.0", - "react-addons-perf": "0.14.8", + "react-addons-perf": "^15.4.0", "react-addons-shallow-compare": "0.14.8", "react-addons-test-utils": "^15.4.0", "react-collapse": "2.3.1", diff --git a/src/core/components/auth/authorization-popup.jsx b/src/core/components/auth/authorization-popup.jsx index 887a06a5..0746bcb5 100644 --- a/src/core/components/auth/authorization-popup.jsx +++ b/src/core/components/auth/authorization-popup.jsx @@ -23,7 +23,7 @@ export default class AuthorizationPopup extends React.Component {

Available authorizations

diff --git a/src/core/components/auth/authorize-btn.jsx b/src/core/components/auth/authorize-btn.jsx index b7266bce..6106dfd6 100644 --- a/src/core/components/auth/authorize-btn.jsx +++ b/src/core/components/auth/authorize-btn.jsx @@ -25,7 +25,7 @@ export default class AuthorizeBtn extends React.Component { { showPopup && } diff --git a/src/core/components/auth/authorize-operation-btn.jsx b/src/core/components/auth/authorize-operation-btn.jsx index 2753bffe..7c2f2448 100644 --- a/src/core/components/auth/authorize-operation-btn.jsx +++ b/src/core/components/auth/authorize-operation-btn.jsx @@ -24,7 +24,7 @@ export default class AuthorizeOperationBtn extends React.Component { return ( diff --git a/src/core/components/models.jsx b/src/core/components/models.jsx index 86c3256d..1af412ab 100644 --- a/src/core/components/models.jsx +++ b/src/core/components/models.jsx @@ -24,7 +24,7 @@ export default class Models extends Component { return

layoutActions.show("models", !showModels)}> Models - +

diff --git a/src/core/components/operations.jsx b/src/core/components/operations.jsx index e4022754..ef1d4248 100644 --- a/src/core/components/operations.jsx +++ b/src/core/components/operations.jsx @@ -109,7 +109,7 @@ export default class Operations extends React.Component { diff --git a/src/core/components/param-body.jsx b/src/core/components/param-body.jsx index e64a65cc..48373f5f 100644 --- a/src/core/components/param-body.jsx +++ b/src/core/components/param-body.jsx @@ -69,7 +69,9 @@ export default class ParamBody extends PureComponent { let { param, fn:{inferSchema} } = this.props let schema = inferSchema(param.toJS()) - return getSampleSchema(schema, xml) + return getSampleSchema(schema, xml, { + includeWriteOnly: true + }) } onChange = (value, { isEditBox, isXml }) => { diff --git a/src/core/components/parameter-row.jsx b/src/core/components/parameter-row.jsx index 77581242..04466a38 100644 --- a/src/core/components/parameter-row.jsx +++ b/src/core/components/parameter-row.jsx @@ -82,10 +82,11 @@ export default class ParameterRow extends Component { 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(["items", "type"]) + let itemType = param.getIn(isOAS3 && isOAS3() ? ["schema", "items", "type"] : ["items", "type"]) let parameter = specSelectors.getParameter(pathMethod, param.get("name")) let value = parameter ? parameter.get("value") : "" @@ -96,7 +97,7 @@ export default class ParameterRow extends Component { { param.get("name") } { !required ? null :  * } -
{ param.get("type") } { itemType && `[${itemType}]` }
+
{ type } { itemType && `[${itemType}]` }
{ isOAS3 && isOAS3() && param.get("deprecated") ? "deprecated": null }
diff --git a/src/core/components/response.jsx b/src/core/components/response.jsx index 9d30849d..8c0a8bf1 100644 --- a/src/core/components/response.jsx +++ b/src/core/components/response.jsx @@ -83,11 +83,16 @@ export default class Response extends React.Component { if(isOAS3()) { let oas3SchemaForContentType = response.getIn(["content", this.state.responseContentType, "schema"]) - sampleResponse = oas3SchemaForContentType ? getSampleSchema(oas3SchemaForContentType.toJS(), this.state.responseContentType, { includeReadOnly: true }) : null + sampleResponse = oas3SchemaForContentType ? getSampleSchema(oas3SchemaForContentType.toJS(), this.state.responseContentType, { + includeReadOnly: true + }) : null schema = oas3SchemaForContentType ? inferSchema(oas3SchemaForContentType.toJS()) : null } else { schema = inferSchema(response.toJS()) - sampleResponse = schema ? getSampleSchema(schema, contentType, { includeReadOnly: true }) : null + sampleResponse = schema ? getSampleSchema(schema, contentType, { + includeReadOnly: true, + includeWriteOnly: true // writeOnly has no filtering effect in swagger 2.0 + }) : null } let example = getExampleComponent( sampleResponse, examples, HighlightCode ) diff --git a/src/core/curlify.js b/src/core/curlify.js index 2564ed92..328830c1 100644 --- a/src/core/curlify.js +++ b/src/core/curlify.js @@ -20,7 +20,7 @@ export default function curl( request ){ if ( request.get("body") ){ if(type === "multipart/form-data" && request.get("method") === "POST") { - for( let [ k,v ] of request.get("body").values()) { + for( let [ k,v ] of request.get("body").entrySeq()) { curlified.push( "-F" ) if (v instanceof win.File) { curlified.push( `"${k}=@${v.name};type=${v.type}"` ) diff --git a/src/core/index.js b/src/core/index.js index 3dbcede4..9d9c0ac3 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -6,6 +6,11 @@ import ApisPreset from "core/presets/apis" import * as AllPlugins from "core/plugins/all" import { parseSearch } from "core/utils" +if (process.env.NODE_ENV !== "production") { + const Perf = require("react-addons-perf") + window.Perf = Perf +} + // eslint-disable-next-line no-undef const { GIT_DIRTY, GIT_COMMIT, PACKAGE_VERSION, HOSTNAME, BUILD_TIME } = buildInfo diff --git a/src/core/plugins/oas3/components/request-body.jsx b/src/core/plugins/oas3/components/request-body.jsx index cce3b9f3..2cbf491a 100644 --- a/src/core/plugins/oas3/components/request-body.jsx +++ b/src/core/plugins/oas3/components/request-body.jsx @@ -16,7 +16,9 @@ const RequestBody = ({ requestBody, getComponent, specSelectors, contentType }) const mediaTypeValue = requestBodyContent.get(contentType) - const sampleSchema = getSampleSchema(mediaTypeValue.get("schema").toJS(), contentType) + const sampleSchema = getSampleSchema(mediaTypeValue.get("schema").toJS(), contentType, { + includeWriteOnly: true + }) return
{ requestBodyDescription && diff --git a/src/core/plugins/samples/fn.js b/src/core/plugins/samples/fn.js index d4f46c1b..9f166f20 100644 --- a/src/core/plugins/samples/fn.js +++ b/src/core/plugins/samples/fn.js @@ -9,7 +9,7 @@ const primitives = { "number": () => 0, "number_float": () => 0.0, "integer": () => 0, - "boolean": (schema) => typeof schema.default === "boolean" ? schema.default : true + "boolean": (schema) => typeof schema.default === "boolean" ? schema.default : true } const primitive = (schema) => { @@ -27,7 +27,7 @@ const primitive = (schema) => { export const sampleFromSchema = (schema, config={}) => { let { type, example, properties, additionalProperties, items } = objectify(schema) - let { includeReadOnly } = config + let { includeReadOnly, includeWriteOnly } = config if(example !== undefined) return example @@ -46,16 +46,20 @@ export const sampleFromSchema = (schema, config={}) => { let props = objectify(properties) let obj = {} for (var name in props) { - if ( !props[name].readOnly || includeReadOnly ) { - obj[name] = sampleFromSchema(props[name], { includeReadOnly: includeReadOnly }) + if ( props[name].readOnly && !includeReadOnly ) { + continue } + if ( props[name].writeOnly && !includeWriteOnly ) { + continue + } + obj[name] = sampleFromSchema(props[name], config) } if ( additionalProperties === true ) { obj.additionalProp1 = {} } else if ( additionalProperties ) { let additionalProps = objectify(additionalProperties) - let additionalPropVal = sampleFromSchema(additionalProps, { includeReadOnly: includeReadOnly }) + let additionalPropVal = sampleFromSchema(additionalProps, config) for (let i = 1; i < 4; i++) { obj["additionalProp" + i] = additionalPropVal @@ -65,7 +69,7 @@ export const sampleFromSchema = (schema, config={}) => { } if(type === "array") { - return [ sampleFromSchema(items, { includeReadOnly: includeReadOnly }) ] + return [ sampleFromSchema(items, config) ] } if(schema["enum"]) { @@ -96,7 +100,7 @@ export const inferSchema = (thing) => { export const sampleXmlFromSchema = (schema, config={}) => { let objectifySchema = objectify(schema) let { type, properties, additionalProperties, items, example } = objectifySchema - let { includeReadOnly } = config + let { includeReadOnly, includeWriteOnly } = config let defaultValue = objectifySchema.default let res = {} let _attr = {} @@ -177,27 +181,32 @@ export const sampleXmlFromSchema = (schema, config={}) => { example = example || {} for (let propName in props) { - if ( !props[propName].readOnly || includeReadOnly ) { - props[propName].xml = props[propName].xml || {} + if ( props[propName].readOnly && !includeReadOnly ) { + continue + } + if ( props[propName].writeOnly && !includeWriteOnly ) { + continue + } - if (props[propName].xml.attribute) { - let enumAttrVal = Array.isArray(props[propName].enum) && props[propName].enum[0] - let attrExample = props[propName].example - let attrDefault = props[propName].default - _attr[props[propName].xml.name || propName] = attrExample!== undefined && attrExample - || example[propName] !== undefined && example[propName] || attrDefault !== undefined && attrDefault - || enumAttrVal || primitive(props[propName]) + props[propName].xml = props[propName].xml || {} + + if (props[propName].xml.attribute) { + let enumAttrVal = Array.isArray(props[propName].enum) && props[propName].enum[0] + let attrExample = props[propName].example + let attrDefault = props[propName].default + _attr[props[propName].xml.name || propName] = attrExample!== undefined && attrExample + || example[propName] !== undefined && example[propName] || attrDefault !== undefined && attrDefault + || enumAttrVal || primitive(props[propName]) + } else { + props[propName].xml.name = props[propName].xml.name || propName + props[propName].example = props[propName].example !== undefined ? props[propName].example : example[propName] + let t = sampleXmlFromSchema(props[propName]) + if (Array.isArray(t)) { + res[displayName] = res[displayName].concat(t) } else { - props[propName].xml.name = props[propName].xml.name || propName - props[propName].example = props[propName].example !== undefined ? props[propName].example : example[propName] - let t = sampleXmlFromSchema(props[propName]) - if (Array.isArray(t)) { - res[displayName] = res[displayName].concat(t) - } else { - res[displayName].push(t) - } - + res[displayName].push(t) } + } } diff --git a/src/style/_topbar.scss b/src/style/_topbar.scss index d3b76aa6..4bded694 100644 --- a/src/style/_topbar.scss +++ b/src/style/_topbar.scss @@ -43,6 +43,7 @@ margin: 0; border: 2px solid #547f00; + border-radius: 4px 0 0 4px; outline: none; } diff --git a/test/core/curlify.js b/test/core/curlify.js index 82ee6a8a..8084bd6e 100644 --- a/test/core/curlify.js +++ b/test/core/curlify.js @@ -132,10 +132,10 @@ describe("curlify", function() { url: "http://example.com", method: "POST", headers: { "content-type": "multipart/form-data" }, - body: [ - ["id", "123"], - ["name", "Sahar"] - ] + body: { + id: "123", + name: "Sahar" + } } let curlified = curl(Im.fromJS(req)) @@ -152,10 +152,10 @@ describe("curlify", function() { url: "http://example.com", method: "POST", headers: { "content-type": "multipart/form-data" }, - body: [ - ["id", "123"], - ["file", file] - ] + body: { + id: "123", + file + } } let curlified = curl(Im.fromJS(req)) diff --git a/test/core/plugins/samples/fn.js b/test/core/plugins/samples/fn.js index a2d01876..12ec783f 100644 --- a/test/core/plugins/samples/fn.js +++ b/test/core/plugins/samples/fn.js @@ -1,6 +1,105 @@ -import { createXMLExample } from "corePlugins/samples/fn" +import { createXMLExample, sampleFromSchema } from "corePlugins/samples/fn" import expect from "expect" +describe("sampleFromSchema", function() { + it("returns object with no readonly fields for parameter", function () { + var definition = { + type: "object", + properties: { + id: { + type: "integer" + }, + readOnlyDog: { + readOnly: true, + type: "string" + } + }, + xml: { + name: "animals" + } + } + + var expected = { + id: 0 + } + + expect(sampleFromSchema(definition, { includeReadOnly: false })).toEqual(expected) + }) + + it("returns object with readonly fields for parameter, with includeReadOnly", function () { + var definition = { + type: "object", + properties: { + id: { + type: "integer" + }, + readOnlyDog: { + readOnly: true, + type: "string" + } + }, + xml: { + name: "animals" + } + } + + var expected = { + id: 0, + readOnlyDog: "string" + } + + expect(sampleFromSchema(definition, { includeReadOnly: true })).toEqual(expected) + }) + + it("returns object without writeonly fields for parameter", function () { + var definition = { + type: "object", + properties: { + id: { + type: "integer" + }, + writeOnlyDog: { + writeOnly: true, + type: "string" + } + }, + xml: { + name: "animals" + } + } + + var expected = { + id: 0 + } + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + + it("returns object with writeonly fields for parameter, with includeWriteOnly", function () { + var definition = { + type: "object", + properties: { + id: { + type: "integer" + }, + writeOnlyDog: { + writeOnly: true, + type: "string" + } + }, + xml: { + name: "animals" + } + } + + var expected = { + id: 0, + writeOnlyDog: "string" + } + + expect(sampleFromSchema(definition, { includeWriteOnly: true })).toEqual(expected) + }) +}) describe("createXMLExample", function () { var sut = createXMLExample @@ -554,6 +653,69 @@ describe("createXMLExample", function () { expect(sut(definition, { includeReadOnly: false })).toEqual(expected) }) + it("returns object with readonly fields for parameter, with includeReadOnly", function () { + var expected = "\n\n\t0\n\tstring\n" + var definition = { + type: "object", + properties: { + id: { + type: "integer" + }, + dog: { + readOnly: true, + type: "string" + } + }, + xml: { + name: "animals" + } + } + + expect(sut(definition, { includeReadOnly: true })).toEqual(expected) + }) + + it("returns object without writeonly fields for parameter", function () { + var expected = "\n\n\t0\n" + var definition = { + type: "object", + properties: { + id: { + type: "integer" + }, + dog: { + writeOnly: true, + type: "string" + } + }, + xml: { + name: "animals" + } + } + + expect(sut(definition)).toEqual(expected) + }) + + it("returns object with writeonly fields for parameter, with includeWriteOnly", function () { + var expected = "\n\n\t0\n\tstring\n" + var definition = { + type: "object", + properties: { + id: { + type: "integer" + }, + dog: { + writeOnly: true, + type: "string" + } + }, + xml: { + name: "animals" + } + } + + expect(sut(definition, { includeWriteOnly: true })).toEqual(expected) + }) + it("returns object with passed property as attribute", function () { var expected = "\n\n\tstring\n" var definition = {