diff --git a/docs/usage/configuration.md b/docs/usage/configuration.md index 21533af781b..9cfeb274233 100644 --- a/docs/usage/configuration.md +++ b/docs/usage/configuration.md @@ -58,6 +58,7 @@ Parameter name | Docker variable | Description `operationsSorter` | _Unavailable_ | `Function=(a => a)`. Apply a sort to the operation list of each API. It can be 'alpha' (sort by paths alphanumerically), 'method' (sort by HTTP method) or a function (see Array.prototype.sort() to know how sort function works). Default is the order returned by the server unchanged. `showExtensions` | `SHOW_EXTENSIONS` | `Boolean=false`. Controls the display of vendor extension (`x-`) fields and values for Operations, Parameters, and Schema. `showCommonExtensions` | `SHOW_COMMON_EXTENSIONS` | `Boolean=false`. Controls the display of extensions (`pattern`, `maxLength`, `minLength`, `maximum`, `minimum`) fields and values for Parameters. +`showAlternativeSchemaExample` | _Unavailable_ | `Boolean=false`. Controls the display of alternative schemas (oneOf/anyOf) in the Example View. `tagsSorter` | _Unavailable_ | `Function=(a => a)`. Apply a sort to the tag list of each API. It can be 'alpha' (sort by paths alphanumerically) or a function (see [Array.prototype.sort()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) to learn how to write a sort function). Two tag name strings are passed to the sorter for each pass. Default is the order determined by Swagger UI. `useUnsafeMarkdown` | `USE_UNSAFE_MARKDOWN` | `Boolean=false`. When enabled, sanitizer will leave `style`, `class` and `data-*` attributes untouched on all HTML Elements declared inside markdown strings. This parameter is **Deprecated** and will be removed in `4.0.0`. `onComplete` | _Unavailable_ | `Function=NOOP`. Provides a mechanism to be notified when Swagger UI has finished rendering a newly provided definition. diff --git a/src/core/components/alternative-schema-select.jsx b/src/core/components/alternative-schema-select.jsx new file mode 100644 index 00000000000..ff4387dbf76 --- /dev/null +++ b/src/core/components/alternative-schema-select.jsx @@ -0,0 +1,91 @@ +import React, { Component } from "react" +import PropTypes from "prop-types" + +export default class AlternativeSchemaSelect extends Component { + + static propTypes = { + alternativeSchemaSelections: PropTypes.object.isRequired, + onSelectionChanged: PropTypes.func.isRequired, + isManualMode: PropTypes.bool.isRequired, + alternativeSchemas: PropTypes.object.isRequired + } + + constructor(props) { + super(props) + this.onModeChange = this.onModeChange.bind(this) + this.oneOfChange = this.oneOfChange.bind(this) + this.selectOneOfComponent = this.selectOneOfComponent.bind(this) + this.state = { + isManualMode: false, + alternativeSchemaSelections: {} + } + } + + onModeChange(e) { + var { onSelectionChanged } = this.props + var { alternativeSchemaSelections } = this.state + + onSelectionChanged( (e.target.dataset.name === "MANUAL" ? alternativeSchemaSelections :{})) + this.setState({isManualMode: e.target.dataset.name === "MANUAL"}) + } + + oneOfChange(e, id) { + var { onSelectionChanged } = this.props + var { alternativeSchemaSelections } = this.state + + alternativeSchemaSelections[id] = parseInt(e.target.value) + + onSelectionChanged( alternativeSchemaSelections ) + } + + selectOneOfComponent(attributePath, options, defaultOption, type) { + if (options) { + return ( +
+
+ Choose {type} {attributePath}: +
+ +
+ ) + } + return null + } + + render() { + + const { alternativeSchemas } = this.props + const { isManualMode } = this.state + + var oneOfComponents = [] + if (isManualMode && alternativeSchemas) { + alternativeSchemas.map((attribute) => { + oneOfComponents.push(this.selectOneOfComponent(attribute.key, attribute.options, attribute.selectedIndex, attribute.type)) + return true + }) + } + return ( +
+ + {oneOfComponents} +
+ ) + } +} \ No newline at end of file diff --git a/src/core/components/model-example.jsx b/src/core/components/model-example.jsx index b7498ebdbef..13b42588fcf 100644 --- a/src/core/components/model-example.jsx +++ b/src/core/components/model-example.jsx @@ -13,6 +13,9 @@ export default class ModelExample extends React.Component { specPath: ImPropTypes.list.isRequired, includeReadOnly: PropTypes.bool, includeWriteOnly: PropTypes.bool, + alternativeSchemas: PropTypes.object, + onSelectedAlternativeOptionChanged: PropTypes.func, + onChange: PropTypes.func, } constructor(props, context) { @@ -54,10 +57,11 @@ export default class ModelExample extends React.Component { } render() { - let { getComponent, specSelectors, schema, example, isExecute, getConfigs, specPath, includeReadOnly, includeWriteOnly } = this.props + let { getComponent, specSelectors, schema, example, isExecute, getConfigs, specPath, includeReadOnly, includeWriteOnly, alternativeSchemas, onChange } = this.props let { defaultModelExpandDepth } = getConfigs() const ModelWrapper = getComponent("ModelWrapper") const HighlightCode = getComponent("highlightCode") + const AlternativeSchemaSelect = getComponent("alternativeSchemaSelect") let isOAS3 = specSelectors.isOAS3() @@ -80,6 +84,14 @@ export default class ModelExample extends React.Component { ) ) : null } + { + this.state.activeTab === "example" && alternativeSchemas && onChange ? ( + + ) : null + } { this.state.activeTab === "model" && { + this.setState({alternativeSchemaSelections: alternativeSchemaSelections}) + }} onChangeIncludeEmpty={(name, value) => { oas3Actions.setRequestBodyInclusion({ pathMethod, diff --git a/src/core/components/response.jsx b/src/core/components/response.jsx index 2e078c99dd6..efc8f72b762 100644 --- a/src/core/components/response.jsx +++ b/src/core/components/response.jsx @@ -22,6 +22,7 @@ export default class Response extends React.Component { this.state = { responseContentType: "", + alternativeSchemaSelections: {} } } @@ -40,7 +41,8 @@ export default class Response extends React.Component { contentType: PropTypes.string, activeExamplesKey: PropTypes.string, controlsAcceptHeader: PropTypes.bool, - onContentTypeChange: PropTypes.func + onContentTypeChange: PropTypes.func, + alternativeSchemaSelections: PropTypes.object } static defaultProps = { @@ -57,6 +59,10 @@ export default class Response extends React.Component { }) } + _onAlternativeSchemaChanged = (alternativeSchemaSelections) =>{ + this.setState({alternativeSchemaSelections: alternativeSchemaSelections}) + } + getTargetExamplesKey = () => { const { response, contentType, activeExamplesKey } = this.props @@ -85,6 +91,8 @@ export default class Response extends React.Component { oas3Actions, } = this.props + const { showAlternativeSchemaExample } = getConfigs() + let { inferSchema } = fn let isOAS3 = specSelectors.isOAS3() @@ -131,8 +139,11 @@ export default class Response extends React.Component { sampleResponse = stringify(activeMediaType.get("example")) } else { // use an example value generated based on the schema + var alternativeSchemas = showAlternativeSchemaExample === true ? [] : undefined sampleResponse = getSampleSchema(oas3SchemaForContentType.toJS(), this.state.responseContentType, { - includeReadOnly: true + includeReadOnly: true, + alternativeSchemas: alternativeSchemas, + alternativeSchemaSelections: this.state.alternativeSchemaSelections || {} }) } } else { @@ -219,7 +230,10 @@ export default class Response extends React.Component { specSelectors={ specSelectors } schema={ fromJSOrdered(schema) } example={ example } - includeReadOnly={ true }/> + includeReadOnly={ true } + alternativeSchemas= { alternativeSchemas } + onChange = { this._onAlternativeSchemaChanged }/> + ) : null } { isOAS3 && examplesForMediaType ? ( diff --git a/src/core/index.js b/src/core/index.js index 057586216d9..268f3df9544 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -50,6 +50,7 @@ export default function SwaggerUI(opts) { defaultModelsExpandDepth: 1, showExtensions: false, showCommonExtensions: false, + showAlternativeSchemaExample: false, withCredentials: undefined, supportedSubmitMethods: [ "get", diff --git a/src/core/plugins/oas3/components/request-body.jsx b/src/core/plugins/oas3/components/request-body.jsx index 05aa3dd0fcd..614e6158e55 100644 --- a/src/core/plugins/oas3/components/request-body.jsx +++ b/src/core/plugins/oas3/components/request-body.jsx @@ -4,7 +4,7 @@ import ImPropTypes from "react-immutable-proptypes" import { Map, OrderedMap, List } from "immutable" import { getCommonExtensions, getSampleSchema, stringify, isEmptyValue } from "core/utils" -function getDefaultRequestBodyValue(requestBody, mediaType, activeExamplesKey) { +function getDefaultRequestBodyValue(requestBody, mediaType, activeExamplesKey, alternativeSchemas, alternativeSchemaSelections) { let mediaTypeValue = requestBody.getIn(["content", mediaType]) let schema = mediaTypeValue.get("schema").toJS() let example = @@ -25,7 +25,9 @@ function getDefaultRequestBodyValue(requestBody, mediaType, activeExamplesKey) { return stringify( example || getSampleSchema(schema, mediaType, { - includeWriteOnly: true + includeWriteOnly: true, + alternativeSchemas: alternativeSchemas, + alternativeSchemaSelections: alternativeSchemaSelections }) || "" ) @@ -50,6 +52,8 @@ const RequestBody = ({ onChangeIncludeEmpty, activeExamplesKey, updateActiveExamplesKey, + alternativeSchemaSelections, + onAlternativeSchemaChange, }) => { const handleFile = (e) => { onChange(e.target.files[0]) @@ -76,7 +80,7 @@ const RequestBody = ({ const Example = getComponent("Example") const ParameterIncludeEmpty = getComponent("ParameterIncludeEmpty") - const { showCommonExtensions } = getConfigs() + const { showCommonExtensions, showAlternativeSchemaExample } = getConfigs() const requestBodyDescription = (requestBody && requestBody.get("description")) || null const requestBodyContent = (requestBody && requestBody.get("content")) || new OrderedMap() @@ -206,6 +210,8 @@ const RequestBody = ({ } + var alternativeSchemas = showAlternativeSchemaExample === true ? [] : undefined + return
{ requestBodyDescription && @@ -239,6 +245,7 @@ const RequestBody = ({ />
) : ( + } @@ -288,6 +299,8 @@ RequestBody.propTypes = { specPath: PropTypes.array.isRequired, activeExamplesKey: PropTypes.string, updateActiveExamplesKey: PropTypes.func, + alternativeSchemaSelections: PropTypes.object, + onAlternativeSchemaChange: PropTypes.func, } export default RequestBody diff --git a/src/core/plugins/samples/fn.js b/src/core/plugins/samples/fn.js index 401ba329141..8fd15dbd26a 100644 --- a/src/core/plugins/samples/fn.js +++ b/src/core/plugins/samples/fn.js @@ -30,11 +30,73 @@ const primitive = (schema) => { return "Unknown Type: " + schema.type } +const extractDiscriminatorMappingValues = (discriminator) => { + var discriminatorMappingValues + if (discriminator && discriminator.propertyName && discriminator.mapping){ + discriminatorMappingValues ={} + Object.keys(discriminator.mapping).map(function(key) { + var mappingKey = discriminator.mapping[key] + if(mappingKey){ + var mappingName = mappingKey.split("#") + discriminatorMappingValues[mappingName[mappingName.length-1]] = key + } + }) + } + return discriminatorMappingValues +} -export const sampleFromSchema = (schema, config={}) => { - let { type, example, properties, additionalProperties, items } = objectify(schema) - let { includeReadOnly, includeWriteOnly } = config +const evaluateOptionName = (valueObj) => { + if (valueObj.title){ + return valueObj.title + } else if (valueObj.$$ref){ + return valueObj.$$ref.split("/").pop(-1) + } else if (valueObj.properties){ + let attr = Object.keys(valueObj.properties) + return "Item " + (attr.length == 1 ? "(" + attr[0] + ")": attr.length > 1 ? "(" + attr[0] + ", ...)": "" ) + } else { + return "Item" + } +} +const extractAlternativeSchema = (oneOfSchema, config, path, type, discriminator) => { + if ( Array.isArray(oneOfSchema) && oneOfSchema.length > 0) { + + let { alternativeSchemas, alternativeSchemaSelections } = config + + let index = 0 + let options = {} + let discriminatorMappingValues = extractDiscriminatorMappingValues(discriminator) + + oneOfSchema.map(valueObj => { + options["#" + index++] = "#" + index + ": " + evaluateOptionName(valueObj) + + if (discriminatorMappingValues && valueObj.properties && valueObj.$$ref){ + var discriminatorProperty = valueObj.properties[discriminator.propertyName] + if (discriminatorProperty && !discriminatorProperty["example"]) { + var mappingNane = valueObj.$$ref.split("#") + var example = discriminatorMappingValues[mappingNane[mappingNane.length-1]] + if(example){ + discriminatorProperty["example"] = example + } + } + } + return true + }) + + let selectedIndex = alternativeSchemaSelections[path] || 0 + if ( selectedIndex >= oneOfSchema.length || selectedIndex < -1) { + selectedIndex = 0 + } + alternativeSchemas.push({ key: path, options: options, selectedIndex: selectedIndex, type}) + + return selectedIndex >-1 ? oneOfSchema[selectedIndex] : undefined + } + return +} + +export const sampleFromSchema = (schema, config={}, path="#") => { + let { type, example, properties, additionalProperties, items, oneOf, anyOf, discriminator } = objectify(schema) + let { includeReadOnly, includeWriteOnly, alternativeSchemas } = config if(example !== undefined) { return deeplyStripKey(example, "$$ref", (val) => { @@ -44,10 +106,31 @@ export const sampleFromSchema = (schema, config={}) => { }) } - if(!type) { - if(properties) { + if (alternativeSchemas && !items) { + if (oneOf) { + let oneOfSchema = extractAlternativeSchema(oneOf, config, path, "one of", discriminator) + oneOfSchema = Object.assign({}, schema, oneOfSchema) + if (schema.properties) { + Object.assign(oneOfSchema.properties, schema.properties) + } + delete oneOfSchema.oneOf + return sampleFromSchema(oneOfSchema, config, path) + } + if (anyOf) { + let anyOfSchema = extractAlternativeSchema(anyOf, config, path, "any of", discriminator) + anyOfSchema = Object.assign({}, schema, anyOfSchema) + if (schema.properties) { + Object.assign(anyOfSchema.properties, schema.properties) + } + delete anyOfSchema.anyOf + return sampleFromSchema(anyOfSchema, config, path) + } + } + + if (!type) { + if (properties) { type = "object" - } else if(items) { + } else if (items) { type = "array" } else { return @@ -56,6 +139,7 @@ export const sampleFromSchema = (schema, config={}) => { if(type === "object") { let props = objectify(properties) + let obj = {} for (var name in props) { if ( props[name] && props[name].deprecated ) { @@ -67,7 +151,7 @@ export const sampleFromSchema = (schema, config={}) => { if ( props[name] && props[name].writeOnly && !includeWriteOnly ) { continue } - obj[name] = sampleFromSchema(props[name], config) + obj[name] = sampleFromSchema(props[name], config, path + "/" + name) } if ( additionalProperties === true ) { @@ -85,14 +169,14 @@ export const sampleFromSchema = (schema, config={}) => { if(type === "array") { if(Array.isArray(items.anyOf)) { - return items.anyOf.map(i => sampleFromSchema(i, config)) + return items.anyOf.map(i => sampleFromSchema(i, config, path + "[]")) } - if(Array.isArray(items.oneOf)) { - return items.oneOf.map(i => sampleFromSchema(i, config)) + if(Array.isArray(items.oneOf) && !alternativeSchemas) { + return items.oneOf.map(i => sampleFromSchema(i, config, path + "[]")) } - return [ sampleFromSchema(items, config) ] + return [ sampleFromSchema(items, config, path + "[]") ] } if(schema["enum"]) { diff --git a/src/core/presets/base.js b/src/core/presets/base.js index 6fe8f43c618..9fa50d05890 100644 --- a/src/core/presets/base.js +++ b/src/core/presets/base.js @@ -68,6 +68,7 @@ import Schemes from "core/components/schemes" import SchemesContainer from "core/containers/schemes" import ModelCollapse from "core/components/model-collapse" import ModelExample from "core/components/model-example" +import AlternativeSchemaSelect from "core/components/alternative-schema-select" import ModelWrapper from "core/components/model-wrapper" import Model from "core/components/model" import Models from "core/components/models" @@ -134,6 +135,7 @@ export default function() { schemes: Schemes, SchemesContainer, modelExample: ModelExample, + alternativeSchemaSelect: AlternativeSchemaSelect, ModelWrapper, ModelCollapse, Model, diff --git a/src/style/_layout.scss b/src/style/_layout.scss index 91493496fee..ea9f8afa8bd 100644 --- a/src/style/_layout.scss +++ b/src/style/_layout.scss @@ -824,6 +824,14 @@ } } +.response-control-alternative-examples { + font-size: 12px; + &__title { + display: block; + margin-bottom: 0.2em; + } +} + @keyframes blinker { 50% diff --git a/test/mocha/components/response.jsx b/test/mocha/components/response.jsx index 0cbde9ecb3a..d688865c2e8 100644 --- a/test/mocha/components/response.jsx +++ b/test/mocha/components/response.jsx @@ -16,6 +16,7 @@ describe("", function () { operationLink: dummyComponent, contentType: dummyComponent } + const props = { getComponent: c => components[c], specSelectors: { @@ -29,6 +30,7 @@ describe("", function () { contentType: "application/json", className: "for-test", specPath: List(), + getConfigs: function(){ return {showAlternativeSchemaExample: undefined}}, response: fromJS({ schema: { type: "object", diff --git a/test/mocha/core/plugins/samples/fn.js b/test/mocha/core/plugins/samples/fn.js index 496adafd7ca..d3173e7a7b3 100644 --- a/test/mocha/core/plugins/samples/fn.js +++ b/test/mocha/core/plugins/samples/fn.js @@ -512,7 +512,536 @@ describe("sampleFromSchema", function() { expect(sampleFromSchema(definition)).toEqual(expected) }) + }), + + describe("alternative schema (oneOf/anyOf)", function() { + var definition = { + "type": "object", + "properties": { + "address": { + "type": "string" + }, + "options": { + "oneOf": [ + { + "type": "object", + "properties": { + "shipping": { + "type": "string", + "enum": [ + "FAST", + "STANDARD" + ] + } + } + }, + { + "type": "object", + "properties": { + "remarks": { + "type": "string" + }, + "comment": { + "type": "string", + "example": "My comment" + }, + "count": { + "type": "integer", + "example": 99 + } + } + } + ] + } + } + } + + it("returns first `oneOf` item as example", function() { + + var expected = { + "address": "string", + "options": { + "remarks": "string", + "comment": "My comment", + "count": 99 + } + } + + var alternativeSchemas= [] + var alternativeSchemaSelections= {"#/options": 1} + + var result = sampleFromSchema(definition, {alternativeSchemas: alternativeSchemas, alternativeSchemaSelections: {"#/options": 1} }) + expect(result).toEqual(expected) + }) + + it("extracts `oneOf` options", function() { + + var expected = [ + { + "selectedIndex": 1, + "key": "#/options", + "options": { + "#0": "#1: Item (shipping)", + "#1": "#2: Item (remarks, ...)" + }, + "type": "one of" + } + ] + + var alternativeSchemas= [] + var alternativeSchemaSelections= {"#/options": 1} + + sampleFromSchema(definition, {alternativeSchemas: alternativeSchemas, alternativeSchemaSelections: alternativeSchemaSelections }) + expect(alternativeSchemas).toEqual(expected) + }) + + it("extracts `oneOf` options and adapted selection ", function() { + + var expected = [ + { + "selectedIndex": 0, + "key": "#/options", + "options": { + "#0": "#1: Item (shipping)", + "#1": "#2: Item (remarks, ...)" + }, + "type": "one of" + } + ] + + var alternativeSchemas= [] + + sampleFromSchema(definition, {alternativeSchemas: alternativeSchemas, alternativeSchemaSelections: {"#/options": 99999} }) + expect(alternativeSchemas).toEqual(expected) + }) + + it("extracts `oneOf` options and select first element ", function() { + + var expected = [ + { + "selectedIndex": 0, + "key": "#/options", + "options": { + "#0": "#1: Item (shipping)", + "#1": "#2: Item (remarks, ...)" + }, + "type": "one of" + } + ] + + var alternativeSchemas= [] + + sampleFromSchema(definition, {alternativeSchemas: alternativeSchemas, alternativeSchemaSelections:{} }) + expect(alternativeSchemas).toEqual(expected) + }) + + it("returns example without and alternative schema as undefined ()", function() { + + var expected = { + "address": "string", + "options": undefined + } + + let result = sampleFromSchema(definition) + expect(result).toEqual(expected) + } + )} + ) + + describe("alternative schema (oneOf/anyOf) attribute", function() { + var definition = { + + "oneOf": [ + { + "type": "string", + "example": "first" + }, + { + "type": "integer", + "example": 99 + } + ] + } + + it("returns first `oneOf` attribute as example", function() { + + var expected = "first" + var alternativeSchemas= [] + + var result = sampleFromSchema(definition, {alternativeSchemas: alternativeSchemas, alternativeSchemaSelections: {"#": 0} }) + expect(result).toEqual(expected) + }) + + it("returns second `oneOf` attribute as example", function() { + + var expected = 99 + var alternativeSchemas= [] + + var result = sampleFromSchema(definition, {alternativeSchemas: alternativeSchemas, alternativeSchemaSelections: {"#": 1} }) + expect(result).toEqual(expected) + }) + + it("extracts `oneOf` options", function() { + + var expected = [ + { + "selectedIndex": 1, + "key": "#", + "options": { + "#0": "#1: Item", + "#1": "#2: Item" + }, + "type": "one of" + } + ] + + var alternativeSchemas= [] + var alternativeSchemaSelections= {"#": 1} + + sampleFromSchema(definition, {alternativeSchemas: alternativeSchemas, alternativeSchemaSelections: alternativeSchemaSelections }) + expect(alternativeSchemas).toEqual(expected) + }) + + }) + + describe("alternative schema on object level (oneOf/anyOf)", function() { + var definition = { + "type": "object", + "properties": { + "emailAddress": { + "type": "string" + }, + }, + "oneOf": [ + { + "type": "object", + "properties": { + "shipping": { + "type": "string", + "enum": [ + "FAST", + "STANDARD" + ] + } + } + }, + { + "type": "object", + "title": "My Title", + "properties": { + "remarks": { + "type": "string" + } + } + } + ] + } + + it("returns first `oneOf` item as example", function() { + + var expected = { + "emailAddress": "string", + "shipping": "FAST" + } + + var alternativeSchemas= [] + + var result = sampleFromSchema(definition, {alternativeSchemas: alternativeSchemas, alternativeSchemaSelections: {"#/options": 1} }) + expect(result).toEqual(expected) + }) + + it("extracts `oneOf` options", function() { + + var expected = [ + { + "selectedIndex": 1, + "key": "#", + "options": { + "#0": "#1: Item (shipping, ...)", + "#1": "#2: My Title" + }, + "type": "one of" + } + ] + + var alternativeSchemas= [] + + sampleFromSchema(definition, {alternativeSchemas: alternativeSchemas, alternativeSchemaSelections: {"#": 1} }) + expect(alternativeSchemas).toEqual(expected) + }) + + it("extracts `oneOf` options and adapted selection ", function() { + + var expected = [ + { + "selectedIndex": 0, + "key": "#", + "options": { + "#0": "#1: Item (shipping, ...)", + "#1": "#2: My Title" + }, + "type": "one of" + } + ] + + var alternativeSchemas= [] + + sampleFromSchema(definition, {alternativeSchemas: alternativeSchemas, alternativeSchemaSelections: {"#": 99999} }) + expect(alternativeSchemas).toEqual(expected) + }) + + it("extracts `oneOf` options and select first element ", function() { + + var expected = [ + { + "selectedIndex": 0, + "key": "#", + "options": { + "#0": "#1: Item (shipping, ...)", + "#1": "#2: My Title" + }, + "type": "one of" + } + ] + + var alternativeSchemas= [] + + sampleFromSchema(definition, {alternativeSchemas: alternativeSchemas, alternativeSchemaSelections:{} }) + expect(alternativeSchemas).toEqual(expected) + }) + + it("returns example without and alternative schema as undefined ()", function() { + + var expected = { + "emailAddress": "string" + } + + let result = sampleFromSchema(definition) + expect(result).toEqual(expected) + } + )} + ) + + describe("alternative schema (oneOf/anyOf) with discriminator", function() { + var definition = { + "type": "object", + "properties": { + "emailAddress": { + "type": "string" + }, + }, + "oneOf": [ + { + "$$ref": "swagger.yaml#/components/schemas/AnObjectA", + "type": "object", + "properties": { + "objectType": { + "type": "string", + }, + "shipping": { + "type": "string", + "enum": [ + "FAST", + "STANDARD" + ] + } + } + }, + { + "$$ref": "#/components/schemas/AnObjectB", + "type": "object", + "properties": { + "objectType": { + "type": "string", + }, + "remarks": { + "type": "string", + "example": "A remark" + } + } + }, + { + "$$ref": "#/components/schemas/AnObjectC", + "type": "object", + "properties": { + "objectType": { + "type": "string", + }, + "comment": { + "type": "string" + } + } + } + ], + "discriminator": { + "propertyName": "objectType", + "mapping": { + "objA": "#/components/schemas/AnObjectA", + "objB": "#/components/schemas/AnObjectB", + "objD": "#/components/schemas/NotMatching" + } + } + } + + it("returns first `oneOf` item with objectType A", function() { + + var expected = { + "emailAddress": "string", + "shipping": "FAST", + "objectType": "objA" + } + + var alternativeSchemas= [] + + var result = sampleFromSchema(definition, {alternativeSchemas: alternativeSchemas, alternativeSchemaSelections: {"#": 0} }) + expect(result).toEqual(expected) + }) + + it("returns second `oneOf` item with objectType B", function() { + + var expected = { + "emailAddress": "string", + "remarks": "A remark", + "objectType": "objB" + } + + var alternativeSchemas= [] + + var result = sampleFromSchema(definition, {alternativeSchemas: alternativeSchemas, alternativeSchemaSelections: {"#": 1} }) + expect(result).toEqual(expected) + }) + + it("returns second `oneOf` item with objectType C", function() { + + var expected = { + "emailAddress": "string", + "comment": "string", + "objectType": "string" + } + + var alternativeSchemas= [] + + var result = sampleFromSchema(definition, {alternativeSchemas: alternativeSchemas, alternativeSchemaSelections: {"#": 2} }) + expect(result).toEqual(expected) + }) }) + + describe("alternative schema (oneOf) of array type", function() { + var definition = { + "type": "array", + "items": { + "type": "object", + "properties": { + "emailAddress": { + "type": "string" + } + }, + "oneOf": [ + { + "type": "object", + "properties": { + "shipping": { + "type": "string", + "enum": [ + "FAST", + "STANDARD" + ] + } + } + }, + { + "type": "object", + "properties": { + "remarks": { + "type": "string" + } + } + } + ] + } + } + + it("returns first `oneOf` item with objectType A", function() { + + var expected = [{ + "emailAddress": "string", + "shipping": "FAST" + }] + + var alternativeSchemas= [] + + var result = sampleFromSchema(definition, {alternativeSchemas: alternativeSchemas, alternativeSchemaSelections: {"#": 0} }) + expect(result).toEqual(expected) + }) + + it("returns second `oneOf` item with objectType B", function() { + + var expected = [{ + "emailAddress": "string", + "remarks": "string" + }] + + var alternativeSchemas= [] + + var result = sampleFromSchema(definition, {alternativeSchemas: alternativeSchemas, alternativeSchemaSelections: {"#[]": 1} }) + expect(result).toEqual(expected) + }) + }) + + describe("alternative schema (anyOf) of array type", function() { + var definition = { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "shipping": { + "type": "string", + "enum": [ + "FAST", + "STANDARD" + ] + } + } + }, + { + "type": "object", + "properties": { + "remarks": { + "type": "string" + } + } + } + ] + } + } + + it("returns all `anyOf` items", function() { + + var expected = [ + {"shipping": "FAST"}, + {"remarks": "string"} + ] + + var alternativeSchemas= [] + + var result = sampleFromSchema(definition, {alternativeSchemas: alternativeSchemas, alternativeSchemaSelections: {"#": 0} }) + expect(result).toEqual(expected) + }) + + it("alternativeSchemaSelections shows allItems", function() { + + var expected = [] + + var alternativeSchemas= [] + + sampleFromSchema(definition, {alternativeSchemas: alternativeSchemas, alternativeSchemaSelections: {"#": 0} }) + expect(alternativeSchemas).toEqual(expected) + }) + + }) + }) describe("createXMLExample", function () {