forked from extern/bruno
feat: graphql schema introspection
This commit is contained in:
parent
05a290839b
commit
8bfb2591c2
@ -27,7 +27,7 @@
|
|||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"formik": "^2.2.9",
|
"formik": "^2.2.9",
|
||||||
"graphiql": "^1.5.9",
|
"graphiql": "^1.5.9",
|
||||||
"graphql": "^16.2.0",
|
"graphql": "^16.6.0",
|
||||||
"graphql-request": "^3.7.0",
|
"graphql-request": "^3.7.0",
|
||||||
"idb": "^7.0.0",
|
"idb": "^7.0.0",
|
||||||
"immer": "^9.0.15",
|
"immer": "^9.0.15",
|
||||||
|
@ -10,6 +10,7 @@ import RequestHeaders from 'components/RequestPane/RequestHeaders';
|
|||||||
import { useTheme } from 'providers/Theme';
|
import { useTheme } from 'providers/Theme';
|
||||||
import { updateRequestGraphqlQuery } from 'providers/ReduxStore/slices/collections';
|
import { updateRequestGraphqlQuery } from 'providers/ReduxStore/slices/collections';
|
||||||
import { sendRequest, saveRequest } from 'providers/ReduxStore/slices/collections/actions';
|
import { sendRequest, saveRequest } from 'providers/ReduxStore/slices/collections/actions';
|
||||||
|
import { findEnvironmentInCollection } from 'utils/collections';
|
||||||
import useGraphqlSchema from './useGraphqlSchema';
|
import useGraphqlSchema from './useGraphqlSchema';
|
||||||
import StyledWrapper from './StyledWrapper';
|
import StyledWrapper from './StyledWrapper';
|
||||||
|
|
||||||
@ -22,13 +23,15 @@ const GraphQLRequestPane = ({ item, collection, leftPaneWidth, onSchemaLoad, tog
|
|||||||
const {
|
const {
|
||||||
storedTheme
|
storedTheme
|
||||||
} = useTheme();
|
} = useTheme();
|
||||||
|
|
||||||
|
const environment = findEnvironmentInCollection(collection, collection.activeEnvironmentUid);
|
||||||
|
|
||||||
let {
|
let {
|
||||||
schema,
|
schema,
|
||||||
loadSchema,
|
loadSchema,
|
||||||
isLoading: isSchemaLoading,
|
isLoading: isSchemaLoading,
|
||||||
error: schemaError
|
error: schemaError
|
||||||
} = useGraphqlSchema(url);
|
} = useGraphqlSchema(url, environment);
|
||||||
|
|
||||||
const loadGqlSchema = () => {
|
const loadGqlSchema = () => {
|
||||||
if(!isSchemaLoading) {
|
if(!isSchemaLoading) {
|
||||||
|
@ -1,27 +1,12 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
import { getIntrospectionQuery, buildClientSchema } from 'graphql';
|
import { buildClientSchema } from 'graphql';
|
||||||
|
import { fetchGqlSchema } from 'utils/network';
|
||||||
import { simpleHash } from 'utils/common';
|
import { simpleHash } from 'utils/common';
|
||||||
|
|
||||||
const schemaHashPrefix = 'bruno.graphqlSchema';
|
const schemaHashPrefix = 'bruno.graphqlSchema';
|
||||||
|
|
||||||
const fetchSchema = (endpoint) => {
|
const useGraphqlSchema = (endpoint, environment) => {
|
||||||
const introspectionQuery = getIntrospectionQuery();
|
|
||||||
const queryParams = {
|
|
||||||
query: introspectionQuery
|
|
||||||
};
|
|
||||||
|
|
||||||
return fetch(endpoint, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
Accept: 'application/json',
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify(queryParams)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const useGraphqlSchema = (endpoint) => {
|
|
||||||
const localStorageKey = `${schemaHashPrefix}.${simpleHash(endpoint)}`;
|
const localStorageKey = `${schemaHashPrefix}.${simpleHash(endpoint)}`;
|
||||||
const [error, setError] = useState(null);
|
const [error, setError] = useState(null);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
@ -40,8 +25,8 @@ const useGraphqlSchema = (endpoint) => {
|
|||||||
|
|
||||||
const loadSchema = () => {
|
const loadSchema = () => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
fetchSchema(endpoint)
|
fetchGqlSchema(endpoint, environment)
|
||||||
.then((res) => res.json())
|
.then((res) => res.data)
|
||||||
.then((s) => {
|
.then((s) => {
|
||||||
if (s && s.data) {
|
if (s && s.data) {
|
||||||
setSchema(buildClientSchema(s.data));
|
setSchema(buildClientSchema(s.data));
|
||||||
|
@ -40,7 +40,8 @@ export default class QueryEditor extends React.Component {
|
|||||||
value: this.props.value || '',
|
value: this.props.value || '',
|
||||||
lineNumbers: true,
|
lineNumbers: true,
|
||||||
tabSize: 2,
|
tabSize: 2,
|
||||||
mode: 'brunovariables',
|
mode: 'graphql',
|
||||||
|
// mode: 'brunovariables',
|
||||||
brunoVarInfo: {
|
brunoVarInfo: {
|
||||||
variables: getEnvironmentVariables(this.props.collection),
|
variables: getEnvironmentVariables(this.props.collection),
|
||||||
},
|
},
|
||||||
@ -176,12 +177,14 @@ export default class QueryEditor extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Todo: Overlay is messing up with schema hint
|
||||||
|
// Fix this
|
||||||
addOverlay = () => {
|
addOverlay = () => {
|
||||||
let variables = getEnvironmentVariables(this.props.collection);
|
// let variables = getEnvironmentVariables(this.props.collection);
|
||||||
this.variables = variables;
|
// this.variables = variables;
|
||||||
|
|
||||||
defineCodeMirrorBrunoVariablesMode(variables, 'graphql');
|
// defineCodeMirrorBrunoVariablesMode(variables, 'graphql');
|
||||||
this.editor.setOption('mode', 'brunovariables');
|
// this.editor.setOption('mode', 'brunovariables');
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -31,6 +31,17 @@ const sendHttpRequest = async (item, collection, environment) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const fetchGqlSchema = async (endpoint, environment) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const { ipcRenderer } = window;
|
||||||
|
|
||||||
|
ipcRenderer
|
||||||
|
.invoke('fetch-gql-schema', endpoint, environment)
|
||||||
|
.then(resolve)
|
||||||
|
.catch(reject);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const cancelNetworkRequest = async (cancelTokenUid) => {
|
export const cancelNetworkRequest = async (cancelTokenUid) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
ipcRenderer
|
ipcRenderer
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
"electron-util": "^0.17.2",
|
"electron-util": "^0.17.2",
|
||||||
"form-data": "^4.0.0",
|
"form-data": "^4.0.0",
|
||||||
"fs-extra": "^10.1.0",
|
"fs-extra": "^10.1.0",
|
||||||
|
"graphql": "^16.6.0",
|
||||||
"is-valid-path": "^0.1.1",
|
"is-valid-path": "^0.1.1",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
|
@ -5,6 +5,7 @@ const { ipcMain } = require('electron');
|
|||||||
const { forOwn, extend, each, get } = require('lodash');
|
const { forOwn, extend, each, get } = require('lodash');
|
||||||
const { ScriptRuntime, TestRuntime } = require('@usebruno/js');
|
const { ScriptRuntime, TestRuntime } = require('@usebruno/js');
|
||||||
const prepareRequest = require('./prepare-request');
|
const prepareRequest = require('./prepare-request');
|
||||||
|
const prepareGqlIntrospectionRequest = require('./prepare-gql-introspection-request');
|
||||||
const { cancelTokens, saveCancelToken, deleteCancelToken } = require('../../utils/cancel-token');
|
const { cancelTokens, saveCancelToken, deleteCancelToken } = require('../../utils/cancel-token');
|
||||||
const { uuid } = require('../../utils/common');
|
const { uuid } = require('../../utils/common');
|
||||||
const interpolateVars = require('./interpolate-vars');
|
const interpolateVars = require('./interpolate-vars');
|
||||||
@ -169,6 +170,33 @@ const registerNetworkIpc = (mainWindow, watcher, lastOpenedCollections) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('fetch-gql-schema', async (event, endpoint, environment) => {
|
||||||
|
try {
|
||||||
|
const envVars = getEnvVars(environment);
|
||||||
|
const request = prepareGqlIntrospectionRequest(endpoint, envVars);
|
||||||
|
|
||||||
|
const response = await axios(request);
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: response.status,
|
||||||
|
statusText: response.statusText,
|
||||||
|
headers: response.headers,
|
||||||
|
data: response.data
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
if(error.response) {
|
||||||
|
return {
|
||||||
|
status: error.response.status,
|
||||||
|
statusText: error.response.statusText,
|
||||||
|
headers: error.response.headers,
|
||||||
|
data: error.response.data
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = registerNetworkIpc;
|
module.exports = registerNetworkIpc;
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
const Mustache = require('mustache');
|
||||||
|
const { getIntrospectionQuery } = require('graphql');
|
||||||
|
|
||||||
|
// override the default escape function to prevent escaping
|
||||||
|
Mustache.escape = function (value) {
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const prepareGqlIntrospectionRequest = (endpoint, envVars) => {
|
||||||
|
if(endpoint && endpoint.length) {
|
||||||
|
endpoint = Mustache.render(endpoint, envVars);
|
||||||
|
}
|
||||||
|
const introspectionQuery = getIntrospectionQuery();
|
||||||
|
const queryParams = {
|
||||||
|
query: introspectionQuery
|
||||||
|
};
|
||||||
|
|
||||||
|
const request = {
|
||||||
|
method: 'POST',
|
||||||
|
url: endpoint,
|
||||||
|
headers: {
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
data: JSON.stringify(queryParams)
|
||||||
|
};
|
||||||
|
|
||||||
|
return request;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = prepareGqlIntrospectionRequest;
|
Loading…
Reference in New Issue
Block a user