Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/neat-wings-bow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@commercetools/ts-client": patch
---

[Feat][DEVX-793] Allow Passing Custom Headers to Http Requests
1 change: 1 addition & 0 deletions packages/sdk-client-v3/src/types/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,7 @@ export type IClientOptions = {
retryConfig?: RetryOptions
maskSensitiveHeaderData?: boolean
httpClientOptions?: object
request?: MiddlewareRequest
}

export type HttpClientOptions = IClientOptions & Optional
Expand Down
2 changes: 1 addition & 1 deletion packages/sdk-client-v3/src/utils/executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export default async function executor(request: HttpClientConfig) {
...options,
...rest,
headers: {
...rest.headers,
...Object.assign({}, rest.headers, rest.request?.headers ?? {}),
},

// for axios
Expand Down
129 changes: 129 additions & 0 deletions packages/sdk-client-v3/tests/utils.test/executor.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import executor from '../../src/utils/executor'
import { HttpClientConfig } from '../../src/types/types'

const mockResponse = {
data: { ok: true },
status: 200,
statusCode: 200,
headers: {},
}

function createExecutorConfig(
overrides: Partial<HttpClientConfig> & {
httpClient?: jest.Mock
} = {}
): HttpClientConfig {
const httpClient =
overrides.httpClient ?? jest.fn(() => Promise.resolve(mockResponse))

return {
url: 'https://api.example.com/test',
method: 'GET',
headers: {},
request: {
uri: '/test',
method: 'GET',
body: null,
headers: {},
},
httpClient,
...overrides,
}
}

describe('executor', () => {
describe('request headers', () => {
test('passes rest.headers to httpClient', async () => {
const httpClient = jest.fn(() => Promise.resolve(mockResponse))

await executor(
createExecutorConfig({
httpClient,
headers: { 'X-Client-Header': 'client-value' },
})
)

expect(httpClient).toHaveBeenCalledWith(
'https://api.example.com/test',
expect.objectContaining({
headers: { 'X-Client-Header': 'client-value' },
})
)
})

test('merges rest.request.headers with rest.headers', async () => {
const httpClient = jest.fn(() => Promise.resolve(mockResponse))

await executor(
createExecutorConfig({
httpClient,
headers: { 'X-Client-Header': 'client-value' },
request: {
uri: '/test',
method: 'GET',
body: null,
headers: { 'X-Request-Header': 'request-value' },
},
})
)

expect(httpClient).toHaveBeenCalledWith(
'https://api.example.com/test',
expect.objectContaining({
headers: {
'X-Client-Header': 'client-value',
'X-Request-Header': 'request-value',
},
})
)
})

test('rest.request.headers override rest.headers on conflict', async () => {
const httpClient = jest.fn(() => Promise.resolve(mockResponse))

await executor(
createExecutorConfig({
httpClient,
headers: { 'X-Shared-Header': 'client-value' },
request: {
uri: '/test',
method: 'GET',
body: null,
headers: { 'X-Shared-Header': 'request-value' },
},
})
)

expect(httpClient).toHaveBeenCalledWith(
'https://api.example.com/test',
expect.objectContaining({
headers: { 'X-Shared-Header': 'request-value' },
})
)
})

test('uses only rest.headers when rest.request.headers is undefined', async () => {
const httpClient = jest.fn(() => Promise.resolve(mockResponse))

await executor(
createExecutorConfig({
httpClient,
headers: { Authorization: 'Bearer token' },
request: {
uri: '/test',
method: 'GET',
body: null,
headers: undefined as unknown as Record<string, string>,
},
})
)

expect(httpClient).toHaveBeenCalledWith(
'https://api.example.com/test',
expect.objectContaining({
headers: { Authorization: 'Bearer token' },
})
)
})
})
})
Loading