Merge branch 'master-with-console' into 'master'

Master with console

See merge request babel/babel-api-doc!1
This commit is contained in:
m.savalanpour 2020-01-19 13:04:24 +03:30
commit 98c0e716c1
31 changed files with 1676 additions and 35 deletions

3
.gitignore vendored
View File

@ -36,3 +36,6 @@ cli/index.js
stats.json
/package-lock.json
/.idea/
# npmrc for local npm
.npmrc

1
demo/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
intent.json

View File

@ -38,8 +38,6 @@ info:
OAuth2 - an open protocol to allow secure authorization in a simple
and standard method from web, mobile and desktop applications.
<SecurityDefinitions />
version: 1.0.0
title: Swagger Petstore
termsOfService: 'http://swagger.io/terms/'

993
demo/petstore.json Normal file
View File

@ -0,0 +1,993 @@
{
"swagger": "2.0",
"info": {
"description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.",
"version": "1.0.0",
"title": "Swagger Petstore",
"termsOfService": "http://swagger.io/terms/",
"contact": {
"email": "apiteam@swagger.io"
},
"license": {
"name": "Apache 2.0",
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
}
},
"host": "petstore.swagger.io",
"basePath": "/v2",
"tags": [{
"name": "pet",
"description": "Everything about your Pets",
"externalDocs": {
"description": "Find out more",
"url": "http://swagger.io"
}
},
{
"name": "store",
"description": "Access to Petstore orders"
},
{
"name": "user",
"description": "Operations about user",
"externalDocs": {
"description": "Find out more about our store",
"url": "http://swagger.io"
}
}
],
"schemes": [
"http"
],
"paths": {
"/pet": {
"post": {
"tags": [
"pet"
],
"summary": "Add a new pet to the store",
"description": "",
"operationId": "addPet",
"consumes": [
"application/json",
"application/xml"
],
"produces": [
"application/xml",
"application/json"
],
"parameters": [{
"in": "body",
"name": "body",
"description": "Pet object that needs to be added to the store",
"required": true,
"schema": {
"$ref": "#/definitions/Pet"
}
}],
"responses": {
"405": {
"description": "Invalid input"
}
},
"security": [{
"petstore_auth": [
"write:pets",
"read:pets"
]
}]
},
"put": {
"tags": [
"pet"
],
"summary": "Update an existing pet",
"description": "",
"operationId": "updatePet",
"consumes": [
"application/json",
"application/xml"
],
"produces": [
"application/xml",
"application/json"
],
"parameters": [{
"in": "body",
"name": "body",
"description": "Pet object that needs to be added to the store",
"required": true,
"schema": {
"$ref": "#/definitions/Pet"
}
}],
"responses": {
"400": {
"description": "Invalid ID supplied"
},
"404": {
"description": "Pet not found"
},
"405": {
"description": "Validation exception"
}
},
"security": [{
"petstore_auth": [
"write:pets",
"read:pets"
]
}]
}
},
"/pet/findByStatus": {
"get": {
"tags": [
"pet"
],
"summary": "Finds Pets by status",
"description": "Multiple status values can be provided with comma separated strings",
"operationId": "findPetsByStatus",
"produces": [
"application/xml",
"application/json"
],
"parameters": [{
"name": "status",
"in": "query",
"description": "Status values that need to be considered for filter",
"required": true,
"type": "array",
"items": {
"type": "string",
"enum": [
"available",
"pending",
"sold"
],
"default": "available"
},
"collectionFormat": "multi"
}],
"responses": {
"200": {
"description": "successful operation",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/Pet"
}
}
},
"400": {
"description": "Invalid status value"
}
},
"security": [{
"petstore_auth": [
"write:pets",
"read:pets"
]
}]
}
},
"/pet/findByTags": {
"get": {
"tags": [
"pet"
],
"summary": "Finds Pets by tags",
"description": "Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.",
"operationId": "findPetsByTags",
"produces": [
"application/xml",
"application/json"
],
"parameters": [{
"name": "tags",
"in": "query",
"description": "Tags to filter by",
"required": true,
"type": "array",
"items": {
"type": "string"
},
"collectionFormat": "multi"
}],
"responses": {
"200": {
"description": "successful operation",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/Pet"
}
}
},
"400": {
"description": "Invalid tag value"
}
},
"security": [{
"petstore_auth": [
"write:pets",
"read:pets"
]
}],
"deprecated": true
}
},
"/pet/{petId}": {
"get": {
"tags": [
"pet"
],
"summary": "Find pet by ID",
"description": "Returns a single pet",
"operationId": "getPetById",
"produces": [
"application/xml",
"application/json"
],
"parameters": [{
"name": "petId",
"in": "path",
"description": "ID of pet to return",
"required": true,
"type": "integer",
"format": "int64"
}],
"responses": {
"200": {
"description": "successful operation",
"schema": {
"$ref": "#/definitions/Pet"
}
},
"400": {
"description": "Invalid ID supplied"
},
"404": {
"description": "Pet not found"
}
},
"security": [{
"api_key": [
]
}]
},
"post": {
"tags": [
"pet"
],
"summary": "Updates a pet in the store with form data",
"description": "",
"operationId": "updatePetWithForm",
"consumes": [
"application/x-www-form-urlencoded"
],
"produces": [
"application/xml",
"application/json"
],
"parameters": [{
"name": "petId",
"in": "path",
"description": "ID of pet that needs to be updated",
"required": true,
"type": "integer",
"format": "int64"
},
{
"name": "name",
"in": "formData",
"description": "Updated name of the pet",
"required": false,
"type": "string"
},
{
"name": "status",
"in": "formData",
"description": "Updated status of the pet",
"required": false,
"type": "string"
}
],
"responses": {
"405": {
"description": "Invalid input"
}
},
"security": [{
"petstore_auth": [
"write:pets",
"read:pets"
]
}]
},
"delete": {
"tags": [
"pet"
],
"summary": "Deletes a pet",
"description": "",
"operationId": "deletePet",
"produces": [
"application/xml",
"application/json"
],
"parameters": [{
"name": "api_key",
"in": "header",
"required": false,
"type": "string"
},
{
"name": "petId",
"in": "path",
"description": "Pet id to delete",
"required": true,
"type": "integer",
"format": "int64"
}
],
"responses": {
"400": {
"description": "Invalid ID supplied"
},
"404": {
"description": "Pet not found"
}
},
"security": [{
"petstore_auth": [
"write:pets",
"read:pets"
]
}]
}
},
"/pet/{petId}/uploadImage": {
"post": {
"tags": [
"pet"
],
"summary": "uploads an image",
"description": "",
"operationId": "uploadFile",
"consumes": [
"multipart/form-data"
],
"produces": [
"application/json"
],
"parameters": [{
"name": "petId",
"in": "path",
"description": "ID of pet to update",
"required": true,
"type": "integer",
"format": "int64"
},
{
"name": "additionalMetadata",
"in": "formData",
"description": "Additional data to pass to server",
"required": false,
"type": "string"
},
{
"name": "file",
"in": "formData",
"description": "file to upload",
"required": false,
"type": "file"
}
],
"responses": {
"200": {
"description": "successful operation",
"schema": {
"$ref": "#/definitions/ApiResponse"
}
}
},
"security": [{
"petstore_auth": [
"write:pets",
"read:pets"
]
}]
}
},
"/store/inventory": {
"get": {
"tags": [
"store"
],
"summary": "Returns pet inventories by status",
"description": "Returns a map of status codes to quantities",
"operationId": "getInventory",
"produces": [
"application/json"
],
"parameters": [
],
"responses": {
"200": {
"description": "successful operation",
"schema": {
"type": "object",
"additionalProperties": {
"type": "integer",
"format": "int32"
}
}
}
},
"security": [{
"api_key": [
]
}]
}
},
"/store/order": {
"post": {
"tags": [
"store"
],
"summary": "Place an order for a pet",
"description": "",
"operationId": "placeOrder",
"produces": [
"application/xml",
"application/json"
],
"parameters": [{
"in": "body",
"name": "body",
"description": "order placed for purchasing the pet",
"required": true,
"schema": {
"$ref": "#/definitions/Order"
}
}],
"responses": {
"200": {
"description": "successful operation",
"schema": {
"$ref": "#/definitions/Order"
}
},
"400": {
"description": "Invalid Order"
}
}
}
},
"/store/order/{orderId}": {
"get": {
"tags": [
"store"
],
"summary": "Find purchase order by ID",
"description": "For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions",
"operationId": "getOrderById",
"produces": [
"application/xml",
"application/json"
],
"parameters": [{
"name": "orderId",
"in": "path",
"description": "ID of pet that needs to be fetched",
"required": true,
"type": "integer",
"maximum": 10.0,
"minimum": 1.0,
"format": "int64"
}],
"responses": {
"200": {
"description": "successful operation",
"schema": {
"$ref": "#/definitions/Order"
}
},
"400": {
"description": "Invalid ID supplied"
},
"404": {
"description": "Order not found"
}
}
},
"delete": {
"tags": [
"store"
],
"summary": "Delete purchase order by ID",
"description": "For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors",
"operationId": "deleteOrder",
"produces": [
"application/xml",
"application/json"
],
"parameters": [{
"name": "orderId",
"in": "path",
"description": "ID of the order that needs to be deleted",
"required": true,
"type": "integer",
"minimum": 1.0,
"format": "int64"
}],
"responses": {
"400": {
"description": "Invalid ID supplied"
},
"404": {
"description": "Order not found"
}
}
}
},
"/user": {
"post": {
"tags": [
"user"
],
"summary": "Create user",
"description": "This can only be done by the logged in user.",
"operationId": "createUser",
"produces": [
"application/xml",
"application/json"
],
"parameters": [{
"in": "body",
"name": "body",
"description": "Created user object",
"required": true,
"schema": {
"$ref": "#/definitions/User"
}
}],
"responses": {
"default": {
"description": "successful operation"
}
}
}
},
"/user/createWithArray": {
"post": {
"tags": [
"user"
],
"summary": "Creates list of users with given input array",
"description": "",
"operationId": "createUsersWithArrayInput",
"produces": [
"application/xml",
"application/json"
],
"parameters": [{
"in": "body",
"name": "body",
"description": "List of user object",
"required": true,
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/User"
}
}
}],
"responses": {
"default": {
"description": "successful operation"
}
}
}
},
"/user/createWithList": {
"post": {
"tags": [
"user"
],
"summary": "Creates list of users with given input array",
"description": "",
"operationId": "createUsersWithListInput",
"produces": [
"application/xml",
"application/json"
],
"parameters": [{
"in": "body",
"name": "body",
"description": "List of user object",
"required": true,
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/User"
}
}
}],
"responses": {
"default": {
"description": "successful operation"
}
}
}
},
"/user/login": {
"get": {
"tags": [
"user"
],
"summary": "Logs user into the system",
"description": "",
"operationId": "loginUser",
"produces": [
"application/xml",
"application/json"
],
"parameters": [{
"name": "username",
"in": "query",
"description": "The user name for login",
"required": true,
"type": "string"
},
{
"name": "password",
"in": "query",
"description": "The password for login in clear text",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "successful operation",
"schema": {
"type": "string"
},
"headers": {
"X-Rate-Limit": {
"type": "integer",
"format": "int32",
"description": "calls per hour allowed by the user"
},
"X-Expires-After": {
"type": "string",
"format": "date-time",
"description": "date in UTC when token expires"
}
}
},
"400": {
"description": "Invalid username/password supplied"
}
}
}
},
"/user/logout": {
"get": {
"tags": [
"user"
],
"summary": "Logs out current logged in user session",
"description": "",
"operationId": "logoutUser",
"produces": [
"application/xml",
"application/json"
],
"parameters": [
],
"responses": {
"default": {
"description": "successful operation"
}
}
}
},
"/user/{username}": {
"get": {
"tags": [
"user"
],
"summary": "Get user by user name",
"description": "",
"operationId": "getUserByName",
"produces": [
"application/xml",
"application/json"
],
"parameters": [{
"name": "username",
"in": "path",
"description": "The name that needs to be fetched. Use user1 for testing. ",
"required": true,
"type": "string"
}],
"responses": {
"200": {
"description": "successful operation",
"schema": {
"$ref": "#/definitions/User"
}
},
"400": {
"description": "Invalid username supplied"
},
"404": {
"description": "User not found"
}
}
},
"put": {
"tags": [
"user"
],
"summary": "Updated user",
"description": "This can only be done by the logged in user.",
"operationId": "updateUser",
"produces": [
"application/xml",
"application/json"
],
"parameters": [{
"name": "username",
"in": "path",
"description": "name that need to be updated",
"required": true,
"type": "string"
},
{
"in": "body",
"name": "body",
"description": "Updated user object",
"required": true,
"schema": {
"$ref": "#/definitions/User"
}
}
],
"responses": {
"400": {
"description": "Invalid user supplied"
},
"404": {
"description": "User not found"
}
}
},
"delete": {
"tags": [
"user"
],
"summary": "Delete user",
"description": "This can only be done by the logged in user.",
"operationId": "deleteUser",
"produces": [
"application/xml",
"application/json"
],
"parameters": [{
"name": "username",
"in": "path",
"description": "The name that needs to be deleted",
"required": true,
"type": "string"
}],
"responses": {
"400": {
"description": "Invalid username supplied"
},
"404": {
"description": "User not found"
}
}
}
}
},
"securityDefinitions": {
"petstore_auth": {
"type": "oauth2",
"authorizationUrl": "http://petstore.swagger.io/oauth/dialog",
"flow": "implicit",
"scopes": {
"write:pets": "modify pets in your account",
"read:pets": "read your pets"
}
},
"api_key": {
"type": "apiKey",
"name": "api_key",
"in": "header"
}
},
"definitions": {
"Order": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int64"
},
"petId": {
"type": "integer",
"format": "int64"
},
"quantity": {
"type": "integer",
"format": "int32"
},
"shipDate": {
"type": "string",
"format": "date-time"
},
"status": {
"type": "string",
"description": "Order Status",
"enum": [
"placed",
"approved",
"delivered"
]
},
"complete": {
"type": "boolean",
"default": false
}
},
"xml": {
"name": "Order"
}
},
"User": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int64"
},
"username": {
"type": "string"
},
"firstName": {
"type": "string"
},
"lastName": {
"type": "string"
},
"email": {
"type": "string"
},
"password": {
"type": "string"
},
"phone": {
"type": "string"
},
"userStatus": {
"type": "integer",
"format": "int32",
"description": "User Status"
}
},
"xml": {
"name": "User"
}
},
"Category": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int64"
},
"name": {
"type": "string"
}
},
"xml": {
"name": "Category"
}
},
"Tag": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int64"
},
"name": {
"type": "string"
}
},
"xml": {
"name": "Tag"
}
},
"Pet": {
"type": "object",
"required": [
"name",
"photoUrls"
],
"properties": {
"id": {
"type": "integer",
"format": "int64"
},
"category": {
"$ref": "#/definitions/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": "#/definitions/Tag"
}
},
"status": {
"type": "string",
"description": "pet status in the store",
"enum": [
"available",
"pending",
"sold"
]
}
},
"xml": {
"name": "Pet"
}
},
"ApiResponse": {
"type": "object",
"properties": {
"code": {
"type": "integer",
"format": "int32"
},
"type": {
"type": "string"
},
"message": {
"type": "string"
}
}
}
},
"externalDocs": {
"description": "Find out more about Swagger",
"url": "http://swagger.io"
}
}

