Skip to content

Commit

Permalink
feat: warn about actor not being initialized before using storage met…
Browse files Browse the repository at this point in the history
…hods (#126)
  • Loading branch information
vladfrangu authored Nov 13, 2022
1 parent 485bdc1 commit 91cd246
Showing 1 changed file with 57 additions and 0 deletions.
57 changes: 57 additions & 0 deletions packages/apify/src/actor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,17 @@ export class Actor<Data extends Dictionary = Dictionary> {
*/
readonly eventManager: EventManager;

/**
* Whether the actor instance was initialized. This is set by calling {@apilink Actor.init}.
*/
initialized = false;

/**
* Set if the actor called a method that requires the instance to be initialized, but did not do so.
* A call to `init` after this warning is emitted is considered an invalid state and will throw an error.
*/
private warnedAboutMissingInitCall = false;

constructor(options: ConfigurationOptions = {}) {
// use default configuration object if nothing overridden (it fallbacks to env vars)
this.config = Object.keys(options).length === 0 ? Configuration.getGlobalConfig() : new Configuration(options);
Expand Down Expand Up @@ -165,6 +176,20 @@ export class Actor<Data extends Dictionary = Dictionary> {
* @ignore
*/
async init(options: InitOptions = {}): Promise<void> {
if (this.initialized) {
return;
}

// If the warning about forgotten init call was emitted, we will not continue the init procedure.
if (this.warnedAboutMissingInitCall) {
throw new Error([
'Actor.init() was called after a method that would access a storage client was used.',
'This in an invalid state. Please make sure to call Actor.init() before such methods are called.',
].join('\n'));
}

this.initialized = true;

logSystemInfo();
printOutdatedSdkWarning();

Expand Down Expand Up @@ -523,6 +548,8 @@ export class Actor<Data extends Dictionary = Dictionary> {
* @ignore
*/
async pushData(item: Data | Data[]): Promise<void> {
this._ensureActorInit('pushData');

const dataset = await this.openDataset();
return dataset.pushData(item);
}
Expand Down Expand Up @@ -551,6 +578,8 @@ export class Actor<Data extends Dictionary = Dictionary> {
forceCloud: ow.optional.boolean,
}));

this._ensureActorInit('openDataset');

return this._openStorage<Dataset<Data>>(Dataset, datasetIdOrName, options);
}

Expand Down Expand Up @@ -583,6 +612,8 @@ export class Actor<Data extends Dictionary = Dictionary> {
* @ignore
*/
async getValue<T = unknown>(key: string): Promise<T | null> {
this._ensureActorInit('getValue');

const store = await this.openKeyValueStore();
return store.getValue<T>(key);
}
Expand Down Expand Up @@ -619,6 +650,8 @@ export class Actor<Data extends Dictionary = Dictionary> {
* @ignore
*/
async setValue<T>(key: string, value: T | null, options: RecordOptions = {}): Promise<void> {
this._ensureActorInit('setValue');

const store = await this.openKeyValueStore();
return store.setValue(key, value, options);
}
Expand Down Expand Up @@ -653,6 +686,8 @@ export class Actor<Data extends Dictionary = Dictionary> {
* @ignore
*/
async getInput<T = Dictionary | string | Buffer>(): Promise<T | null> {
this._ensureActorInit('getInput');

const inputSecretsPrivateKeyFile = this.config.get('inputSecretsPrivateKeyFile');
const inputSecretsPrivateKeyPassphrase = this.config.get('inputSecretsPrivateKeyPassphrase');
const input = await this.getValue<T>(this.config.get('inputKey'));
Expand Down Expand Up @@ -687,6 +722,8 @@ export class Actor<Data extends Dictionary = Dictionary> {
forceCloud: ow.optional.boolean,
}));

this._ensureActorInit('openKeyValueStore');

return this._openStorage(KeyValueStore, storeIdOrName, options);
}

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

this._ensureActorInit('openRequestQueue');

return this._openStorage(RequestQueue, queueIdOrName, options);
}

Expand Down Expand Up @@ -1374,6 +1413,24 @@ export class Actor<Data extends Dictionary = Dictionary> {
const client = options.forceCloud ? this.apifyClient : undefined;
return StorageManager.openStorage<T>(storageClass, id, client, this.config);
}

private _ensureActorInit(methodCalled: string) {
// If we already warned the user once, don't do it again to prevent spam
if (this.warnedAboutMissingInitCall) {
return;
}

if (this.initialized) {
return;
}

this.warnedAboutMissingInitCall = true;

log.warning([
`Actor.${methodCalled}() was called but the actor instance was not initialized.`,
'Did you forget to call Actor.init()?',
].join('\n'));
}
}

export interface InitOptions {
Expand Down

0 comments on commit 91cd246

Please sign in to comment.