Skip to content
Draft
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
3 changes: 1 addition & 2 deletions auth/authenticators/basic-authenticator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
* limitations under the License.
*/

import extend from 'extend';
import { computeBasicAuthHeader, validateInput } from '../utils/helpers';
import { Authenticator } from './authenticator';
import { AuthenticateOptions } from './authenticator-interface';
Expand Down Expand Up @@ -72,7 +71,7 @@ export class BasicAuthenticator extends Authenticator {
*/
public authenticate(requestOptions: AuthenticateOptions): Promise<void> {
return new Promise((resolve) => {
requestOptions.headers = extend(true, {}, requestOptions.headers, this.authHeader);
requestOptions.headers = { ...requestOptions.headers, ...this.authHeader };
logger.debug(`Authenticated outbound request (type=${this.authenticationType()})`);
resolve();
});
Expand Down
3 changes: 1 addition & 2 deletions auth/authenticators/bearer-token-authenticator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
* limitations under the License.
*/

import extend from 'extend';
import { validateInput } from '../utils/helpers';
import { Authenticator } from './authenticator';
import { AuthenticateOptions } from './authenticator-interface';
Expand Down Expand Up @@ -76,7 +75,7 @@ export class BearerTokenAuthenticator extends Authenticator {
public authenticate(requestOptions: AuthenticateOptions): Promise<void> {
return new Promise((resolve) => {
const authHeader = { Authorization: `Bearer ${this.bearerToken}` };
requestOptions.headers = extend(true, {}, requestOptions.headers, authHeader);
requestOptions.headers = { ...requestOptions.headers, ...authHeader };
logger.debug(`Authenticated outbound request (type=${this.authenticationType()})`);
resolve();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
* limitations under the License.
*/

import extend from 'extend';
import { OutgoingHttpHeaders } from 'http';
import { JwtTokenManager } from '../token-managers/jwt-token-manager';
import { Authenticator } from './authenticator';
Expand Down Expand Up @@ -89,7 +88,7 @@ export class TokenRequestBasedAuthenticatorImmutable extends Authenticator {
public authenticate(requestOptions: AuthenticateOptions): Promise<void> {
return this.tokenManager.getToken().then((token) => {
const authHeader = { Authorization: `Bearer ${token}` };
requestOptions.headers = extend(true, {}, requestOptions.headers, authHeader);
requestOptions.headers = { ...requestOptions.headers, ...authHeader };
logger.debug(`Authenticated outbound request (type=${this.authenticationType()})`);
});
}
Expand Down
3 changes: 1 addition & 2 deletions auth/token-managers/cp4d-token-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
* limitations under the License.
*/

import extend from 'extend';
import { validateInput } from '../utils/helpers';
import { buildUserAgent } from '../../lib/build-user-agent';
import { JwtTokenManager, JwtTokenManagerOptions } from './jwt-token-manager';
Expand Down Expand Up @@ -118,7 +117,7 @@ export class Cp4dTokenManager extends JwtTokenManager {
api_key: this.apikey,
},
method: 'POST',
headers: extend(true, {}, this.headers, requiredHeaders),
headers: { ...this.headers, ...requiredHeaders },
rejectUnauthorized: !this.disableSslVerification,
},
};
Expand Down
3 changes: 1 addition & 2 deletions auth/token-managers/iam-request-based-token-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
* limitations under the License.
*/

import extend from 'extend';
import { OutgoingHttpHeaders } from 'http';
import logger from '../../lib/logger';
import { computeBasicAuthHeader, getCurrentTime, onlyOne, removeSuffix } from '../utils/helpers';
Expand Down Expand Up @@ -165,7 +164,7 @@ export class IamRequestBasedTokenManager extends JwtTokenManager {
options: {
url: this.url + OPERATION_PATH,
method: 'POST',
headers: extend(true, {}, this.headers, requiredHeaders),
headers: { ...this.headers, ...requiredHeaders },
form: this.formData,
rejectUnauthorized: !this.disableSslVerification,
},
Expand Down
3 changes: 1 addition & 2 deletions auth/token-managers/mcsp-token-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
* limitations under the License.
*/

import extend from 'extend';
import { validateInput } from '../utils/helpers';
import { buildUserAgent } from '../../lib/build-user-agent';
import { JwtTokenManager, JwtTokenManagerOptions } from './jwt-token-manager';
Expand Down Expand Up @@ -96,7 +95,7 @@ export class McspTokenManager extends JwtTokenManager {
apikey: this.apikey,
},
method: 'POST',
headers: extend(true, {}, this.headers, requiredHeaders),
headers: { ...this.headers, ...requiredHeaders },
rejectUnauthorized: !this.disableSslVerification,
},
};
Expand Down
3 changes: 1 addition & 2 deletions auth/token-managers/mcspv2-token-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
* limitations under the License.
*/

import extend from 'extend';
import { validateInput } from '../utils/helpers';
import { buildUserAgent } from '../../lib/build-user-agent';
import { JwtTokenManager, JwtTokenManagerOptions } from './jwt-token-manager';
Expand Down Expand Up @@ -174,7 +173,7 @@ export class McspV2TokenManager extends JwtTokenManager {
'User-Agent': this.userAgent,
};

const requestHeaders = extend(true, {}, this.headers, requiredHeaders);
const requestHeaders = { ...this.headers, ...requiredHeaders };

// The keys used here must match the path parameter references in PATH_TEMPLATE above.
const pathParams = {
Expand Down
11 changes: 4 additions & 7 deletions lib/base-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
* limitations under the License.
*/

import extend from 'extend';
import type { CookieJar } from 'tough-cookie';
import { OutgoingHttpHeaders } from 'http';
import { AuthenticatorInterface, checkCredentials, readExternalSources } from '../auth';
Expand Down Expand Up @@ -284,12 +283,10 @@ export class BaseService {
const userAgent = {
'User-Agent': this.defaultUserAgent,
};
parameters.defaultOptions.headers = extend(
true,
{},
userAgent,
parameters.defaultOptions.headers
);
parameters.defaultOptions.headers = {
...userAgent,
...parameters.defaultOptions.headers,
};

return this.authenticator.authenticate(parameters.defaultOptions).then(() =>
// resolve() handles rejection as well, so resolving the result of sendRequest should allow for proper handling later
Expand Down
4 changes: 1 addition & 3 deletions lib/cookie-support.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
*/

import { Axios, AxiosResponse, InternalAxiosRequestConfig, isAxiosError } from 'axios';
import extend from 'extend';
import { Cookie, CookieJar } from 'tough-cookie';
import logger from './logger';

Expand All @@ -34,8 +33,7 @@
const cookieHeaderValue = await cookieJar.getCookieString(config.url);
if (cookieHeaderValue) {
logger.debug('CookieInterceptor: setting cookie header');
const cookieHeader = { cookie: cookieHeaderValue };
config.headers = extend(true, {}, config.headers, cookieHeader);
config.headers['cookie'] = cookieHeaderValue;

Check failure on line 36 in lib/cookie-support.ts

View workflow job for this annotation

GitHub Actions / build-test (node v22)

["cookie"] is better written in dot notation

Check failure on line 36 in lib/cookie-support.ts

View workflow job for this annotation

GitHub Actions / build-test (node v24)

["cookie"] is better written in dot notation

Check failure on line 36 in lib/cookie-support.ts

View workflow job for this annotation

GitHub Actions / build-test (node v20)

["cookie"] is better written in dot notation
} else {
logger.debug(`CookieInterceptor: no cookies for: ${config.url}`);
}
Expand Down
21 changes: 10 additions & 11 deletions lib/request-wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import axios, {
InternalAxiosRequestConfig,
} from 'axios';
import * as rax from 'retry-axios';
import extend from 'extend';
import FormData from 'form-data';
import { OutgoingHttpHeaders } from 'http';
import { Agent } from 'https';
Expand Down Expand Up @@ -70,18 +69,16 @@ export class RequestWrapper {
private raxConfig: rax.RetryConfig;

constructor(axiosOptions?) {
axiosOptions = axiosOptions || {};
axiosOptions ??= {};
this.compressRequestData = Boolean(axiosOptions.enableGzipCompression);

// override a couple axios defaults
// override a couple axios defaults then merge axios config into default
const axiosConfig: AxiosRequestConfig = {
maxContentLength: -1,
maxBodyLength: Infinity,
...axiosOptions,
};

// merge axios config into default
extend(true, axiosConfig, axiosOptions);

// if the user explicitly sets `disableSslVerification` to true,
// `rejectUnauthorized` must be set to false in the https agent
if (axiosOptions.disableSslVerification === true) {
Expand Down Expand Up @@ -248,9 +245,11 @@ export class RequestWrapper {
* @throws Error
*/
public async sendRequest(parameters): Promise<any> {
const options = extend(true, {}, parameters.defaultOptions, parameters.options);
const { path, body, form, formData, qs, method, serviceUrl, axiosOptions } = options;
let { headers, url } = options;
const options = { ...parameters.defaultOptions, ...parameters.options };
let headers = { ...parameters.defaultOptions?.headers, ...parameters.options?.headers };
const qs = { ...parameters.defaultOptions?.qs, ...parameters.options?.qs };
const { path, body, form, formData, method, serviceUrl, axiosOptions } = options;
let { url } = options;
Comment on lines +248 to +252
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this change if parameters.options contains nested objects, they get replaced entirely rather than merged with. This differs from the previous deep merge behaviour provided by extend.

I added a new test case in the main branch testing this specific edge case.


const multipartForm = new FormData();

Expand Down Expand Up @@ -313,11 +312,11 @@ export class RequestWrapper {
if (formData) {
data = multipartForm;
// form-data generates headers that MUST be included or the request will fail
headers = extend(true, {}, headers, multipartForm.getHeaders());
headers = { ...headers, ...multipartForm.getHeaders() };
}

// accept gzip encoded responses if Accept-Encoding is not already set
headers['Accept-Encoding'] = headers['Accept-Encoding'] || 'gzip';
headers['Accept-Encoding'] ||= 'gzip';

// compress request body data if enabled
if (this.compressRequestData) {
Expand Down
Loading