feat: graphql schema introspection

This commit is contained in:
Anoop M D 2023-02-01 05:09:42 +05:30 committed by Anoop M D
parent 05a290839b
commit 8bfb2591c2
8 changed files with 89 additions and 27 deletions

View File

@ -27,7 +27,7 @@
"file-saver": "^2.0.5",
"formik": "^2.2.9",
"graphiql": "^1.5.9",
"graphql": "^16.2.0",
"graphql": "^16.6.0",
"graphql-request": "^3.7.0",
"idb": "^7.0.0",
"immer": "^9.0.15",

View File

@ -10,6 +10,7 @@ import RequestHeaders from 'components/RequestPane/RequestHeaders';
import { useTheme } from 'providers/Theme';
import { updateRequestGraphqlQuery } from 'providers/ReduxStore/slices/collections';
import { sendRequest, saveRequest } from 'providers/ReduxStore/slices/collections/actions';
import { findEnvironmentInCollection } from 'utils/collections';
import useGraphqlSchema from './useGraphqlSchema';
import StyledWrapper from './StyledWrapper';
@ -22,13 +23,15 @@ const GraphQLRequestPane = ({ item, collection, leftPaneWidth, onSchemaLoad, tog
const {
storedTheme
} = useTheme();
const environment = findEnvironmentInCollection(collection, collection.activeEnvironmentUid);
let {
schema,
loadSchema,
isLoading: isSchemaLoading,
error: schemaError
} = useGraphqlSchema(url);
} = useGraphqlSchema(url, environment);
const loadGqlSchema = () => {
if(!isSchemaLoading) {

View File

@ -1,27 +1,12 @@
import { useState } from 'react';
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';
const schemaHashPrefix = 'bruno.graphqlSchema';
const fetchSchema = (endpoint) => {
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 useGraphqlSchema = (endpoint, environment) => {
const localStorageKey = `${schemaHashPrefix}.${simpleHash(endpoint)}`;
const [error, setError] = useState(null);
const [isLoading, setIsLoading] = useState(false);
@ -40,8 +25,8 @@ const useGraphqlSchema = (endpoint) => {
const loadSchema = () => {
setIsLoading(true);
fetchSchema(endpoint)
.then((res) => res.json())
fetchGqlSchema(endpoint, environment)
.then((res) => res.data)
.then((s) => {
if (s && s.data) {
setSchema(buildClientSchema(s.data));

View File

@ -40,7 +40,8 @@ export default class QueryEditor extends React.Component {
value: this.props.value || '',
lineNumbers: true,
tabSize: 2,
mode: 'brunovariables',
mode: 'graphql',
// mode: 'brunovariables',
brunoVarInfo: {
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 = () => {
let variables = getEnvironmentVariables(this.props.collection);
this.variables = variables;
// let variables = getEnvironmentVariables(this.props.collection);
// this.variables = variables;
defineCodeMirrorBrunoVariablesMode(variables, 'graphql');
this.editor.setOption('mode', 'brunovariables');
// defineCodeMirrorBrunoVariablesMode(variables, 'graphql');
// this.editor.setOption('mode', 'brunovariables');
}
render() {

View File

@ -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) => {
return new Promise((resolve, reject) => {
ipcRenderer

View File

@ -28,6 +28,7 @@
"electron-util": "^0.17.2",
"form-data": "^4.0.0",
"fs-extra": "^10.1.0",
"graphql": "^16.6.0",
"is-valid-path": "^0.1.1",
"lodash": "^4.17.21",
"moment": "^2.29.4",

View File

@ -5,6 +5,7 @@ const { ipcMain } = require('electron');
const { forOwn, extend, each, get } = require('lodash');
const { ScriptRuntime, TestRuntime } = require('@usebruno/js');
const prepareRequest = require('./prepare-request');
const prepareGqlIntrospectionRequest = require('./prepare-gql-introspection-request');
const { cancelTokens, saveCancelToken, deleteCancelToken } = require('../../utils/cancel-token');
const { uuid } = require('../../utils/common');
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;

View File

@ -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;