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
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "apify-client",
"version": "2.9.7",
"version": "2.9.8",
"description": "Apify API client for JavaScript",
"main": "dist/index.js",
"module": "dist/index.mjs",
Expand Down
3 changes: 3 additions & 0 deletions src/apify_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export class ApifyClient {
requestInterceptors: ow.optional.array,
timeoutSecs: ow.optional.number,
token: ow.optional.string,
userAgentSuffix: ow.optional.any(ow.string, ow.array.ofType(ow.string)),
}));

const {
Expand All @@ -77,6 +78,7 @@ export class ApifyClient {
timeoutSecs,
logger: this.logger,
token: this.token,
userAgentSuffix: options.userAgentSuffix,
});
}

Expand Down Expand Up @@ -341,4 +343,5 @@ export interface ApifyClientOptions {
/** @default 360 */
timeoutSecs?: number;
token?: string;
userAgentSuffix?: string | string[];
}
20 changes: 11 additions & 9 deletions src/http_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import KeepAliveAgent from 'agentkeepalive';
import retry, { RetryFunction } from 'async-retry';
import axios, {
AxiosError,
AxiosHeaders,
AxiosInstance,
AxiosRequestConfig,
InternalAxiosRequestConfig,
AxiosResponse,
AxiosHeaders,
InternalAxiosRequestConfig,
} from 'axios';

import { ApifyApiError } from './apify_api_error';
Expand All @@ -21,12 +21,7 @@ import {
responseInterceptors,
} from './interceptors';
import { Statistics } from './statistics';
import {
isNode,
getVersionData,
cast,
isStream,
} from './utils';
import { asArray, cast, getVersionData, isNode, isStream } from './utils';

const { version } = getVersionData();

Expand Down Expand Up @@ -116,7 +111,12 @@ export class HttpClient {
if (isNode()) {
// Works only in Node. Cannot be set in browser
const isAtHome = !!process.env[APIFY_ENV_VARS.IS_AT_HOME];
const userAgent = `ApifyClient/${version} (${os.type()}; Node/${process.version}); isAtHome/${isAtHome}`;
let userAgent = `ApifyClient/${version} (${os.type()}; Node/${process.version}); isAtHome/${isAtHome}`;

if (options.userAgentSuffix) {
userAgent += `; ${asArray(options.userAgentSuffix).join('; ')}`;
}

this.axios.defaults.headers['User-Agent'] = userAgent;
}

Expand Down Expand Up @@ -288,4 +288,6 @@ export interface HttpClientOptions {
logger: Log;
token?: string;
workflowKey?: string;
/** @internal */
userAgentSuffix?: string | string[];
}
8 changes: 8 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,14 @@ export function cast<T>(input: unknown): T {
return input as T;
}

export function asArray<T>(value: T | T[]): T[] {
if (Array.isArray(value)) {
return value;
}

return [value];
}

export type Dictionary<T = unknown> = Record<PropertyKey, T>;

export type DistributiveOptional<T, K extends keyof T> = T extends any ? Omit<T, K> & Partial<Pick<T, K>> : never;
26 changes: 8 additions & 18 deletions test/http_client.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,7 @@ describe('HttpClient', () => {
baseUrl,
timeoutSecs: 1,
maxRetries: 0,
requestInterceptors: [(config) => {
config.headers = {};
return config;
}],
userAgentSuffix: ['SDK/3.1.1', 'Crawlee/3.11.5'],
});
});
afterEach(async () => {
Expand All @@ -41,20 +38,13 @@ describe('HttpClient', () => {
const context = { delayMillis: 3000 };
const resourceId = Buffer.from(JSON.stringify(context)).toString('hex');

expect.assertions(2);
try {
await client.actor(resourceId).get();
} catch (err) {
expect(err.message).toMatch('timeout of 1000ms exceeded');
}
await expect(client.actor(resourceId).get()).rejects.toThrow('timeout of 1000ms exceeded');
const ua = mockServer.getLastRequest().headers['user-agent'];
expect(ua).toMatch(/ApifyClient\/\d+\.\d+\.\d+/);
expect(ua).toMatch('isAtHome/false; SDK/3.1.1; Crawlee/3.11.5');

try {
const r = await page.evaluate((rId) => client.task(rId).get(), resourceId);
expect(r).toBeDefined();
} catch (err) {
expect(err).toBeInstanceOf(Error);
// this is failing after axios upgrade, the error is returned with a wrong name and message
// expect(err.message).toMatch('timeout of 1000ms exceeded');
}
await expect(page.evaluate((rId) => client.task(rId).get(), resourceId)).rejects.toThrow();
// this is failing after axios upgrade, the error is returned with a wrong name and message
// expect(err.message).toMatch('timeout of 1000ms exceeded');
});
});