diff --git a/README.md b/README.md index 42929918f..ddb51dcf0 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ This repo consists of the following npm modules: - [@readme/api-explorer](https://npm.im/@readme/api-explorer) - the React components that make up the explorer - [@readme/markdown](https://npm.im/@readme/markdown) - the markdown parser +- [@readme/markdown-magic](https://npm.im/@readme/markdown-magic) - the legacy "magic block"-based markdown parser - [@readme/oas-extensions](https://npm.im/@readme/oas-extensions) - an exported object of our [OAS extensions](https://docs.readme.com/docs/swagger-extensions) - [@readme/oas-to-har](https://npm.im/@readme/oas-to-har) - utility to transform an OAS operation into a HAR representation - [@readme/syntax-highlighter](https://npm.im/@readme/syntax-highlighter) - the syntax highlighter in use on ReadMe diff --git a/example/index.html b/example/index.html index dd49dfd2f..17820ed84 100644 --- a/example/index.html +++ b/example/index.html @@ -9,6 +9,20 @@ .api-list-header { padding-left: 30px; } + + .api-experiments label { + margin-top: 0; + font-size: 14px; + line-height: 24px; + color: #747c84; + padding-left: 5px; + } + + .api-experiments li { + display: inline; + padding-right: 10px; + } + /* Removing sidebar padding */ section#hub-content { padding-left: 0; diff --git a/example/src/Demo.jsx b/example/src/Demo.jsx index 95442eafb..7e60be66e 100644 --- a/example/src/Demo.jsx +++ b/example/src/Demo.jsx @@ -9,63 +9,130 @@ const ApiExplorer = require('../../packages/api-explorer/src'); const Logs = require('../../packages/api-logs/index'); const ApiList = require('./ApiList'); -require('../../example/swagger-files/types.json'); -require('../../example/swagger-files/response-schemas.json'); - require('../../packages/api-logs/main.css'); -function Demo({ fetchSwagger, status, docs, oas, oauth }) { - return ( -
-
- -
{status.join('\n')}
+class Demo extends React.Component { + constructor(props) { + super(props); + this.state = { + brokenExplorerState: false, + maskErrorMessages: false, + useNewMarkdownEngine: false, + }; + } + + experimentToggles() { + const experiments = { + 'Mask error messages?': { + description: 'Switch between our different error states: consumer and project/API owner.', + stateProp: 'maskErrorMessages', + }, + 'Show fully broken state?': { + description: "Send the Explorer into a broken state. You'll need to refresh the page to get it working again.", + stateProp: 'brokenExplorerState', + }, + 'Use new Markdown engine?': { + description: null, + stateProp: 'useNewMarkdownEngine', + }, + }; + + return ( +
+
    + {Object.keys(experiments).map(name => { + const experiment = experiments[name]; + + return ( +
  • + { + this.setState({ [experiment.stateProp]: e.target.checked }); + this.forceUpdate(); + }} + type="checkbox" + /> + +
  • + ); + })} +
+
+ ); + } + + render() { + const { fetchSwagger, status, docs, oas, oauth } = this.props; + const { brokenExplorerState, maskErrorMessages, useNewMarkdownEngine } = this.state; + + const additionalProps = { + oasFiles: { + 'api-setting': Object.assign(extensions.defaults, oas), + }, + }; + + if (brokenExplorerState) { + delete additionalProps.oasFiles; + } + + return ( +
+
+ + + {status.length > 0 ?
{status.join('\n')}
: this.experimentToggles()} +
+ + {status.length === 0 && ( + + )}
- {status.length === 0 && ( - - )} -
- ); + ); + } } Demo.propTypes = { diff --git a/example/swagger-files/all-of.json b/example/swagger-files/all-of.json deleted file mode 100644 index 904fb8838..000000000 --- a/example/swagger-files/all-of.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "openapi": "3.0.0", - "servers": [ - { - "url": "http://httpbin.org" - } - ], - "info": { - "version": "1.0.0", - "title": "allOf keyword - NOT CURRENTLY SUPPORTED" - }, - "paths": { - "/anything/all-of-object": { - "post": { - "summary": "allOf object - NOT CURRENTLY SUPPORTED", - "description": "", - "requestBody": { - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "title": "First type of object", - "type": "object", - "properties": { - "a": { - "type": "string" - }, - "b": { - "type": "string" - } - } - }, - { - "title": "Second type of object", - "type": "object", - "properties": { - "c": { - "type": "string" - }, - "d": { - "type": "string" - } - } - } - ] - } - } - } - } - } - }, - "/anything/all-of-primitive": { - "post": { - "summary": "allOf primitive - NOT CURRENTLY SUPPORTED", - "description": "", - "requestBody": { - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "title": "First type of primitive", - "type": "string" - }, - { - "title": "Second type of primitive", - "type": "integer" - } - ] - } - } - } - } - } - } - } -} diff --git a/example/swagger-files/any-of.json b/example/swagger-files/any-of.json deleted file mode 100644 index b72047b05..000000000 --- a/example/swagger-files/any-of.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "openapi": "3.0.0", - "servers": [ - { - "url": "http://httpbin.org" - } - ], - "info": { - "version": "1.0.0", - "title": "anyOf keyword" - }, - "paths": { - "/anything/any-of-object": { - "post": { - "summary": "anyOf object", - "description": "", - "requestBody": { - "content": { - "application/json": { - "schema": { - "anyOf": [ - { - "title": "First type of object", - "type": "object", - "properties": { - "a": { - "type": "string" - }, - "b": { - "type": "string" - } - } - }, - { - "title": "Second type of object", - "type": "object", - "properties": { - "c": { - "type": "string" - }, - "d": { - "type": "string" - } - } - } - ] - } - } - } - } - } - }, - "/anything/any-of-primitive": { - "post": { - "summary": "anyOf primitive", - "description": "", - "requestBody": { - "content": { - "application/json": { - "schema": { - "anyOf": [ - { - "title": "First type of primitive", - "type": "string" - }, - { - "title": "Second type of primitive", - "type": "integer" - } - ] - } - } - } - } - } - } - } -} diff --git a/example/swagger-files/circular.json b/example/swagger-files/circular.json deleted file mode 100644 index f2ae09174..000000000 --- a/example/swagger-files/circular.json +++ /dev/null @@ -1,75 +0,0 @@ -{ - "swagger": "2.0", - "info": { - "title": "circular swagger file", - "version": "1.0" - }, - "schemes": [ - "http" - ], - "host": "httpbin.org", - "paths": { - "/circular": { - "post": { - "parameters": [ - { - "in": "body", - "name": "body", - "schema": { - "$ref": "#/definitions/node" - } - } - ], - "responses": { - "200": { - "description": "Circular" - } - } - }, - "put": { - "description": "This example fails due to a bug in RJSF when attempting to compute defaults for objects with refs.", - "requestBody": { - "content": { - "application\/json": { - "schema": { - "type": "object", - "properties": { - "obj": { - "$ref": "#/definitions/user" - } - } - } - } - } - } - } - } - }, - "definitions": { - "node": { - "type": "object", - "properties": { - "string": { - "type": "string" - }, - "children": { - "type": "array", - "items": { - "$ref": "#/definitions/node" - } - } - } - }, - "user": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "otherUser": { - "$ref": "#/definitions/user" - } - } - } - } -} diff --git a/example/swagger-files/cyclical-refs.json b/example/swagger-files/cyclical-refs.json new file mode 100644 index 000000000..67d5d4aea --- /dev/null +++ b/example/swagger-files/cyclical-refs.json @@ -0,0 +1,381 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "Document Resource", + "description": "Use this for adding, updating or removing documents.", + "version": "1.0.0" + }, + "paths": { + "/document": { + "put": { + "summary": "Add or update documents", + "operationId": "putMultiPartFormData", + "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "API key.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "file": { + "$ref": "#/components/schemas/FormDataBodyPart" + }, + "typename": { + "type": "string" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Successfully processed." + }, + "400": { + "description": "Invalid payload. This can be due to invalid headers or form-data params." + }, + "401": { + "description": "Permissions error. The Authorization header may be missing, the value may be invalid or the associated organization/user does not have permission to modify the specified typename." + }, + "500": { + "description": "Server error. Something unexpected happened while processing the request. Try again later." + } + } + } + } + }, + "components": { + "schemas": { + "BodyPart": { + "type": "object", + "properties": { + "contentDisposition": { + "$ref": "#/components/schemas/ContentDisposition" + }, + "entity": { + "type": "object" + }, + "headers": { + "type": "object", + "properties": { + "empty": { + "type": "boolean" + } + }, + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "mediaType": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "subtype": { + "type": "string" + }, + "parameters": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "wildcardType": { + "type": "boolean" + }, + "wildcardSubtype": { + "type": "boolean" + } + } + }, + "messageBodyWorkers": { + "$ref": "#/components/schemas/MessageBodyWorkers" + }, + "parent": { + "$ref": "#/components/schemas/MultiPart" + }, + "providers": { + "type": "object" + }, + "parameterizedHeaders": { + "type": "object", + "properties": { + "empty": { + "type": "boolean" + } + }, + "additionalProperties": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ParameterizedHeader" + } + } + } + } + }, + "ContentDisposition": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "parameters": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "fileName": { + "type": "string" + }, + "creationDate": { + "type": "string", + "format": "date-time" + }, + "modificationDate": { + "type": "string", + "format": "date-time" + }, + "readDate": { + "type": "string", + "format": "date-time" + }, + "size": { + "type": "integer", + "format": "int64" + } + } + }, + "FormDataBodyPart": { + "type": "object", + "properties": { + "contentDisposition": { + "$ref": "#/components/schemas/ContentDisposition" + }, + "entity": { + "type": "object" + }, + "headers": { + "type": "object", + "properties": { + "empty": { + "type": "boolean" + } + }, + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "mediaType": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "subtype": { + "type": "string" + }, + "parameters": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "wildcardType": { + "type": "boolean" + }, + "wildcardSubtype": { + "type": "boolean" + } + } + }, + "messageBodyWorkers": { + "$ref": "#/components/schemas/MessageBodyWorkers" + }, + "parent": { + "$ref": "#/components/schemas/MultiPart" + }, + "providers": { + "type": "object" + }, + "name": { + "type": "string" + }, + "value": { + "type": "string" + }, + "formDataContentDisposition": { + "$ref": "#/components/schemas/FormDataContentDisposition" + }, + "simple": { + "type": "boolean" + }, + "parameterizedHeaders": { + "type": "object", + "properties": { + "empty": { + "type": "boolean" + } + }, + "additionalProperties": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ParameterizedHeader" + } + } + } + }, + "description": "A single file which will be added/updated or a ZIP archive containing files that will be added/updated. The name of the ZIP archive does not matter but the names of the files contained within do.", + "example": "file=@2018-report.pdf" + }, + "FormDataContentDisposition": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "parameters": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "fileName": { + "type": "string" + }, + "creationDate": { + "type": "string", + "format": "date-time" + }, + "modificationDate": { + "type": "string", + "format": "date-time" + }, + "readDate": { + "type": "string", + "format": "date-time" + }, + "size": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + } + }, + "MessageBodyWorkers": { + "type": "object" + }, + "MultiPart": { + "type": "object", + "properties": { + "contentDisposition": { + "$ref": "#/components/schemas/ContentDisposition" + }, + "entity": { + "type": "object" + }, + "headers": { + "type": "object", + "properties": { + "empty": { + "type": "boolean" + } + }, + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "mediaType": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "subtype": { + "type": "string" + }, + "parameters": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "wildcardType": { + "type": "boolean" + }, + "wildcardSubtype": { + "type": "boolean" + } + } + }, + "messageBodyWorkers": { + "$ref": "#/components/schemas/MessageBodyWorkers" + }, + "parent": { + "$ref": "#/components/schemas/MultiPart" + }, + "providers": { + "type": "object" + }, + "bodyParts": { + "type": "array", + "items": { + "$ref": "#/components/schemas/BodyPart" + } + }, + "parameterizedHeaders": { + "type": "object", + "properties": { + "empty": { + "type": "boolean" + } + }, + "additionalProperties": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ParameterizedHeader" + } + } + } + } + }, + "ParameterizedHeader": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "parameters": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + } +} diff --git a/example/swagger-files/one-of.json b/example/swagger-files/one-of.json deleted file mode 100644 index 09550eb29..000000000 --- a/example/swagger-files/one-of.json +++ /dev/null @@ -1,156 +0,0 @@ -{ - "openapi": "3.0.0", - "servers": [ - { - "url": "http://httpbin.org" - } - ], - "info": { - "version": "1.0.0", - "title": "oneOf keyword" - }, - "paths": { - "/anything/one-of-object": { - "post": { - "summary": "oneOf object", - "description": "", - "requestBody": { - "content": { - "application/json": { - "schema": { - "oneOf": [ - { - "title": "First type of object", - "type": "object", - "properties": { - "a": { - "type": "string" - }, - "b": { - "type": "string" - } - } - }, - { - "title": "Second type of object", - "type": "object", - "properties": { - "c": { - "type": "string" - }, - "d": { - "type": "string" - } - } - } - ] - } - } - } - } - } - }, - "/anything/one-of-primitive": { - "post": { - "summary": "oneOf primitive", - "description": "", - "requestBody": { - "content": { - "application/json": { - "schema": { - "oneOf": [ - { - "title": "First type of primitive", - "type": "string" - }, - { - "title": "Second type of primitive", - "type": "integer" - } - ] - } - } - } - } - } - }, - "/anything/one-of-object-ref": { - "post": { - "summary": "oneOf object with $ref pointers", - "description": "", - "requestBody": { - "content": { - "application/json": { - "schema": { - "oneOf": [ - { - "$ref": "#/components/schemas/object1" - }, - { - "$ref": "#/components/schemas/object2" - } - ] - } - } - } - } - } - }, - "/anything/nested-one-of-object-ref": { - "post": { - "summary": "nested oneOf object with $ref pointers", - "description": "", - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "config": { - "oneOf": [ - { - "$ref": "#/components/schemas/object1" - }, - { - "$ref": "#/components/schemas/object2" - } - ] - } - }, - "additionalProperties": false - } - } - } - } - } - } - }, - "components": { - "schemas": { - "object1": { - "title": "First type of object", - "type": "object", - "properties": { - "a": { - "type": "string" - }, - "b": { - "type": "string" - } - } - }, - "object2": { - "title": "Second type of object", - "type": "object", - "properties": { - "c": { - "type": "string" - }, - "d": { - "type": "string" - } - } - } - } - } -} diff --git a/example/swagger-files/parameters-with-refs.json b/example/swagger-files/parameters-with-refs.json index 4974d13f8..00f49e7af 100644 --- a/example/swagger-files/parameters-with-refs.json +++ b/example/swagger-files/parameters-with-refs.json @@ -37,6 +37,16 @@ } ], "responses": {} + }, + "put": { + "summary": "Query param pointing to a $ref that has an escaped ~1 slash", + "description": "", + "parameters": [ + { + "$ref": "#/components/parameters/limit~1Param" + } + ], + "responses": {} } } }, @@ -53,6 +63,18 @@ "default": 20 }, "description": "The numbers of items to return." + }, + "limit/Param": { + "in": "query", + "name": "limit", + "required": false, + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 50, + "default": 20 + }, + "description": "The numbers of items to return." } }, "schemas": { diff --git a/example/swagger-files/polymorphism.json b/example/swagger-files/polymorphism.json new file mode 100644 index 000000000..645522c5c --- /dev/null +++ b/example/swagger-files/polymorphism.json @@ -0,0 +1,424 @@ +{ + "openapi": "3.0.0", + "servers": [ + { + "url": "http://httpbin.org" + } + ], + "info": { + "version": "1.0.0", + "title": "polymorphism support" + }, + "paths": { + "/pets": { + "patch": { + "summary": "oneOf request with a nested allOf", + "requestBody": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/Cat" + }, + { + "$ref": "#/components/schemas/Dog" + } + ], + "discriminator": { + "propertyName": "pet_type" + } + } + } + } + }, + "responses": { + "200": { + "description": "Updated" + } + } + } + }, + "/anything/all-of-object": { + "post": { + "summary": "allOf with listed objects", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "allOf": [ + { + "title": "First type of object", + "type": "object", + "properties": { + "a": { + "type": "string" + }, + "b": { + "type": "string" + } + } + }, + { + "title": "Second type of object", + "type": "object", + "properties": { + "c": { + "type": "string" + }, + "d": { + "type": "string" + } + } + } + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/anything/any-of-object": { + "post": { + "summary": "anyOf object", + "description": "", + "requestBody": { + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "title": "First type of object", + "type": "object", + "properties": { + "a": { + "type": "string" + }, + "b": { + "type": "string" + } + } + }, + { + "title": "Second type of object", + "type": "object", + "properties": { + "c": { + "type": "string" + }, + "d": { + "type": "string" + } + } + } + ] + } + } + } + } + } + }, + "/anything/any-of-primitive": { + "post": { + "summary": "anyOf primitive", + "description": "", + "requestBody": { + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "title": "First type of primitive", + "type": "string" + }, + { + "title": "Second type of primitive", + "type": "integer" + } + ] + } + } + } + } + } + }, + "/anything/one-of-object": { + "post": { + "summary": "oneOf object", + "description": "", + "requestBody": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "title": "First type of object", + "type": "object", + "properties": { + "a": { + "type": "string" + }, + "b": { + "type": "string" + } + } + }, + { + "title": "Second type of object", + "type": "object", + "properties": { + "c": { + "type": "string" + }, + "d": { + "type": "string" + } + } + } + ] + } + } + } + } + } + }, + "/anything/one-of-primitive": { + "post": { + "summary": "oneOf primitive", + "description": "", + "requestBody": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "title": "First type of primitive", + "type": "string" + }, + { + "title": "Second type of primitive", + "type": "integer" + } + ] + } + } + } + } + } + }, + "/anything/one-of-object-ref": { + "post": { + "summary": "oneOf object with $ref pointers", + "description": "", + "requestBody": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/object1" + }, + { + "$ref": "#/components/schemas/object2" + } + ] + } + } + } + } + } + }, + "/anything/nested-one-of-object-ref": { + "post": { + "summary": "nested oneOf object with $ref pointers", + "description": "", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "config": { + "oneOf": [ + { + "$ref": "#/components/schemas/object1" + }, + { + "$ref": "#/components/schemas/object2" + } + ] + } + }, + "additionalProperties": false + } + } + } + } + } + }, + "/anything/nested-one-of-ref": { + "post": { + "description": "Use case for https://github.com/readmeio/api-explorer/issues/495", + "requestBody": { + "$ref": "#/components/requestBodies/nested-one-of-ref" + } + } + }, + "/anything/nested-one-of-object-with-nested-one-of": { + "post": { + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "config": { + "oneOf": [ + { + "$ref": "#/components/schemas/object1" + }, + { + "$ref": "#/components/schemas/object2" + }, + { + "oneOf": [ + { + "$ref": "#/components/schemas/Cat" + }, + { + "$ref": "#/components/schemas/Dog" + } + ], + "discriminator": { + "propertyName": "pet_type" + } + } + ] + } + }, + "additionalProperties": false + } + } + } + } + } + } + }, + "components": { + "requestBodies": { + "nested-one-of-ref": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "config": { + "oneOf": [ + { + "$ref": "#/components/schemas/string1" + }, + { + "$ref": "#/components/schemas/string2" + } + ] + } + }, + "additionalProperties": false + } + } + } + } + }, + "schemas": { + "Pet": { + "type": "object", + "required": [ + "pet_type" + ], + "properties": { + "pet_type": { + "type": "string" + } + }, + "discriminator": { + "propertyName": "pet_type" + } + }, + "Dog": { + "allOf": [ + { + "$ref": "#/components/schemas/Pet" + }, + { + "type": "object", + "properties": { + "bark": { + "type": "boolean" + }, + "breed": { + "type": "string", + "enum": ["Dingo", "Husky", "Retriever", "Shepherd"] + } + } + } + ] + }, + "Cat": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/Pet" + }, + { + "type": "object", + "properties": { + "hunts": { + "type": "boolean" + }, + "age": { + "type": "integer" + } + } + } + ] + }, + "object1": { + "title": "First type of object", + "type": "object", + "properties": { + "a": { + "type": "string" + }, + "b": { + "type": "string" + } + } + }, + "object2": { + "title": "Second type of object", + "type": "object", + "properties": { + "c": { + "type": "string" + }, + "d": { + "type": "string" + } + } + }, + "string1": { + "type": "string" + }, + "string2": { + "type": "string" + } + } + } +} diff --git a/example/swagger-files/types.json b/example/swagger-files/types.json index dccdbfb3d..a73e5486e 100644 --- a/example/swagger-files/types.json +++ b/example/swagger-files/types.json @@ -67,10 +67,10 @@ "type": "string", "format": "binary" }, - "string (format: json)": { - "description": "This is a special ReadMe type to render a