diff --git a/lib/pushgateway.js b/lib/pushgateway.js index f7d4f757..6ac7f495 100644 --- a/lib/pushgateway.js +++ b/lib/pushgateway.js @@ -1,6 +1,5 @@ 'use strict'; -const url = require('url'); const http = require('http'); const https = require('https'); const { gzipSync } = require('zlib'); @@ -45,33 +44,27 @@ class Pushgateway { return useGateway.call(this, 'DELETE', params.jobName, params.groupings); } } + async function useGateway(method, job, groupings) { - // `URL` first added in v6.13.0 - // eslint-disable-next-line n/no-deprecated-api - const gatewayUrlParsed = url.parse(this.gatewayUrl); + // `URL` is a global since Node.js v10 - no `require('url')` needed. + const requestParams = new URL(this.gatewayUrl); const gatewayUrlPath = - gatewayUrlParsed.pathname && gatewayUrlParsed.pathname !== '/' - ? gatewayUrlParsed.pathname + requestParams.pathname && requestParams.pathname !== '/' + ? requestParams.pathname : ''; const jobPath = job ? `/job/${encodeURIComponent(job)}${generateGroupings(groupings)}` : ''; - const path = `${gatewayUrlPath}/metrics${jobPath}`; + requestParams.pathname = `${gatewayUrlPath}/metrics${jobPath}`; - // eslint-disable-next-line n/no-deprecated-api - const target = url.resolve(this.gatewayUrl, path); - // eslint-disable-next-line n/no-deprecated-api - const requestParams = url.parse(target); - const httpModule = isHttps(requestParams.href) ? https : http; - const options = Object.assign(requestParams, this.requestOptions, { - method, - }); + const httpModule = requestParams.protocol === 'https:' ? https : http; + const options = { ...this.requestOptions, method }; return new Promise((resolve, reject) => { if (method === 'DELETE' && options.headers) { delete options.headers['Content-Encoding']; } - const req = httpModule.request(options, resp => { + const req = httpModule.request(requestParams, options, resp => { let body = ''; resp.setEncoding('utf8'); resp.on('data', chunk => { @@ -129,8 +122,4 @@ function generateGroupings(groupings) { .join(''); } -function isHttps(href) { - return href.search(/^https/) !== -1; -} - module.exports = Pushgateway; diff --git a/test/__snapshots__/pushgatewayWithPathTest.js.snap b/test/__snapshots__/pushgatewayWithPathTest.js.snap new file mode 100644 index 00000000..f01f1700 --- /dev/null +++ b/test/__snapshots__/pushgatewayWithPathTest.js.snap @@ -0,0 +1,37 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`pushgateway with path and OpenMetrics registry global registry pins the http.request(url, options) call shape (no legacy options translation) 1`] = ` +{ + "options": { + "method": "PUT", + }, + "url": "http://192.168.99.100:9091/path/metrics/job/testJob", +} +`; + +exports[`pushgateway with path and OpenMetrics registry registry instance pins the http.request(url, options) call shape (no legacy options translation) 1`] = ` +{ + "options": { + "method": "PUT", + }, + "url": "http://192.168.99.100:9091/path/metrics/job/testJob", +} +`; + +exports[`pushgateway with path and Prometheus registry global registry pins the http.request(url, options) call shape (no legacy options translation) 1`] = ` +{ + "options": { + "method": "PUT", + }, + "url": "http://192.168.99.100:9091/path/metrics/job/testJob", +} +`; + +exports[`pushgateway with path and Prometheus registry registry instance pins the http.request(url, options) call shape (no legacy options translation) 1`] = ` +{ + "options": { + "method": "PUT", + }, + "url": "http://192.168.99.100:9091/path/metrics/job/testJob", +} +`; diff --git a/test/pushgatewayWithPathTest.js b/test/pushgatewayWithPathTest.js index df274eb4..7dd75427 100644 --- a/test/pushgatewayWithPathTest.js +++ b/test/pushgatewayWithPathTest.js @@ -37,27 +37,29 @@ describe.each([ instance.pushAdd({ jobName: 'testJob' }); expect(mockHttp).toHaveBeenCalledTimes(1); - const invocation = mockHttp.mock.calls[0][0]; - expect(invocation.method).toEqual('POST'); - expect(invocation.path).toEqual('/path/metrics/job/testJob'); + const [requestUrl, requestOptions] = mockHttp.mock.calls[0]; + expect(requestOptions.method).toEqual('POST'); + expect(requestUrl.pathname).toEqual('/path/metrics/job/testJob'); }); it('should use groupings', () => { instance.pushAdd({ jobName: 'testJob', groupings: { key: 'value' } }); expect(mockHttp).toHaveBeenCalledTimes(1); - const invocation = mockHttp.mock.calls[0][0]; - expect(invocation.method).toEqual('POST'); - expect(invocation.path).toEqual('/path/metrics/job/testJob/key/value'); + const [requestUrl, requestOptions] = mockHttp.mock.calls[0]; + expect(requestOptions.method).toEqual('POST'); + expect(requestUrl.pathname).toEqual( + '/path/metrics/job/testJob/key/value', + ); }); it('should escape groupings', () => { instance.pushAdd({ jobName: 'testJob', groupings: { key: 'va&lue' } }); expect(mockHttp).toHaveBeenCalledTimes(1); - const invocation = mockHttp.mock.calls[0][0]; - expect(invocation.method).toEqual('POST'); - expect(invocation.path).toEqual( + const [requestUrl, requestOptions] = mockHttp.mock.calls[0]; + expect(requestOptions.method).toEqual('POST'); + expect(requestUrl.pathname).toEqual( '/path/metrics/job/testJob/key/va%26lue', ); }); @@ -68,18 +70,18 @@ describe.each([ instance.push({ jobName: 'testJob' }); expect(mockHttp).toHaveBeenCalledTimes(1); - const invocation = mockHttp.mock.calls[0][0]; - expect(invocation.method).toEqual('PUT'); - expect(invocation.path).toEqual('/path/metrics/job/testJob'); + const [requestUrl, requestOptions] = mockHttp.mock.calls[0]; + expect(requestOptions.method).toEqual('PUT'); + expect(requestUrl.pathname).toEqual('/path/metrics/job/testJob'); }); it('should uri encode url', () => { instance.push({ jobName: 'test&Job' }); expect(mockHttp).toHaveBeenCalledTimes(1); - const invocation = mockHttp.mock.calls[0][0]; - expect(invocation.method).toEqual('PUT'); - expect(invocation.path).toEqual('/path/metrics/job/test%26Job'); + const [requestUrl, requestOptions] = mockHttp.mock.calls[0]; + expect(requestOptions.method).toEqual('PUT'); + expect(requestUrl.pathname).toEqual('/path/metrics/job/test%26Job'); }); }); @@ -88,9 +90,9 @@ describe.each([ instance.delete({ jobName: 'testJob' }); expect(mockHttp).toHaveBeenCalledTimes(1); - const invocation = mockHttp.mock.calls[0][0]; - expect(invocation.method).toEqual('DELETE'); - expect(invocation.path).toEqual('/path/metrics/job/testJob'); + const [requestUrl, requestOptions] = mockHttp.mock.calls[0]; + expect(requestOptions.method).toEqual('DELETE'); + expect(requestUrl.pathname).toEqual('/path/metrics/job/testJob'); }); }); @@ -111,27 +113,30 @@ describe.each([ instance.pushAdd({ jobName: 'testJob' }); expect(mockHttp).toHaveBeenCalledTimes(1); - const invocation = mockHttp.mock.calls[0][0]; - expect(invocation.method).toEqual('POST'); - expect(invocation.auth).toEqual(auth); + const [requestUrl, requestOptions] = mockHttp.mock.calls[0]; + expect(requestOptions.method).toEqual('POST'); + expect(requestUrl.username).toEqual(USERNAME); + expect(requestUrl.password).toEqual(PASSWORD); }); it('push should send PUT request with basic auth data', () => { instance.push({ jobName: 'testJob' }); expect(mockHttp).toHaveBeenCalledTimes(1); - const invocation = mockHttp.mock.calls[0][0]; - expect(invocation.method).toEqual('PUT'); - expect(invocation.auth).toEqual(auth); + const [requestUrl, requestOptions] = mockHttp.mock.calls[0]; + expect(requestOptions.method).toEqual('PUT'); + expect(requestUrl.username).toEqual(USERNAME); + expect(requestUrl.password).toEqual(PASSWORD); }); it('delete should send DELETE request with basic auth data', () => { instance.delete({ jobName: 'testJob' }); expect(mockHttp).toHaveBeenCalledTimes(1); - const invocation = mockHttp.mock.calls[0][0]; - expect(invocation.method).toEqual('DELETE'); - expect(invocation.auth).toEqual(auth); + const [requestUrl, requestOptions] = mockHttp.mock.calls[0]; + expect(requestOptions.method).toEqual('DELETE'); + expect(requestUrl.username).toEqual(USERNAME); + expect(requestUrl.password).toEqual(PASSWORD); }); }); @@ -149,8 +154,22 @@ describe.each([ instance.push({ jobName: 'testJob' }); expect(mockHttp).toHaveBeenCalledTimes(1); - const invocation = mockHttp.mock.calls[0][0]; - expect(invocation.headers).toEqual({ 'unit-test': '1' }); + const [, requestOptions] = mockHttp.mock.calls[0]; + expect(requestOptions.headers).toEqual({ 'unit-test': '1' }); + }); + + it('pins the http.request(url, options) call shape (no legacy options translation)', () => { + instance.push({ jobName: 'testJob' }); + + expect(mockHttp).toHaveBeenCalledTimes(1); + const [requestUrl, requestOptions] = mockHttp.mock.calls[0]; + + expect(requestUrl).toBeInstanceOf(URL); + + expect({ + url: requestUrl.toString(), + options: requestOptions, + }).toMatchSnapshot(); }); }; describe('global registry', () => {