diff --git a/packages/api-explorer/__tests__/ResponseSchema.test.jsx b/packages/api-explorer/__tests__/ResponseSchema.test.jsx index 79084cca1..44f39cd12 100644 --- a/packages/api-explorer/__tests__/ResponseSchema.test.jsx +++ b/packages/api-explorer/__tests__/ResponseSchema.test.jsx @@ -93,55 +93,10 @@ test('should not contain ResponseSchemaBody element if $ref not exist', () => { oas, }; const responseSchema = shallow(); - expect(responseSchema.find('table').length).toBe(0); -}); - -test('should show "string" response type', () => { - const testProps = { - operation: oas.operation('/user/login', 'get'), - oas, - }; - - const responseSchema = shallow(); - expect( - responseSchema - .find('p span') - .last() - .text(), - ).toBe('string'); -}); - -test('should show "object" response schema type', () => { - const testProps = { - operation: oas.operation('/store/inventory', 'get'), - oas, - }; - - const responseSchema = shallow(); - expect( - responseSchema - .find('p span') - .last() - .text(), - ).toBe('object'); -}); - -test('should show "array" response schema type', () => { - const testProps = { - operation: oas.operation('/pet/findByTags', 'get'), - oas, - }; - - const responseSchema = shallow(); - expect( - responseSchema - .find('p span') - .last() - .text(), - ).toBe('array of objects'); + expect(responseSchema.find('ResponseSchemaBody').length).toBe(0); }); -test('should render schema type from "application/json"', () => { +test('should render schema from "application/json"', () => { const testProps = { operation: new Operation( {}, @@ -166,12 +121,7 @@ test('should render schema type from "application/json"', () => { }; const responseSchema = shallow(); - expect( - responseSchema - .find('p span') - .last() - .text(), - ).toBe('string'); + expect(responseSchema.find('ResponseSchemaBody').length).toBe(1); }); test('should contain ResponseSchemaBody element if $ref exist for "application/xml"', () => { @@ -206,7 +156,6 @@ test('should change selectedStatus in component', () => { const responseSchema = shallow(); const selectedStatus = responseSchema.state().selectedStatus; - // const selectedStatus = responseSchema.instance().selectedStatus responseSchema.instance().changeHandler({ target: { value: '404' } }); const newSelectedStatus = responseSchema.state().selectedStatus; @@ -214,45 +163,6 @@ test('should change selectedStatus in component', () => { expect(newSelectedStatus).toEqual('404'); }); -test('should show "object" response schema type for "application/xml" content', () => { - const testProps = { - operation: new Operation( - {}, - '/', - 'get', - Object.assign({}, oas.operation('/pet/findByTags', 'get'), { - responses: { - '200': { - content: { - 'application/xml': { - description: 'successful operation', - schema: { - type: 'object', - properties: { - code: { - type: 'integer', - format: 'int32', - }, - }, - }, - }, - }, - }, - }, - }), - ), - oas, - }; - - const responseSchema = shallow(); - expect( - responseSchema - .find('p span') - .last() - .text(), - ).toBe('object'); -}); - test('should not break if schema property missing', () => { const testProps = { operation: new Operation( diff --git a/packages/api-explorer/__tests__/ResponseSchemaBody.test.jsx b/packages/api-explorer/__tests__/ResponseSchemaBody.test.jsx index a677f8d84..b90f1af59 100644 --- a/packages/api-explorer/__tests__/ResponseSchemaBody.test.jsx +++ b/packages/api-explorer/__tests__/ResponseSchemaBody.test.jsx @@ -3,6 +3,7 @@ const { shallow, mount } = require('enzyme'); const ResponseSchemaBody = require('../src/ResponseSchemaBody'); const flattenResponseSchema = require('../src/ResponseSchemaBody').flattenResponseSchema; +const flatten = require('../src/ResponseSchemaBody').flatten; const Oas = require('../src/lib/Oas'); const petstore = require('./fixtures/petstore/oas.json'); @@ -19,8 +20,8 @@ test('display object properties in the table', () => { }; const responseSchemaBody = shallow(); - expect(responseSchemaBody.find('th').text()).toContain('a'); - expect(responseSchemaBody.find('td').text()).toEqual('string'); + expect(responseSchemaBody.find('th').text()).toContain('String'); + expect(responseSchemaBody.find('td').text()).toEqual('a'); }); test('display properties if object contains $ref type', () => { @@ -41,13 +42,13 @@ test('display properties if object contains $ref type', () => { const responseSchemaBody = shallow(); expect( responseSchemaBody - .find('th') + .find('td') .map(a => a.text()) .filter(a => a === 'category.name').length, ).toBe(1); }); -test('should flatten array ', () => { +test('should flatten schema to an array', () => { const responseSchema = { type: 'object', properties: { @@ -55,7 +56,6 @@ test('should flatten array ', () => { type: 'array', items: { type: 'string', - properties: null, }, }, }, @@ -63,7 +63,7 @@ test('should flatten array ', () => { expect(flattenResponseSchema(responseSchema)).toEqual([ { name: 'category', - type: 'array of strings', + type: '[String]', description: undefined, }, ]); @@ -86,7 +86,7 @@ test('display object properties inside another object in the table', () => { const responseSchemaBody = shallow(); expect( responseSchemaBody - .find('th') + .find('td') .map(a => a.text()) .filter(a => a === 'a.a').length, ).toBe(1); @@ -126,15 +126,15 @@ test('display $ref items inside object', () => { const responseSchemaBody = shallow(); expect( responseSchemaBody - .find('td') + .find('th') .map(a => a.text()) - .filter(a => a === 'array of objects').length, + .filter(a => a === '[Object]').length, ).toBe(1); expect( responseSchemaBody - .find('th') + .find('td') .map(a => a.text()) - .filter(a => a === '| | index').length, + .filter(a => a === 'a.pets[].index').length, ).toBe(1); }); @@ -177,15 +177,15 @@ test('render top level array of $ref', () => { const responseSchemaBody = shallow(); expect( responseSchemaBody - .find('th') + .find('td') .map(a => a.text()) .filter(a => a === 'name').length, ).toBe(1); expect( responseSchemaBody - .find('td') + .find('th') .map(a => a.text()) - .filter(a => a === 'string').length, + .filter(a => a === 'String').length, ).toBe(1); }); @@ -217,13 +217,13 @@ test('not render more than 3 level deep object', () => { const responseSchemaBody = shallow(); expect( responseSchemaBody - .find('th') + .find('td') .map(a => a.text()) .filter(a => a === 'a.a.a').length, ).toBe(1); expect( responseSchemaBody - .find('th') + .find('td') .map(a => a.text()) .filter(a => a === 'a.a.a.a').length, ).toBe(0); @@ -246,15 +246,15 @@ test('render top level array of objects', () => { const responseSchemaBody = shallow(); expect( responseSchemaBody - .find('th') + .find('td') .map(a => a.text()) .filter(a => a === 'name').length, ).toBe(1); expect( responseSchemaBody - .find('td') + .find('th') .map(a => a.text()) - .filter(a => a === 'string').length, + .filter(a => a === 'String').length, ).toBe(1); }); @@ -275,3 +275,67 @@ test('should render markdown in the description', () => { 'Description', ); }); + +test('should show "string" response type', () => { + const schema = { + type: 'string', + }; + + const responseSchemaBody = mount(); + + expect( + responseSchemaBody + .find('p span') + .last() + .text(), + ).toBe('string'); +}); + +test('should show "string" response type', () => { + const schema = { + type: 'object', + properties: { + items: { + type: 'string', + }, + }, + }; + + const responseSchemaBody = mount(); + + expect( + responseSchemaBody + .find('p span') + .last() + .text(), + ).toBe('object'); +}); + +test('should show "array" response schema type', () => { + const schema = { + type: 'array', + items: { + type: 'object', + properties: { + name: { + type: 'string', + example: 'doggie', + }, + }, + }, + }; + + const responseSchemaBody = mount(); + + expect( + responseSchemaBody + .find('p span') + .last() + .text(), + ).toBe('array of objects'); +}); + +test('should flatten array ', () => { + const array = [[1], [2, 3], [[4, 5]]]; + expect(flatten(array)).toEqual([1, 2, 3, 4, 5]); +}); diff --git a/packages/api-explorer/api-explorer.css b/packages/api-explorer/api-explorer.css index bff528e2b..4f5d2125d 100644 --- a/packages/api-explorer/api-explorer.css +++ b/packages/api-explorer/api-explorer.css @@ -466,3 +466,8 @@ form.rjsf fieldset { float: right; margin-right: 3%; }*/ + +.response-schema{ + overflow-y: auto; + max-height: 600px; +} diff --git a/packages/api-explorer/src/ResponseSchema.jsx b/packages/api-explorer/src/ResponseSchema.jsx index 138525887..b26ad7442 100644 --- a/packages/api-explorer/src/ResponseSchema.jsx +++ b/packages/api-explorer/src/ResponseSchema.jsx @@ -7,16 +7,6 @@ const ResponseSchemaBody = require('./ResponseSchemaBody'); const { Operation } = Oas; -function getSchemaType(schema) { - if (schema.type !== 'array') { - return schema.type; - } - if (schema.items.$ref) { - return 'array of objects'; - } - return `array of ${schema.items.type}s`; -} - class ResponseSchema extends React.Component { constructor(props) { super(props); @@ -28,37 +18,34 @@ class ResponseSchema extends React.Component { } getSchema(operation) { - if (!this.validateOperation(operation)) return null; + const content = this.getContent(operation); + + if (!content) return null; - const content = operation.responses[this.state.selectedStatus].content; const oas = this.props.oas; if ( - content['application/json'] && - content['application/json'].schema && - content['application/json'].schema.$ref + (content['application/json'] || content['application/xml']) && + (content['application/json'] || content['application/xml']).schema && + (content['application/json'] || content['application/xml']).schema.$ref ) { - return findSchemaDefinition(content['application/json'].schema.$ref, oas); + return findSchemaDefinition( + (content['application/json'] || content['application/xml']).schema.$ref, + oas, + ); } + if ( - content['application/xml'] && - content['application/xml'].schema && - content['application/xml'].schema.$ref + (content['application/xml'] || content['application/json']) && + (content['application/xml'] || content['application/json']).schema ) { - return findSchemaDefinition(content['application/xml'].schema.$ref, oas); - } - if (content['application/xml'] && content['application/xml'].schema) { - return content['application/xml'].schema; - } - - if (content['application/json'] && content['application/json'].schema) { - return content['application/json'].schema; + return (content['application/xml'] || content['application/json']).schema; } return null; } - validateOperation(operation) { + getContent(operation) { const status = this.state.selectedStatus; return ( operation && @@ -106,17 +93,10 @@ class ResponseSchema extends React.Component { return (
{this.renderHeader()} -
+
{operation.responses[this.state.selectedStatus].description && (

{operation.responses[this.state.selectedStatus].description}

)} - {schema && - schema.type && ( -

- {`Response schema type: `} - {getSchemaType(schema)} -

- )} {schema && }
diff --git a/packages/api-explorer/src/ResponseSchemaBody.jsx b/packages/api-explorer/src/ResponseSchemaBody.jsx index b2c8e1b18..477a5e532 100644 --- a/packages/api-explorer/src/ResponseSchemaBody.jsx +++ b/packages/api-explorer/src/ResponseSchemaBody.jsx @@ -11,26 +11,20 @@ const getName = (parent, prop) => { return `${parent}.${prop}`; }; -function flattenResponseSchema(obj, oas, parent = '', level = 0) { - const prefix = new Array(level + 2).join('| '); - if (level > 2) { - return []; - } +const capitalizeFirstLetter = string => (string || '').charAt(0).toUpperCase() + string.slice(1); - // top level array - if (obj.type === 'array' && obj.items) { - if (obj.items.$ref) { - const value = findSchemaDefinition(obj.items.$ref, oas); - return flattenResponseSchema(value, oas); - } - - return flattenResponseSchema(obj.items, oas); +function getSchemaType(schema) { + if (schema.type !== 'array') { + return schema.type; } - - if (obj && !obj.properties) { - return []; + if (schema.items.$ref) { + return 'array of objects'; } + return `array of ${schema.items.type}s`; +} +/* eslint-disable no-use-before-define */ +function flattenObject(obj, parent, level, oas) { return flatten( Object.keys(obj.properties).map(prop => { let value = obj.properties[prop]; @@ -44,47 +38,98 @@ function flattenResponseSchema(obj, oas, parent = '', level = 0) { } if (value.type === 'array' && value.items) { - if (value.items.$ref) { - value.items = findSchemaDefinition(value.items.$ref, oas); + let items = value.items; + if (items.$ref) { + items = findSchemaDefinition(items.$ref, oas); } - if (value.items.type) { + if (items.type) { array.push({ name: getName(parent, prop), - type: `array of ${value.items.type}s`, + type: `[${capitalizeFirstLetter(items.type)}]`, description: value.description, }); } - if (value.items.type === 'object') { - array.push(flattenResponseSchema(value.items, oas, `${prefix}`, level + 1)); + const newParent = parent ? `${parent}.` : ''; + if (items.type === 'object') { + array.push(flattenResponseSchema(items, oas, `${newParent}${prop}[]`, level + 1)); } return array; } array.unshift({ name: getName(parent, prop), - type: value.type, + type: capitalizeFirstLetter(value.type), description: value.description, }); return array; }), ); } +/* eslint-enable no-use-before-define */ + +function flattenResponseSchema(obj, oas, parent = '', level = 0) { + if (level > 2) { + return []; + } + let newParent; + // top level array + if (obj.type === 'array' && obj.items) { + if (obj.items.$ref) { + const value = findSchemaDefinition(obj.items.$ref, oas); + return flattenResponseSchema(value, oas); + } + newParent = parent ? `${parent}.[]` : ''; + return flattenResponseSchema(obj.items, oas, `${newParent}`, level + 1); + } + + if (obj && !obj.properties) { + return []; + } + + return flattenObject(obj, parent, level, oas); +} function ResponseSchemaBody({ schema, oas }) { - const rows = flattenResponseSchema(schema, oas).map(row => ( - - {row.name} - + const rows = flatten(flattenResponseSchema(schema, oas)).map(row => ( + + {row.type} + + + {row.name} {row.description && marked(row.description)} )); return ( - - {rows} -
+
+ {schema && + schema.type && ( +

+ {`Response schema type: `} + {getSchemaType(schema)} +

+ )} + + {rows} +
+
); }