From eda5492363e496f69f2db9abefb86fcf3a4ed931 Mon Sep 17 00:00:00 2001 From: Bobby Bonestell Date: Fri, 15 Nov 2024 16:21:35 -0700 Subject: [PATCH 1/4] Added bruno-cli support for mapping inherited apikey auth from collection --- .../bruno-cli/src/runner/prepare-request.js | 17 ++- .../tests/runner/prepare-request.spec.js | 122 ++++++++++++++++++ 2 files changed, 137 insertions(+), 2 deletions(-) diff --git a/packages/bruno-cli/src/runner/prepare-request.js b/packages/bruno-cli/src/runner/prepare-request.js index bc2b228866..3465f4e476 100644 --- a/packages/bruno-cli/src/runner/prepare-request.js +++ b/packages/bruno-cli/src/runner/prepare-request.js @@ -35,7 +35,7 @@ const prepareRequest = (request, collectionRoot) => { }; const collectionAuth = get(collectionRoot, 'request.auth'); - if (collectionAuth && request.auth.mode === 'inherit') { + if (collectionAuth && request.auth?.mode === 'inherit') { if (collectionAuth.mode === 'basic') { axiosRequest.auth = { username: get(collectionAuth, 'basic.username'), @@ -46,9 +46,22 @@ const prepareRequest = (request, collectionRoot) => { if (collectionAuth.mode === 'bearer') { axiosRequest.headers['Authorization'] = `Bearer ${get(collectionAuth, 'bearer.token')}`; } + + if (collectionAuth.mode === 'apikey') { + if (collectionAuth.apikey?.placement === 'header') { + axiosRequest.headers[collectionAuth.apikey?.key] = collectionAuth.apikey?.value; + } + + if (collectionAuth.apikey?.placement === 'queryparams') { + if (axiosRequest.url) { + const prefix = (!axiosRequest.url.includes('?')) ? '?' : '&'; + axiosRequest.url += `${prefix}${collectionAuth.apikey.key}=${collectionAuth.apikey.value}`; + } + } + } } - if (request.auth) { + if (request.auth && request.auth.mode !== 'inherit') { if (request.auth.mode === 'basic') { axiosRequest.auth = { username: get(request, 'auth.basic.username'), diff --git a/packages/bruno-cli/tests/runner/prepare-request.spec.js b/packages/bruno-cli/tests/runner/prepare-request.spec.js index 6e2219af83..28e6c13f19 100644 --- a/packages/bruno-cli/tests/runner/prepare-request.spec.js +++ b/packages/bruno-cli/tests/runner/prepare-request.spec.js @@ -18,4 +18,126 @@ describe('prepare-request: prepareRequest', () => { expect(result.data).toEqual(expected); }); }); + + describe('Properly maps inherited auth from collectionRoot', () => { + // Initialize Test Fixtures + let collectionRoot, request; + + beforeEach(() => { + // Reset test fixtures + collectionRoot = { + request: { + auth: {} + } + }; + + request = { + url: 'https://www.usebruno.com', + auth: { + mode: "inherit" + }, + body: { + mode: 'json', + json: '{\n"test": {{someVar}} // comment\n}' + } + }; + }); + + it('If collection auth is apikey in header', () => { + collectionRoot.request.auth = { + mode: "apikey", + apikey: { + key: "x-api-key", + value: "{{apiKey}}", + placement: "header" + } + }; + + const result = prepareRequest(request, collectionRoot); + expect(result.headers).toHaveProperty('x-api-key', '{{apiKey}}'); + }); + + + it('If collection auth is apikey in header and request has existing headers', () => { + collectionRoot.request.auth = { + mode: "apikey", + apikey: { + key: "x-api-key", + value: "{{apiKey}}", + placement: "header" + } + }; + + request['headers'] = [{ name: 'Content-Type', value: 'application/json', enabled: true }]; + + const result = prepareRequest(request, collectionRoot); + expect(result.headers).toHaveProperty('Content-Type', 'application/json'); + expect(result.headers).toHaveProperty('x-api-key', '{{apiKey}}'); + }); + + it('If collection auth is apikey in query parameters', () => { + collectionRoot.request.auth = { + mode: "apikey", + apikey: { + key: "apiKey", + value: "{{apiKey}}", + placement: "queryparams" + } + }; + + const expected = `${request.url}?${collectionRoot.request.auth.apikey.key}=${collectionRoot.request.auth.apikey.value}`; + const result = prepareRequest(request, collectionRoot); + expect(result.url).toEqual(expected); + }); + + it('If request does not have auth configured', () => { + delete request.auth; + let result; + expect(() => { + result = prepareRequest(request, collectionRoot); + }).not.toThrow(); + expect(result).toBeDefined(); + }); + + it('If collection auth is basic auth', () => { + collectionRoot.request.auth = { + mode: 'basic', + basic: { + username: 'testUser', + password: 'testPass123' + } + }; + + const result = prepareRequest(request, collectionRoot); + const expected = { username: 'testUser', password: 'testPass123' }; + expect(result.auth).toEqual(expected); + }); + + it('If collection auth is bearer token', () => { + collectionRoot.request.auth = { + mode: 'bearer', + bearer: { + token: 'token' + } + }; + + const result = prepareRequest(request, collectionRoot); + expect(result.headers).toHaveProperty('Authorization', 'Bearer token'); + }); + + it('If collection auth is bearer token and request has existing headers', () => { + collectionRoot.request.auth = { + mode: 'bearer', + bearer: { + token: 'token' + } + }; + + request['headers'] = [{ name: 'Content-Type', value: 'application/json', enabled: true }]; + + const result = prepareRequest(request, collectionRoot); + expect(result.headers).toHaveProperty('Authorization', 'Bearer token'); + expect(result.headers).toHaveProperty('Content-Type', 'application/json'); + }); + }); }); From 3c71554aac2f46613641077254e085a847cd023d Mon Sep 17 00:00:00 2001 From: Anoop M D Date: Wed, 15 Jan 2025 20:35:52 +0530 Subject: [PATCH 2/4] chore: updated request mock inside unit test --- packages/bruno-cli/tests/runner/prepare-request.spec.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/bruno-cli/tests/runner/prepare-request.spec.js b/packages/bruno-cli/tests/runner/prepare-request.spec.js index 8504b744a8..100cdedf70 100644 --- a/packages/bruno-cli/tests/runner/prepare-request.spec.js +++ b/packages/bruno-cli/tests/runner/prepare-request.spec.js @@ -37,6 +37,7 @@ describe('prepare-request: prepareRequest', () => { request = { url: 'https://www.usebruno.com', + method: 'POST', auth: { mode: "inherit" }, From d16a67966afe9e1e2d98a9ddaddcfd70be690fd9 Mon Sep 17 00:00:00 2001 From: Anoop M D Date: Fri, 17 Jan 2025 20:15:51 +0530 Subject: [PATCH 3/4] chore: updated tests --- .../tests/runner/prepare-request.spec.js | 226 ++++++++++-------- 1 file changed, 121 insertions(+), 105 deletions(-) diff --git a/packages/bruno-cli/tests/runner/prepare-request.spec.js b/packages/bruno-cli/tests/runner/prepare-request.spec.js index 100cdedf70..ffc9986dfd 100644 --- a/packages/bruno-cli/tests/runner/prepare-request.spec.js +++ b/packages/bruno-cli/tests/runner/prepare-request.spec.js @@ -1,5 +1,4 @@ -const { describe, it, expect } = require('@jest/globals'); - +const { describe, it, expect, beforeEach } = require('@jest/globals'); const prepareRequest = require('../../src/runner/prepare-request'); describe('prepare-request: prepareRequest', () => { @@ -25,124 +24,141 @@ describe('prepare-request: prepareRequest', () => { describe('Properly maps inherited auth from collectionRoot', () => { // Initialize Test Fixtures - let collectionRoot, request; + let collection, item; beforeEach(() => { - // Reset test fixtures - collectionRoot = { - request: { - auth: {} - } - }; - - request = { - url: 'https://www.usebruno.com', - method: 'POST', - auth: { - mode: "inherit" - }, - body: { - mode: 'json', - json: '{\n"test": {{someVar}} // comment\n}' - } - }; - }); - - it('If collection auth is apikey in header', () => { - collectionRoot.request.auth = { - mode: "apikey", - apikey: { - key: "x-api-key", - value: "{{apiKey}}", - placement: "header" - } - }; - - const result = prepareRequest(request, collectionRoot); - expect(result.headers).toHaveProperty('x-api-key', '{{apiKey}}'); - }); - - - it('If collection auth is apikey in header and request has existing headers', () => { - collectionRoot.request.auth = { - mode: "apikey", - apikey: { - key: "x-api-key", - value: "{{apiKey}}", - placement: "header" + collection = { + name: 'Test Collection', + root: { + request: { + auth: {} + } } }; - - request['headers'] = [{ name: 'Content-Type', value: 'application/json', enabled: true }]; - const result = prepareRequest(request, collectionRoot); - expect(result.headers).toHaveProperty('Content-Type', 'application/json'); - expect(result.headers).toHaveProperty('x-api-key', '{{apiKey}}'); - }); - - it('If collection auth is apikey in query parameters', () => { - collectionRoot.request.auth = { - mode: "apikey", - apikey: { - key: "apiKey", - value: "{{apiKey}}", - placement: "queryparams" + item = { + name: 'Test Request', + type: 'http-request', + request: { + method: 'GET', + headers: [], + params: [], + url: 'https://usebruno.com', + auth: { + mode: 'inherit' + }, + script: { + req: 'console.log("Pre Request")', + res: 'console.log("Post Response")' + } } }; - - const expected = `${request.url}?${collectionRoot.request.auth.apikey.key}=${collectionRoot.request.auth.apikey.value}`; - const result = prepareRequest(request, collectionRoot); - expect(result.url).toEqual(expected); }); - it('If request does not have auth configured', () => { - delete request.auth; - let result; - expect(() => { - result = prepareRequest(request, collectionRoot); - }).not.toThrow(); - expect(result).toBeDefined(); + describe('API Key Authentication', () => { + it('If collection auth is apikey in header', () => { + collection.root.request.auth = { + mode: "apikey", + apikey: { + key: "x-api-key", + value: "{{apiKey}}", + placement: "header" + } + }; + + const result = prepareRequest(item, collection); + expect(result.headers).toHaveProperty('x-api-key', '{{apiKey}}'); + }); + + it('If collection auth is apikey in header and request has existing headers', () => { + collection.root.request.auth = { + mode: "apikey", + apikey: { + key: "x-api-key", + value: "{{apiKey}}", + placement: "header" + } + }; + + item.request.headers.push({ name: 'Content-Type', value: 'application/json', enabled: true }); + const result = prepareRequest(item, collection); + expect(result.headers).toHaveProperty('Content-Type', 'application/json'); + expect(result.headers).toHaveProperty('x-api-key', '{{apiKey}}'); + }); + + it('If collection auth is apikey in query parameters', () => { + collection.root.request.auth = { + mode: "apikey", + apikey: { + key: "x-api-key", + value: "{{apiKey}}", + placement: "queryparams" + } + }; + + const urlObj = new URL(item.request.url); + urlObj.searchParams.set(collection.root.request.auth.apikey.key, collection.root.request.auth.apikey.value); + + const expected = urlObj.toString(); + const result = prepareRequest(item, collection); + expect(result.url).toEqual(expected); + }); }); - it('If collection auth is basic auth', () => { - collectionRoot.request.auth = { - mode: 'basic', - basic: { - username: 'testUser', - password: 'testPass123' - } - }; - - const result = prepareRequest(request, collectionRoot); - const expected = { username: 'testUser', password: 'testPass123' }; - expect(result.auth).toEqual(expected); + describe('Basic Authentication', () => { + it('If collection auth is basic auth', () => { + collection.root.request.auth = { + mode: 'basic', + basic: { + username: 'testUser', + password: 'testPass123' + } + }; + + const result = prepareRequest(item, collection); + const expected = { username: 'testUser', password: 'testPass123' }; + expect(result.auth).toEqual(expected); + }); }); - it('If collection auth is bearer token', () => { - collectionRoot.request.auth = { - mode: 'bearer', - bearer: { - token: 'token' - } - }; - - const result = prepareRequest(request, collectionRoot); - expect(result.headers).toHaveProperty('Authorization', 'Bearer token'); + describe('Bearer Token Authentication', () => { + it('If collection auth is bearer token', () => { + collection.root.request.auth = { + mode: 'bearer', + bearer: { + token: 'token' + } + }; + + const result = prepareRequest(item, collection); + expect(result.headers).toHaveProperty('Authorization', 'Bearer token'); + }); + + it('If collection auth is bearer token and request has existing headers', () => { + collection.root.request.auth = { + mode: 'bearer', + bearer: { + token: 'token' + } + }; + + item.request.headers.push({ name: 'Content-Type', value: 'application/json', enabled: true }); + + const result = prepareRequest(item, collection); + expect(result.headers).toHaveProperty('Authorization', 'Bearer token'); + expect(result.headers).toHaveProperty('Content-Type', 'application/json'); + }); }); - it('If collection auth is bearer token and request has existing headers', () => { - collectionRoot.request.auth = { - mode: 'bearer', - bearer: { - token: 'token' - } - }; - - request['headers'] = [{ name: 'Content-Type', value: 'application/json', enabled: true }]; - - const result = prepareRequest(request, collectionRoot); - expect(result.headers).toHaveProperty('Authorization', 'Bearer token'); - expect(result.headers).toHaveProperty('Content-Type', 'application/json'); + describe('No Authentication', () => { + it('If request does not have auth configured', () => { + delete item.request.auth; + let result; + expect(() => { + result = prepareRequest(item, collection); + }).not.toThrow(); + expect(result).toBeDefined(); + }); }); }); }); From 525394bda9610252ee41b08eb20d56f189349f64 Mon Sep 17 00:00:00 2001 From: Anoop M D Date: Fri, 17 Jan 2025 20:29:21 +0530 Subject: [PATCH 4/4] chore: using URL object to set the api key --- packages/bruno-cli/src/runner/prepare-request.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/bruno-cli/src/runner/prepare-request.js b/packages/bruno-cli/src/runner/prepare-request.js index 65c6a0e868..11963bd296 100644 --- a/packages/bruno-cli/src/runner/prepare-request.js +++ b/packages/bruno-cli/src/runner/prepare-request.js @@ -54,9 +54,14 @@ const prepareRequest = (item = {}, collection = {}) => { } if (collectionAuth.apikey?.placement === 'queryparams') { - if (axiosRequest.url) { - const prefix = (!axiosRequest.url.includes('?')) ? '?' : '&'; - axiosRequest.url += `${prefix}${collectionAuth.apikey.key}=${collectionAuth.apikey.value}`; + if (axiosRequest.url && collectionAuth.apikey?.key) { + try { + const urlObj = new URL(request.url); + urlObj.searchParams.set(collectionAuth.apikey?.key, collectionAuth.apikey?.value); + axiosRequest.url = urlObj.toString(); + } catch (error) { + console.error('Invalid URL:', request.url, error); + } } } }