From d5ce307f67e2b3d8d5cd675a19a53b273f04ae62 Mon Sep 17 00:00:00 2001 From: Olivier Van den Mooter Date: Wed, 17 Sep 2025 10:29:20 +0200 Subject: [PATCH 1/2] Complete features for fetch, remove axios --- example/index.js | 3 +- lib/request.logger.js | 71 +++++++++-------- lib/request.middleware.js | 2 +- package-lock.json | 56 ++++++------- package.json | 7 +- test/helpers/server.js | 1 + test/request.logger.test.js | 135 +++++++++++++++++--------------- test/request.middleware.test.js | 53 +++++++------ 8 files changed, 170 insertions(+), 158 deletions(-) diff --git a/example/index.js b/example/index.js index 6856333..4f62f75 100644 --- a/example/index.js +++ b/example/index.js @@ -1,5 +1,4 @@ const express = require('express'); -const axios = require('axios'); const { requestMiddleware, requestlogger } = require('../lib'); @@ -45,7 +44,7 @@ async function start() { try { initializeExpress(); const startedapp = await startListening(); - await axios.get('http://localhost:2000?page=1').catch(() => {}); + await global.fetch('http://localhost:2000?page=1').catch(() => {}); return startedapp; } catch (err) { console.log(`Error occured ${err}`); diff --git a/lib/request.logger.js b/lib/request.logger.js index 7dff518..a0a1915 100644 --- a/lib/request.logger.js +++ b/lib/request.logger.js @@ -16,57 +16,60 @@ module.exports = (config) => { if(original) { global.fetch = async (requestUrl, options) => { const parsedUrl = url.parse(requestUrl); + const params = Object.fromEntries(new URLSearchParams(parsedUrl.search)); let correlationId; if (options && options.headers) { correlationId = options.headers['Dgp-Correlation'] || options.headers['dgp-correlation'] || validatedConfig.correlationIdfallback; } const timer = startTimer(); - const response = await original(requestUrl, options) - const duration = timer.getDuration(); const req = { headers: options?.headers || undefined, host: parsedUrl.host, - path: parsedUrl.path, + path: parsedUrl.pathname, method: options?.method || 'GET', - payload: options?.body || undefined, + payload: options?.body || params || undefined, search: parsedUrl.search, }; - const res = { - status: response.status, - duration, - }; - const log = logRequest(logger, correlationId, req, res, parsedUrl.protocol ); - let logged = false; - if (validatedConfig.logResponsePayload) { - const json = () => - response - .clone() - .json() - .then((data) => { - log(data); + try { + const response = await original(requestUrl, options) + const duration = timer.getDuration(); + const res = { + headers: Object.fromEntries(response.headers), + status: response.status, + duration, + }; + const log = logRequest(logger, correlationId, req, res, parsedUrl.protocol ); + let logged = false; + if (validatedConfig.logResponsePayload) { + try { + if(response.json) { + const json = await response.clone().json(); + log(json); logged = true; - return (data) - }); - const text = () => - response - .clone() - .text() - .then((data) => { - log(data); + } + } catch { + if(response.text) { + const text = await response.clone().text(); + log(text); logged = true; - return (data) - }); - // Response is not parsed json/text - setTimeout(() => { + } + } if(!logged) log(); - }, 1) - response.json = json; - response.text = text; - } else { + } else { + log(); + } + return response; + } catch(e) { + const duration = timer.getDuration(); + const res = { + status: `${e.message}: ${e.cause?.code}`, + duration, + }; + const log = logRequest(logger, correlationId, req, res, parsedUrl.protocol ); log(); + throw e; } - return response; } } diff --git a/lib/request.middleware.js b/lib/request.middleware.js index c7af0d7..6cd72f7 100644 --- a/lib/request.middleware.js +++ b/lib/request.middleware.js @@ -45,7 +45,7 @@ module.exports = (config) => { payload: req.body, }, response: { - headers: res.getHeaders(), + headers: { ...res.getHeaders() }, status: res.statusCode, duration: timer.getDuration(), payload: responsebody, diff --git a/package-lock.json b/package-lock.json index 0d27f5e..121fbc2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,14 +12,14 @@ "@digipolis/log": "^1.1.1" }, "devDependencies": { - "@eslint/js": "^9.33.0", - "axios": "^1.11.0", + "@eslint/js": "^9.35.0", + "axios": "^1.12.2", "c8": "^10.1.3", "eslint-plugin-mocha": "^11.1.0", "express": "^5.1.0", - "globals": "^16.3.0", + "globals": "^16.4.0", "jsonschema": "^1.5.0", - "mocha": "^11.7.1", + "mocha": "^11.7.2", "sinon": "^21.0.0" } }, @@ -163,9 +163,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.33.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.33.0.tgz", - "integrity": "sha512-5K1/mKhWaMfreBGJTwval43JJmkip0RmM+3+IuqupeSKNC/Th2Kc7ucaq5ovTSra/OOKB9c58CGSz3QMVbWt0A==", + "version": "9.35.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.35.0.tgz", + "integrity": "sha512-30iXE9whjlILfWobBkNerJo+TXYsgVM5ERQwMcMKCHckHflCmf7wXDAHlARoWnh0s1U72WqlbeyE7iAcCzuCPw==", "dev": true, "license": "MIT", "engines": { @@ -607,9 +607,9 @@ "dev": true }, "node_modules/axios": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz", - "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz", + "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", "dev": true, "license": "MIT", "dependencies": { @@ -1662,9 +1662,9 @@ } }, "node_modules/globals": { - "version": "16.3.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz", - "integrity": "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==", + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", + "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==", "dev": true, "license": "MIT", "engines": { @@ -2194,9 +2194,9 @@ } }, "node_modules/mocha": { - "version": "11.7.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.1.tgz", - "integrity": "sha512-5EK+Cty6KheMS/YLPPMJC64g5V61gIR25KsRItHw6x4hEKT6Njp1n9LOlH4gpevuwMVS66SXaBBpg+RWZkza4A==", + "version": "11.7.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.2.tgz", + "integrity": "sha512-lkqVJPmqqG/w5jmmFtiRvtA2jkDyNVUcefFJKb2uyX4dekk8Okgqop3cgbFiaIvj8uCRJVTP5x9dfxGyXm2jvQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3336,9 +3336,9 @@ } }, "@eslint/js": { - "version": "9.33.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.33.0.tgz", - "integrity": "sha512-5K1/mKhWaMfreBGJTwval43JJmkip0RmM+3+IuqupeSKNC/Th2Kc7ucaq5ovTSra/OOKB9c58CGSz3QMVbWt0A==", + "version": "9.35.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.35.0.tgz", + "integrity": "sha512-30iXE9whjlILfWobBkNerJo+TXYsgVM5ERQwMcMKCHckHflCmf7wXDAHlARoWnh0s1U72WqlbeyE7iAcCzuCPw==", "dev": true }, "@eslint/object-schema": { @@ -3652,9 +3652,9 @@ "dev": true }, "axios": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz", - "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz", + "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", "dev": true, "requires": { "follow-redirects": "^1.15.6", @@ -4383,9 +4383,9 @@ } }, "globals": { - "version": "16.3.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz", - "integrity": "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==", + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", + "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==", "dev": true }, "gopd": { @@ -4747,9 +4747,9 @@ "dev": true }, "mocha": { - "version": "11.7.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.1.tgz", - "integrity": "sha512-5EK+Cty6KheMS/YLPPMJC64g5V61gIR25KsRItHw6x4hEKT6Njp1n9LOlH4gpevuwMVS66SXaBBpg+RWZkza4A==", + "version": "11.7.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.2.tgz", + "integrity": "sha512-lkqVJPmqqG/w5jmmFtiRvtA2jkDyNVUcefFJKb2uyX4dekk8Okgqop3cgbFiaIvj8uCRJVTP5x9dfxGyXm2jvQ==", "dev": true, "requires": { "browser-stdout": "^1.3.1", diff --git a/package.json b/package.json index de07dd2..25c8f5c 100644 --- a/package.json +++ b/package.json @@ -26,14 +26,13 @@ "@digipolis/log": "^1.1.1" }, "devDependencies": { - "@eslint/js": "^9.33.0", + "@eslint/js": "^9.35.0", "eslint-plugin-mocha": "^11.1.0", - "axios": "^1.11.0", "c8": "^10.1.3", "jsonschema": "^1.5.0", "express": "^5.1.0", - "globals": "^16.3.0", - "mocha": "^11.7.1", + "globals": "^16.4.0", + "mocha": "^11.7.2", "sinon": "^21.0.0" } } diff --git a/test/helpers/server.js b/test/helpers/server.js index 0b3534b..c591f36 100644 --- a/test/helpers/server.js +++ b/test/helpers/server.js @@ -14,6 +14,7 @@ function initializeExpress(config) { }); app.get('/externalcall', (req, res) => res.json({ ok: 'ok' })); app.post('/externalcall', (req, res) => res.json({ ok: 'ok' })); + app.get('/externalcalltext', (req, res) => res.send('ok')); app.use(requestMiddleware(config)); app.get('/internalcall', (req, res) => res.json({ ok: 'ok' })); diff --git a/test/request.logger.test.js b/test/request.logger.test.js index 98531a5..5c3cfd9 100644 --- a/test/request.logger.test.js +++ b/test/request.logger.test.js @@ -3,7 +3,6 @@ const https = require('https'); const http = require('http'); const { URL } = require('url'); const sinon = require('sinon'); -const axios = require('axios'); const { requestlogger } = require('../lib'); const app = require('./helpers/server'); @@ -28,7 +27,7 @@ describe('Requestlog:', () => { it('GET /externalcall {} 200', async () => { const logger = requestlogger(); const logspy = sandbox.spy(logger, 'log'); - await axios.get(`http://localhost:${server.address().port}/externalcall`); + await global.fetch(`http://localhost:${server.address().port}/externalcall`); sinon.assert.calledWith(logspy, { request: { host: sinon.match(/localhost:[0-9]+/gm), @@ -248,7 +247,7 @@ describe('Requestlog:', () => { it('GET /externalcall?page=1 {} should not log query 200', async () => { const logger = requestlogger(); const logspy = sandbox.spy(logger, 'log'); - await axios.get(`http://localhost:${server.address().port}/externalcall?page=1`); + await global.fetch(`http://localhost:${server.address().port}/externalcall?page=1`); sinon.assert.calledWith(logspy, { type: ['application'], request: { @@ -266,7 +265,7 @@ describe('Requestlog:', () => { it('GET /externalcall { logRequestSearchParams: true } should log query 200', async () => { const logger = requestlogger({ logRequestSearchParams: true }); const logspy = sandbox.spy(logger, 'log'); - await axios.get(`http://localhost:${server.address().port}/externalcall?page=1`); + await global.fetch(`http://localhost:${server.address().port}/externalcall?page=1`); sinon.assert.calledWith(logspy, { type: ['application'], request: { @@ -324,7 +323,7 @@ describe('Requestlog:', () => { const logger = requestlogger(); const logspy = sandbox.spy(logger, 'log'); try { - await axios.get('http://localhost:1234/error'); + await global.fetch('http://localhost:1234/error'); } catch { sinon.assert.calledWith(logspy, { request: { @@ -345,7 +344,7 @@ describe('Requestlog:', () => { const logger = requestlogger(); const logspy = sandbox.spy(logger, 'log'); try { - await axios.get('https://www.google.com/notfound'); + await global.fetch('https://www.google.com/notfound'); } catch { sinon.assert.calledWith(logspy, { type: ['application'], @@ -363,7 +362,7 @@ describe('Requestlog:', () => { const logger = requestlogger(); const logspy = sandbox.spy(logger, 'log'); try { - await axios.get('https://superfakedomain.fakextention/externalcall'); + await global.fetch('https://superfakedomain.fakextention/externalcall'); } catch { sinon.assert.calledWith(logspy, { type: ['application'], @@ -372,15 +371,15 @@ describe('Requestlog:', () => { path: '/externalcall', method: 'GET', }, - response: { status: 'getaddrinfo ENOTFOUND superfakedomain.fakextention', duration: sinon.match.number }, + response: { status: 'fetch failed: ENOTFOUND', duration: sinon.match.number }, protocol: 'https:', }); } }); - it('GET /externalcall { logResponsePayload: true } 200', async () => { + it('GET /externalcall { logResponsePayload: true } 200', async() => { const logger = requestlogger({ logResponsePayload: true }); const logspy = sandbox.spy(logger, 'log'); - await axios.get(`http://localhost:${server.address().port}/externalcall`); + await global.fetch(`http://localhost:${server.address().port}/externalcall`); sinon.assert.calledWith(logspy, { type: ['application'], request: { @@ -389,9 +388,9 @@ describe('Requestlog:', () => { method: 'GET', }, response: { - payload: '{"ok":"ok"}', status: 200, duration: sinon.match.any, + payload: {"ok":"ok"}, }, protocol: 'http:', }); @@ -410,9 +409,9 @@ describe('Requestlog:', () => { method: 'GET', }, response: { - payload: {"ok":"ok"}, status: 200, duration: sinon.match.any, + payload: {"ok":"ok"}, }, protocol: 'http:', }); @@ -422,17 +421,16 @@ describe('Requestlog:', () => { const logger = requestlogger({ logResponsePayload: true }); const logspy = sandbox.spy(logger, 'log'); if(fetchTest) { - const response = await global.fetch(`http://localhost:${server.address().port}/externalcall`); - await response.text(); + await global.fetch(`http://localhost:${server.address().port}/externalcalltext`); return sinon.assert.calledWith(logspy, { type: ['application'], request: { host: sinon.match(/localhost:[0-9]+/gm), - path: '/externalcall', + path: '/externalcalltext', method: 'GET', }, response: { - payload: '{"ok":"ok"}', + payload: 'ok', status: 200, duration: sinon.match.any, }, @@ -443,7 +441,8 @@ describe('Requestlog:', () => { it('POST /externalcall {} 200', async () => { const logger = requestlogger(); const logspy = sandbox.spy(logger, 'log'); - await axios.post(`http://localhost:${server.address().port}/externalcall`, { param: 'paramval' }); + const query = new URLSearchParams({ param: 'paramval' }); + await global.fetch(`http://localhost:${server.address().port}/externalcall?${query}`, { method: 'POST' }); sinon.assert.calledWith(logspy, { type: ['application'], request: { @@ -458,13 +457,14 @@ describe('Requestlog:', () => { it('POST /externalcall default { logRequestPayload: true }', async () => { const logger = requestlogger({ logRequestPayload: true }); const logspy = sandbox.spy(logger, 'log'); - await axios.post(`http://localhost:${server.address().port}/externalcall`, { param: 'paramval' }); + const query = new URLSearchParams({ param: 'paramval' }); + await global.fetch(`http://localhost:${server.address().port}/externalcall?${query}`, { method: 'POST' }); sinon.assert.calledWith(logspy, { type: ['application'], request: { host: sinon.match(/localhost:[0-9]+/gm), path: '/externalcall', - payload: '{"param":"paramval"}', + payload: {"param":"paramval"}, method: 'POST', }, response: { status: 200, duration: sinon.match.number }, @@ -474,21 +474,20 @@ describe('Requestlog:', () => { it('POST /externalcall default { logRequestHeaders: true }', async () => { const logger = requestlogger({ logRequestHeaders: true }); const logspy = sandbox.spy(logger, 'log'); - await axios.post(`http://localhost:${server.address().port}/externalcall`, { param: 'paramval' }, { - headers: { - myheader: 'header', - }, - }); + const query = new URLSearchParams({ param: 'paramval' }); + await global.fetch( + `http://localhost:${server.address().port}/externalcall?${query}`, + { + method: 'POST', + headers: { + myheader: 'header', + }, + }); sinon.assert.calledWith(logspy, { type: ['application'], request: { headers: { - Accept: 'application/json, text/plain, */*', - 'Content-Type': 'application/json', myheader: 'header', - 'Accept-Encoding': sinon.match.any, - 'User-Agent': sinon.match(/axios\/*/gm), - 'Content-Length': sinon.match.any, }, host: sinon.match(/localhost:[0-9]+/gm), path: '/externalcall', @@ -501,11 +500,15 @@ describe('Requestlog:', () => { it('POST /externalcall dgp-correlation {}', async () => { const logger = requestlogger({}); const logspy = sandbox.spy(logger, 'log'); - await axios.post(`http://localhost:${server.address().port}/externalcall`, { param: 'paramval' }, { - headers: { - 'dgp-correlation': 'correlationid', - }, - }); + const query = new URLSearchParams({ param: 'paramval' }); + await global.fetch( + `http://localhost:${server.address().port}/externalcall?${query}`, + { + method: 'POST', + headers: { + 'dgp-correlation': 'correlationid', + }, + }); sinon.assert.calledWith(logspy, { type: ['application'], correlationId: 'correlationid', @@ -527,29 +530,30 @@ describe('Requestlog:', () => { correlationIdfallback: '_no_correlation_', }); const logspy = sandbox.spy(logger, 'log'); - await axios.post(`http://localhost:${server.address().port}/externalcall`, { param: 'paramval' }, { - headers: { - }, - }); + const query = new URLSearchParams({ param: 'paramval' }); + await global.fetch( + `http://localhost:${server.address().port}/externalcall?${query}`, + { + method: 'POST', + headers: { + myheader: 'header', + }, + }); sinon.assert.calledWith(logspy, { type: ['application'], correlationId: '_no_correlation_', request: { headers: { - Accept: 'application/json, text/plain, */*', - 'Content-Type': 'application/json', - 'Accept-Encoding': sinon.match.any, - 'User-Agent': sinon.match(/axios\/*/gm), - 'Content-Length': sinon.match.any, + myheader: 'header', }, host: sinon.match(/localhost:[0-9]+/gm), path: '/externalcall', - payload: '{"param":"paramval"}', + payload: {"param":"paramval"}, method: 'POST', }, response: { headers: sinon.match.any, - payload: '{"ok":"ok"}', + payload: {"ok":"ok"}, status: 200, duration: sinon.match.number, }, @@ -564,31 +568,30 @@ describe('Requestlog:', () => { logResponseHeaders: true, }); const logspy = sandbox.spy(logger, 'log'); - await axios.post(`http://localhost:${server.address().port}/externalcall`, { param: 'paramval' }, { - headers: { - 'dgp-correlation': 'correlationid', - }, - }); + const query = new URLSearchParams({ param: 'paramval' }); + await global.fetch( + `http://localhost:${server.address().port}/externalcall?${query}`, + { + method: 'POST', + headers: { + 'dgp-correlation': 'correlationid', + }, + }); sinon.assert.calledWith(logspy, { type: ['application'], correlationId: 'correlationid', request: { headers: { - Accept: 'application/json, text/plain, */*', - 'Content-Type': 'application/json', 'dgp-correlation': 'correlationid', - 'Accept-Encoding': sinon.match.any, - 'User-Agent': sinon.match(/axios\/*/gm), - 'Content-Length': sinon.match.any, }, host: sinon.match(/localhost:[0-9]+/gm), path: '/externalcall', - payload: '{"param":"paramval"}', + payload: {"param":"paramval"}, method: 'POST', }, response: { headers: sinon.match.any, - payload: '{"ok":"ok"}', + payload: {"ok":"ok"}, status: 200, duration: sinon.match.number, }, @@ -603,11 +606,15 @@ describe('Requestlog:', () => { logResponseHeaders: ['x-powered-by'], }); const logspy = sandbox.spy(logger, 'log'); - await axios.post(`http://localhost:${server.address().port}/externalcall`, { param: 'paramval' }, { - headers: { - 'dgp-correlation': 'correlationid', - }, - }); + const query = new URLSearchParams({ param: 'paramval' }); + await global.fetch( + `http://localhost:${server.address().port}/externalcall?${query}`, + { + method: 'POST', + headers: { + 'dgp-correlation': 'correlationid', + }, + }); sinon.assert.calledWith(logspy, { type: ['application'], correlationId: 'correlationid', @@ -617,14 +624,14 @@ describe('Requestlog:', () => { }, host: sinon.match(/localhost:[0-9]+/gm), path: '/externalcall', - payload: '{"param":"paramval"}', + payload: {"param":"paramval"}, method: 'POST', }, response: { headers: { 'x-powered-by': 'Express', }, - payload: '{"ok":"ok"}', + payload: {"ok":"ok"}, status: 200, duration: sinon.match.number, }, diff --git a/test/request.middleware.test.js b/test/request.middleware.test.js index d886841..bc835a5 100644 --- a/test/request.middleware.test.js +++ b/test/request.middleware.test.js @@ -1,6 +1,5 @@ const assert = require('node:assert/strict'); const sinon = require('sinon'); -const axios = require('axios'); const { Validator } = require('jsonschema'); const logschema = require('./data/logschema.json'); const app = require('./helpers/server.js'); @@ -27,7 +26,7 @@ describe('middleware:', () => { server = await app.start({ type: 'json', }); - await axios.get(`http://localhost:${server.address().port}/internalcall`); + await global.fetch(`http://localhost:${server.address().port}/internalcall`); sinon.assert.calledWith(logspy, { timestamp: sinon.match.any, correlationId: sinon.match.any, @@ -46,12 +45,12 @@ describe('middleware:', () => { server = await app.start({ type: 'silent', }); - await axios.get(`http://localhost:${server.address().port}/internalcall`); + await global.fetch(`http://localhost:${server.address().port}/internalcall`); sinon.assert.calledOnceWithExactly(logspy, sinon.match(/Express server listening on port/)); }); it('GET /internalcall { logResponsePayload: true } 200', async () => { server = await app.start({ type: 'json', logResponsePayload: true }); - await axios.get(`http://localhost:${server.address().port}/internalcall`); + await global.fetch(`http://localhost:${server.address().port}/internalcall`); sinon.assert.calledWith(logspy, { timestamp: sinon.match.any, type: ['application'], @@ -72,7 +71,7 @@ describe('middleware:', () => { }); it('GET /internalcall?query=true { logRequestSearchParams: true } 200', async () => { server = await app.start({ type: 'json', logRequestSearchParams: true }); - await axios.get(`http://localhost:${server.address().port}/internalcall?query=true`); + await global.fetch(`http://localhost:${server.address().port}/internalcall?query=true`); sinon.assert.calledWith(logspy, { timestamp: sinon.match.any, type: ['application'], @@ -92,7 +91,7 @@ describe('middleware:', () => { }); it('GET /internalcall?query=true { logRequestSearchParams: false } (default) 200', async () => { server = await app.start({ type: 'json', logRequestSearchParams: false }); - await axios.get(`http://localhost:${server.address().port}/internalcall?query=true`); + await global.fetch(`http://localhost:${server.address().port}/internalcall?query=true`); sinon.assert.calledWith(logspy, { timestamp: sinon.match.any, type: ['application'], @@ -112,7 +111,7 @@ describe('middleware:', () => { }); it('GET /write { logResponsePayload: true } 200', async () => { server = await app.start({ type: 'json', logResponsePayload: true }); - await axios.get(`http://localhost:${server.address().port}/write`); + await global.fetch(`http://localhost:${server.address().port}/write`); sinon.assert.calledWith(logspy, { timestamp: sinon.match.any, type: ['application'], @@ -139,7 +138,7 @@ describe('middleware:', () => { logRequestPayload: true, logResponseHeaders: true, }); - await axios.get(`http://localhost:${server.address().port}/internalcall`); + await global.fetch(`http://localhost:${server.address().port}/internalcall`); sinon.assert.calledWith(logspy, { timestamp: sinon.match.any, type: ['application'], @@ -147,11 +146,13 @@ describe('middleware:', () => { correlationId: sinon.match.any, request: { headers: { - accept: 'application/json, text/plain, */*', - 'user-agent': sinon.match(/axios\/*/gm), - 'accept-encoding': sinon.match.any, host: sinon.match(/localhost:[0-9]+/gm), - connection: sinon.match.any, + connection: 'keep-alive', + accept: '*/*', + 'accept-language': '*', + 'sec-fetch-mode': 'cors', + 'user-agent': 'node', + 'accept-encoding': 'gzip, deflate' }, host: sinon.match(/localhost:[0-9]+/gm), path: '/internalcall', @@ -187,7 +188,7 @@ describe('middleware:', () => { logResponseHeaders: true, }); const host = `localhost:${server.address().port}`; - await axios.get( + await global.fetch( `http://${host}/internalcall`, { headers: { @@ -202,12 +203,14 @@ describe('middleware:', () => { correlationId: 'dgpheadervalue', request: { headers: { - accept: 'application/json, text/plain, */*', + host: sinon.match(/localhost:[0-9]+/gm), + connection: 'keep-alive', + accept: '*/*', 'dgp-correlation': 'dgpheadervalue', - 'user-agent': sinon.match(/axios\/*/gm), - 'accept-encoding': sinon.match.any, - host, - connection: sinon.match.any, + 'accept-language': '*', + 'sec-fetch-mode': 'cors', + 'user-agent': 'node', + 'accept-encoding': 'gzip, deflate' }, host, path: '/internalcall', @@ -243,7 +246,7 @@ describe('middleware:', () => { logResponseHeaders: ['x-powered-by'], }); const host = `localhost:${server.address().port}`; - await axios.get( + await global.fetch( `http://${host}/internalcall`, { headers: { @@ -295,7 +298,7 @@ describe('middleware:', () => { logResponseHeaders: ['x-powered-by'], }); const host = `localhost:${server.address().port}`; - await axios.get( + await global.fetch( `http://${host}/internalcall`, { headers: { @@ -341,7 +344,7 @@ describe('middleware:', () => { correlationIdLocation: 'id', }); const host = `localhost:${server.address().port}`; - await axios.get( + await global.fetch( `http://${host}/internalcall`, { headers: { @@ -386,7 +389,7 @@ describe('middleware:', () => { correlationIdfallback: '_no_correlation_', }); const host = `localhost:${server.address().port}`; - await axios.get( + await global.fetch( `http://${host}/internalcall`, { headers: { @@ -431,7 +434,7 @@ describe('middleware:', () => { correlationIdfallback: '_no_correlation_', }); const host = `localhost:${server.address().port}`; - await axios.get( + await global.fetch( `http://${host}/internalcall`, { headers: { @@ -476,7 +479,7 @@ describe('middleware:', () => { correlationIdLocation: 'info.id', }); const host = `localhost:${server.address().port}`; - await axios.get( + await global.fetch( `http://${host}/internalcall`, { headers: { @@ -521,7 +524,7 @@ describe('middleware:', () => { correlationIdLocation: 'this.path.is.wrong.id', }); const host = `localhost:${server.address().port}`; - await axios.get( + await global.fetch( `http://${host}/internalcall`, { headers: { From fa548176a882e1cd62204c5fa3c9996bd0d365aa Mon Sep 17 00:00:00 2001 From: Olivier Van den Mooter Date: Wed, 17 Sep 2025 11:32:00 +0200 Subject: [PATCH 2/2] cover --- lib/request.logger.js | 12 +--- test/request.logger.test.js | 118 ++++++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+), 9 deletions(-) diff --git a/lib/request.logger.js b/lib/request.logger.js index a0a1915..305357c 100644 --- a/lib/request.logger.js +++ b/lib/request.logger.js @@ -28,7 +28,7 @@ module.exports = (config) => { host: parsedUrl.host, path: parsedUrl.pathname, method: options?.method || 'GET', - payload: options?.body || params || undefined, + payload: options?.body || params, search: parsedUrl.search, }; try { @@ -40,22 +40,16 @@ module.exports = (config) => { duration, }; const log = logRequest(logger, correlationId, req, res, parsedUrl.protocol ); - let logged = false; if (validatedConfig.logResponsePayload) { try { if(response.json) { const json = await response.clone().json(); log(json); - logged = true; } } catch { - if(response.text) { - const text = await response.clone().text(); - log(text); - logged = true; - } + const text = await response.clone().text(); + log(text); } - if(!logged) log(); } else { log(); } diff --git a/test/request.logger.test.js b/test/request.logger.test.js index 5c3cfd9..7bd9256 100644 --- a/test/request.logger.test.js +++ b/test/request.logger.test.js @@ -24,6 +24,124 @@ describe('Requestlog:', () => { app.stop(); done(); }); + it('http POST with body /externalcall {} 200', () => { + const logger = requestlogger({ logResponsePayload: true, logRequestPayload: true }); + const logspy = sandbox.spy(logger, 'log'); + const postData = JSON.stringify({ + 'msg': 'Hello World!', + }); + + const options = { + hostname: `localhost`, + port: server.address().port, + path: '/externalcall', + protocol: 'http:', + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Content-Length': Buffer.byteLength(postData), + }, + }; + + return new Promise((resolve, reject) => { + const req = http.request(options, (res) => { + res.on('data', () => { + }); + res.on('end', () => { + console.log('Response ended: '); + sinon.assert.calledWith(logspy, { + request: { + host: sinon.match(/localhost:[0-9]+/gm), + path: '/externalcall', + method: 'POST', + payload: '{"msg":"Hello World!"}' + }, + response: { + status: 200, + duration: sinon.match.number, + payload: '{"ok":"ok"}' + }, + protocol: 'http:', + type: ['application'], + }); + resolve('ok'); + }); + }).on('error', (err) => { + console.log('Error: ', err.message); + reject(err); + }); + req.write(postData); + req.end(); + }); + }); + it('http fails', () => { + const logger = requestlogger({ logResponsePayload: true, log: true }); + const logspy = sandbox.spy(logger, 'log'); + return new Promise((resolve, reject) => { + http.get(`http://localhost/externalcall`, (res) => { + res.on('data', () => { + }); + res.on('end', () => { + reject('should not end'); + }); + }).on('error', (err) => { + console.log('Error: ', err.message); + sinon.assert.calledWith(logspy, { + request: { + host: 'localhost', + path: '/externalcall', + }, + response: { + status: sinon.match.any, + duration: sinon.match.any, + }, + protocol: 'http:', + type: ['application'], + }); + resolve(); + }); + }); + }); + it('http fails with correlationid header', () => { + const logger = requestlogger({ logResponsePayload: true, log: true }); + const logspy = sandbox.spy(logger, 'log'); + const options = { + hostname: `localhost`, + path: '/externalcall', + protocol: 'http:', + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'dgp-correlation': 'correlationid', + }, + }; + return new Promise((resolve, reject) => { + http.request(options, (res) => { + res.on('data', () => { + }); + res.on('end', () => { + reject('should not end'); + }); + }).on('error', (err) => { + console.log('Error: ', err.message); + sinon.assert.calledWith(logspy, { + correlationId: 'correlationid', + request: { + host: 'localhost', + path: '/externalcall', + method: 'POST', + }, + response: { + status: sinon.match.any, + duration: sinon.match.any, + }, + protocol: 'http:', + type: ['application'], + }); + resolve(); + }); + }); + }); it('GET /externalcall {} 200', async () => { const logger = requestlogger(); const logspy = sandbox.spy(logger, 'log');