diff --git a/modules/sdk-api/src/bitgoAPI.ts b/modules/sdk-api/src/bitgoAPI.ts index e947001cae..6867e9330a 100644 --- a/modules/sdk-api/src/bitgoAPI.ts +++ b/modules/sdk-api/src/bitgoAPI.ts @@ -132,10 +132,12 @@ export class BitGoAPI implements BitGoBase { protected _validate: boolean; public readonly cookiesPropagationEnabled: boolean; private _customProxyAgent?: Agent; + private _requestIdPrefix?: string; private getAdditionalHeadersCb?: AdditionalHeadersCallback; constructor(params: BitGoAPIOptions = {}) { this.getAdditionalHeadersCb = params.getAdditionalHeadersCb; + this._requestIdPrefix = params.requestIdPrefix; this.cookiesPropagationEnabled = false; if ( !common.validateParams( @@ -395,7 +397,9 @@ export class BitGoAPI implements BitGoBase { } if (!_.isUndefined(this._reqId)) { - req.set('Request-ID', this._reqId.toString()); + const reqId = this._reqId.toString(); + const requestId = this._requestIdPrefix ? `${this._requestIdPrefix}${reqId}` : reqId; + req.set('Request-ID', requestId); // increment after setting the header so the sequence numbers start at 0 this._reqId.inc(); diff --git a/modules/sdk-api/src/types.ts b/modules/sdk-api/src/types.ts index 8afbec79ea..b3d878e7be 100644 --- a/modules/sdk-api/src/types.ts +++ b/modules/sdk-api/src/types.ts @@ -64,6 +64,7 @@ export interface BitGoAPIOptions { validate?: boolean; cookiesPropagationEnabled?: boolean; getAdditionalHeadersCb?: AdditionalHeadersCallback; + requestIdPrefix?: string; evm?: { [key: string]: { baseUrl?: string; diff --git a/modules/sdk-api/test/unit/bitgoAPI.ts b/modules/sdk-api/test/unit/bitgoAPI.ts index 3ac85320d9..6b2f206d9d 100644 --- a/modules/sdk-api/test/unit/bitgoAPI.ts +++ b/modules/sdk-api/test/unit/bitgoAPI.ts @@ -38,6 +38,107 @@ describe('Constructor', function () { bitgo.cookiesPropagationEnabled.should.equal(false); }); }); + + describe('requestIdPrefix argument', function () { + afterEach(function () { + nock.cleanAll(); + }); + + it('should prepend requestIdPrefix to Request-ID header when set', async function () { + const bitgo = new BitGoAPI({ + env: 'custom', + customRootURI: 'https://app.example.local', + requestIdPrefix: 'test-prefix-', + }); + + const scope = nock('https://app.example.local') + .get('/api/v1/ping') + .matchHeader('Request-ID', /^test-prefix-/) + .reply(200, { status: 'ok' }); + + await bitgo.ping({ + reqId: { + toString: () => '12345', + inc: () => { + /* mock */ + }, + } as any, + }); + + scope.isDone().should.be.true(); + }); + + it('should not modify Request-ID header when requestIdPrefix is not set', async function () { + const bitgo = new BitGoAPI({ + env: 'custom', + customRootURI: 'https://app.example.local', + }); + + const scope = nock('https://app.example.local') + .get('/api/v1/ping') + .matchHeader('Request-ID', /^12345$/) + .reply(200, { status: 'ok' }); + + await bitgo.ping({ + reqId: { + toString: () => '12345', + inc: () => { + /* mock */ + }, + } as any, + }); + + scope.isDone().should.be.true(); + }); + + it('should correctly format Request-ID with prefix and numeric sequence', async function () { + const bitgo = new BitGoAPI({ + env: 'custom', + customRootURI: 'https://app.example.local', + requestIdPrefix: 'myapp-v1-', + }); + + const scope = nock('https://app.example.local') + .get('/api/v1/ping') + .matchHeader('Request-ID', 'myapp-v1-trace-123') + .reply(200, { status: 'ok' }); + + await bitgo.ping({ + reqId: { + toString: () => 'trace-123', + inc: () => { + /* mock */ + }, + } as any, + }); + + scope.isDone().should.be.true(); + }); + + it('should work with empty string prefix', async function () { + const bitgo = new BitGoAPI({ + env: 'custom', + customRootURI: 'https://app.example.local', + requestIdPrefix: '', + }); + + const scope = nock('https://app.example.local') + .get('/api/v1/ping') + .matchHeader('Request-ID', 'abc-123') + .reply(200, { status: 'ok' }); + + await bitgo.ping({ + reqId: { + toString: () => 'abc-123', + inc: () => { + /* mock */ + }, + } as any, + }); + + scope.isDone().should.be.true(); + }); + }); describe('http proxy agent', function () { it('http proxy agent shall be created when proxy(customProxyagent) is set', function () { const customProxyAgent = new ProxyAgent({