add onegraph explorer to graphiql online (close #1816 ) (#1817)

This commit is contained in:
Rishichandra Wawhal 2019-03-19 12:14:04 +05:30 committed by Shahidh K Muhammed
parent a5890623c4
commit 2777b45335
8 changed files with 17016 additions and 13 deletions

File diff suppressed because it is too large Load Diff

View File

@ -44,8 +44,9 @@
"babel-polyfill": "^6.26.0",
"deep-equal": "^1.0.1",
"graphiql": "^0.11.11",
"graphiql-explorer-hasura": "0.0.7",
"graphql": "^0.13.2",
"hasura-console-graphiql": "0.0.1",
"hasura-console-graphiql": "0.0.10",
"history": "^3.0.0",
"hoist-non-react-statics": "^1.0.3",
"invariant": "^2.2.0",

View File

@ -150,7 +150,6 @@
-webkit-box-flex: 1;
-ms-flex: 1;
flex: 1;
overflow-y: hidden;
}
.graphiql-container .queryWrap {
@ -227,7 +226,7 @@
-webkit-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
height: 29px;
height: 30px;
position: relative;
}
@ -732,6 +731,245 @@ div.CodeMirror-lint-tooltip > * + * {
.cm-atom {
color: #ca9800;
}
/* Hasura Analyse modal css */
.wd25 {
width: 25%;
display: inline-block;
}
.wd75 {
width: 75%;
display: inline-block;
}
.wd40 {
width: 40%;
display: inline-block;
}
.wd60 {
width: 60%;
display: inline-block;
}
.modalWrapper {
position: absolute;
top: 40px;
left: 40px;
right: 40px;
bottom: 40px;
border: 1px solid rgb(204, 204, 204);
background: rgb(255, 255, 255);
border-radius: 4px;
outline: none;
}
.myOverlayClass {
position: fixed;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
background-color: rgba(255, 255, 255, 0.75);
z-index: 100;
}
.modalHeader {
background-color: #43495a;
border-bottom: 1px solid #ccc;
padding: 10px 20px;
position: relative;
color: #ffc627;
}
.modalTitle {
font-size: 18px;
font-weight: 500;
}
.modalClose {
position: absolute;
right: 10px;
top: 10px;
}
.modalClose button {
cursor: pointer;
border-radius: 5px;
border: 0;
background-color: transparent;
font-size: 16px;
font-weight: 700;
color: #ccc;
padding: 0px;
height: auto;
}
.modalClose button:hover {
border: 0;
background-color: transparent;
color: #fff;
}
.modalClose button:focus {
outline: none;
}
.modalBody {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
height: calc(100% - 46px);
}
.topLevelNodesWrapper {
border-right: 1px solid #ccc;
height: 100%;
}
.textCenter {
text-align: center;
}
.topLevelNodesWrapper .title {
padding: 15px 20px;
background-color: #fff3d5;
margin-bottom: 20px;
color: #767e93;
font-weight: 600;
font-size: 16px;
}
.analysisWrapper {
height: 100%;
position: relative;
width: 100%;
}
.topLevelNodesWrapper ul {
-webkit-padding-start: 0px;
padding-inline-start: 0px;
-webkit-padding-inline-start: 0px;
-moz-padding-inline-start: 0px;
-ms-padding-inline-start: 0px;
-o-padding-inline-start: 0px;
margin-top: 0;
margin-bottom: 0;
}
.borderRight {
border-right: 1px solid #ccc;
}
.topLevelNodesWrapper ul li {
list-style-type: none;
padding: 10px 0;
padding-left: 20px;
cursor: pointer;
}
.topLevelNodesWrapper ul li i {
margin-right: 5px;
font-size: 12px;
}
.topLevelNodesWrapper ul li.active {
color: #fd9540;
}
.topLevelNodesWrapper ul li:hover {
color: #e2701a;
}
.plansWrapper {
height: 50%;
position: relative;
padding: 0px 20px;
}
.plansTitle {
padding: 10px 0px;
color: #767e93;
font-weight: 600;
}
.copyGenerated {
position: absolute;
bottom: 30px;
right: 60px;
cursor: pointer;
}
.copyGenerated img,
.copyExecution img {
width: 20px;
opacity: 0.6;
}
.copyGenerated img:hover,
.copyExecution img:hover {
opacity: 1;
}
.copyGenerated:focus,
.copyExecution:focus {
outline: none;
}
.codeBlock {
/* position: relative;
padding: 10px 20px; */
background-color: rgb(253, 249, 237);
/* margin: 20px;
width: 100%; */
width: auto;
border-radius: 5px;
max-height: calc(100% - 60px);
overflow: auto;
margin-top: 0px;
min-height: calc(100% - 60px);
}
.codeBlock pre {
display: block;
padding: 10px 20px;
margin: 0px;
font-size: 13px;
line-height: unset;
word-break: unset;
word-wrap: unset;
color: #000;
background: none;
border: none;
border-radius: 0;
overflow: unset;
padding-bottom: 10px;
}
.codeBlock code {
color: #000;
background: none;
}
.graphiql-container .analyse-button-wrap {
position: relative;
}
.copyTooltip {
position: relative;
display: inline-block;
}
.copyTooltip .tooltiptext {
background-color: #555;
color: #fff;
text-align: center;
border-radius: 6px;
padding: 4px 0px;
font-size: 14px;
position: absolute;
z-index: 1000000000;
right: -21px;
bottom: 30px;
opacity: 0;
-webkit-transition: opacity 0.3s;
transition: opacity 0.3s;
display: none;
width: 57px;
}
.copyTooltip .tooltiptext::after {
content: '';
position: absolute;
top: 24px;
right: 22px;
margin-left: -5px;
border-width: 5px;
border-style: solid;
border-color: #555 transparent transparent transparent;
}
.copyTooltip:hover .tooltiptext {
visibility: visible;
opacity: 1;
display: block;
}
/* BASICS */
.CodeMirror {
@ -1240,24 +1478,23 @@ span.CodeMirror-selectedtext {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
height: 34px;
line-height: 14px;
padding: 8px 8px 5px;
align-items: center;
position: relative;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.doc-explorer-title {
height: 34px;
}
.graphiql-container .doc-explorer-title,
.graphiql-container .history-title {
-webkit-box-flex: 1;
-ms-flex: 1;
flex: 1;
font-weight: bold;
overflow-x: initial;
padding: 10px 0 10px 10px;
overflow-x: hidden;
text-align: center;
text-overflow: ellipsis;
-webkit-user-select: initial;
@ -1549,12 +1786,19 @@ span.CodeMirror-selectedtext {
.CodeMirror-foldgutter-folded:after {
content: '\25B8';
}
.graphiql-container .history-contents {
.graphiql-container .history-contents,
.graphiql-container .history-contents input {
font-family: 'Consolas', 'Inconsolata', 'Droid Sans Mono', 'Monaco', monospace;
padding: 0;
}
.graphiql-container .history-contents p {
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
font-size: 12px;
overflow: hidden;
text-overflow: ellipsis;
@ -1564,9 +1808,29 @@ span.CodeMirror-selectedtext {
border-bottom: 1px solid #e0e0e0;
}
.graphiql-container .history-contents p.editable {
padding-bottom: 6px;
padding-top: 7px;
}
.graphiql-container .history-contents input {
-webkit-box-flex: 1;
-ms-flex-positive: 1;
flex-grow: 1;
font-size: 12px;
}
.graphiql-container .history-contents p:hover {
cursor: pointer;
}
.graphiql-container .history-contents p span.history-label {
-webkit-box-flex: 1;
-ms-flex-positive: 1;
flex-grow: 1;
overflow: hidden;
text-overflow: ellipsis;
}
.CodeMirror-info {
background: white;
border-radius: 2px;

View File

@ -2,6 +2,7 @@ import React, { Component } from 'react';
import GraphiQL from 'hasura-console-graphiql';
import PropTypes from 'prop-types';
import ErrorBoundary from './ErrorBoundary';
import OneGraphExplorer from './OneGraphExplorer';
import { graphQLFetcherFinal, getRemoteQueries } from './Actions';
import './GraphiQL.css';
@ -50,6 +51,12 @@ class GraphiQLWrapper extends Component {
} else if (this.state.queries) {
graphiqlProps.query = this.state.queries;
}
const renderGraphiql = props => (
<GraphiQL
{...graphiqlProps}
{...props}
/>
)
return (
<ErrorBoundary>
<div
@ -60,7 +67,12 @@ class GraphiQLWrapper extends Component {
styles.graphQLHeight
}
>
<GraphiQL {...graphiqlProps} />
<OneGraphExplorer
renderGraphiql={renderGraphiql}
endpoint={this.props.data.url}
headers={this.props.data.headers}
query={query}
/>
</div>
</ErrorBoundary>
);

View File

@ -0,0 +1,49 @@
.graphiql-explorer-root {
font-size: 12px;
text-overflow: ellipsis;
overflow: unset;
white-space: nowrap;
margin: 0;
padding: 8px;
font-family: Consolas, Inconsolata, 'Droid Sans Mono', Monaco, monospace;
}
.graphiql-explorer-abstractfields-label {
color: #ca9800;
padding-left: 5px;
}
.graphiql-explorer-abstractargs-label {
color: #8b2bb9;
padding-left: 5px;
}
.graphiql-explorer-fieldname {
color: rgb(31, 97, 160);
padding-left: 5px;
}
.graphiql-explorer-node {
margin-top: 5px;
}
.graphiql-explorer-args-wrapper {
margin-top: 5px;
}
.explorerGraphiqlSeparator {
height: 100%;
right: -5px;
position: absolute;
top: 0;
width: 10px;
z-index: 10;
}
.explorerCursorResize {
cursor: col-resize;
}
.gqlexplorer {
position: relative;
}

View File

@ -0,0 +1,164 @@
import React from 'react';
import { getIntrospectionQuery, buildClientSchema } from 'graphql';
import { getHeadersAsJSON } from './utils';
import GraphiQLExplorer from 'graphiql-explorer-hasura';
import './GraphiQL.css';
import './OneGraphExplorer.css';
import {
makeDefaultArg,
getDefaultScalarArgValue,
getExplorerWidthFromLocalStorage,
setExplorerWidthInLocalStorage,
} from './onegraphUtils';
class OneGraphExplorer extends React.Component {
state = {
explorerOpen: false,
explorerWidth: getExplorerWidthFromLocalStorage(),
explorerClientX: null,
schema: null,
query: this.props.query || '',
isResizing: false,
headers: [],
};
componentDidMount() {
this.introspect();
}
componentDidUpdate() {
if (this.shouldIntrospect(this.props.headers, this.state.headers)) {
this.introspect()
}
}
shouldIntrospect(newHeadersArray, oldHeadersArray) {
if (this.props.headerFocus) {
return false;
}
const oldHeaders = getHeadersAsJSON(oldHeadersArray);
const headers = getHeadersAsJSON(newHeadersArray);
if (Object.keys(oldHeaders).length !== Object.keys(headers).length) {
return true;
}
for (var i = Object.keys(headers).length - 1; i >= 0; i--) {
const key = Object.keys(headers)[i];
const value = headers[key];
if (oldHeaders[key] !== value) {
return true;
}
}
return false;
}
introspect() {
const { endpoint, headers } = this.props;
fetch(endpoint, {
method: 'POST',
headers: getHeadersAsJSON(headers || []),
body: JSON.stringify({
query: getIntrospectionQuery(),
}),
})
.then(response => response.json())
.then(result => {
this.setState({
schema: buildClientSchema(result.data),
headers: JSON.parse(JSON.stringify(headers))
});
})
.catch(error => {
this.setState({
schema: null,
headers: JSON.parse(JSON.stringify(headers))
});
})
}
onExplorerResize = e => {
const { explorerClientX, explorerWidth } = this.state;
if (explorerClientX === null) {
this.setState({ explorerClientX: e.clientX });
} else {
const newExplorerWidth = explorerWidth + e.clientX - explorerClientX;
setExplorerWidthInLocalStorage(newExplorerWidth);
this.setState({
explorerWidth: newExplorerWidth,
explorerClientX: e.clientX,
});
}
};
editQuery = query => {
this.setState({ query });
};
toggleExplorer = () => {
this.setState(state => ({
explorerOpen: !state.explorerOpen,
}));
};
handleExplorerResize = e => {
e.preventDefault();
document.addEventListener('mousemove', this.onExplorerResize);
this.setState({
isResizing: true,
});
};
handleExplorerResizeStop = e => {
e.preventDefault();
document.removeEventListener('mousemove', this.onExplorerResize);
this.setState({
isResizing: false,
});
};
render() {
const {
schema,
explorerOpen,
query,
explorerWidth,
isResizing,
} = this.state;
const { renderGraphiql } = this.props;
return (
<div
className={`graphiql-container ${
isResizing ? 'explorerCursorResize' : ''
}`}
onMouseUp={this.handleExplorerResizeStop}
>
<div className="gqlexplorer">
{explorerOpen && (
<div
className="explorerGraphiqlSeparator explorerCursorResize"
onMouseDown={this.handleExplorerResize}
onMouseUp={this.handleExplorerResizeStop}
/>
)}
<GraphiQLExplorer
schema={schema}
query={query}
onEdit={this.editQuery}
explorerIsOpen={explorerOpen}
onToggleExplorer={this.toggleExplorer}
getDefaultScalarArgValue={getDefaultScalarArgValue}
makeDefaultArg={makeDefaultArg}
width={explorerWidth}
/>
</div>
{renderGraphiql({
query,
schema,
onEditQuery: this.editQuery,
toggleExplorer: this.toggleExplorer,
})}
</div>
);
}
}
export default OneGraphExplorer;

View File

@ -0,0 +1,21 @@
import GraphiQLExplorer from 'graphiql-explorer-hasura';
export const makeDefaultArg = () => {
return false;
};
export const getDefaultScalarArgValue = (parentField, arg, argType) => {
return GraphiQLExplorer.defaultValue(argType);
};
export const getExplorerWidthFromLocalStorage = () => {
const val = parseInt(
window.localStorage.getItem('graphiql:explorerWidth'),
10
);
return isNaN(val) ? 350 : val;
};
export const setExplorerWidthInLocalStorage = width => {
localStorage.setItem('graphiql:explorerWidth', width);
};

View File

@ -37,8 +37,8 @@ class LoginComponent extends React.Component {
type="submit"
onClick={(e) => {
e.preventDefault();
const emailRegex = /^(http[s]?:\/\/){0,1}(www\.){0,1}[a-zA-Z0-9\.\-]+\.[a-zA-Z]{2,5}[\.]{0,1}/;
if (!emailRegex.test(this.state.graphqlEndpoint)) {
const urlRegex= /^(http[s]?:\/\/){0,1}(www\.){0,1}[a-zA-Z0-9\.\-]+\.[a-zA-Z]{2,5}[\.]{0,1}/;
if (!urlRegex.test(this.state.graphqlEndpoint)) {
alert('Please enter a valid URL');
} else {
dispatch(updateGraphQLEndpoint(this.state.graphqlEndpoint));