Skip to content

Commit 91cd246

Browse files
authored
feat: warn about actor not being initialized before using storage methods (#126)
1 parent 485bdc1 commit 91cd246

1 file changed

Lines changed: 57 additions & 0 deletions

File tree

packages/apify/src/actor.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,17 @@ export class Actor<Data extends Dictionary = Dictionary> {
6868
*/
6969
readonly eventManager: EventManager;
7070

71+
/**
72+
* Whether the actor instance was initialized. This is set by calling {@apilink Actor.init}.
73+
*/
74+
initialized = false;
75+
76+
/**
77+
* Set if the actor called a method that requires the instance to be initialized, but did not do so.
78+
* A call to `init` after this warning is emitted is considered an invalid state and will throw an error.
79+
*/
80+
private warnedAboutMissingInitCall = false;
81+
7182
constructor(options: ConfigurationOptions = {}) {
7283
// use default configuration object if nothing overridden (it fallbacks to env vars)
7384
this.config = Object.keys(options).length === 0 ? Configuration.getGlobalConfig() : new Configuration(options);
@@ -165,6 +176,20 @@ export class Actor<Data extends Dictionary = Dictionary> {
165176
* @ignore
166177
*/
167178
async init(options: InitOptions = {}): Promise<void> {
179+
if (this.initialized) {
180+
return;
181+
}
182+
183+
// If the warning about forgotten init call was emitted, we will not continue the init procedure.
184+
if (this.warnedAboutMissingInitCall) {
185+
throw new Error([
186+
'Actor.init() was called after a method that would access a storage client was used.',
187+
'This in an invalid state. Please make sure to call Actor.init() before such methods are called.',
188+
].join('\n'));
189+
}
190+
191+
this.initialized = true;
192+
168193
logSystemInfo();
169194
printOutdatedSdkWarning();
170195

@@ -523,6 +548,8 @@ export class Actor<Data extends Dictionary = Dictionary> {
523548
* @ignore
524549
*/
525550
async pushData(item: Data | Data[]): Promise<void> {
551+
this._ensureActorInit('pushData');
552+
526553
const dataset = await this.openDataset();
527554
return dataset.pushData(item);
528555
}
@@ -551,6 +578,8 @@ export class Actor<Data extends Dictionary = Dictionary> {
551578
forceCloud: ow.optional.boolean,
552579
}));
553580

581+
this._ensureActorInit('openDataset');
582+
554583
return this._openStorage<Dataset<Data>>(Dataset, datasetIdOrName, options);
555584
}
556585

@@ -583,6 +612,8 @@ export class Actor<Data extends Dictionary = Dictionary> {
583612
* @ignore
584613
*/
585614
async getValue<T = unknown>(key: string): Promise<T | null> {
615+
this._ensureActorInit('getValue');
616+
586617
const store = await this.openKeyValueStore();
587618
return store.getValue<T>(key);
588619
}
@@ -619,6 +650,8 @@ export class Actor<Data extends Dictionary = Dictionary> {
619650
* @ignore
620651
*/
621652
async setValue<T>(key: string, value: T | null, options: RecordOptions = {}): Promise<void> {
653+
this._ensureActorInit('setValue');
654+
622655
const store = await this.openKeyValueStore();
623656
return store.setValue(key, value, options);
624657
}
@@ -653,6 +686,8 @@ export class Actor<Data extends Dictionary = Dictionary> {
653686
* @ignore
654687
*/
655688
async getInput<T = Dictionary | string | Buffer>(): Promise<T | null> {
689+
this._ensureActorInit('getInput');
690+
656691
const inputSecretsPrivateKeyFile = this.config.get('inputSecretsPrivateKeyFile');
657692
const inputSecretsPrivateKeyPassphrase = this.config.get('inputSecretsPrivateKeyPassphrase');
658693
const input = await this.getValue<T>(this.config.get('inputKey'));
@@ -687,6 +722,8 @@ export class Actor<Data extends Dictionary = Dictionary> {
687722
forceCloud: ow.optional.boolean,
688723
}));
689724

725+
this._ensureActorInit('openKeyValueStore');
726+
690727
return this._openStorage(KeyValueStore, storeIdOrName, options);
691728
}
692729

@@ -713,6 +750,8 @@ export class Actor<Data extends Dictionary = Dictionary> {
713750
forceCloud: ow.optional.boolean,
714751
}));
715752

753+
this._ensureActorInit('openRequestQueue');
754+
716755
return this._openStorage(RequestQueue, queueIdOrName, options);
717756
}
718757

@@ -1374,6 +1413,24 @@ export class Actor<Data extends Dictionary = Dictionary> {
13741413
const client = options.forceCloud ? this.apifyClient : undefined;
13751414
return StorageManager.openStorage<T>(storageClass, id, client, this.config);
13761415
}
1416+
1417+
private _ensureActorInit(methodCalled: string) {
1418+
// If we already warned the user once, don't do it again to prevent spam
1419+
if (this.warnedAboutMissingInitCall) {
1420+
return;
1421+
}
1422+
1423+
if (this.initialized) {
1424+
return;
1425+
}
1426+
1427+
this.warnedAboutMissingInitCall = true;
1428+
1429+
log.warning([
1430+
`Actor.${methodCalled}() was called but the actor instance was not initialized.`,
1431+
'Did you forget to call Actor.init()?',
1432+
].join('\n'));
1433+
}
13771434
}
13781435

13791436
export interface InitOptions {

0 commit comments

Comments
 (0)