MOBILE-3565 core: Fix some ESLint of CoreCronProvider

main
Dani Palou 2020-10-14 08:29:45 +02:00
parent a2275ab52e
commit 8d42aaf964
1 changed files with 113 additions and 102 deletions

View File

@ -19,105 +19,50 @@ import { CoreConfig } from '@services/config';
import { CoreUtils } from '@services/utils/utils'; import { CoreUtils } from '@services/utils/utils';
import { CoreConstants } from '@core/constants'; import { CoreConstants } from '@core/constants';
import { SQLiteDB } from '@classes/sqlitedb'; import { SQLiteDB } from '@classes/sqlitedb';
import { CoreError } from '@classes/errors/error';
import { makeSingleton, Network } from '@singletons/core.singletons'; import { makeSingleton, Network } from '@singletons/core.singletons';
import { CoreLogger } from '@singletons/logger'; import { CoreLogger } from '@singletons/logger';
/** const CRON_TABLE = 'cron';
* Interface that all cron handlers must implement.
*/
export interface CoreCronHandler {
/**
* A name to identify the handler.
*/
name: string;
/**
* Whether the handler is running. Used internally by the provider, there's no need to set it.
*/
running?: boolean;
/**
* Timeout ID for the handler scheduling. Used internally by the provider, there's no need to set it.
*/
timeout?: number;
/**
* Returns handler's interval in milliseconds. Defaults to CoreCronDelegate.DEFAULT_INTERVAL.
*
* @return Interval time (in milliseconds).
*/
getInterval?(): number;
/**
* Check whether the process uses network or not. True if not defined.
*
* @return Whether the process uses network or not
*/
usesNetwork?(): boolean;
/**
* Check whether it's a synchronization process or not. True if not defined.
*
* @return Whether it's a synchronization process or not.
*/
isSync?(): boolean;
/**
* Check whether the sync can be executed manually. Call isSync if not defined.
*
* @return Whether the sync can be executed manually.
*/
canManualSync?(): boolean;
/**
* Execute the process.
*
* @param siteId ID of the site affected. If not defined, all sites.
* @param force Determines if it's a forced execution.
* @return Promise resolved when done. If the promise is rejected, this function will be called again often,
* it shouldn't be abused.
*/
execute?(siteId?: string, force?: boolean): Promise<any>;
}
/* /*
* Service to handle cron processes. The registered processes will be executed every certain time. * Service to handle cron processes. The registered processes will be executed every certain time.
*/ */
@Injectable() @Injectable()
export class CoreCronDelegate { export class CoreCronDelegate {
// Constants. // Constants.
static DEFAULT_INTERVAL = 3600000; // Default interval is 1 hour. static readonly DEFAULT_INTERVAL = 3600000; // Default interval is 1 hour.
static MIN_INTERVAL = 300000; // Minimum interval is 5 minutes. static readonly MIN_INTERVAL = 300000; // Minimum interval is 5 minutes.
static DESKTOP_MIN_INTERVAL = 60000; // Minimum interval in desktop is 1 minute. static readonly DESKTOP_MIN_INTERVAL = 60000; // Minimum interval in desktop is 1 minute.
static MAX_TIME_PROCESS = 120000; // Max time a process can block the queue. Defaults to 2 minutes. static readonly MAX_TIME_PROCESS = 120000; // Max time a process can block the queue. Defaults to 2 minutes.
// Variables for database. // Variables for database.
protected CRON_TABLE = 'cron';
protected tableSchema: CoreAppSchema = { protected tableSchema: CoreAppSchema = {
name: 'CoreCronDelegate', name: 'CoreCronDelegate',
version: 1, version: 1,
tables: [ tables: [
{ {
name: this.CRON_TABLE, name: CRON_TABLE,
columns: [ columns: [
{ {
name: 'id', name: 'id',
type: 'TEXT', type: 'TEXT',
primaryKey: true primaryKey: true,
}, },
{ {
name: 'value', name: 'value',
type: 'INTEGER' type: 'INTEGER',
}, },
], ],
}, },
], ],
}; };
protected logger; protected logger: CoreLogger;
protected appDB: SQLiteDB; protected appDB: SQLiteDB;
protected dbReady: Promise<any>; // Promise resolved when the app DB is initialized. protected dbReady: Promise<void>; // Promise resolved when the app DB is initialized.
protected handlers: { [s: string]: CoreCronHandler } = {}; protected handlers: { [s: string]: CoreCronHandler } = {};
protected queuePromise = Promise.resolve(); protected queuePromise = Promise.resolve();
@ -139,7 +84,7 @@ export class CoreCronDelegate {
// Export the sync provider so Behat tests can trigger cron tasks without waiting. // Export the sync provider so Behat tests can trigger cron tasks without waiting.
if (CoreAppProvider.isAutomated()) { if (CoreAppProvider.isAutomated()) {
(<any> window).cronProvider = this; (<WindowForAutomatedTests> window).cronProvider = this;
} }
} }
@ -152,12 +97,13 @@ export class CoreCronDelegate {
* @param siteId Site ID. If not defined, all sites. * @param siteId Site ID. If not defined, all sites.
* @return Promise resolved if handler is executed successfully, rejected otherwise. * @return Promise resolved if handler is executed successfully, rejected otherwise.
*/ */
protected checkAndExecuteHandler(name: string, force?: boolean, siteId?: string): Promise<any> { protected checkAndExecuteHandler(name: string, force?: boolean, siteId?: string): Promise<void> {
if (!this.handlers[name] || !this.handlers[name].execute) { if (!this.handlers[name] || !this.handlers[name].execute) {
// Invalid handler. // Invalid handler.
this.logger.debug('Cannot execute handler because is invalid: ' + name); const message = `Cannot execute handler because is invalid: ${name}`;
this.logger.debug(message);
return Promise.reject(null); return Promise.reject(new CoreError(message));
} }
const usesNetwork = this.handlerUsesNetwork(name); const usesNetwork = this.handlerUsesNetwork(name);
@ -166,17 +112,17 @@ export class CoreCronDelegate {
if (usesNetwork && !CoreApp.instance.isOnline()) { if (usesNetwork && !CoreApp.instance.isOnline()) {
// Offline, stop executing. // Offline, stop executing.
this.logger.debug('Cannot execute handler because device is offline: ' + name); const message = `Cannot execute handler because device is offline: ${name}`;
this.logger.debug(message);
this.stopHandler(name); this.stopHandler(name);
return Promise.reject(null); return Promise.reject(new CoreError(message));
} }
if (isSync) { if (isSync) {
// Check network connection. // Check network connection.
promise = CoreConfig.instance.get(CoreConstants.SETTINGS_SYNC_ONLY_ON_WIFI, false).then((syncOnlyOnWifi) => { promise = CoreConfig.instance.get(CoreConstants.SETTINGS_SYNC_ONLY_ON_WIFI, false)
return !syncOnlyOnWifi || CoreApp.instance.isWifi(); .then((syncOnlyOnWifi) => !syncOnlyOnWifi || CoreApp.instance.isWifi());
});
} else { } else {
promise = Promise.resolve(true); promise = Promise.resolve(true);
} }
@ -184,17 +130,17 @@ export class CoreCronDelegate {
return promise.then((execute: boolean) => { return promise.then((execute: boolean) => {
if (!execute) { if (!execute) {
// Cannot execute in this network connection, retry soon. // Cannot execute in this network connection, retry soon.
this.logger.debug('Cannot execute handler because device is using limited connection: ' + name); const message = `Cannot execute handler because device is using limited connection: ${name}`;
this.logger.debug(message);
this.scheduleNextExecution(name, CoreCronDelegate.MIN_INTERVAL); this.scheduleNextExecution(name, CoreCronDelegate.MIN_INTERVAL);
return Promise.reject(null); return Promise.reject(new CoreError(message));
} }
// Add the execution to the queue. // Add the execution to the queue.
this.queuePromise = this.queuePromise.catch(() => { this.queuePromise = this.queuePromise.catch(() => {
// Ignore errors in previous handlers. // Ignore errors in previous handlers.
}).then(() => { }).then(() => this.executeHandler(name, force, siteId).then(() => {
return this.executeHandler(name, force, siteId).then(() => {
this.logger.debug(`Execution of handler '${name}' was a success.`); this.logger.debug(`Execution of handler '${name}' was a success.`);
return this.setHandlerLastExecutionTime(name, Date.now()).then(() => { return this.setHandlerLastExecutionTime(name, Date.now()).then(() => {
@ -202,12 +148,12 @@ export class CoreCronDelegate {
}); });
}, (error) => { }, (error) => {
// Handler call failed. Retry soon. // Handler call failed. Retry soon.
this.logger.error(`Execution of handler '${name}' failed.`, error); const message = `Execution of handler '${name}' failed.`;
this.logger.error(message, error);
this.scheduleNextExecution(name, CoreCronDelegate.MIN_INTERVAL); this.scheduleNextExecution(name, CoreCronDelegate.MIN_INTERVAL);
return Promise.reject(null); return Promise.reject(new CoreError(message));
}); }));
});
return this.queuePromise; return this.queuePromise;
}); });
@ -221,10 +167,8 @@ export class CoreCronDelegate {
* @param siteId Site ID. If not defined, all sites. * @param siteId Site ID. If not defined, all sites.
* @return Promise resolved when the handler finishes or reaches max time, rejected if it fails. * @return Promise resolved when the handler finishes or reaches max time, rejected if it fails.
*/ */
protected executeHandler(name: string, force?: boolean, siteId?: string): Promise<any> { protected executeHandler(name: string, force?: boolean, siteId?: string): Promise<void> {
return new Promise((resolve, reject): void => { return new Promise((resolve, reject): void => {
let cancelTimeout;
this.logger.debug('Executing handler: ' + name); this.logger.debug('Executing handler: ' + name);
// Wrap the call in Promise.resolve to make sure it's a promise. // Wrap the call in Promise.resolve to make sure it's a promise.
@ -232,7 +176,7 @@ export class CoreCronDelegate {
clearTimeout(cancelTimeout); clearTimeout(cancelTimeout);
}); });
cancelTimeout = setTimeout(() => { const cancelTimeout = setTimeout(() => {
// The handler took too long. Resolve because we don't want to retry soon. // The handler took too long. Resolve because we don't want to retry soon.
this.logger.debug(`Resolving execution of handler '${name}' because it took too long.`); this.logger.debug(`Resolving execution of handler '${name}' because it took too long.`);
resolve(); resolve();
@ -247,7 +191,7 @@ export class CoreCronDelegate {
* @param siteId Site ID. If not defined, all sites. * @param siteId Site ID. If not defined, all sites.
* @return Promise resolved if all handlers are executed successfully, rejected otherwise. * @return Promise resolved if all handlers are executed successfully, rejected otherwise.
*/ */
forceSyncExecution(siteId?: string): Promise<any> { async forceSyncExecution(siteId?: string): Promise<void> {
const promises = []; const promises = [];
for (const name in this.handlers) { for (const name in this.handlers) {
@ -257,7 +201,7 @@ export class CoreCronDelegate {
} }
} }
return CoreUtils.instance.allPromises(promises); await CoreUtils.instance.allPromises(promises);
} }
/** /**
@ -268,7 +212,7 @@ export class CoreCronDelegate {
* @param siteId Site ID. If not defined, all sites. * @param siteId Site ID. If not defined, all sites.
* @return Promise resolved if handler has been executed successfully, rejected otherwise. * @return Promise resolved if handler has been executed successfully, rejected otherwise.
*/ */
forceCronHandlerExecution(name?: string, siteId?: string): Promise<any> { forceCronHandlerExecution(name?: string, siteId?: string): Promise<void> {
const handler = this.handlers[name]; const handler = this.handlers[name];
// Mark the handler as running (it might be running already). // Mark the handler as running (it might be running already).
@ -327,7 +271,7 @@ export class CoreCronDelegate {
const id = this.getHandlerLastExecutionId(name); const id = this.getHandlerLastExecutionId(name);
try { try {
const entry = await this.appDB.getRecord(this.CRON_TABLE, { id }); const entry = await this.appDB.getRecord(CRON_TABLE, { id });
const time = parseInt(entry.value, 10); const time = parseInt(entry.value, 10);
return isNaN(time) ? 0 : time; return isNaN(time) ? 0 : time;
@ -489,16 +433,16 @@ export class CoreCronDelegate {
* @param time Time to set. * @param time Time to set.
* @return Promise resolved when the execution time is saved. * @return Promise resolved when the execution time is saved.
*/ */
protected async setHandlerLastExecutionTime(name: string, time: number): Promise<any> { protected async setHandlerLastExecutionTime(name: string, time: number): Promise<void> {
await this.dbReady; await this.dbReady;
const id = this.getHandlerLastExecutionId(name); const id = this.getHandlerLastExecutionId(name);
const entry = { const entry = {
id, id,
value: time value: time,
}; };
return this.appDB.insertRecord(this.CRON_TABLE, entry); await this.appDB.insertRecord(CRON_TABLE, entry);
} }
/** /**
@ -559,6 +503,73 @@ export class CoreCronDelegate {
clearTimeout(this.handlers[name].timeout); clearTimeout(this.handlers[name].timeout);
delete this.handlers[name].timeout; delete this.handlers[name].timeout;
} }
} }
export class CoreCron extends makeSingleton(CoreCronDelegate) {} export class CoreCron extends makeSingleton(CoreCronDelegate) {}
/**
* Interface that all cron handlers must implement.
*/
export interface CoreCronHandler {
/**
* A name to identify the handler.
*/
name: string;
/**
* Whether the handler is running. Used internally by the provider, there's no need to set it.
*/
running?: boolean;
/**
* Timeout ID for the handler scheduling. Used internally by the provider, there's no need to set it.
*/
timeout?: number;
/**
* Returns handler's interval in milliseconds. Defaults to CoreCronDelegate.DEFAULT_INTERVAL.
*
* @return Interval time (in milliseconds).
*/
getInterval?(): number;
/**
* Check whether the process uses network or not. True if not defined.
*
* @return Whether the process uses network or not
*/
usesNetwork?(): boolean;
/**
* Check whether it's a synchronization process or not. True if not defined.
*
* @return Whether it's a synchronization process or not.
*/
isSync?(): boolean;
/**
* Check whether the sync can be executed manually. Call isSync if not defined.
*
* @return Whether the sync can be executed manually.
*/
canManualSync?(): boolean;
/**
* Execute the process.
*
* @param siteId ID of the site affected. If not defined, all sites.
* @param force Determines if it's a forced execution.
* @return Promise resolved when done. If the promise is rejected, this function will be called again often,
* it shouldn't be abused.
*/
execute?(siteId?: string, force?: boolean): Promise<void>;
}
/**
* Extended window type for automated tests.
*/
export type WindowForAutomatedTests = Window & {
cronProvider?: CoreCronDelegate;
};