apify-client 3.0.0-beta.42 → 3.0.0-beta.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bundle.js +10 -10
- package/dist/bundle.js.map +1 -1
- package/dist/resource_clients/run.d.ts +55 -0
- package/dist/resource_clients/run.js +95 -1
- package/package.json +1 -1
|
@@ -4,6 +4,7 @@ import type { ApiClientOptionsWithOptionalResourcePath } from '../base/api_clien
|
|
|
4
4
|
import { ResourceClient } from '../base/resource_client.js';
|
|
5
5
|
import type { ApifyResponse } from '../http_clients/index.js';
|
|
6
6
|
import type { TimeoutOptions } from '../timeouts.js';
|
|
7
|
+
import type { PaginationOptions } from '../utils.js';
|
|
7
8
|
import type { ActorInput, ActorRun } from './actor.js';
|
|
8
9
|
import { DatasetClient } from './dataset.js';
|
|
9
10
|
import { KeyValueStoreClient } from './key_value_store.js';
|
|
@@ -294,6 +295,41 @@ export declare class RunClient extends ResourceClient {
|
|
|
294
295
|
* @since Added in 2.20.0
|
|
295
296
|
*/
|
|
296
297
|
getStreamedLog(options?: GetStreamedLogOptions): Promise<StreamedLog | undefined>;
|
|
298
|
+
/**
|
|
299
|
+
* Iterates over the items of the run's default dataset while the run is still producing them.
|
|
300
|
+
*
|
|
301
|
+
* While the run has not finished, each poll yields the rows below the dataset's `itemCount` and then waits up to
|
|
302
|
+
* `pollIntervalSecs` for the run to finish, so the last rows are read as soon as it does. Each page is requested
|
|
303
|
+
* with a `limit` that ends at `itemCount`, so it covers exactly the rows it asks for, whatever the filters or
|
|
304
|
+
* `unwind` do to the items. `itemCount` lags a few seconds behind
|
|
305
|
+
* the pushed items, so once the run reaches a terminal status, the rows past it are read a page at a time until
|
|
306
|
+
* none are left, and the iterator returns. On a `lastRun()` client, the iterator sticks to the run that its first
|
|
307
|
+
* request resolves to.
|
|
308
|
+
*
|
|
309
|
+
* @param options - Iteration options
|
|
310
|
+
* @param options.offset - Number of rows to skip from the beginning. Default is 0.
|
|
311
|
+
* @param options.limit - Maximum number of dataset rows to scan. Fewer items are yielded when filters drop some,
|
|
312
|
+
* more when `unwind` splits a row into several. Default is no limit.
|
|
313
|
+
* @param options.clean - If `true`, returns only non-empty items and skips hidden fields. Default is `false`.
|
|
314
|
+
* @param options.fields - Array of field names to include in the results. Omits all other fields.
|
|
315
|
+
* @param options.omit - Array of field names to exclude from the results.
|
|
316
|
+
* @param options.unwind - Array of field names to unwind. Each array value creates a separate item.
|
|
317
|
+
* @param options.skipEmpty - If `true`, skips empty items. Default is `false`.
|
|
318
|
+
* @param options.skipHidden - If `true`, skips hidden fields (fields starting with `#`). Default is `false`.
|
|
319
|
+
* @param options.chunkSize - Maximum number of dataset rows requested per API call. Default is 1000.
|
|
320
|
+
* @param options.pollIntervalSecs - How long to wait for the run to finish between polls, in seconds. Default is 5.
|
|
321
|
+
* @param options.timeoutSecs - Timeout for each API request. Default is `'long'`.
|
|
322
|
+
* @returns An async iterable of the dataset items
|
|
323
|
+
* @see https://docs.apify.com/api/v2/dataset-items-get
|
|
324
|
+
*
|
|
325
|
+
* @example
|
|
326
|
+
* ```javascript
|
|
327
|
+
* for await (const item of client.run('run-id').iterateDatasetItems()) {
|
|
328
|
+
* console.log(item);
|
|
329
|
+
* }
|
|
330
|
+
* ```
|
|
331
|
+
*/
|
|
332
|
+
iterateDatasetItems<Data extends Record<string | number, any> = Record<string | number, unknown>>(options?: RunIterateDatasetItemsOptions): AsyncGenerator<Data, void, undefined>;
|
|
297
333
|
}
|
|
298
334
|
/**
|
|
299
335
|
* Options for getting a streamed log.
|
|
@@ -386,3 +422,22 @@ export interface RunWaitForFinishOptions extends TimeoutOptions {
|
|
|
386
422
|
*/
|
|
387
423
|
waitSecs?: number;
|
|
388
424
|
}
|
|
425
|
+
/**
|
|
426
|
+
* Options for iterating over the items of a run's default dataset while the run is still producing them.
|
|
427
|
+
*/
|
|
428
|
+
export interface RunIterateDatasetItemsOptions extends PaginationOptions, TimeoutOptions {
|
|
429
|
+
/** If `true`, returns only non-empty items and skips hidden fields. */
|
|
430
|
+
clean?: boolean;
|
|
431
|
+
/** Field names to include in the items. Omits all other fields. */
|
|
432
|
+
fields?: string[];
|
|
433
|
+
/** Field names to exclude from the items. */
|
|
434
|
+
omit?: string[];
|
|
435
|
+
/** If `true`, skips empty items. */
|
|
436
|
+
skipEmpty?: boolean;
|
|
437
|
+
/** If `true`, skips hidden fields (fields starting with `#`). */
|
|
438
|
+
skipHidden?: boolean;
|
|
439
|
+
/** Field names to unwind. Each array value creates a separate item. */
|
|
440
|
+
unwind?: string[];
|
|
441
|
+
/** How long to wait for the run to finish between polls, in seconds. Default is 5. */
|
|
442
|
+
pollIntervalSecs?: number;
|
|
443
|
+
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { ACT_JOB_TERMINAL_STATUSES } from '@apify/consts';
|
|
2
3
|
import { LEVELS, Log } from '@apify/log';
|
|
3
4
|
import { ResourceClient } from '../base/resource_client.js';
|
|
4
5
|
import * as schemas from '../schemas.js';
|
|
5
6
|
import { optionalSignalSchema, optionalTimeoutSchema, timeoutOptionsSchema, timeoutOptionsShape } from '../timeouts.js';
|
|
6
7
|
import { runtime } from '#runtime';
|
|
7
|
-
import { anyObjectSchema, parseArgument, parseResponse } from '../utils.js';
|
|
8
|
+
import { anyObjectSchema, paginationOptionsShape, parseArgument, parseResponse, SCANNED_COUNT } from '../utils.js';
|
|
8
9
|
import { DatasetClient } from './dataset.js';
|
|
9
10
|
import { KeyValueStoreClient } from './key_value_store.js';
|
|
10
11
|
import { LogClient, LoggerActorRedirect, StreamedLog } from './log.js';
|
|
@@ -34,6 +35,18 @@ const chargeOptionsSchema = z.strictObject({
|
|
|
34
35
|
...timeoutOptionsShape,
|
|
35
36
|
});
|
|
36
37
|
const waitForFinishOptionsSchema = z.strictObject({ waitSecs: z.number().optional(), ...timeoutOptionsShape });
|
|
38
|
+
const iterateDatasetItemsOptionsSchema = z.strictObject({
|
|
39
|
+
clean: z.boolean().optional(),
|
|
40
|
+
fields: z.array(z.string()).optional(),
|
|
41
|
+
omit: z.array(z.string()).optional(),
|
|
42
|
+
...paginationOptionsShape,
|
|
43
|
+
skipEmpty: z.boolean().optional(),
|
|
44
|
+
skipHidden: z.boolean().optional(),
|
|
45
|
+
unwind: z.array(z.string()).optional(),
|
|
46
|
+
pollIntervalSecs: z.number().min(0).optional(),
|
|
47
|
+
...timeoutOptionsShape,
|
|
48
|
+
});
|
|
49
|
+
const DEFAULT_ITERATE_CHUNK_SIZE = 1000;
|
|
37
50
|
/**
|
|
38
51
|
* Client for managing a specific Actor run.
|
|
39
52
|
*
|
|
@@ -451,4 +464,85 @@ export class RunClient extends ResourceClient {
|
|
|
451
464
|
}
|
|
452
465
|
return new StreamedLog({ logClient: this.log(), toLog, fromStart, signal });
|
|
453
466
|
}
|
|
467
|
+
/**
|
|
468
|
+
* Iterates over the items of the run's default dataset while the run is still producing them.
|
|
469
|
+
*
|
|
470
|
+
* While the run has not finished, each poll yields the rows below the dataset's `itemCount` and then waits up to
|
|
471
|
+
* `pollIntervalSecs` for the run to finish, so the last rows are read as soon as it does. Each page is requested
|
|
472
|
+
* with a `limit` that ends at `itemCount`, so it covers exactly the rows it asks for, whatever the filters or
|
|
473
|
+
* `unwind` do to the items. `itemCount` lags a few seconds behind
|
|
474
|
+
* the pushed items, so once the run reaches a terminal status, the rows past it are read a page at a time until
|
|
475
|
+
* none are left, and the iterator returns. On a `lastRun()` client, the iterator sticks to the run that its first
|
|
476
|
+
* request resolves to.
|
|
477
|
+
*
|
|
478
|
+
* @param options - Iteration options
|
|
479
|
+
* @param options.offset - Number of rows to skip from the beginning. Default is 0.
|
|
480
|
+
* @param options.limit - Maximum number of dataset rows to scan. Fewer items are yielded when filters drop some,
|
|
481
|
+
* more when `unwind` splits a row into several. Default is no limit.
|
|
482
|
+
* @param options.clean - If `true`, returns only non-empty items and skips hidden fields. Default is `false`.
|
|
483
|
+
* @param options.fields - Array of field names to include in the results. Omits all other fields.
|
|
484
|
+
* @param options.omit - Array of field names to exclude from the results.
|
|
485
|
+
* @param options.unwind - Array of field names to unwind. Each array value creates a separate item.
|
|
486
|
+
* @param options.skipEmpty - If `true`, skips empty items. Default is `false`.
|
|
487
|
+
* @param options.skipHidden - If `true`, skips hidden fields (fields starting with `#`). Default is `false`.
|
|
488
|
+
* @param options.chunkSize - Maximum number of dataset rows requested per API call. Default is 1000.
|
|
489
|
+
* @param options.pollIntervalSecs - How long to wait for the run to finish between polls, in seconds. Default is 5.
|
|
490
|
+
* @param options.timeoutSecs - Timeout for each API request. Default is `'long'`.
|
|
491
|
+
* @returns An async iterable of the dataset items
|
|
492
|
+
* @see https://docs.apify.com/api/v2/dataset-items-get
|
|
493
|
+
*
|
|
494
|
+
* @example
|
|
495
|
+
* ```javascript
|
|
496
|
+
* for await (const item of client.run('run-id').iterateDatasetItems()) {
|
|
497
|
+
* console.log(item);
|
|
498
|
+
* }
|
|
499
|
+
* ```
|
|
500
|
+
*/
|
|
501
|
+
async *iterateDatasetItems(options = {}) {
|
|
502
|
+
const { offset, limit, chunkSize, pollIntervalSecs = 5, timeoutSecs = 'long', signal, ...itemOptions } = parseArgument(options, iterateDatasetItemsOptionsSchema, 'RunIterateDatasetItemsOptions');
|
|
503
|
+
const pageSize = chunkSize || DEFAULT_ITERATE_CHUNK_SIZE;
|
|
504
|
+
let position = offset ?? 0;
|
|
505
|
+
const end = limit ? position + limit : undefined;
|
|
506
|
+
let run = await this.get({ timeoutSecs, signal });
|
|
507
|
+
// A `lastRun()` client resolves `runs/last` on each request, so a newer run would swap the dataset mid-iteration.
|
|
508
|
+
const runClient = run && run.id !== this.id ? this.apifyClient.run(run.id) : this;
|
|
509
|
+
const datasetClient = runClient.dataset();
|
|
510
|
+
const listPage = async (pageOffset, pageLimit) => datasetClient.listItems({ ...itemOptions, offset: pageOffset, limit: pageLimit, timeoutSecs, signal });
|
|
511
|
+
while (true) {
|
|
512
|
+
const isFinished = !run || ACT_JOB_TERMINAL_STATUSES.includes(run.status);
|
|
513
|
+
const dataset = await datasetClient.get({ timeoutSecs, signal });
|
|
514
|
+
let itemCount = dataset?.itemCount ?? 0;
|
|
515
|
+
if (end !== undefined)
|
|
516
|
+
itemCount = Math.min(itemCount, end);
|
|
517
|
+
while (position < itemCount) {
|
|
518
|
+
const pageLimit = Math.min(pageSize, itemCount - position);
|
|
519
|
+
const page = await listPage(position, pageLimit);
|
|
520
|
+
yield* page.items;
|
|
521
|
+
position += pageLimit;
|
|
522
|
+
}
|
|
523
|
+
if (end !== undefined && position >= end)
|
|
524
|
+
return;
|
|
525
|
+
if (isFinished)
|
|
526
|
+
break;
|
|
527
|
+
run = await runClient.waitForFinish({ waitSecs: pollIntervalSecs, timeoutSecs, signal });
|
|
528
|
+
}
|
|
529
|
+
const { clean, skipEmpty, unwind } = itemOptions;
|
|
530
|
+
while (true) {
|
|
531
|
+
const pageLimit = end !== undefined ? Math.min(pageSize, end - position) : pageSize;
|
|
532
|
+
const page = await listPage(position, pageLimit);
|
|
533
|
+
yield* page.items;
|
|
534
|
+
// Only an empty page marks the end, as filters can shorten a full one. A page that `clean`, `skipEmpty` or
|
|
535
|
+
// `unwind` emptied past a lagging `itemCount` reports no scanned rows either, so a plain read checks.
|
|
536
|
+
const isEmpty = page.items.length === 0 && !page[SCANNED_COUNT];
|
|
537
|
+
if (isEmpty &&
|
|
538
|
+
(!(clean || skipEmpty || unwind?.length) ||
|
|
539
|
+
(await datasetClient.listItems({ offset: position, limit: 1, timeoutSecs, signal })).items
|
|
540
|
+
.length === 0)) {
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
position += pageLimit;
|
|
544
|
+
if (end !== undefined && position >= end)
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
454
548
|
}
|