diff --git a/helpers/callbackend.js b/helpers/callbackend.js index 60e03a8..5cea33c 100644 --- a/helpers/callbackend.js +++ b/helpers/callbackend.js @@ -14,10 +14,8 @@ module.exports = async ({ context: { req }, requestOptions }) => { if (!req) { throw new Error('Request data is not available') } - // Reading the headers Out of the context since it is not passed automatically - const { headers: { authorization } } = req - const headers = { authorization } - const bodyAndHeaders = getBodyAndHeaders(body, bodyType, headers) + + const bodyAndHeaders = getBodyAndHeaders(body, bodyType, req.headers) const response = await fetch(url, { method, diff --git a/helpers/get-body-headers.js b/helpers/get-body-headers.js index d731736..54baf24 100644 --- a/helpers/get-body-headers.js +++ b/helpers/get-body-headers.js @@ -1,22 +1,33 @@ const { URLSearchParams } = require('url') - +const HEADERS_TO_PROXY = ['authorization', 'x-forwarded', 'x-original'] module.exports = (body, bodyType, headers) => { + const headersToProxy = {} + if (headers) { + Object.entries(headers).forEach(([key, value]) => { + for (const header of HEADERS_TO_PROXY) { + if (key.indexOf(header) !== -1) { + headersToProxy[key] = value + } + } + }) + } + if (!body) { - return { headers } + return { headers: headersToProxy } } if (bodyType === 'json') { return { headers: { 'Content-Type': 'application/json', - ...headers + ...headersToProxy }, body: JSON.stringify(body) } } return { - headers, + headers: headersToProxy, body: new URLSearchParams(body) } } diff --git a/package.json b/package.json index c1ad92b..1769d0d 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,35 @@ { + "name": "gql-gateway", + "description": "GraphQL Gateway that does magic on top of REST services that provides Swagger/OpenAPI specifications.", + "keywords": [ + "graphql", + "gateway", + "swagger" + ], + "version": "1.0.9", + "license": "MIT", "author": { "name": "Ivan Martinez Pupo", "email": "segpacto@yahoo.es" }, + "homepage": "https://github.com/segpacto/gql-gateway#readme", + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/segpacto/gql-gateway.git" + }, "bugs": { "url": "https://github.com/segpacto/gql-gateway/issues" }, + "files": [ + "index.js", + "helpers" + ], + "main": "index.js", + "scripts": { + "lint:fix": "npx standard --fix", + "lint": "npx standard", + "test": "jest --coverage --detectOpenHandles --runInBand --testRegex=test/.*\\.spec\\.js --testPathIgnorePatterns=test/mocks/*.js" + }, "dependencies": { "apollo-server": "^2.9.16", "graphql": "^14.5.8", @@ -14,7 +38,6 @@ "swagger-to-graphql": "^4.0.2", "node-fetch": "^2.6.0" }, - "description": "GraphQL Gateway that does magic on top of REST services that provides Swagger/OpenAPI specifications.", "devDependencies": { "apollo-server-testing": "^2.9.16", "dotenv": "^8.2.0", @@ -23,32 +46,9 @@ "nodemon": "^2.0.2", "standard": "^14.3.1" }, - "homepage": "https://github.com/segpacto/gql-gateway#readme", - "keywords": [ - "graphql", - "gateway", - "swagger" - ], - "license": "MIT", - "main": "index.js", - "name": "gql-gateway", - "repository": { - "type": "git", - "url": "git+ssh://git@github.com/segpacto/gql-gateway.git" - }, - "files": [ - "index.js", - "helpers" - ], - "scripts": { - "lint:fix": "npx standard --fix", - "lint": "npx standard", - "test": "jest --coverage --detectOpenHandles --runInBand --testRegex=test/.*\\.spec\\.js --testPathIgnorePatterns=test/mocks/*.js" - }, "standard": { "env": [ "jest" ] - }, - "version": "1.0.8" -} + } +} \ No newline at end of file diff --git a/test/construct.spec.js b/test/construct.spec.js index e7fd9a0..0dab0f7 100644 --- a/test/construct.spec.js +++ b/test/construct.spec.js @@ -16,7 +16,7 @@ describe('Server construct', () => { await gateway({ endpointsList }) } catch (err) { expect(err.code).toBe('ENOTFOUND') - expect(err.message).toBe('request to https://no-existent-endpoint/swagger.json failed, reason: getaddrinfo ENOTFOUND no-existent-endpoint no-existent-endpoint:443') + expect(err.message).toBe('request to https://no-existent-endpoint/swagger.json failed, reason: getaddrinfo ENOTFOUND no-existent-endpoint') } }) diff --git a/test/unit/helpers/callbackend.spec.js b/test/unit/helpers/callbackend.spec.js index f2eee32..43cbe1e 100644 --- a/test/unit/helpers/callbackend.spec.js +++ b/test/unit/helpers/callbackend.spec.js @@ -1,5 +1,6 @@ const callbackend = require('./../../../helpers/callbackend') require('./../../mocks/gql-gateway-requests') +const nock = require('nock') describe('callbackend', () => { it('throw error when no req is attached to context', async () => { @@ -27,4 +28,21 @@ describe('callbackend', () => { expect(err.message).toBe('request to http://localhost/client-api/client?=with-error failed, reason: Whatever error') } }) + + it('x-headers on backend requests should be preserved', async () => { + const context = { req: { headers: { authorization: 'Bearer XXXXX', 'x-forwarded-host': 'http://gql-gateway-test.com', 'x-original-forwarded-for': '88.88.88.88' } } } + const requestOptions = { baseUrl: 'http://localhost', path: '/headers' } + const scope = nock('http://localhost') + .get('/headers') + .reply(200) + .persist() + + scope.on('request', function (req, interceptor, body) { + expect(req.headers).toMatchObject({ + 'x-forwarded-host': ['http://gql-gateway-test.com'], + 'x-original-forwarded-for': ['88.88.88.88'] + }) + }) + await callbackend({ context, requestOptions }) + }) }) diff --git a/test/unit/helpers/get-body-headers.spec.js b/test/unit/helpers/get-body-headers.spec.js index e6cc43c..d78b763 100644 --- a/test/unit/helpers/get-body-headers.spec.js +++ b/test/unit/helpers/get-body-headers.spec.js @@ -2,22 +2,40 @@ const getBodyHeaders = require('./../../../helpers/get-body-headers') const { URLSearchParams } = require('url') describe('get body and headers', () => { - it('return headers only when no body occurs', () => { - expect(getBodyHeaders(undefined, undefined, { test: 'test' })).toStrictEqual({ headers: { test: 'test' } }) - expect(getBodyHeaders({ test: 'my-body' }, 'json', { test: 'test' })).toStrictEqual( + it('return only headers when no body occurs', () => { + expect(getBodyHeaders(undefined, undefined, { 'x-forwarded-port': 8080 })).toStrictEqual({ headers: { 'x-forwarded-port': 8080 } }) + }) + + it('should return headers and body with json Content-Type', () => { + expect(getBodyHeaders({ test: 'my-body' }, 'json', { authorization: 'api-key some-key', 'x-forwarded-port': 8080 })).toStrictEqual( { headers: { 'Content-Type': 'application/json', - test: 'test' + authorization: 'api-key some-key', + 'x-forwarded-port': 8080 }, body: JSON.stringify({ test: 'my-body' }) } ) }) - expect(getBodyHeaders({ test: 'my-body' }, 'application/xml', { test: 'test' })).toStrictEqual( - { - headers: { test: 'test' }, - body: new URLSearchParams({ test: 'my-body' }) - } - ) + + it('should return headers and URLSearchParams', () => { + expect(getBodyHeaders({ test: 'my-body' }, 'application/xml', { 'x-forwarded-host': 'http://gql-gateway-test.com' })).toStrictEqual( + { + headers: { + 'x-forwarded-host': 'http://gql-gateway-test.com' + }, + body: new URLSearchParams({ test: 'my-body' }) + } + ) + }) + + it('It should proxy only headers to proxy', () => { + expect(getBodyHeaders({ test: 'my-body' }, 'application/xml', { test: 'test', 'x-forwarded-host': 'http://gql-gateway-test.com' })).toStrictEqual( + { + headers: { 'x-forwarded-host': 'http://gql-gateway-test.com' }, + body: new URLSearchParams({ test: 'my-body' }) + } + ) + }) })