Skip to content

Commit

Permalink
fix: correct validation for Task call & start, and loosen up KeyValue…
Browse files Browse the repository at this point in the history
…Store getRecord options (apify#222)
  • Loading branch information
vladfrangu authored Jan 10, 2022
1 parent fc7566d commit 00a0d31
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 8 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
2.0.6 / 2022/01/03
===================
- For TypeScript users, the input type for Task#start and Task#call have been corrected
(these methods expect an optional input of an object, not an object or an array of objects)
- For TypeScript users, the overloads for KeyValueStore#getRecord have been relaxed and their order has been corrected

2.0.5 / 2022/01/03
===================
- For TypeScript users, the `WebhookEventType` type was corrected to represent its correct value.
Expand Down
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.0.5",
"version": "2.0.6",
"description": "Apify API client for JavaScript",
"main": "dist/index.js",
"module": "dist/index.mjs",
Expand Down
6 changes: 4 additions & 2 deletions src/resource_clients/actor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ export class ActorClient extends ResourceClient {
* https://docs.apify.com/api/v2#/reference/actors/run-collection/run-actor
*/
async start(input?: unknown, options: ActorStartOptions = {}): Promise<ActorRun> {
// input can be anything, pointless to validate
// input can be anything, so no point in validating it. E.g. if you set content-type to application/pdf
// then it will process input as a buffer.
ow(options, ow.object.exactShape({
build: ow.optional.string,
contentType: ow.optional.string,
Expand Down Expand Up @@ -105,7 +106,8 @@ export class ActorClient extends ResourceClient {
* https://docs.apify.com/api/v2#/reference/actors/run-collection/run-actor
*/
async call(input?: unknown, options: ActorStartOptions = {}): Promise<ActorRun> {
// input can be anything, pointless to validate
// input can be anything, so no point in validating it. E.g. if you set content-type to application/pdf
// then it will process input as a buffer.
ow(options, ow.object.exactShape({
build: ow.optional.string,
contentType: ow.optional.string,
Expand Down
11 changes: 8 additions & 3 deletions src/resource_clients/key_value_store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,10 @@ export class KeyValueStoreClient extends ResourceClient {
*/
async getRecord(key: string): Promise<KeyValueStoreRecord<JsonValue> | undefined>;

async getRecord(key: string, options: { buffer: true }): Promise<KeyValueStoreRecord<Buffer> | undefined>;

async getRecord(key: string, options: { stream: true }): Promise<KeyValueStoreRecord<ReadableStream> | undefined>;
async getRecord<Options extends KeyValueClientGetRecordOptions = KeyValueClientGetRecordOptions>(
key: string,
options: Options
): Promise<KeyValueStoreRecord<ReturnTypeFromOptions<Options>> | undefined>;

async getRecord(key: string, options: KeyValueClientGetRecordOptions = {}): Promise<KeyValueStoreRecord<unknown> | undefined> {
ow(key, ow.string);
Expand Down Expand Up @@ -231,3 +232,7 @@ export interface KeyValueStoreRecord<T> {
value: T;
contentType?: string;
}

export type ReturnTypeFromOptions<Options extends KeyValueClientGetRecordOptions> = Options['stream'] extends true
? ReadableStream
: Options['buffer'] extends true ? Buffer : JsonValue;
7 changes: 5 additions & 2 deletions src/resource_clients/task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export class TaskClient extends ResourceClient {
* Starts a task and immediately returns the Run object.
* https://docs.apify.com/api/v2#/reference/actor-tasks/run-collection/run-task
*/
async start(input: Dictionary | Dictionary[], options: TaskStartOptions = {}): Promise<ActorRun> {
async start(input: Dictionary, options: TaskStartOptions = {}): Promise<ActorRun> {
ow(input, ow.optional.object);
ow(options, ow.object.exactShape({
build: ow.optional.string,
Expand Down Expand Up @@ -83,6 +83,9 @@ export class TaskClient extends ResourceClient {
// Apify internal property. Tells the request serialization interceptor
// to stringify functions to JSON, instead of omitting them.
stringifyFunctions: true,
headers: {
'Content-Type': 'application/json',
},
};

const response = await this.httpClient.call(request);
Expand All @@ -94,7 +97,7 @@ export class TaskClient extends ResourceClient {
* It waits indefinitely, unless the `waitSecs` option is provided.
* https://docs.apify.com/api/v2#/reference/actor-tasks/run-collection/run-task
*/
async call(input: Dictionary | Dictionary[], options: TaskStartOptions = {}): Promise<ActorRun> {
async call(input: Dictionary, options: TaskStartOptions = {}): Promise<ActorRun> {
ow(input, ow.optional.object);
ow(options, ow.object.exactShape({
build: ow.optional.string,
Expand Down
35 changes: 35 additions & 0 deletions test/tasks.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,41 @@ describe('Task methods', () => {
validateRequest({}, { taskId });
});

test('start() with query parameters works', async () => {
const taskId = 'some-id';
const query = {
waitForFinish: 100,
memory: 512,
};

const res = await client.task(taskId).start(undefined, query);
expect(res.id).toEqual('run-task');
validateRequest(query, { taskId });

const browserRes = await page.evaluate((id, q) => client.task(id).start(undefined, q), taskId, query);
expect(browserRes).toEqual(res);
validateRequest(query, { taskId });
});

test.skip('start() works with pre-stringified JSON', async () => {
const taskId = 'some-id2';
const input = { foo: 'bar' };
const body = JSON.stringify(input);

const query = {
waitForFinish: 100,
memory: 512,
};

const res = await client.task(taskId).start(body, query);
expect(res.id).toEqual('run-task');
validateRequest(query, { taskId }, input);

const browserRes = await page.evaluate((id, i, opts) => client.task(id).start(i, opts), taskId, input, query);
expect(browserRes).toEqual(res);
validateRequest(query, { taskId }, input);
});

test('start() works with input and options overrides', async () => {
const taskId = 'some-id';
const input = { foo: 'bar' };
Expand Down

0 comments on commit 00a0d31

Please sign in to comment.