Skip to content
This repository has been archived by the owner on Nov 28, 2022. It is now read-only.

Commit

Permalink
fix linting errors, add component for better testing, fix current bug…
Browse files Browse the repository at this point in the history
…s, implement $ref lookups for schemas
  • Loading branch information
0xVolodya committed Oct 23, 2018
1 parent bbb5a12 commit b02c845
Show file tree
Hide file tree
Showing 5 changed files with 231 additions and 141 deletions.
1 change: 0 additions & 1 deletion example/src/Demo.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ const Logs = require('../../packages/api-logs/index');
const ApiList = require('./ApiList');

require('../../example/swagger-files/types.json');
require('../../example/swagger-files/response-schema.json');
require('../../packages/api-explorer/api-explorer.css');
require('../../packages/api-logs/main.css');

Expand Down
20 changes: 16 additions & 4 deletions packages/api-explorer/__tests__/ResponseSchema.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,21 @@ test('should work if responses is an empty object', () => {
expect(responseSchema.html()).toBe(null);
});

test('display object properties in the table response', () => {

test('should contain ResponseSchemaBody element if $ref exist', () => {
const responseSchema = shallow(<ResponseSchema {...props} />);
expect(responseSchema.last('th').text()).toContain('string');
expect(responseSchema.last('td').text()).toContain('status');
expect(responseSchema.text()).toContain('ResponseSchemaBody');
});

test('should contain ResponseSchemaBody element if $ref not exist', () => {
const testProps = {
operation: new Operation(
{},
'/',
'get',
Object.assign({}, oas.operation('/pet/{petId}', 'get'), { responses: {} }),
),
oas,
};
const responseSchema = shallow(<ResponseSchema {...testProps} />);
expect(responseSchema.find('table').length).toBe(0);
});
94 changes: 94 additions & 0 deletions packages/api-explorer/__tests__/ResponseSchemaBody.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
const React = require('react');
const { shallow } = require('enzyme');

const ResponseSchemaBody = require('../src/ResponseSchemaBody');
const Oas = require('../src/lib/Oas');
const petstore = require('./fixtures/petstore/oas.json');

const oas = new Oas(petstore);

test('display object properties in the table', () => {
const schema = {
type: 'object',
properties: {
a: {
type: 'string',
},
},
};
const responseSchemaBody = shallow(<ResponseSchemaBody oas={oas} schema={schema} />);

expect(responseSchemaBody.find('th').text()).toContain('a');
expect(responseSchemaBody.find('td').text()).toEqual('string');
});

test('display object properties in the table', () => {
const schema = {
type: 'object',
properties: {
a: {
type: 'string',
},
},
};
const responseSchemaBody = shallow(<ResponseSchemaBody oas={oas} schema={schema} />);

expect(responseSchemaBody.find('th').text()).toContain('a');
expect(responseSchemaBody.find('td').text()).toEqual('string');
});

test('display properties if object contains $ref type', () => {
const schema = {
type: 'object',
required: ['name', 'photoUrls'],
properties: {
id: {
type: 'integer',
format: 'int64',
readOnly: true,
},
category: {
$ref: '#/components/schemas/Category',
},
name: {
type: 'string',
example: 'doggie',
},
photoUrls: {
type: 'array',
xml: {
name: 'photoUrl',
wrapped: true,
},
items: {
type: 'string',
},
},
tags: {
type: 'array',
xml: {
name: 'tag',
wrapped: true,
},
items: {
$ref: '#/components/schemas/Tag',
},
},
status: {
type: 'string',
description: 'pet status in the store',
enum: ['available', 'pending', 'sold'],
},
},
xml: {
name: 'Pet',
},
};
const responseSchemaBody = shallow(<ResponseSchemaBody oas={oas} schema={schema} />);
expect(
responseSchemaBody
.find('th')
.map(a => a.text())
.filter(a => a === 'category.name').length,
).toBe(1);
});
201 changes: 65 additions & 136 deletions packages/api-explorer/src/ResponseSchema.jsx
Original file line number Diff line number Diff line change
@@ -1,119 +1,79 @@
const React = require('react')
const PropTypes = require('prop-types')
const _ = require('lodash')
const React = require('react');
const PropTypes = require('prop-types');

const Oas = require('./lib/Oas')
const marked = require('../../markdown/index')
const Oas = require('./lib/Oas');
const findSchemaDefinition = require('./lib/find-schema-definition');
const ResponseSchemaBody = require('./ResponseSchemaBody');

const { Operation } = Oas

// const convertToParams = require('../../../legacy-stuff/swagger');
const { Operation } = Oas;

class ResponseSchema extends React.Component {
constructor (props) {
super(props)
constructor(props) {
super(props);
this.state = {
selectedStatus: Object.keys(props.operation.responses || {})[0],
oas: props.oas
}
this.selectedStatus = this.selectedStatus.bind(this)
this.changeHandler = this.changeHandler.bind(this)
};
this.selectedStatus = this.selectedStatus.bind(this);
this.changeHandler = this.changeHandler.bind(this);
}

selectedStatus (selected) {
this.setState({ selectedStatus: selected });
}
getSchema(operation) {
if (!this.validateOperation(operation)) return null;

changeHandler (e) {
this.selectedStatus(e.target.value)
}
const content = operation.responses[this.state.selectedStatus].content;
const oas = this.props.oas;

// TODO https://github.com/readmeio/api-explorer/issues/43
renderSchema (operation) {
let schema
const oas = this.state.oas
if(!this.validateOperation(operation, this.state.selectedStatus)) return

if (operation.responses[this.state.selectedStatus].content) {
if (
operation.responses[this.state.selectedStatus].content['application/json'].schema.type ===
'object' &&
operation.responses[this.state.selectedStatus].content['application/json'].schema
.properties
) {
schema =
operation.responses[this.state.selectedStatus].content['application/json']
}
if (
content['application/json'] &&
content['application/json'].schema &&
content['application/json'].schema.type === 'object'
) {
return content['application/json'].schema;
}
else if (
operation.responses[this.state.selectedStatus].content['application/xml'].schema.type ===
'object' &&
operation.responses[this.state.selectedStatus].content['application/xml'].schema.properties
if (
content['application/xml'] &&
content['application/xml'].schema &&
content['application/xml'].schema.type === 'object'
) {
schema =
operation.responses[this.state.selectedStatus].content['application/xml']
return content['application/xml'].schema;
}
if(operation.responses[this.state.selectedStatus].content &&
operation.responses[this.state.selectedStatus].content['application/json'].schema &&
operation.responses[this.state.selectedStatus].content['application/json'].schema.$ref
){
schema = {
schema: findSchemaDefinition(operation.responses[this.state.selectedStatus].content['application/json'].schema.$ref, oas)
}

if (content && content['application/json'].schema && content['application/json'].schema.$ref) {
return findSchemaDefinition(content['application/json'].schema.$ref, oas);
}
return schema && (
<table>
{this.convertToParams([schema], 'response').map((param, index) => {
return (<tr key={index}>
<th>{param.name}</th>
<td>
{param.type}
{param.description && marked(param.description)}
</td>
</tr>)
})}
</table>
)

if (content && content['application/xml'].schema && content['application/xml'].schema.$ref) {
return findSchemaDefinition(content['application/xml'].schema.$ref, oas);
}
return null;
}

convertBodyToParams (params, isChild, parent, stack = 0) {
let paramsOut = []
const oas = this.state.oas
_.each(isChild ? params : params[0].schema.properties, (p, k) => {
// if (p.readOnly) return // These only show up in responses
if(p.$ref){
p = findSchemaDefinition(p.$ref, oas)
}

const param = _.clone(p)
param.name = (isChild ? `${isChild}.` : '') + k

paramsOut.push(param)
if (_.isUndefined(param.required)) {
if (isChild) { // I don't know if it should only be root elements... but for now, why not
param.required = parent.required && parent.required.includes(k)
} else if (!parent) {
param.required = params[0].schema.required && params[0].schema.required.indexOf(param.name) >= 0
}
}

if (p.type === 'object' && stack < 3) {
paramsOut = paramsOut.concat(this.convertBodyToParams(p.properties, param.name, p, stack + 1))
}
})

return paramsOut
validateOperation(operation) {
const status = this.state.selectedStatus;
return (
operation &&
operation.responses &&
operation.responses[status] &&
operation.responses[status].content
);
}

renderHeader () {
const keys = Object.keys(this.props.operation.responses)
changeHandler(e) {
this.selectedStatus(e.target.value);
}

selectedStatus(selected) {
this.setState({ selectedStatus: selected });
}

renderHeader() {
const keys = Object.keys(this.props.operation.responses);

return (
<h3>
<div className='pull-right'>
<div className="pull-right">
<select
className='switcher-switch'
className="switcher-switch"
value={this.state.selectedStatus}
onChange={this.changeHandler}
>
Expand All @@ -126,62 +86,31 @@ class ResponseSchema extends React.Component {
</div>
Response
</h3>
)
);
}

render () {
const { operation } = this.props
if (!operation.responses || Object.keys(operation.responses).length === 0) return null
render() {
const { operation, oas } = this.props;
if (!operation.responses || Object.keys(operation.responses).length === 0) return null;
const schema = this.getSchema(operation);

return (
<div className='hub-reference-response-definitions'>
<div className="hub-reference-response-definitions">
{this.renderHeader()}
<div>
{operation.responses[this.state.selectedStatus].description && (
<p className='desc'>{operation.responses[this.state.selectedStatus].description}</p>
<p className="desc">{operation.responses[this.state.selectedStatus].description}</p>
)}
{this.renderSchema(operation)}
{schema && <ResponseSchemaBody schema={schema} oas={oas} />}
</div>
</div>
)
}


convertToParams (params, paramIn) {
// if (paramIn !== 'body' && paramIn !== 'response') {
// return convertOtherToParams(params)
// }
return this.convertBodyToParams(params)
}

validateOperation(operation, status){
return operation && operation.responses && operation.responses[status]
&& operation.responses[status].content
);
}


}

// function convertOtherToParams (params, parent) {
// let paramsOut = []
// _.each(params, (p, k) => {
// if (p.readOnly) return // These only show up in responses
// const param = _.clone(p)
// param.name = parent ? `${parent.name}.${k}` : `${p.name}`
// paramsOut.push(param)
//
// if (p.type === 'object') {
// paramsOut = paramsOut.concat(exports.convertOtherToParams(p.properties, p))
// }
// })
//
// return paramsOut
// };


ResponseSchema.propTypes = {
operation: PropTypes.instanceOf(Operation).isRequired,
oas: PropTypes.instanceOf(Oas).isRequired
}
oas: PropTypes.instanceOf(Oas).isRequired,
};

module.exports = ResponseSchema
module.exports = ResponseSchema;
Loading

0 comments on commit b02c845

Please sign in to comment.