View File

@ -25,7 +25,10 @@ const specUrl =
(userUrl && userUrl[1]) || (swagger ? 'swagger.yaml' : big ? 'big-openapi.json' : 'openapi.yaml');
let store;
const options: RedocRawOptions = { nativeScrollbars: false };
const headers = {
'x-nutanix-client': 'ui',
};
const options: RedocRawOptions = { nativeScrollbars: false, enableConsole: true, providedByName: 'Intent ApiDocs by Nutanix', providedByUri: 'http://www.nutanix.com', additionalHeaders: headers };
async function init() {
const spec = await loadAndBundleSpec(specUrl);

View File

@ -1,10 +1,10 @@
import { renderToString } from 'react-dom/server';
import * as React from 'react';
import { ServerStyleSheet } from 'styled-components';
// @ts-ignore
import { Redoc, createStore } from '../../';
import { readFileSync } from 'fs';
import { resolve } from 'path';
import * as React from 'react';
import { renderToString } from 'react-dom/server';
import { ServerStyleSheet } from 'styled-components';
import { createStore, Redoc } from '../../';
const yaml = require('yaml-js');
const http = require('http');
@ -19,7 +19,7 @@ const server = http.createServer(async (request, response) => {
fs.createReadStream('bundles/redoc.standalone.js', 'utf8').pipe(response);
} else if (request.url === '/') {
const spec = yaml.load(readFileSync(resolve(__dirname, '../openapi.yaml')));
let store = await createStore(spec, 'path/to/spec.yaml');
const store = await createStore(spec, 'path/to/spec.yaml');
const sheet = new ServerStyleSheet();

View File

@ -56,6 +56,26 @@ const babelHotLoader = {
},
};
let proxy = {};
let https = false;
//we are using our own proxy here
if (process.env.PROXY && process.env.USERNAME && process.env.PASSWORD) {
proxy = {
"/api": {
auth: `${process.env.USERNAME}:${process.env.PASSWORD}`,
target: process.env.PROXY,
"secure": false,
changeOrigin: true,
ws: true,
xfwd: true
}
}
https = true;
console.log('Using proxy configuration provided with command line, https in use.\n');
} else {
console.log('Using proxy from PC/PE local server\n');
}
export default (env: { playground?: boolean; bench?: boolean } = {}, { mode }) => ({
entry: [
root('../src/polyfills.ts'),
@ -79,8 +99,12 @@ export default (env: { playground?: boolean; bench?: boolean } = {}, { mode }) =
port: 9090,
disableHostCheck: true,
stats: 'minimal',
https,
proxy,
},
devtool: 'source-map',
resolve: {
extensions: ['.ts', '.tsx', '.js', '.json'],
alias:

View File

@ -70,7 +70,9 @@
"@types/mark.js": "^8.11.4",
"@types/marked": "^0.6.5",
"@types/prismjs": "^1.16.0",
"@types/promise": "^7.1.30",
"@types/prop-types": "^15.7.1",
"@types/qs": "^6.5.1",
"@types/react": "^16.8.23",
"@types/react-dom": "^16.8.5",
"@types/react-hot-loader": "^4.1.0",
@ -134,6 +136,9 @@
},
"dependencies": {
"classnames": "^2.2.6",
"ajv": "^6.4.0",
"ajv-errors": "^1.0.0",
"brace": "^0.11.1",
"decko": "^1.2.0",
"dompurify": "^1.0.11",
"eventemitter3": "^4.0.0",
@ -153,6 +158,8 @@
"react-hot-loader": "^4.12.10",
"react-tabs": "^3.0.0",
"slugify": "^1.3.4",
"qs": "^6.5.2",
"react-ace": "^6.0.0",
"stickyfill": "^1.1.1",
"swagger2openapi": "^5.3.1",
"tslib": "^1.10.0",

View File

@ -0,0 +1,20 @@
import styled from '../styled-components';
export const Button = styled.button`
background: #248fb2;
border-radius: 0px;
border: none;
color: white;
font-size: 0.929em;
padding: 5px;
`;
export const SendButton = styled(Button)`
background: #B0045E;
`;
export const ConsoleButton = styled(Button)`
background: #e2e2e2;
color: black;
float: right;
`;

View File

@ -9,3 +9,6 @@ export * from './mixins';
export * from './tabs';
export * from './samples';
export * from './perfect-scrollbar';
export * from './toggle';
export * from './input';
export * from './buttons';

View File

@ -0,0 +1,8 @@
import styled from '../styled-components';
export const TextField = styled.input`
padding: 0.5em;
margin: 0.5em;
border: 1px solid rgba(38,50,56,0.5);
border-radius: 3px;
`;

View File

@ -73,3 +73,17 @@ export const Row = styled.div`
flex-direction: column;
`};
`;
export const FlexLayout = styled.div`
align-items: flex-end;
display: flex;
width: 100%;
`;
export const ConsoleActionsRow = styled(FlexLayout)`
padding: 5px 0px;
`;
export const FlexLayoutReverse = styled(FlexLayout)`
flex-direction: row-reverse;
`;

View File

@ -0,0 +1,10 @@
import styled from '../styled-components';
export const Toggle = styled.input`
padding: 0.5em;
margin: 0.5em;
color: palevioletred;
background: papayawhip;
border: none;
border-radius: 3px;
`;

View File

@ -0,0 +1,75 @@
import { observer } from 'mobx-react';
import * as React from 'react';
import AceEditor from 'react-ace';
import 'brace/mode/curly';
import 'brace/mode/json';
import 'brace/theme/github';
import 'brace/theme/monokai';
import { MediaTypeModel } from '../../services/models';
export interface ConsoleEditorProps {
mediaTypes: MediaTypeModel[];
}
@observer
export class ConsoleEditor extends React.Component<ConsoleEditorProps> {
editor: any;
render() {
const { mediaTypes } = this.props;
if (!mediaTypes.length) {
return null;
}
let sample = {};
for (const mediaType of mediaTypes) {
if (mediaType.name.indexOf('json') > -1) {
if (mediaType.examples) {
const example = getDefaultOrFirst(mediaType.examples);
sample = example && example.value;
}
break;
}
}
return (
<div>
<AceEditor
setOptions={{
enableBasicAutocompletion: true,
enableLiveAutocompletion: true,
enableSnippets: true,
showLineNumbers: true,
tabSize: 2,
}}
fontSize={10}
mode="json"
theme="monokai"
name="request-builder-editor"
editorProps={{ $blockScrolling: true }}
value={JSON.stringify(sample, null, 2)}
ref={(ace: AceEditor) => (this.editor = ace)}
width="100%"
height="400px"
/>
</div>
);
}
}
function getDefaultOrFirst(object) {
if (typeof object === 'object') {
if (typeof object.default === 'object') {
return object.default;
} else {
return object[Object.keys(object)[0]];
}
} else {
return false;
}
}

View File

@ -0,0 +1,205 @@
import { observer } from 'mobx-react';
import * as React from 'react';
import { SendButton } from '../../common-elements/buttons';
import { ConsoleActionsRow } from '../../common-elements/panels';
import { FieldModel, OperationModel } from '../../services/models';
import { OpenAPISchema } from '../../types';
import { SourceCodeWithCopy } from '../SourceCode/SourceCode';
import { ConsoleEditor } from './ConsoleEditor';
const qs = require('qs');
export interface ConsoleViewerProps {
operation: OperationModel;
additionalHeaders?: object;
queryParamPrefix?: string;
queryParamSuffix?: string;
}
export interface ConsoleViewerState {
result: any;
}
export interface Schema {
_$ref?: any;
rawSchema?: OpenAPISchema;
}
@observer
export class ConsoleViewer extends React.Component<ConsoleViewerProps, ConsoleViewerState> {
operation: OperationModel;
additionalHeaders: object;
visited = new Set();
private consoleEditor: ConsoleEditor;
constructor(props) {
super(props);
this.state = {
result: null,
};
}
onClickSend = async () => {
const ace = this.consoleEditor && this.consoleEditor.editor;
const { operation, additionalHeaders = {} } = this.props;
let value = ace && ace.editor.getValue();
const content = operation.requestBody && operation.requestBody.content;
const mediaType = content && content.mediaTypes[content.activeMimeIdx];
const endpoint = {
method: operation.httpVerb,
path: operation.servers[0].url + operation.path,
};
if (value) {
value = JSON.parse(value);
}
const contentType = mediaType && mediaType.name || 'application/json';
const contentTypeHeader = { 'Content-Type': contentType };
const headers = { ...additionalHeaders, ...contentTypeHeader };
let result;
try {
result = await this.invoke(endpoint, value, headers);
this.setState({
result,
});
} catch (error) {
this.setState({
result: error,
});
}
};
/*
* If we have a url like foo/bar/{uuid} uuid will be replaced with what user has typed in.
*/
addParamsToUrl(url: string, params: FieldModel[]) {
const queryParamPrefix = '{';
const queryParamSuffix = '}';
for (const fieldModel of params) {
console.log(fieldModel.name + ' ' + url);
console.log(fieldModel.$value);
if (url.indexOf(`${queryParamPrefix}${fieldModel.name}${queryParamSuffix}`) > -1 && fieldModel.$value.length > 0) {
url = url.replace(`${queryParamPrefix}${fieldModel.name}${queryParamSuffix}`, fieldModel.$value);
}
}
if (url.split(queryParamPrefix).length > 1) {
console.error('** we have missing query params ** ', url);
throw Error(`** we have missing query params ** ${url}`);
}
return url;
}
async invoke(endpoint, body, headers = {}) {
try {
const { operation } = this.props;
let url = this.addParamsToUrl(endpoint.path, operation.parameters || []);
if (endpoint.method.toLocaleLowerCase() === 'get') {
url = url + '?' + qs.stringify(body || '');
}
const myHeaders = new Headers();
for (const [key, value] of Object.entries(headers)) {
myHeaders.append(key, `${value}`);
}
const request = new Request(url, {
method: endpoint.method,
credentials: 'include',
redirect: 'manual',
headers: myHeaders,
body: (body) ? JSON.stringify(body) : undefined,
});
const result = await fetch(request);
const contentType = result.headers.get('content-type');
if (contentType && contentType.indexOf('application/json') !== -1) {
const resp = await result.json();
return { json: resp, statusCode: result.status, _fetchRes: result };
} else if (result.status === 200 && contentType && contentType.indexOf('text/plain') !== -1) {
const resp = await result.text();
return { resp, _fetchRes: result };
} else {
if (result && result.type && result.type === 'opaqueredirect') {
return {
json: {
endpoint,
error_code: 'RECEIVED_LOGIN_REDIRECT',
details: 'Your session expired. Please refresh the page.',
severity: 'error',
},
};
}
return {
json: {
endpoint,
error_code: 'INVALID_SERVER_RESPONSE',
details: 'Either server not authenticated or error on server',
severity: 'error',
},
};
}
} catch (error) {
console.error(error);
}
}
render() {
const { operation } = this.props;
const requestBodyContent = operation.requestBody && operation.requestBody.content && operation.requestBody.content;
const hasBodySample = requestBodyContent && requestBodyContent.hasSample;
const samples = operation.codeSamples;
const mediaTypes = (requestBodyContent && requestBodyContent.mediaTypes) ? requestBodyContent.mediaTypes : [];
const { result } = this.state;
return (
<div>
<h3> Console </h3>
{hasBodySample && (
<ConsoleEditor
mediaTypes={mediaTypes}
ref={(editor: ConsoleEditor) => (this.consoleEditor = editor)}
/>
)}
{false && samples.map(sample => (
<SourceCodeWithCopy lang={sample.lang} source={sample.source} />
))}
<ConsoleActionsRow>
<SendButton onClick={this.onClickSend} >Send Request</SendButton>
</ConsoleActionsRow>
{result &&
<SourceCodeWithCopy lang="json" source={JSON.stringify(result, null, 2)} />
}
</div>
);
}
getSchema() {
const { operation } = this.props;
const requestBodyContent = operation.requestBody && operation.requestBody.content && operation.requestBody.content;
const mediaTypes = (requestBodyContent && requestBodyContent.mediaTypes) ? requestBodyContent.mediaTypes : [];
if (!mediaTypes.length) {
return null;
}
const schema: Schema = {
};
for (const mediaType of mediaTypes) {
if (mediaType.name.indexOf('json') > -1) {
if (mediaType.schema) {
schema.rawSchema = mediaType.schema && mediaType.schema.rawSchema;
console.log('rawSchema : ' + JSON.stringify(schema));
console.log('schema : ' + JSON.stringify(mediaType.schema.schema));
}
break;
}
}
return schema;
}
}

View File

@ -0,0 +1,2 @@
export * from './ConsoleEditor';
export * from './ConsoleViewer';

View File

@ -13,7 +13,7 @@ import {
WrappedShelfIcon,
} from '../../common-elements/fields-layout';
import { ShelfIcon } from '../../common-elements/';
import { ShelfIcon, TextField } from '../../common-elements/';
import { FieldModel } from '../../services/models';
import { Schema, SchemaOptions } from '../Schema/Schema';
@ -33,6 +33,12 @@ export class Field extends React.Component<FieldProps> {
toggle = () => {
this.props.field.toggle();
};
onFieldChange = e => {
console.log('Textfield value is ' + e.target.placeholder + ' - ' + e.target.value);
this.props.field.setValue(e.target.value);
};
render() {
const { className, field, isLast } = this.props;
const { name, expanded, deprecated, required, kind } = field;
@ -53,12 +59,12 @@ export class Field extends React.Component<FieldProps> {
{required && <RequiredLabel> required </RequiredLabel>}
</ClickablePropertyNameCell>
) : (
<PropertyNameCell className={deprecated ? 'deprecated' : undefined} kind={kind} title={name}>
<PropertyBullet />
{name}
{required && <RequiredLabel> required </RequiredLabel>}
</PropertyNameCell>
);
<PropertyNameCell className={deprecated ? 'deprecated' : undefined} kind={kind} title={name}>
<PropertyBullet />
{name}
{required && <RequiredLabel> required </RequiredLabel>}
</PropertyNameCell>
);
return (
<>
@ -67,6 +73,9 @@ export class Field extends React.Component<FieldProps> {
<PropertyDetailsCell>
<FieldDetails {...this.props} />
</PropertyDetailsCell>
{field && field.in === 'path' &&
<td><TextField placeholder={field.name} onChange={this.onFieldChange} /></td>
}
</tr>
{field.expanded && withSubSchema && (
<tr key={field.name + 'inner'}>

View File

@ -3,11 +3,12 @@ import { SecurityRequirements } from '../SecurityRequirement/SecurityRequirement
import { observer } from 'mobx-react';
import { Badge, DarkRightPanel, H2, MiddlePanel, Row } from '../../common-elements';
import { Badge, ConsoleButton, DarkRightPanel, FlexLayoutReverse, H2, MiddlePanel, Row } from '../../common-elements';
import { OptionsContext } from '../OptionsProvider';
import { ShareLink } from '../../common-elements/linkify';
import { ConsoleViewer } from '../Console/ConsoleViewer';
import { Endpoint } from '../Endpoint/Endpoint';
import { ExternalDocumentation } from '../ExternalDocumentation/ExternalDocumentation';
import { Markdown } from '../Markdown/Markdown';
@ -35,13 +36,33 @@ export interface OperationProps {
operation: OperationType;
}
export interface OperationState {
executeMode: boolean;
}
@observer
export class Operation extends React.Component<OperationProps> {
export class Operation extends React.Component<OperationProps, OperationState> {
constructor(props) {
super(props);
this.state = {
executeMode: false,
};
}
onConsoleClick = () => {
this.setState({
executeMode: !this.state.executeMode,
});
}
render() {
const { operation } = this.props;
const { executeMode } = this.state;
const { name: summary, description, deprecated, externalDocs } = operation;
const hasDescription = !!(description || externalDocs);
const consoleButtonLabel = (executeMode) ? 'Hide Console' : 'Show Console';
return (
<OptionsContext.Consumer>
@ -52,6 +73,11 @@ export class Operation extends React.Component<OperationProps> {
<ShareLink to={operation.id} />
{summary} {deprecated && <Badge type="warning"> Deprecated </Badge>}
</H2>
{options.enableConsole &&
<FlexLayoutReverse>
<ConsoleButton onClick={this.onConsoleClick}>{consoleButtonLabel}</ConsoleButton>
</FlexLayoutReverse>
}
{options.pathInMiddlePanel && <Endpoint operation={operation} inverted={true} />}
{hasDescription && (
<Description>
@ -66,8 +92,17 @@ export class Operation extends React.Component<OperationProps> {
</MiddlePanel>
<DarkRightPanel>
{!options.pathInMiddlePanel && <Endpoint operation={operation} />}
<RequestSamples operation={operation} />
<ResponseSamples operation={operation} />
{executeMode &&
<div>
<ConsoleViewer operation={operation} additionalHeaders={options.additionalHeaders} queryParamPrefix={options.queryParamPrefix} queryParamSuffix={options.queryParamSuffix} />
</div>
}
{!executeMode &&
<RequestSamples operation={operation} />
}
{!executeMode &&
<ResponseSamples operation={operation} />
}
</DarkRightPanel>
</OperationRow>
)}

View File

@ -1,8 +1,10 @@
import { observer } from 'mobx-react';
import * as React from 'react';
import { TokenGroup } from '..';
import { DarkRightPanel, H2, MiddlePanel, Row, Section, ShareLink } from '../../common-elements';
import { SecuritySchemesModel } from '../../services/models';
import { H2, MiddlePanel, Row, Section, ShareLink } from '../../common-elements';
import { OpenAPISecurityScheme } from '../../types';
import { titleize } from '../../utils/helpers';
import { Markdown } from '../Markdown/Markdown';
@ -18,11 +20,12 @@ const AUTH_TYPES = {
export interface OAuthFlowProps {
type: string;
flow: OpenAPISecurityScheme['flows'][keyof OpenAPISecurityScheme['flows']];
token?: string;
}
export class OAuthFlow extends React.PureComponent<OAuthFlowProps> {
render() {
const { type, flow } = this.props;
const { type, flow, token } = this.props;
return (
<tr>
<th> {type} OAuth Flow </th>
@ -56,6 +59,7 @@ export class OAuthFlow extends React.PureComponent<OAuthFlowProps> {
))}
</ul>
</td>
<td> {token} </td>
</tr>
);
}
@ -65,7 +69,31 @@ export interface SecurityDefsProps {
securitySchemes: SecuritySchemesModel;
}
export class SecurityDefs extends React.PureComponent<SecurityDefsProps> {
export interface SecurityDefsState {
tokens: Dict<string>;
}
@observer
export class SecurityDefs extends React.PureComponent<SecurityDefsProps, SecurityDefsState> {
state = {
tokens: {},
};
mutateToken = (scheme, id) => {
return () => {
scheme.setToken(this.state.tokens[id]);
};
};
setToken = id => {
return token => {
const tokens = this.state.tokens;
tokens[id] = token;
this.setState({tokens});
};
};
render() {
return this.props.securitySchemes.schemes.map(scheme => (
<Section id={scheme.sectionId} key={scheme.id}>
@ -82,22 +110,26 @@ export class SecurityDefs extends React.PureComponent<SecurityDefsProps> {
<tr>
<th> Security scheme type: </th>
<td> {AUTH_TYPES[scheme.type] || scheme.type} </td>
<td> Value </td>
</tr>
{scheme.apiKey ? (
<tr>
<th> {titleize(scheme.apiKey.in || '')} parameter name:</th>
<td> {scheme.apiKey.name} </td>
<td> {scheme.token} </td>
</tr>
) : scheme.http ? (
[
<tr key="scheme">
<th> HTTP Authorization Scheme </th>
<td> {scheme.http.scheme} </td>
<td> {scheme.token} </td>
</tr>,
scheme.http.scheme === 'bearer' && scheme.http.bearerFormat && (
<tr key="bearer">
<th> Bearer format </th>
<td> "{scheme.http.bearerFormat}" </td>
<td> {scheme.token} </td>
</tr>
),
]
@ -109,16 +141,25 @@ export class SecurityDefs extends React.PureComponent<SecurityDefsProps> {
{scheme.openId.connectUrl}
</a>
</td>
<td> {scheme.token} </td>
</tr>
) : scheme.flows ? (
Object.keys(scheme.flows).map(type => (
<OAuthFlow key={type} type={type} flow={scheme.flows[type]} />
<OAuthFlow key={type} type={type} token={scheme.token} flow={scheme.flows[type]} />
))
) : null}
</tbody>
</table>
</StyledMarkdownBlock>
</MiddlePanel>
<DarkRightPanel>
<TokenGroup
title={'Enter ' + scheme.id}
description={'You can add token here and store it to use in your request calls in this page.'}
onChange={this.setToken(scheme.sectionId)}
onSubmit={this.mutateToken(scheme, scheme.sectionId)}
/>
</DarkRightPanel>
</Row>
</Section>
));

View File

@ -0,0 +1,84 @@
import * as React from 'react';
import {Button, RightPanelHeader} from '../../common-elements';
import styled from '../../styled-components';
const SaveTokenButton = styled(Button)`
padding: 10px 30px;
border-radius: 0 4px 4px 0;
cursor: pointer;
text-align: center;
outline: none;
margin: 0
min-width: 60px;
max-width: 100px;
font-weight: bold;
flex: 1 1;
order: 2;
`;
const TokenTextField = styled.input`
padding: 10px 30px 10px 20px;
border-radius: 4px 0 0 4px;
background-color: ${props => props.theme.codeSample.backgroundColor};
color: ${props => props.theme.codeSample.textColor}
white-space: nowrap;
align-items: center;
border: none;
direction: ltr;
min-width: 300px;
flex: 4 1;
order: 1;
`;
const TokenGroupContainer = styled.div`
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: flex-start;
align-items: stretch;
align-content: flex-start;
`;
const Description = styled.p`
color: white;
`;
interface TokenGroupProps {
title: string;
description?: string;
onSubmit: () => void;
onChange: (value: string) => void;
}
export class TokenGroup extends React.PureComponent<TokenGroupProps> {
constructor(props) {
super(props);
this.submit = this.submit.bind(this);
this.change = this.change.bind(this);
}
submit() {
this.props.onSubmit();
}
change(e) {
this.props.onChange(e.target.value);
}
render() {
return (
<>
<RightPanelHeader>
{this.props.title}
</RightPanelHeader>
<TokenGroupContainer>
<TokenTextField onChange={this.change} />
<SaveTokenButton onClick={this.submit}>Save</SaveTokenButton>
</TokenGroupContainer>
<Description>
{this.props.description}
</Description>
</>
);
}
}

View File

@ -30,3 +30,4 @@ export * from './StickySidebar/StickyResponsiveSidebar';
export * from './SearchBox/SearchBox';
export * from './SchemaDefinition/SchemaDefinition';
export * from './SourceCode/SourceCode';
export * from './TokenGroup/TokenGroup';

View File

@ -9,6 +9,8 @@ export interface RedocRawOptions {
theme?: ThemeInterface;
scrollYOffset?: number | string | (() => number);
hideHostname?: boolean | string;
enableConsole?: boolean;
additionalHeaders?: object;
expandResponses?: string | 'all';
requiredPropsFirst?: boolean | string;
sortPropsAlphabetically?: boolean | string;
@ -25,6 +27,11 @@ export interface RedocRawOptions {
menuToggle?: boolean | string;
jsonSampleExpandLevel?: number | string | 'all';
providedByName?: string;
providedByUri?: string;
queryParamPrefix?: string;
queryParamSuffix?: string;
unstable_ignoreMimeParameters?: boolean;
allowedMdComponents?: Dict<MDXComponentMeta>;
@ -141,6 +148,12 @@ export class RedocNormalizedOptions {
menuToggle: boolean;
jsonSampleExpandLevel: number;
enumSkipQuotes: boolean;
enableConsole: boolean;
additionalHeaders: object;
providedByName: string;
providedByUri: string;
queryParamPrefix: string;
queryParamSuffix: string;
/* tslint:disable-next-line */
unstable_ignoreMimeParameters: boolean;
@ -177,6 +190,12 @@ export class RedocNormalizedOptions {
raw.jsonSampleExpandLevel,
);
this.enumSkipQuotes = argValueToBoolean(raw.enumSkipQuotes);
this.enableConsole = argValueToBoolean(raw.enableConsole);
this.additionalHeaders = raw.additionalHeaders || {};
this.providedByName = raw.providedByName || 'Documentation Powered by ReDoc';
this.providedByUri = raw.providedByUri || 'https://github.com/Rebilly/ReDoc';
this.queryParamPrefix = raw.queryParamPrefix || '{';
this.queryParamSuffix = raw.queryParamSuffix || '}';
this.unstable_ignoreMimeParameters = argValueToBoolean(raw.unstable_ignoreMimeParameters);

View File

@ -9,7 +9,7 @@ let worker: new () => Worker;
if (IS_BROWSER) {
try {
// tslint:disable-next-line
worker = require('workerize-loader?inline&fallback=false!./SearchWorker.worker');
worker = require('workerize-loader?fallback=false!./SearchWorker.worker');
} catch (e) {
worker = require('./SearchWorker.worker').default;
}

View File

@ -29,9 +29,8 @@ function getDefaultStyleValue(parameterLocation: OpenAPIParameterLocation): Open
* Field or Parameter model ready to be used by components
*/
export class FieldModel {
@observable
expanded: boolean = false;
@observable expanded: boolean = false;
@observable $value: string = '';
schema: SchemaModel;
name: string;
required: boolean;
@ -92,4 +91,9 @@ export class FieldModel {
toggle() {
this.expanded = !this.expanded;
}
@action
setValue(value: string) {
this.$value = value;
}
}

View File

@ -1,3 +1,4 @@
import {observable} from 'mobx';
import { OpenAPISecurityScheme, Referenced } from '../../types';
import { SECURITY_SCHEMES_SECTION_PREFIX } from '../../utils/openapi';
import { OpenAPIParser } from '../OpenAPIParser';
@ -22,6 +23,9 @@ export class SecuritySchemeModel {
connectUrl: string;
};
@observable
token?: string = '';
constructor(parser: OpenAPIParser, id: string, scheme: Referenced<OpenAPISecurityScheme>) {
const info = parser.deref(scheme);
this.id = id;
@ -52,9 +56,14 @@ export class SecuritySchemeModel {
this.flows = info.flows;
}
}
setToken(token: string) {
this.token = token;
}
}
export class SecuritySchemesModel {
@observable
schemes: SecuritySchemeModel[];
constructor(parser: OpenAPIParser) {

View File

@ -149,6 +149,10 @@ const defaultTheme: ThemeInterface = {
},
codeSample: {
backgroundColor: ({ rightPanel }) => darken(0.1, rightPanel.backgroundColor),
textColor: ({ rightPanel }) => rightPanel.textColor,
},
styledPre: {
maxHeight: '500px',
},
};
@ -321,9 +325,13 @@ export interface ResolvedThemeInterface {
};
codeSample: {
backgroundColor: string;
textColor: string;
};
extensionsHook?: (name: string, props: any) => string;
styledPre: {
maxHeight: string;
};
}
export type primitive = string | number | boolean | undefined | null;

1
src/utils/fetch.ts Normal file
View File

@ -0,0 +1 @@
//parseparseFetchFetch

View File

@ -9,11 +9,20 @@ export async function loadAndBundleSpec(specUrlOrObject: object | string): Promi
resolve: { http: { withCredentials: false } },
} as object)) as any;
let v2Specs = spec;
if (spec.swagger !== undefined) {
return convertSwagger2OpenAPI(spec);
} else {
return spec;
v2Specs = await convertSwagger2OpenAPI(spec);
}
// we can derefrence the schema here for future use.
// import { cloneDeep } from 'lodash';
// const derefrencedSpec = await parser.dereference(cloneDeep(spec));
// const derefed = await parser.dereference(v2Specs, {
// resolve: { http: { withCredentials: false } },
// } as object);
return v2Specs;
}
export function convertSwagger2OpenAPI(spec: any): Promise<OpenAPISpec> {

View File

@ -17,7 +17,7 @@
"quotemark": [true, "single", "avoid-template", "jsx-double"],
"variable-name": [true, "ban-keywords", "check-format", "allow-leading-underscore", "allow-pascal-case"],
"arrow-parens": [true, "ban-single-arg-parens"],
"no-submodule-imports": [true, "prismjs", "perfect-scrollbar", "react-dom", "core-js"],
"no-submodule-imports": [true, "prismjs", "perfect-scrollbar", "react-dom", "core-js", "brace"],
"object-literal-key-quotes": [true, "as-needed"],
"no-unused-expression": [true, "allow-tagged-template"],
"semicolon": [true, "always", "ignore-bound-class-methods"],

View File

@ -570,6 +570,14 @@
"@babel/helper-regex" "^7.4.4"
regexpu-core "^4.5.4"
"@babel/polyfill@^7.4.4":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/polyfill/-/polyfill-7.8.3.tgz#2333fc2144a542a7c07da39502ceeeb3abe4debd"
integrity sha512-0QEgn2zkCzqGIkSWWAEmvxD7e00Nm9asTtQvi7HdlYvMhjy/J38V/1Y9ode0zEJeIuxAI0uftiAzqc7nVeWUGg==
dependencies:
core-js "^2.6.5"
regenerator-runtime "^0.13.2"
"@babel/preset-env@^7.0.0":
version "7.5.5"
resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.5.5.tgz#bc470b53acaa48df4b8db24a570d6da1fef53c9a"
@ -1052,11 +1060,21 @@
resolved "https://registry.yarnpkg.com/@types/prismjs/-/prismjs-1.16.0.tgz#4328c9f65698e59f4feade8f4e5d928c748fd643"
integrity sha512-mEyuziLrfDCQ4juQP1k706BUU/c8OGn/ZFl69AXXY6dStHClKX4P+N8+rhqpul1vRDA2VOygzMRSJJZHyDEOfw==
"@types/promise@^7.1.30":
version "7.1.30"
resolved "https://registry.yarnpkg.com/@types/promise/-/promise-7.1.30.tgz#1b6714b321fdfc54d1527e7a17116a0e1f2ab810"
integrity sha1-G2cUsyH9/FTRUn56FxFqDh8quBA=
"@types/prop-types@*", "@types/prop-types@^15.7.1":
version "15.7.1"
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.1.tgz#f1a11e7babb0c3cad68100be381d1e064c68f1f6"
integrity sha512-CFzn9idOEpHrgdw8JsoTkaDDyRWk1jrzIV8djzcgpq0y9tG4B4lFT+Nxh52DVpDXV+n4+NPNv7M1Dj5uMp6XFg==
"@types/qs@^6.5.1":
version "6.9.0"
resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.0.tgz#2a5fa918786d07d3725726f7f650527e1cfeaffd"
integrity sha512-c4zji5CjWv1tJxIZkz1oUtGcdOlsH3aza28Nqmm+uNDWBRHoMsjooBEN4czZp1V3iXPihE/VRUOBqg+4Xq0W4g==
"@types/react-dom@^16.8.5":
version "16.8.5"
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.8.5.tgz#3e3f4d99199391a7fb40aa3a155c8dd99b899cbd"
@ -1449,7 +1467,7 @@ ajv@^5.5.2:
fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.3.0"
ajv@^6.1.0, ajv@^6.10.2, ajv@^6.5.5, ajv@^6.9.1:
ajv@^6.1.0, ajv@^6.10.2, ajv@^6.4.0, ajv@^6.5.5, ajv@^6.9.1:
version "6.10.2"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52"
integrity sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==
@ -1953,6 +1971,11 @@ brace-expansion@^1.1.7:
balanced-match "^1.0.0"
concat-map "0.0.1"
brace@^0.11.1:
version "0.11.1"
resolved "https://registry.yarnpkg.com/brace/-/brace-0.11.1.tgz#4896fcc9d544eef45f4bb7660db320d3b379fe58"
integrity sha1-SJb8ydVE7vRfS7dmDbMg07N5/lg=
braces@^2.3.1, braces@^2.3.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729"
@ -2879,6 +2902,11 @@ core-js@^2.5.7:
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.9.tgz#6b4b214620c834152e179323727fc19741b084f2"
integrity sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==
core-js@^2.6.5:
version "2.6.11"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c"
integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==
core-js@^3.1.4:
version "3.1.4"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.1.4.tgz#3a2837fc48e582e1ae25907afcd6cf03b0cc7a07"
@ -3323,6 +3351,11 @@ dezalgo@^1.0.0:
asap "^2.0.0"
wrappy "1"
diff-match-patch@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/diff-match-patch/-/diff-match-patch-1.0.4.tgz#6ac4b55237463761c4daf0dc603eb869124744b1"
integrity sha512-Uv3SW8bmH9nAtHKaKSanOQmj2DnlH65fUpcrMdfdaOxUG02QQ4YGZ8AE7kKOMisF7UqvOlGKVYWRvezdncW9lg==
diff-sequences@^24.3.0:
version "24.3.0"
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.3.0.tgz#0f20e8a1df1abddaf4d9c226680952e64118b975"
@ -6205,6 +6238,11 @@ lodash.flattendeep@^4.4.0:
resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2"
integrity sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=
lodash.get@^4.4.2:
version "4.4.2"
resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=
lodash.isequal@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
@ -7886,6 +7924,11 @@ qs@6.7.0:
resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==
qs@^6.5.2:
version "6.9.1"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.1.tgz#20082c65cb78223635ab1a9eaca8875a29bf8ec9"
integrity sha512-Cxm7/SS/y/Z3MHWSxXb8lIFqgqBowP5JMlTUFyJN88y0SGQhVmZnqFK/PeuMX9LzUyWsqqhNxIyg0jlzq946yA==
qs@~6.5.2:
version "6.5.2"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
@ -7976,6 +8019,18 @@ rc@^1.2.7:
minimist "^1.2.0"
strip-json-comments "~2.0.1"
react-ace@^6.0.0:
version "6.6.0"
resolved "https://registry.yarnpkg.com/react-ace/-/react-ace-6.6.0.tgz#a79457ef03c3b1f8d4fc598a003b1d6ad464f1a0"
integrity sha512-Jehhp8bxa8kqiXk07Jzy+uD5qZMBwo43O+raniGHjdX7Qk93xFkKaAz8LxtUVZPJGlRnV5ODMNj0qHwDSN+PBw==
dependencies:
"@babel/polyfill" "^7.4.4"
brace "^0.11.1"
diff-match-patch "^1.0.4"
lodash.get "^4.4.2"
lodash.isequal "^4.5.0"
prop-types "^15.7.2"
react-dom@^16.8.6:
version "16.8.6"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.8.6.tgz#71d6303f631e8b0097f56165ef608f051ff6e10f"