From af130bac17fb52851d04838fa7fd10c9cd9fa85c Mon Sep 17 00:00:00 2001 From: Jonathan Gruber Date: Thu, 5 Oct 2023 16:35:22 +0200 Subject: [PATCH] feat(usebruno#354): Add authentication for GraphQL requests Schema introspection query will also use specified authentication. --- .../RequestPane/GraphQLRequestPane/index.js | 16 ++++++++++++++- .../GraphQLRequestPane/useGraphqlSchema.js | 4 ++-- packages/bruno-app/src/utils/network/index.js | 7 +++++-- .../bruno-electron/src/ipc/network/index.js | 8 +++++--- .../prepare-gql-introspection-request.js | 20 ++++++++++++++++--- 5 files changed, 44 insertions(+), 11 deletions(-) diff --git a/packages/bruno-app/src/components/RequestPane/GraphQLRequestPane/index.js b/packages/bruno-app/src/components/RequestPane/GraphQLRequestPane/index.js index 773d8011c..45a345a6a 100644 --- a/packages/bruno-app/src/components/RequestPane/GraphQLRequestPane/index.js +++ b/packages/bruno-app/src/components/RequestPane/GraphQLRequestPane/index.js @@ -6,6 +6,7 @@ import { IconRefresh, IconLoader2, IconBook, IconDownload } from '@tabler/icons' import { useSelector, useDispatch } from 'react-redux'; import { updateRequestPaneTab } from 'providers/ReduxStore/slices/tabs'; import QueryEditor from 'components/RequestPane/QueryEditor'; +import Auth from 'components/RequestPane/Auth'; import GraphQLVariables from 'components/RequestPane/GraphQLVariables'; import RequestHeaders from 'components/RequestPane/RequestHeaders'; import Vars from 'components/RequestPane/Vars'; @@ -32,7 +33,14 @@ const GraphQLRequestPane = ({ item, collection, leftPaneWidth, onSchemaLoad, tog const environment = findEnvironmentInCollection(collection, collection.activeEnvironmentUid); - let { schema, loadSchema, isLoading: isSchemaLoading, error: schemaError } = useGraphqlSchema(url, environment); + const request = item.draft ? item.draft.request : item.request; + + let { + schema, + loadSchema, + isLoading: isSchemaLoading, + error: schemaError + } = useGraphqlSchema(url, environment, request, collection.collectionVariables); const loadGqlSchema = () => { if (!isSchemaLoading) { @@ -90,6 +98,9 @@ const GraphQLRequestPane = ({ item, collection, leftPaneWidth, onSchemaLoad, tog case 'headers': { return ; } + case 'auth': { + return ; + } case 'vars': { return ; } @@ -135,6 +146,9 @@ const GraphQLRequestPane = ({ item, collection, leftPaneWidth, onSchemaLoad, tog
selectTab('headers')}> Headers
+
selectTab('auth')}> + Auth +
selectTab('vars')}> Vars
diff --git a/packages/bruno-app/src/components/RequestPane/GraphQLRequestPane/useGraphqlSchema.js b/packages/bruno-app/src/components/RequestPane/GraphQLRequestPane/useGraphqlSchema.js index 7cfe7f951..e59127717 100644 --- a/packages/bruno-app/src/components/RequestPane/GraphQLRequestPane/useGraphqlSchema.js +++ b/packages/bruno-app/src/components/RequestPane/GraphQLRequestPane/useGraphqlSchema.js @@ -6,7 +6,7 @@ import { simpleHash } from 'utils/common'; const schemaHashPrefix = 'bruno.graphqlSchema'; -const useGraphqlSchema = (endpoint, environment) => { +const useGraphqlSchema = (endpoint, environment, request, collectionVariables) => { const localStorageKey = `${schemaHashPrefix}.${simpleHash(endpoint)}`; const [error, setError] = useState(null); const [isLoading, setIsLoading] = useState(false); @@ -25,7 +25,7 @@ const useGraphqlSchema = (endpoint, environment) => { const loadSchema = () => { setIsLoading(true); - fetchGqlSchema(endpoint, environment) + fetchGqlSchema(endpoint, environment, request, collectionVariables) .then((res) => res.data) .then((s) => { if (s && s.data) { diff --git a/packages/bruno-app/src/utils/network/index.js b/packages/bruno-app/src/utils/network/index.js index f4af70926..2c5c3a1d3 100644 --- a/packages/bruno-app/src/utils/network/index.js +++ b/packages/bruno-app/src/utils/network/index.js @@ -29,11 +29,14 @@ const sendHttpRequest = async (item, collection, environment, collectionVariable }); }; -export const fetchGqlSchema = async (endpoint, environment) => { +export const fetchGqlSchema = async (endpoint, environment, request, collectionVariables) => { return new Promise((resolve, reject) => { const { ipcRenderer } = window; - ipcRenderer.invoke('fetch-gql-schema', endpoint, environment).then(resolve).catch(reject); + ipcRenderer + .invoke('fetch-gql-schema', endpoint, environment, request, collectionVariables) + .then(resolve) + .catch(reject); }); }; diff --git a/packages/bruno-electron/src/ipc/network/index.js b/packages/bruno-electron/src/ipc/network/index.js index c3ade9c05..89740b8bc 100644 --- a/packages/bruno-electron/src/ipc/network/index.js +++ b/packages/bruno-electron/src/ipc/network/index.js @@ -458,10 +458,10 @@ const registerNetworkIpc = (mainWindow) => { }); }); - ipcMain.handle('fetch-gql-schema', async (event, endpoint, environment) => { + ipcMain.handle('fetch-gql-schema', async (event, endpoint, environment, request, collectionVariables) => { try { const envVars = getEnvVars(environment); - const request = prepareGqlIntrospectionRequest(endpoint, envVars); + const preparedRequest = prepareGqlIntrospectionRequest(endpoint, envVars, request); const preferences = getPreferences(); const sslVerification = get(preferences, 'request.sslVerification', true); @@ -472,7 +472,9 @@ const registerNetworkIpc = (mainWindow) => { }); } - const response = await axios(request); + interpolateVars(preparedRequest, envVars, collectionVariables); + + const response = await axios(preparedRequest); return { status: response.status, diff --git a/packages/bruno-electron/src/ipc/network/prepare-gql-introspection-request.js b/packages/bruno-electron/src/ipc/network/prepare-gql-introspection-request.js index a36666e31..d41be8f4c 100644 --- a/packages/bruno-electron/src/ipc/network/prepare-gql-introspection-request.js +++ b/packages/bruno-electron/src/ipc/network/prepare-gql-introspection-request.js @@ -1,12 +1,13 @@ const Mustache = require('mustache'); const { getIntrospectionQuery } = require('graphql'); +const { get } = require('lodash'); // override the default escape function to prevent escaping Mustache.escape = function (value) { return value; }; -const prepareGqlIntrospectionRequest = (endpoint, envVars) => { +const prepareGqlIntrospectionRequest = (endpoint, envVars, request) => { if (endpoint && endpoint.length) { endpoint = Mustache.render(endpoint, envVars); } @@ -15,7 +16,7 @@ const prepareGqlIntrospectionRequest = (endpoint, envVars) => { query: introspectionQuery }; - const request = { + let axiosRequest = { method: 'POST', url: endpoint, headers: { @@ -25,7 +26,20 @@ const prepareGqlIntrospectionRequest = (endpoint, envVars) => { data: JSON.stringify(queryParams) }; - return request; + if (request.auth) { + if (request.auth.mode === 'basic') { + axiosRequest.auth = { + username: get(request, 'auth.basic.username'), + password: get(request, 'auth.basic.password') + }; + } + + if (request.auth.mode === 'bearer') { + axiosRequest.headers.authorization = `Bearer ${get(request, 'auth.bearer.token')}`; + } + } + + return axiosRequest; }; module.exports = prepareGqlIntrospectionRequest;