diff --git a/CHANGELOG.md b/CHANGELOG.md index fb6f707..8eb4c5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,12 @@ ### Fixed +## [4.1.0] + +### Added + +- Possibility to add additionalHeaders to global and local configuration ([@JorenSaeyTL](https://github.com/JorenSaeyTL) in [#314](https://github.com/teamleadercrm/sdk-js/pull/314)) + ## [4.0.2] - 2020-01-14 ### Fixed diff --git a/README.md b/README.md index c4da099..729ec99 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,30 @@ const init = async () => { }; ``` +- `additionalHeaders`: (object) request headers that are passed on top of the existing ones. + +`additionalHeaders` can also be provided at local level, in that case it will override the global headers. + +```js +import API from '@teamleader/api'; + +const { users } = API({ + getAccessToken: () => 'thisisatoken', + additionalHeaders: { + 'x-tl-feature-flags': 'core.some-feature-flag=true', + }, +}); + +const init = async () => { + // (options, plugins) + const me = await users.me(undefined, { + additionalHeaders: { + 'x-tl-feature-flags': 'core.some-feature-flag=true', + }, + }); // The X-TL-Feature_flags header is passed +}; +``` + ## Additional actions You can also add additional actions (the domain will also be created if needed), which will be handled the same way as the available actions. @@ -149,7 +173,7 @@ const { users } = API({ }); // own plugin -const addMeta = data => ({ ...data, meta: { size: data.length } }); +const addMeta = (data) => ({ ...data, meta: { size: data.length } }); const init = async () => { // (options, plugins) diff --git a/package.json b/package.json index 0d2c9b5..2cdb2fa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@teamleader/api", - "version": "4.0.2", + "version": "4.1.0", "description": "Teamleader API SDK", "main": "dist/cjs/main.js", "module": "dist/es/main.js", diff --git a/src/utils/createFetchOptions.js b/src/utils/createFetchOptions.js index 02381db..7009d4d 100644 --- a/src/utils/createFetchOptions.js +++ b/src/utils/createFetchOptions.js @@ -2,10 +2,15 @@ import createRequestHeaders from './createRequestHeaders'; import applyPlugins from './applyPlugins'; export default async ({ configuration, parameters } = {}) => { - const { getAccessToken, plugins: { request: requestPlugins = [] } = {}, version } = configuration; + const { + getAccessToken, + plugins: { request: requestPlugins = [] } = {}, + version, + additionalHeaders = {}, + } = configuration; return { - headers: await createRequestHeaders({ getAccessToken, version }), + headers: await createRequestHeaders({ getAccessToken, version, additionalHeaders }), body: JSON.stringify(applyPlugins(parameters, requestPlugins)), method: 'POST', }; diff --git a/src/utils/createRequestHeaders.js b/src/utils/createRequestHeaders.js index 5ee5f93..4ee1239 100644 --- a/src/utils/createRequestHeaders.js +++ b/src/utils/createRequestHeaders.js @@ -1,8 +1,9 @@ -export default async ({ getAccessToken, version } = {}) => { +export default async ({ getAccessToken, version, additionalHeaders } = {}) => { const accessToken = getAccessToken && (await getAccessToken()); return { 'Content-Type': 'application/json', ...(typeof accessToken !== 'undefined' && { Authorization: `Bearer ${accessToken}` }), ...(version && { 'X-Api-Version': version }), + ...additionalHeaders, }; }; diff --git a/src/utils/mergeConfigurations.js b/src/utils/mergeConfigurations.js index fe59ebc..070fa40 100644 --- a/src/utils/mergeConfigurations.js +++ b/src/utils/mergeConfigurations.js @@ -1,4 +1,5 @@ import mergePlugins from './mergePlugins'; +import mergeHeaders from './mergeHeaders'; export default ({ globalConfiguration = {}, localConfiguration = {} }) => { const { @@ -11,11 +12,13 @@ export default ({ globalConfiguration = {}, localConfiguration = {} }) => { const { version: localVersion, fetchAll } = localConfiguration; const plugins = mergePlugins(globalConfiguration.plugins, localConfiguration.plugins); + const additionalHeaders = mergeHeaders(globalConfiguration.additionalHeaders, localConfiguration.additionalHeaders); return { baseUrl, plugins, fetchAll, + additionalHeaders, ...((accessToken || getAccessToken) && { getAccessToken: getAccessToken || (() => accessToken) }), ...((localVersion || globalVersion) && { version: localVersion || globalVersion }), }; diff --git a/src/utils/mergeHeaders.js b/src/utils/mergeHeaders.js new file mode 100644 index 0000000..ba5cb73 --- /dev/null +++ b/src/utils/mergeHeaders.js @@ -0,0 +1,7 @@ +export default (...headers) => + headers.reduce((combined, headerConfiguration) => { + if (!headerConfiguration || typeof headerConfiguration !== 'object') { + return combined; + } + return { ...combined, ...headerConfiguration }; + }, {}); diff --git a/test/utils/mergeConfigurations.test.js b/test/utils/mergeConfigurations.test.js index 4a359cd..da8e039 100644 --- a/test/utils/mergeConfigurations.test.js +++ b/test/utils/mergeConfigurations.test.js @@ -14,12 +14,18 @@ describe(`merge configurations`, () => { response: [camelCase], }, version: '2018-09-20', + additionalHeaders: { + 'x-tl-feature-flags': 'core.elastified-companies', + }, }; const localConfiguration = { plugins: { request: [snakeCase], }, + additionalHeaders: { + 'x-some-other-header': 'test', + }, }; it(`should merge the configurations in a correct way`, () => { @@ -33,6 +39,10 @@ describe(`merge configurations`, () => { response: [camelCase], }, version: '2018-09-20', + additionalHeaders: { + 'x-tl-feature-flags': 'core.elastified-companies', + 'x-some-other-header': 'test', + }, }; expect(configuration).toEqual(expectedConfiguration); @@ -54,6 +64,9 @@ describe(`merge configurations`, () => { request: [snakeCase], response: [camelCase], }, + additionalHeaders: { + 'x-some-other-header': 'test', + }, }; expect(configuration).toEqual(expectedConfiguration); @@ -85,6 +98,10 @@ describe(`merge configurations`, () => { request: [snakeCase], response: [camelCase], }, + additionalHeaders: { + 'x-tl-feature-flags': 'core.elastified-companies', + 'x-some-other-header': 'test', + }, }; expect(configuration).toEqual(expectedConfiguration); @@ -105,6 +122,10 @@ describe(`merge configurations`, () => { }, fetchAll: true, version: '2018-11-20', + additionalHeaders: { + 'x-tl-feature-flags': 'core.elastified-companies', + 'x-some-other-header': 'test', + }, }; expect(configuration).toEqual(expectedConfiguration); @@ -124,6 +145,10 @@ describe(`merge configurations`, () => { response: [camelCase], }, version: '2018-09-20', + additionalHeaders: { + 'x-tl-feature-flags': 'core.elastified-companies', + 'x-some-other-header': 'test', + }, }; expect(configuration).toEqual(expectedConfiguration); diff --git a/test/utils/mergeHeaders.test.js b/test/utils/mergeHeaders.test.js new file mode 100644 index 0000000..889ca47 --- /dev/null +++ b/test/utils/mergeHeaders.test.js @@ -0,0 +1,83 @@ +import mergeHeaders from '../../src/utils/mergeHeaders'; + +describe(`merge headers`, () => { + it(`should merge the headers in a correct way`, () => { + const globalHeaders = { + 'x-header-1': 'headerValue1', + 'x-header-2': 'headerValue2', + }; + + const localHeaders = { + 'x-header-3': 'headerValue3', + 'x-header-4': 'headerValue4', + }; + + const headers = mergeHeaders(globalHeaders, localHeaders); + + const expectedHeaders = { + 'x-header-1': 'headerValue1', + 'x-header-2': 'headerValue2', + 'x-header-3': 'headerValue3', + 'x-header-4': 'headerValue4', + }; + + expect(headers).toEqual(expectedHeaders); + }); + + it(`should merge the headers in a correct way when one header object is given`, () => { + const globalHeaders = { + 'x-header-1': 'headerValue1', + 'x-header-2': 'headerValue2', + }; + + const headers = mergeHeaders(globalHeaders); + + const expectedHeaders = { + 'x-header-1': 'headerValue1', + 'x-header-2': 'headerValue2', + }; + + expect(headers).toEqual(expectedHeaders); + }); + + it(`should merge the headers in a correct way when one header object is invalid`, () => { + const globalHeaders = { + 'x-header-1': 'headerValue1', + 'x-header-2': 'headerValue2', + }; + + const localHeaders = 'invalid'; + + const headers = mergeHeaders(globalHeaders, localHeaders); + + const expectedHeaders = { + 'x-header-1': 'headerValue1', + 'x-header-2': 'headerValue2', + }; + + expect(headers).toEqual(expectedHeaders); + }); + + it(`should merge the headers in a correct way when header objects are undefined`, () => { + const headers = mergeHeaders(undefined, undefined); + + expect(headers).toEqual({}); + }); + it(`should merge the headers in a correct way when one header object is invalid`, () => { + const globalHeaders = { + 'x-header-1': 'headerValue1', + 'x-header-2': 'headerValue2', + }; + + const localHeaders = 'invalid'; + + const headers = mergeHeaders(globalHeaders, localHeaders); + + const expectedHeaders = { + 'x-header-1': 'headerValue1', + 'x-header-2': 'headerValue2', + }; + + expect(headers).toEqual(expectedHeaders); + }); +});