diff --git a/docs/schema.md b/docs/schema.md
new file mode 100644
index 0000000..7086196
--- /dev/null
+++ b/docs/schema.md
@@ -0,0 +1,54 @@
+# Schema support
+
+The `Schema` object in [Open API v3 spec][oaschema] describes several properties
+that are shared from JSON Schema, deviations from JSON Schema, or in addition
+to JSON Schema. The document descibes this project's support for these
+properties.
+
+## Properties
+
+The following properties are supported, and implemented according to the
+[JSON Schema Validation spec][jsschema]:
+
+- [x] multipleOf
+- [x] maximum
+- [x] exclusiveMaximum
+- [x] minimum
+- [x] exclusiveMinimum
+- [x] maxLength
+- [x] minLength
+- [x] pattern
+- [x] maxItems
+- [x] minItems
+- [x] uniqueItems
+- [x] maxProperties
+- [x] minProperties
+- [x] format
+- [x] required
+- [x] enum
+
+## Adjusted JSON Schema Properties
+
+The OpenAPI specification also supports several additional properties from JSON
+Schema, with some adjustments. This project attempts to honor these adjustments,
+with any exceptions outlined below:
+
+- [x] type - Value may be an array, multiple types are supported.
+- [x] allOf
+- [ ] oneOf
+- [ ] anyOf
+- [ ] not
+- [x] items
+- [x] properties
+- [ ] additionalProperties
+- [x] description
+- [x] format
+- [x] default
+
+## Fixed Fields
+
+At this time, the project supports no [fixed fields][ff].
+
+[ff]: https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.md#fixed-fields-20
+[jsschema]: http://json-schema.org/latest/json-schema-validation.html#rfc.section.6
+[oaschema]: https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.md#schemaObject
diff --git a/src/components/ArrayProperty/ArrayProperty.js b/src/components/ArrayProperty/ArrayProperty.js
new file mode 100644
index 0000000..b77ac6b
--- /dev/null
+++ b/src/components/ArrayProperty/ArrayProperty.js
@@ -0,0 +1,53 @@
+import React, { PureComponent } from 'react';
+import PropTypes from 'prop-types';
+
+export default class ArrayProperty extends PureComponent {
+ render() {
+ const { constraints } = this.props;
+
+ if (!constraints) {
+ return null;
+ }
+
+ const { minItems, maxItems, uniqueItems } = constraints;
+ const validations = [];
+
+ if (uniqueItems) {
+ validations.push('items must be unique');
+ }
+
+ if (maxItems !== undefined && minItems !== undefined) {
+ // Be succint if the minItems is the same maxItems
+ // ie. value can only be of `x` length.
+ if (maxItems === minItems) {
+ validations.push(`${minItems} items`);
+ } else {
+ validations.push(`${minItems}-${maxItems} items`);
+ }
+ } else if (minItems !== undefined) {
+ validations.push(`at least ${minItems} items`);
+ } else if (maxItems !== undefined) {
+ validations.push(`at most ${maxItems} items`);
+ }
+
+ if (!validations.length) {
+ return null;
+ }
+
+ return (
+
+ {validations.map(constraint =>
+ {constraint}
+ )}
+
+ );
+ }
+}
+
+ArrayProperty.propTypes = {
+ constraints: PropTypes.shape({
+ maxItems: PropTypes.number,
+ minItems: PropTypes.number,
+ uniqueItems: PropTypes.bool
+ })
+};
diff --git a/src/components/BodySchema/BodySchema.js b/src/components/BodySchema/BodySchema.js
index 81f0c04..04153d4 100644
--- a/src/components/BodySchema/BodySchema.js
+++ b/src/components/BodySchema/BodySchema.js
@@ -38,19 +38,20 @@ export default class BodySchema extends Component {
if (properties.length === iterator) {
isLast = true;
}
- if (property.type === 'array' && expandedProp.indexOf(property.name) !== -1 && property.properties !== undefined) {
+
+ if (property.type.includes('array') && expandedProp.includes(property.name) && property.properties !== undefined) {
return createFragment({
property: this.renderPropertyRow(property, isLast, true),
subset: this.renderSubsetProperties(property, true)
});
- } else if (property.type === 'array' && property.properties !== undefined) {
+ } else if (property.type.includes('array') && property.properties !== undefined) {
return this.renderPropertyRow(property, isLast, false);
- } else if (property.type === 'object' && expandedProp.indexOf(property.name) !== -1 && property.properties !== undefined) {
+ } else if (property.type.includes('object') && expandedProp.includes(property.name) && property.properties !== undefined) {
return createFragment({
property: this.renderPropertyRow(property, isLast, true),
subset: this.renderSubsetProperties(property)
});
- } else if (property.type === 'object' && property.properties !== undefined) {
+ } else if (property.type.includes('object') && property.properties !== undefined) {
return this.renderPropertyRow(property, isLast, false);
} else {
return this.renderPropertyRow(property, isLast);
@@ -71,6 +72,7 @@ export default class BodySchema extends Component {
description={property.description}
enumValues={property.enum}
defaultValue={property.defaultValue}
+ constraints={property.constraints}
onClick={this.onClick.bind(this, property.name)}
isRequired={property.required}
isOpen={isOpen}
@@ -110,7 +112,7 @@ export default class BodySchema extends Component {
onClick(propertyName) {
const { expandedProp } = this.state;
- if (expandedProp.indexOf(propertyName) !== -1) {
+ if (expandedProp.includes(propertyName)) {
const newExpanded = expandedProp.filter((prop) => prop !== propertyName);
this.setState({ expandedProp: newExpanded });
} else {
diff --git a/src/components/Method/Method.js b/src/components/Method/Method.js
index 2a03330..372015c 100644
--- a/src/components/Method/Method.js
+++ b/src/components/Method/Method.js
@@ -75,14 +75,7 @@ export default class Method extends Component {
return (
Responses
- {responses.map((response) => {
- return (
-
- );
- })}
+ {responses.map((r) => )}
);
}
diff --git a/src/components/NumericProperty/NumericProperty.js b/src/components/NumericProperty/NumericProperty.js
new file mode 100644
index 0000000..2c36b07
--- /dev/null
+++ b/src/components/NumericProperty/NumericProperty.js
@@ -0,0 +1,58 @@
+import React, { PureComponent } from 'react';
+import PropTypes from 'prop-types';
+
+export default class NumericProperty extends PureComponent {
+ render() {
+ const { constraints } = this.props;
+
+ if (!constraints) {
+ return null;
+ }
+
+ const { exclusiveMinimum, exclusiveMaximum, maximum, minimum, multipleOf } = constraints;
+ const validations = [];
+
+ if (multipleOf) {
+ validations.push(`multiple of ${multipleOf}`);
+ }
+
+ // We're following JSON-Schema Draft 6, which states that exclusive* are
+ // integers, not boolean values. Also using `undefined` to prevent edge
+ // cases where the value is 0 or 1.
+ if (maximum !== undefined && minimum !== undefined) {
+ validations.push(`${minimum}…${maximum}`);
+ } else if (exclusiveMaximum !== undefined && exclusiveMinimum !== undefined) {
+ validations.push(`${exclusiveMinimum}…${exclusiveMaximum}`);
+ } else if (minimum !== undefined) {
+ validations.push(`≥ ${minimum}`);
+ } else if (maximum !== undefined) {
+ validations.push(`≤ ${maximum}`);
+ } else if (exclusiveMinimum !== undefined) {
+ validations.push(`> ${exclusiveMinimum}`);
+ } else if (exclusiveMaximum !== undefined) {
+ validations.push(`< ${exclusiveMaximum}`);
+ }
+
+ if (!validations.length) {
+ return null;
+ }
+
+ return (
+
+ {validations.map(constraint =>
+ {constraint}
+ )}
+
+ );
+ }
+}
+
+NumericProperty.propTypes = {
+ constraints: PropTypes.shape({
+ exclusiveMinimum: PropTypes.number,
+ exclusiveMaximum: PropTypes.number,
+ maximum: PropTypes.number,
+ minimum: PropTypes.number,
+ multipleOf: PropTypes.number
+ })
+};
diff --git a/src/components/ObjectProperty/ObjectProperty.js b/src/components/ObjectProperty/ObjectProperty.js
new file mode 100644
index 0000000..0420696
--- /dev/null
+++ b/src/components/ObjectProperty/ObjectProperty.js
@@ -0,0 +1,48 @@
+import React, { PureComponent } from 'react';
+import PropTypes from 'prop-types';
+
+export default class ObjectProperty extends PureComponent {
+ render() {
+ const { constraints } = this.props;
+
+ if (!constraints) {
+ return null;
+ }
+
+ const { minProperties, maxProperties } = constraints;
+ const validations = [];
+
+ if (maxProperties !== undefined && minProperties !== undefined) {
+ // Be succint if the minProperties is the same maxProperties
+ // ie. value can only be of `x` length.
+ if (maxProperties === minProperties) {
+ validations.push(`${minProperties} properties`);
+ } else {
+ validations.push(`${minProperties}-${maxProperties} properties`);
+ }
+ } else if (minProperties !== undefined) {
+ validations.push(`at least ${minProperties} properties`);
+ } else if (maxProperties !== undefined) {
+ validations.push(`at most ${maxProperties} properties`);
+ }
+
+ if (!validations.length) {
+ return null;
+ }
+
+ return (
+
+ {validations.map(constraint =>
+ {constraint}
+ )}
+
+ );
+ }
+}
+
+ObjectProperty.propTypes = {
+ constraints: PropTypes.shape({
+ maxProperties: PropTypes.number,
+ minProperties: PropTypes.number
+ })
+};
diff --git a/src/components/Property/Property.js b/src/components/Property/Property.js
index b512abc..dfbcfa4 100644
--- a/src/components/Property/Property.js
+++ b/src/components/Property/Property.js
@@ -2,14 +2,20 @@ import React, { Component } from 'react';
import classNames from 'classnames';
import PropTypes from 'prop-types';
+import ArrayProperty from '../ArrayProperty/ArrayProperty';
import Description from '../Description/Description';
import Indicator from '../Indicator/Indicator';
+import NumericProperty from '../NumericProperty/NumericProperty';
+import ObjectProperty from '../ObjectProperty/ObjectProperty';
+import StringProperty from '../StringProperty/StringProperty';
import './Property.scss';
export default class Property extends Component {
render() {
- const { name, type, description, isRequired, enumValues, defaultValue, onClick, isOpen, isLast } = this.props;
+ const {
+ name, type, description, constraints, isRequired, enumValues, defaultValue, onClick, isOpen, isLast
+ } = this.props;
let subtype;
if (type.includes('array')) {
@@ -20,6 +26,7 @@ export default class Property extends Component {
if (isOpen !== undefined) {
isClickable = true;
}
+
let status;
if (isOpen) {
status = 'open';
@@ -41,10 +48,18 @@ export default class Property extends Component {
}
- {type.join(', ')}{subtype && of {subtype}}
+
+ {!subtype ? type.join(', ') : {subtype}[]}
+ {!subtype && constraints && constraints.format &&
+ <{constraints.format}>}
+
{isRequired && Required}
+ {['number', 'integer'].some(t => type.includes(t)) && }
+ {type.includes('string') && }
+ {type.includes('array') && }
+ {type.includes('object') && }
{enumValues && this.renderEnumValues(enumValues)}
- {defaultValue && this.renderDefaultValue(defaultValue)}
+ {defaultValue !== undefined && this.renderDefaultValue(defaultValue)}
{description && }
|
@@ -97,10 +112,19 @@ Property.propTypes = {
description: PropTypes.string,
constraints: PropTypes.shape({
format: PropTypes.string,
- minLength: PropTypes.number,
- exclusiveMinimum: PropTypes.bool,
+ exclusiveMinimum: PropTypes.number,
+ exclusiveMaximum: PropTypes.number,
+ maximum: PropTypes.number,
+ maxItems: PropTypes.number,
maxLength: PropTypes.number,
- exclusiveMaximum: PropTypes.bool
+ maxProperties: PropTypes.number,
+ minimum: PropTypes.number,
+ minItems: PropTypes.number,
+ minLength: PropTypes.number,
+ minProperties: PropTypes.number,
+ multipleOf: PropTypes.number,
+ pattern: PropTypes.string,
+ uniqueItems: PropTypes.bool
}),
enumValues: PropTypes.array,
defaultValue: PropTypes.any,
diff --git a/src/components/StringProperty/StringProperty.js b/src/components/StringProperty/StringProperty.js
new file mode 100644
index 0000000..053410a
--- /dev/null
+++ b/src/components/StringProperty/StringProperty.js
@@ -0,0 +1,53 @@
+import React, { PureComponent } from 'react';
+import PropTypes from 'prop-types';
+
+export default class StringProperty extends PureComponent {
+ render() {
+ const { constraints } = this.props;
+
+ if (!constraints) {
+ return null;
+ }
+
+ const { pattern, minLength, maxLength } = constraints;
+ const validations = [];
+
+ if (pattern) {
+ validations.push(`/${pattern}/`);
+ }
+
+ if (maxLength !== undefined && minLength !== undefined) {
+ // Be succint if the minLength is the same maxLength
+ // ie. value can only be of `x` length.
+ if (maxLength === minLength) {
+ validations.push(`${minLength} chars`);
+ } else {
+ validations.push(`${minLength}-${maxLength} chars`);
+ }
+ } else if (minLength !== undefined) {
+ validations.push(`at least ${minLength} chars`);
+ } else if (maxLength !== undefined) {
+ validations.push(`at most ${maxLength} chars`);
+ }
+
+ if (!validations.length) {
+ return null;
+ }
+
+ return (
+
+ {validations.map(constraint =>
+ {constraint}
+ )}
+
+ );
+ }
+}
+
+StringProperty.propTypes = {
+ constraints: PropTypes.shape({
+ pattern: PropTypes.string,
+ maxLength: PropTypes.number,
+ minLength: PropTypes.number
+ })
+};
diff --git a/src/parser/open-api/constraintsParser.js b/src/parser/open-api/constraintsParser.js
index 66847a1..39cec5d 100644
--- a/src/parser/open-api/constraintsParser.js
+++ b/src/parser/open-api/constraintsParser.js
@@ -1,5 +1,5 @@
// https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.md#schema-object
-const VALIDATION_KEYWORDS = [
+export const VALIDATION_KEYWORDS = [
'format',
'exclusiveMaximum',
'exclusiveMinimum',
@@ -28,8 +28,19 @@ export function hasConstraints(property) {
);
}
+/**
+ * Given a property, extract all the constraints from it and return a new
+ * object with those constraints.
+ *
+ * @param {Object} property
+ * @return {Object}
+ */
export function getConstraints(property) {
- return {
+ return Object.keys(property).reduce((constraints, key) => {
+ if (VALIDATION_KEYWORDS.includes(key)) {
+ constraints[key] = property[key];
+ }
- };
+ return constraints;
+ }, {});
}
diff --git a/test/components/ArrayProperty.test.js b/test/components/ArrayProperty.test.js
new file mode 100644
index 0000000..bcc789f
--- /dev/null
+++ b/test/components/ArrayProperty.test.js
@@ -0,0 +1,67 @@
+import React from 'react';
+import ArrayProperty from './../../src/components/ArrayProperty/ArrayProperty';
+import renderer from 'react-test-renderer';
+
+describe('', () => {
+ it('renders nothing if there are no applicable constraints', () => {
+ let tree = renderer.create(
+
+ ).toJSON();
+
+ expect(tree).toBeNull();
+
+ tree = renderer.create(
+
+ ).toJSON();
+
+ expect(tree).toBeNull();
+ });
+
+ it('renders uniqueItems', () => {
+ const tree = renderer.create(
+
+ );
+
+ expect(tree).toMatchSnapshot();
+ });
+
+ it('renders minItems', () => {
+ const tree = renderer.create(
+
+ );
+
+ expect(tree).toMatchSnapshot();
+ });
+
+ it('renders maxItems', () => {
+ const tree = renderer.create(
+
+ );
+
+ expect(tree).toMatchSnapshot();
+ });
+
+ it('renders minItems and maxItems', () => {
+ const tree = renderer.create(
+
+ );
+
+ expect(tree).toMatchSnapshot();
+ });
+
+ it('renders minItems and maxItems when they are the same value', () => {
+ const tree = renderer.create(
+
+ );
+
+ expect(tree).toMatchSnapshot();
+ });
+
+ it('renders uniqueItems, minItems and maxItems', () => {
+ const tree = renderer.create(
+
+ );
+
+ expect(tree).toMatchSnapshot();
+ });
+});
diff --git a/test/components/NumericProperty.test.js b/test/components/NumericProperty.test.js
new file mode 100644
index 0000000..23fa0d2
--- /dev/null
+++ b/test/components/NumericProperty.test.js
@@ -0,0 +1,83 @@
+import React from 'react';
+import NumericProperty from './../../src/components/NumericProperty/NumericProperty';
+import renderer from 'react-test-renderer';
+
+describe('', () => {
+ it('renders nothing if there are no applicable constraints', () => {
+ let tree = renderer.create(
+
+ ).toJSON();
+
+ expect(tree).toBeNull();
+
+ tree = renderer.create(
+
+ ).toJSON();
+
+ expect(tree).toBeNull();
+ });
+
+ it('renders multipleOf', () => {
+ const tree = renderer.create(
+
+ );
+
+ expect(tree).toMatchSnapshot();
+ });
+
+ it('renders maximum', () => {
+ const tree = renderer.create(
+
+ );
+
+ expect(tree).toMatchSnapshot();
+ });
+
+ it('renders minimum', () => {
+ const tree = renderer.create(
+
+ );
+
+ expect(tree).toMatchSnapshot();
+ });
+
+ it('renders minimum and maximum', () => {
+ const tree = renderer.create(
+
+ );
+
+ expect(tree).toMatchSnapshot();
+ });
+
+ it('renders exclusiveMaximum', () => {
+ const tree = renderer.create(
+
+ );
+
+ expect(tree).toMatchSnapshot();
+ });
+
+ it('renders exclusiveMinimum', () => {
+ const tree = renderer.create(
+
+ );
+
+ expect(tree).toMatchSnapshot();
+ });
+
+ it('renders exclusiveMinimum and exclusiveMaximum', () => {
+ const tree = renderer.create(
+
+ );
+
+ expect(tree).toMatchSnapshot();
+ });
+
+ it('renders a combination of constraints', () => {
+ const tree = renderer.create(
+
+ );
+
+ expect(tree).toMatchSnapshot();
+ });
+});
diff --git a/test/components/ObjectProperty.test.js b/test/components/ObjectProperty.test.js
new file mode 100644
index 0000000..58456c9
--- /dev/null
+++ b/test/components/ObjectProperty.test.js
@@ -0,0 +1,45 @@
+import React from 'react';
+import ObjectProperty from './../../src/components/ObjectProperty/ObjectProperty';
+import renderer from 'react-test-renderer';
+
+describe('', () => {
+ it('renders nothing if there are no constraints', () => {
+ const tree = renderer.create(
+
+ ).toJSON();
+
+ expect(tree).toBeNull();
+ });
+
+ it('renders minProperties', () => {
+ const tree = renderer.create(
+
+ );
+
+ expect(tree).toMatchSnapshot();
+ });
+
+ it('renders maxProperties', () => {
+ const tree = renderer.create(
+
+ );
+
+ expect(tree).toMatchSnapshot();
+ });
+
+ it('renders minProperties and maxProperties', () => {
+ const tree = renderer.create(
+
+ );
+
+ expect(tree).toMatchSnapshot();
+ });
+
+ it('renders minProperties and maxProperties when they are the same value', () => {
+ const tree = renderer.create(
+
+ );
+
+ expect(tree).toMatchSnapshot();
+ });
+});
diff --git a/test/components/Property.test.js b/test/components/Property.test.js
index 711591e..9f16742 100644
--- a/test/components/Property.test.js
+++ b/test/components/Property.test.js
@@ -49,6 +49,19 @@ describe('', () => {
expect(tree).toMatchSnapshot();
});
+ it('can render a property with numerical constraints', () => {
+ const shallow = new ReactShallowRenderer();
+ const tree = shallow.render(
+
+ );
+
+ expect(tree).toMatchSnapshot();
+ });
+
it('can render a property with multiple types', () => {
const tree = renderer.create(
', () => {
expect(tree).toMatchSnapshot();
});
+
+ it('can render a property with a format', () => {
+ const tree = renderer.create(
+
+ );
+
+ expect(tree).toMatchSnapshot();
+ });
});
diff --git a/test/components/StringProperty.test.js b/test/components/StringProperty.test.js
new file mode 100644
index 0000000..f3da774
--- /dev/null
+++ b/test/components/StringProperty.test.js
@@ -0,0 +1,67 @@
+import React from 'react';
+import StringProperty from './../../src/components/StringProperty/StringProperty';
+import renderer from 'react-test-renderer';
+
+describe('', () => {
+ it('renders nothing if there are no applicable constraints', () => {
+ let tree = renderer.create(
+
+ ).toJSON();
+
+ expect(tree).toBeNull();
+
+ tree = renderer.create(
+
+ ).toJSON();
+
+ expect(tree).toBeNull();
+ });
+
+ it('renders pattern', () => {
+ const tree = renderer.create(
+
+ );
+
+ expect(tree).toMatchSnapshot();
+ });
+
+ it('renders minLength', () => {
+ const tree = renderer.create(
+
+ );
+
+ expect(tree).toMatchSnapshot();
+ });
+
+ it('renders maxLength', () => {
+ const tree = renderer.create(
+
+ );
+
+ expect(tree).toMatchSnapshot();
+ });
+
+ it('renders minLength and maxLength', () => {
+ const tree = renderer.create(
+
+ );
+
+ expect(tree).toMatchSnapshot();
+ });
+
+ it('renders minLength and maxLength when they are the same value', () => {
+ const tree = renderer.create(
+
+ );
+
+ expect(tree).toMatchSnapshot();
+ });
+
+ it('renders pattern, minLength and maxLength', () => {
+ const tree = renderer.create(
+
+ );
+
+ expect(tree).toMatchSnapshot();
+ });
+});
diff --git a/test/components/__snapshots__/ArrayProperty.test.js.snap b/test/components/__snapshots__/ArrayProperty.test.js.snap
new file mode 100644
index 0000000..bd287c7
--- /dev/null
+++ b/test/components/__snapshots__/ArrayProperty.test.js.snap
@@ -0,0 +1,66 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[` renders maxItems 1`] = `
+
+
+ at most 10 items
+
+
+`;
+
+exports[` renders minItems 1`] = `
+
+
+ at least 2 items
+
+
+`;
+
+exports[` renders minItems and maxItems 1`] = `
+
+
+ 4-10 items
+
+
+`;
+
+exports[` renders minItems and maxItems when they are the same value 1`] = `
+
+
+ 4 items
+
+
+`;
+
+exports[` renders uniqueItems 1`] = `
+
+
+ items must be unique
+
+
+`;
+
+exports[` renders uniqueItems, minItems and maxItems 1`] = `
+
+
+ items must be unique
+
+
+ 4-10 items
+
+
+`;
diff --git a/test/components/__snapshots__/NumericProperty.test.js.snap b/test/components/__snapshots__/NumericProperty.test.js.snap
new file mode 100644
index 0000000..3ef8085
--- /dev/null
+++ b/test/components/__snapshots__/NumericProperty.test.js.snap
@@ -0,0 +1,86 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[` renders a combination of constraints 1`] = `
+
+
+ multiple of 2
+
+
+ 2…20
+
+
+`;
+
+exports[` renders exclusiveMaximum 1`] = `
+
+
+ < 2
+
+
+`;
+
+exports[` renders exclusiveMinimum 1`] = `
+
+
+ > 2
+
+
+`;
+
+exports[` renders exclusiveMinimum and exclusiveMaximum 1`] = `
+
+
+ 2…5
+
+
+`;
+
+exports[` renders maximum 1`] = `
+
+
+ ≤ 2
+
+
+`;
+
+exports[` renders minimum 1`] = `
+
+
+ ≥ 2
+
+
+`;
+
+exports[` renders minimum and maximum 1`] = `
+
+
+ 2…5
+
+
+`;
+
+exports[` renders multipleOf 1`] = `
+
+
+ multiple of 2
+
+
+`;
diff --git a/test/components/__snapshots__/ObjectProperty.test.js.snap b/test/components/__snapshots__/ObjectProperty.test.js.snap
new file mode 100644
index 0000000..a05805f
--- /dev/null
+++ b/test/components/__snapshots__/ObjectProperty.test.js.snap
@@ -0,0 +1,41 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[` renders maxProperties 1`] = `
+
+
+ at most 10 properties
+
+
+`;
+
+exports[` renders minProperties 1`] = `
+
+
+ at least 2 properties
+
+
+`;
+
+exports[` renders minProperties and maxProperties 1`] = `
+
+
+ 4-10 properties
+
+
+`;
+
+exports[` renders minProperties and maxProperties when they are the same value 1`] = `
+
+
+ 4 properties
+
+
+`;
diff --git a/test/components/__snapshots__/Property.test.js.snap b/test/components/__snapshots__/Property.test.js.snap
index 468c6aa..09ed8ba 100644
--- a/test/components/__snapshots__/Property.test.js.snap
+++ b/test/components/__snapshots__/Property.test.js.snap
@@ -15,7 +15,9 @@ exports[` can render a basic property 1`] = `
-
+
string
can render a basic property 1`] = `
`;
-exports[` can render a property with a subtype 1`] = `
+exports[` can render a property with a format 1`] = `
can render a property with a subtype 1`] = `
className="property-name"
>
- data
+ value
|
-
- array
+
+ string
+
+ <
+ email
+ >
+
+ |
+
+`;
+
+exports[` can render a property with a subtype 1`] = `
+
+
- of
- object
+ data
+
+ |
+
+
+
+ object
+ []
+
|
@@ -68,7 +104,9 @@ exports[` can render a property with description 1`] = `
-
+
string
can render a property with description 1`] = `
>
Required
+
@@ -98,7 +139,9 @@ exports[` can render a property with enum 1`] = `
|
-
+
string
can render a property with multiple types 1`] = `
|
-
+
string, number
|
`;
+
+exports[` can render a property with numerical constraints 1`] = `
+
+
+
+ type
+
+ |
+
+
+ number
+
+
+ Required
+
+
+ |
+
+`;
diff --git a/test/components/__snapshots__/StringProperty.test.js.snap b/test/components/__snapshots__/StringProperty.test.js.snap
new file mode 100644
index 0000000..ae94b28
--- /dev/null
+++ b/test/components/__snapshots__/StringProperty.test.js.snap
@@ -0,0 +1,66 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[` renders maxLength 1`] = `
+
+
+ at most 10 chars
+
+
+`;
+
+exports[` renders minLength 1`] = `
+
+
+ at least 2 chars
+
+
+`;
+
+exports[` renders minLength and maxLength 1`] = `
+
+
+ 4-10 chars
+
+
+`;
+
+exports[` renders minLength and maxLength when they are the same value 1`] = `
+
+
+ 4 chars
+
+
+`;
+
+exports[` renders pattern 1`] = `
+
+
+ /^[a-zA-Z0-9_-]+$/
+
+
+`;
+
+exports[` renders pattern, minLength and maxLength 1`] = `
+
+
+ /^[a-z]/
+
+
+ 4-10 chars
+
+
+`;
diff --git a/test/parser/constraintsParser.test.js b/test/parser/constraintsParser.test.js
new file mode 100644
index 0000000..2b7af4e
--- /dev/null
+++ b/test/parser/constraintsParser.test.js
@@ -0,0 +1,56 @@
+import { VALIDATION_KEYWORDS, hasConstraints, getConstraints } from '../../src/parser/open-api/constraintsParser';
+
+describe('VALIDATION_KEYWORDS', () => {
+ test('keywords match what is supported by Open API', () => {
+ expect(VALIDATION_KEYWORDS).toEqual([
+ 'format',
+ 'exclusiveMaximum',
+ 'exclusiveMinimum',
+ 'maximum',
+ 'maxItems',
+ 'maxLength',
+ 'maxProperties',
+ 'minimum',
+ 'minItems',
+ 'minLength',
+ 'minProperties',
+ 'multipleOf',
+ 'pattern',
+ 'uniqueItems'
+ ]);
+ });
+});
+
+describe('#hasConstraints', () => {
+ test('returns true when the property contains constraints', () => {
+ const property = {
+ type: 'string',
+ pattern: '^[a-zA-Z0-9_-]+$'
+ };
+
+ expect(hasConstraints(property)).toBeTruthy();
+ });
+
+ test('returns false when the property has no constraints', () => {
+ const property = {
+ type: 'string'
+ };
+
+ expect(hasConstraints(property)).toBeFalsy();
+ });
+});
+
+describe('#getConstraints', () => {
+ test('can create a constraints object with appropriate constraints', () => {
+ const property = {
+ type: 'string',
+ pattern: '^[a-zA-Z0-9_-]+$'
+ };
+
+ const constraints = {
+ pattern: '^[a-zA-Z0-9_-]+$'
+ };
+
+ expect(getConstraints(property)).toEqual(constraints);
+ });
+});
diff --git a/test/parser/open-api/data/schemaParser/complexOutputSchema.json b/test/parser/open-api/data/schemaParser/complexOutputSchema.json
index e626027..9eaaeb0 100644
--- a/test/parser/open-api/data/schemaParser/complexOutputSchema.json
+++ b/test/parser/open-api/data/schemaParser/complexOutputSchema.json
@@ -18,7 +18,10 @@
"required": true,
"type": [
"string"
- ]
+ ],
+ "constraints": {
+ "pattern": "^[a-zA-Z0-9_-]+$"
+ }
},
{
"name": "value",
diff --git a/test/parser/open-api/v3/data/outputs/accounts.json b/test/parser/open-api/v3/data/outputs/accounts.json
index a4c7234..8aa6ae3 100644
--- a/test/parser/open-api/v3/data/outputs/accounts.json
+++ b/test/parser/open-api/v3/data/outputs/accounts.json
@@ -119,7 +119,10 @@
"string"
],
"required": true,
- "description": "Email of the user"
+ "description": "Email of the user",
+ "constraints": {
+ "format": "email"
+ }
}
]
}
@@ -435,7 +438,10 @@
"string"
],
"required": true,
- "description": "Email of the user"
+ "description": "Email of the user",
+ "constraints": {
+ "format": "email"
+ }
},
{
"name": "bearerToken",
diff --git a/test/parser/open-api/v3/data/outputs/carrier.json b/test/parser/open-api/v3/data/outputs/carrier.json
index 6fc2282..caaed4f 100644
--- a/test/parser/open-api/v3/data/outputs/carrier.json
+++ b/test/parser/open-api/v3/data/outputs/carrier.json
@@ -36,6 +36,9 @@
"array"
],
"required": true,
+ "constraints": {
+ "minItems": 1
+ },
"properties": [
{
"name": "id",
@@ -80,7 +83,10 @@
"type": [
"string"
],
- "required": true
+ "required": true,
+ "constraints": {
+ "format": "uri"
+ }
},
{
"name": "services",
@@ -88,6 +94,9 @@
"array"
],
"required": true,
+ "constraints": {
+ "uniqueItems": true
+ },
"properties": [
{
"name": "id",