From 6df1c2109de9ec37465f7640303f1ffc74ac06a2 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Wed, 28 Oct 2020 14:25:18 +0100 Subject: [PATCH] MOBILE-3565 services: Move some DB vars and init to new files --- src/app/app.module.ts | 16 + src/app/classes/site.ts | 2 +- src/app/services/app.db.ts | 41 ++ src/app/services/app.ts | 29 +- src/app/services/config.db.ts | 47 +++ src/app/services/config.ts | 40 +- src/app/services/cron.db.ts | 45 +++ src/app/services/cron.ts | 38 +- src/app/services/filepool.db.ts | 367 ++++++++++++++++++ src/app/services/filepool.ts | 427 +++------------------ src/app/services/local-notifications.db.ts | 80 ++++ src/app/services/local-notifications.ts | 91 +---- src/app/services/sites.db.ts | 233 +++++++++++ src/app/services/sites.ts | 355 +++-------------- src/app/services/sync.db.ts | 62 +++ src/app/services/sync.ts | 18 +- 16 files changed, 1034 insertions(+), 857 deletions(-) create mode 100644 src/app/services/app.db.ts create mode 100644 src/app/services/config.db.ts create mode 100644 src/app/services/cron.db.ts create mode 100644 src/app/services/filepool.db.ts create mode 100644 src/app/services/local-notifications.db.ts create mode 100644 src/app/services/sites.db.ts create mode 100644 src/app/services/sync.db.ts diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 16208a998..daf0e9600 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -50,6 +50,11 @@ import { CoreTimeUtilsProvider } from '@services/utils/time'; import { CoreUrlUtilsProvider } from '@services/utils/url'; import { CoreUtilsProvider } from '@services/utils/utils'; +// Import init DB functions of core services. +import { initCoreFilepoolDB } from '@services/filepool.db'; +import { initCoreSitesDB } from '@services/sites.db'; +import { initCoreSyncDB } from '@services/sync.db'; + // Import core modules. import { CoreEmulatorModule } from '@core/emulator/emulator.module'; import { CoreLoginModule } from '@core/login/login.module'; @@ -121,6 +126,8 @@ export class AppModule { // Set the injector. setSingletonsInjector(injector); + this.initCoreServicesDB(); + // Register a handler for platform ready. CoreInit.instance.registerProcess({ name: 'CorePlatformReady', @@ -154,4 +161,13 @@ export class AppModule { CoreInit.instance.executeInitProcesses(); } + /** + * Init the DB of core services. + */ + protected initCoreServicesDB(): void { + initCoreFilepoolDB(); + initCoreSitesDB(); + initCoreSyncDB(); + } + } diff --git a/src/app/classes/site.ts b/src/app/classes/site.ts index 53e66669e..539b3259d 100644 --- a/src/app/classes/site.ts +++ b/src/app/classes/site.ts @@ -36,7 +36,7 @@ import { CoreIonLoadingElement } from './ion-loading'; /** * Class that represents a site (combination of site + user). * It will have all the site data and provide utility functions regarding a site. - * To add tables to the site's database, please use CoreSitesProvider.registerSiteSchema. This will make sure that + * To add tables to the site's database, please use registerSiteSchema exported in @services/sites.ts. This will make sure that * the tables are created in all the sites, not just the current one. * * @todo: Refactor this class to improve "temporary" sites support (not fully authenticated). diff --git a/src/app/services/app.db.ts b/src/app/services/app.db.ts new file mode 100644 index 000000000..6621bafc6 --- /dev/null +++ b/src/app/services/app.db.ts @@ -0,0 +1,41 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { SQLiteDBTableSchema } from '@classes/sqlitedb'; + +/** + * Database variables for CoreApp service. + */ +export const DBNAME = 'MoodleMobile'; +export const SCHEMA_VERSIONS_TABLE_NAME = 'schema_versions'; + +export const SCHEMA_VERSIONS_TABLE_SCHEMA: SQLiteDBTableSchema = { + name: SCHEMA_VERSIONS_TABLE_NAME, + columns: [ + { + name: 'name', + type: 'TEXT', + primaryKey: true, + }, + { + name: 'version', + type: 'INTEGER', + }, + ], +}; + +export type SchemaVersionsDBEntry = { + name: string; + version: number; +}; diff --git a/src/app/services/app.ts b/src/app/services/app.ts index a22293047..db708eef2 100644 --- a/src/app/services/app.ts +++ b/src/app/services/app.ts @@ -24,9 +24,7 @@ import { CoreConstants } from '@core/constants'; import { makeSingleton, Keyboard, Network, StatusBar, Platform } from '@singletons/core.singletons'; import { CoreLogger } from '@singletons/logger'; - -const DBNAME = 'MoodleMobile'; -const SCHEMA_VERSIONS_TABLE = 'schema_versions'; +import { DBNAME, SCHEMA_VERSIONS_TABLE_NAME, SCHEMA_VERSIONS_TABLE_SCHEMA, SchemaVersionsDBEntry } from '@services/app.db'; /** * Factory to provide some global functionalities, like access to the global app database. @@ -57,27 +55,13 @@ export class CoreAppProvider { // Variables for DB. protected createVersionsTableReady: Promise; - protected versionsTableSchema: SQLiteDBTableSchema = { - name: SCHEMA_VERSIONS_TABLE, - columns: [ - { - name: 'name', - type: 'TEXT', - primaryKey: true, - }, - { - name: 'version', - type: 'INTEGER', - }, - ], - }; constructor(appRef: ApplicationRef, zone: NgZone) { this.logger = CoreLogger.getInstance('CoreAppProvider'); this.db = CoreDB.instance.getDB(DBNAME); // Create the schema versions table. - this.createVersionsTableReady = this.db.createTableFromSchema(this.versionsTableSchema); + this.createVersionsTableReady = this.db.createTableFromSchema(SCHEMA_VERSIONS_TABLE_SCHEMA); Keyboard.instance.onKeyboardShow().subscribe((data) => { // Execute the callback in the Angular zone, so change detection doesn't stop working. @@ -175,7 +159,7 @@ export class CoreAppProvider { await this.createVersionsTableReady; // Fetch installed version of the schema. - const entry = await this.db.getRecord(SCHEMA_VERSIONS_TABLE, { name: schema.name }); + const entry = await this.db.getRecord(SCHEMA_VERSIONS_TABLE_NAME, { name: schema.name }); oldVersion = entry.version; } catch (error) { @@ -198,7 +182,7 @@ export class CoreAppProvider { } // Set installed version. - await this.db.insertRecord(SCHEMA_VERSIONS_TABLE, { name: schema.name, version: schema.version }); + await this.db.insertRecord(SCHEMA_VERSIONS_TABLE_NAME, { name: schema.name, version: schema.version }); } /** @@ -741,8 +725,3 @@ export type WindowForAutomatedTests = Window & { appProvider?: CoreAppProvider; appRef?: ApplicationRef; }; - -type SchemaVersionsDBEntry = { - name: string; - version: number; -}; diff --git a/src/app/services/config.db.ts b/src/app/services/config.db.ts new file mode 100644 index 000000000..b441dc78e --- /dev/null +++ b/src/app/services/config.db.ts @@ -0,0 +1,47 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { CoreAppSchema } from '@services/app'; + +/** + * Database variables for for CoreConfig service. + */ +export const CONFIG_TABLE_NAME = 'core_config'; + +export const APP_SCHEMA: CoreAppSchema = { + name: 'CoreConfigProvider', + version: 1, + tables: [ + { + name: CONFIG_TABLE_NAME, + columns: [ + { + name: 'name', + type: 'TEXT', + unique: true, + notNull: true, + }, + { + name: 'value', + }, + ], + }, + ], +}; + +export type ConfigDBEntry = { + name: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + value: any; +}; diff --git a/src/app/services/config.ts b/src/app/services/config.ts index 5fb874158..9cdf15782 100644 --- a/src/app/services/config.ts +++ b/src/app/services/config.ts @@ -14,11 +14,10 @@ import { Injectable } from '@angular/core'; -import { CoreApp, CoreAppSchema } from '@services/app'; +import { CoreApp } from '@services/app'; import { SQLiteDB } from '@classes/sqlitedb'; import { makeSingleton } from '@singletons/core.singletons'; - -const TABLE_NAME = 'core_config'; +import { CONFIG_TABLE_NAME, APP_SCHEMA, ConfigDBEntry } from '@services/config.db'; /** * Factory to provide access to dynamic and permanent config and settings. @@ -28,32 +27,11 @@ const TABLE_NAME = 'core_config'; export class CoreConfigProvider { protected appDB: SQLiteDB; - protected tableSchema: CoreAppSchema = { - name: 'CoreConfigProvider', - version: 1, - tables: [ - { - name: TABLE_NAME, - columns: [ - { - name: 'name', - type: 'TEXT', - unique: true, - notNull: true, - }, - { - name: 'value', - }, - ], - }, - ], - }; - protected dbReady: Promise; // Promise resolved when the app DB is initialized. constructor() { this.appDB = CoreApp.instance.getDB(); - this.dbReady = CoreApp.instance.createTablesFromSchema(this.tableSchema).catch(() => { + this.dbReady = CoreApp.instance.createTablesFromSchema(APP_SCHEMA).catch(() => { // Ignore errors. }); } @@ -67,7 +45,7 @@ export class CoreConfigProvider { async delete(name: string): Promise { await this.dbReady; - await this.appDB.deleteRecords(TABLE_NAME, { name }); + await this.appDB.deleteRecords(CONFIG_TABLE_NAME, { name }); } /** @@ -81,7 +59,7 @@ export class CoreConfigProvider { await this.dbReady; try { - const entry = await this.appDB.getRecord(TABLE_NAME, { name }); + const entry = await this.appDB.getRecord(CONFIG_TABLE_NAME, { name }); return entry.value; } catch (error) { @@ -103,15 +81,9 @@ export class CoreConfigProvider { async set(name: string, value: number | string): Promise { await this.dbReady; - await this.appDB.insertRecord(TABLE_NAME, { name, value }); + await this.appDB.insertRecord(CONFIG_TABLE_NAME, { name, value }); } } export class CoreConfig extends makeSingleton(CoreConfigProvider) {} - -type ConfigDBEntry = { - name: string; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - value: any; -}; diff --git a/src/app/services/cron.db.ts b/src/app/services/cron.db.ts new file mode 100644 index 000000000..d96d2aba1 --- /dev/null +++ b/src/app/services/cron.db.ts @@ -0,0 +1,45 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { CoreAppSchema } from '@services/app'; + +/** + * Database variables for CoreCron service. + */ +export const CRON_TABLE_NAME = 'cron'; +export const APP_SCHEMA: CoreAppSchema = { + name: 'CoreCronDelegate', + version: 1, + tables: [ + { + name: CRON_TABLE_NAME, + columns: [ + { + name: 'id', + type: 'TEXT', + primaryKey: true, + }, + { + name: 'value', + type: 'INTEGER', + }, + ], + }, + ], +}; + +export type CronDBEntry = { + id: string; + value: number; +}; diff --git a/src/app/services/cron.ts b/src/app/services/cron.ts index fd13576ab..f542bfcbc 100644 --- a/src/app/services/cron.ts +++ b/src/app/services/cron.ts @@ -14,7 +14,7 @@ import { Injectable, NgZone } from '@angular/core'; -import { CoreApp, CoreAppProvider, CoreAppSchema } from '@services/app'; +import { CoreApp, CoreAppProvider } from '@services/app'; import { CoreConfig } from '@services/config'; import { CoreUtils } from '@services/utils/utils'; import { CoreConstants } from '@core/constants'; @@ -23,8 +23,7 @@ import { CoreError } from '@classes/errors/error'; import { makeSingleton, Network } from '@singletons/core.singletons'; import { CoreLogger } from '@singletons/logger'; - -const CRON_TABLE = 'cron'; +import { APP_SCHEMA, CRON_TABLE_NAME, CronDBEntry } from '@services/cron.db'; /* * Service to handle cron processes. The registered processes will be executed every certain time. @@ -37,28 +36,6 @@ export class CoreCronDelegate { static readonly MIN_INTERVAL = 300000; // Minimum interval is 5 minutes. static readonly MAX_TIME_PROCESS = 120000; // Max time a process can block the queue. Defaults to 2 minutes. - // Variables for database. - protected tableSchema: CoreAppSchema = { - name: 'CoreCronDelegate', - version: 1, - tables: [ - { - name: CRON_TABLE, - columns: [ - { - name: 'id', - type: 'TEXT', - primaryKey: true, - }, - { - name: 'value', - type: 'INTEGER', - }, - ], - }, - ], - }; - protected logger: CoreLogger; protected appDB: SQLiteDB; protected dbReady: Promise; // Promise resolved when the app DB is initialized. @@ -69,7 +46,7 @@ export class CoreCronDelegate { this.logger = CoreLogger.getInstance('CoreCronDelegate'); this.appDB = CoreApp.instance.getDB(); - this.dbReady = CoreApp.instance.createTablesFromSchema(this.tableSchema).catch(() => { + this.dbReady = CoreApp.instance.createTablesFromSchema(APP_SCHEMA).catch(() => { // Ignore errors. }); @@ -268,7 +245,7 @@ export class CoreCronDelegate { const id = this.getHandlerLastExecutionId(name); try { - const entry = await this.appDB.getRecord(CRON_TABLE, { id }); + const entry = await this.appDB.getRecord(CRON_TABLE_NAME, { id }); const time = Number(entry.value); @@ -431,7 +408,7 @@ export class CoreCronDelegate { value: time, }; - await this.appDB.insertRecord(CRON_TABLE, entry); + await this.appDB.insertRecord(CRON_TABLE_NAME, entry); } /** @@ -562,8 +539,3 @@ export interface CoreCronHandler { export type WindowForAutomatedTests = Window & { cronProvider?: CoreCronDelegate; }; - -type CronDBEntry = { - id: string; - value: number; -}; diff --git a/src/app/services/filepool.db.ts b/src/app/services/filepool.db.ts new file mode 100644 index 000000000..42c8a15b0 --- /dev/null +++ b/src/app/services/filepool.db.ts @@ -0,0 +1,367 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { CoreAppSchema } from '@services/app'; +import { CoreSiteSchema, registerSiteSchema } from '@services/sites'; + +/** + * Database variables for CoreFilepool service. + */ +export const QUEUE_TABLE_NAME = 'filepool_files_queue'; // Queue of files to download. +export const FILES_TABLE_NAME = 'filepool_files'; // Downloaded files. +export const LINKS_TABLE_NAME = 'filepool_files_links'; // Links between downloaded files and components. +export const PACKAGES_TABLE_NAME = 'filepool_packages'; // Downloaded packages (sets of files). +export const APP_SCHEMA: CoreAppSchema = { + name: 'CoreFilepoolProvider', + version: 1, + tables: [ + { + name: QUEUE_TABLE_NAME, + columns: [ + { + name: 'siteId', + type: 'TEXT', + }, + { + name: 'fileId', + type: 'TEXT', + }, + { + name: 'added', + type: 'INTEGER', + }, + { + name: 'priority', + type: 'INTEGER', + }, + { + name: 'url', + type: 'TEXT', + }, + { + name: 'revision', + type: 'INTEGER', + }, + { + name: 'timemodified', + type: 'INTEGER', + }, + { + name: 'isexternalfile', + type: 'INTEGER', + }, + { + name: 'repositorytype', + type: 'TEXT', + }, + { + name: 'path', + type: 'TEXT', + }, + { + name: 'links', + type: 'TEXT', + }, + ], + primaryKeys: ['siteId', 'fileId'], + }, + ], +}; + +export const SITE_SCHEMA: CoreSiteSchema = { + name: 'CoreFilepoolProvider', + version: 1, + tables: [ + { + name: FILES_TABLE_NAME, + columns: [ + { + name: 'fileId', + type: 'TEXT', + primaryKey: true, + }, + { + name: 'url', + type: 'TEXT', + notNull: true, + }, + { + name: 'revision', + type: 'INTEGER', + }, + { + name: 'timemodified', + type: 'INTEGER', + }, + { + name: 'stale', + type: 'INTEGER', + }, + { + name: 'downloadTime', + type: 'INTEGER', + }, + { + name: 'isexternalfile', + type: 'INTEGER', + }, + { + name: 'repositorytype', + type: 'TEXT', + }, + { + name: 'path', + type: 'TEXT', + }, + { + name: 'extension', + type: 'TEXT', + }, + ], + }, + { + name: LINKS_TABLE_NAME, + columns: [ + { + name: 'fileId', + type: 'TEXT', + }, + { + name: 'component', + type: 'TEXT', + }, + { + name: 'componentId', + type: 'TEXT', + }, + ], + primaryKeys: ['fileId', 'component', 'componentId'], + }, + { + name: PACKAGES_TABLE_NAME, + columns: [ + { + name: 'id', + type: 'TEXT', + primaryKey: true, + }, + { + name: 'component', + type: 'TEXT', + }, + { + name: 'componentId', + type: 'TEXT', + }, + { + name: 'status', + type: 'TEXT', + }, + { + name: 'previous', + type: 'TEXT', + }, + { + name: 'updated', + type: 'INTEGER', + }, + { + name: 'downloadTime', + type: 'INTEGER', + }, + { + name: 'previousDownloadTime', + type: 'INTEGER', + }, + { + name: 'extra', + type: 'TEXT', + }, + ], + }, + ], +}; + +/** + * File options. + */ +export type CoreFilepoolFileOptions = { + revision?: number; // File's revision. + timemodified?: number; // File's timemodified. + isexternalfile?: number; // 1 if it's a external file (from an external repository), 0 otherwise. + repositorytype?: string; // Type of the repository this file belongs to. +}; + +/** + * Entry from filepool. + */ +export type CoreFilepoolFileEntry = CoreFilepoolFileOptions & { + /** + * The fileId to identify the file. + */ + fileId: string; + + /** + * File's URL. + */ + url: string; + + /** + * 1 if file is stale (needs to be updated), 0 otherwise. + */ + stale: number; + + /** + * Timestamp when this file was downloaded. + */ + downloadTime: number; + + /** + * File's path. + */ + path: string; + + /** + * File's extension. + */ + extension: string; +}; + +/** + * DB data for entry from file's queue. + */ +export type CoreFilepoolQueueDBEntry = CoreFilepoolFileOptions & { + /** + * The site the file belongs to. + */ + siteId: string; + + /** + * The fileId to identify the file. + */ + fileId: string; + + /** + * Timestamp when the file was added to the queue. + */ + added: number; + + /** + * The priority of the file. + */ + priority: number; + + /** + * File's URL. + */ + url: string; + + /** + * File's path. + */ + path?: string; + + /** + * File links (to link the file to components and componentIds). Serialized to store on DB. + */ + links: string; +}; + +/** + * Entry from the file's queue. + */ +export type CoreFilepoolQueueEntry = CoreFilepoolQueueDBEntry & { + /** + * File links (to link the file to components and componentIds). + */ + linksUnserialized?: CoreFilepoolComponentLink[]; +}; + +/** + * Entry from packages table. + */ +export type CoreFilepoolPackageEntry = { + /** + * Package id. + */ + id?: string; + + /** + * The component to link the files to. + */ + component?: string; + + /** + * An ID to use in conjunction with the component. + */ + componentId?: string | number; + + /** + * Package status. + */ + status?: string; + + /** + * Package previous status. + */ + previous?: string; + + /** + * Timestamp when this package was updated. + */ + updated?: number; + + /** + * Timestamp when this package was downloaded. + */ + downloadTime?: number; + + /** + * Previous download time. + */ + previousDownloadTime?: number; + + /** + * Extra data stored by the package. + */ + extra?: string; +}; + +/** + * A component link. + */ +export type CoreFilepoolComponentLink = { + /** + * Link's component. + */ + component: string; + + /** + * Link's componentId. + */ + componentId?: string | number; +}; + +/** + * Links table record type. + */ +export type CoreFilepoolLinksRecord = { + fileId: string; // File Id. + component: string; // Component name. + componentId: number | string; // Component Id. +}; + +export const initCoreFilepoolDB = (): void => { + registerSiteSchema(SITE_SCHEMA); +}; diff --git a/src/app/services/filepool.ts b/src/app/services/filepool.ts index 6baddc81c..b9bdbed64 100644 --- a/src/app/services/filepool.ts +++ b/src/app/services/filepool.ts @@ -15,12 +15,12 @@ import { Injectable } from '@angular/core'; import { Md5 } from 'ts-md5/dist/md5'; -import { CoreApp, CoreAppSchema } from '@services/app'; +import { CoreApp } from '@services/app'; import { CoreEvents } from '@singletons/events'; import { CoreFile } from '@services/file'; import { CoreInit } from '@services/init'; import { CorePluginFile } from '@services/plugin-file-delegate'; -import { CoreSites, CoreSiteSchema } from '@services/sites'; +import { CoreSites } from '@services/sites'; import { CoreWS, CoreWSExternalFile } from '@services/ws'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreMimetypeUtils } from '@services/utils/mimetype'; @@ -33,6 +33,20 @@ import { CoreError } from '@classes/errors/error'; import { CoreConstants } from '@core/constants'; import { makeSingleton, Network, NgZone, Translate } from '@singletons/core.singletons'; import { CoreLogger } from '@singletons/logger'; +import { + APP_SCHEMA, + FILES_TABLE_NAME, + QUEUE_TABLE_NAME, + PACKAGES_TABLE_NAME, + LINKS_TABLE_NAME, + CoreFilepoolFileEntry, + CoreFilepoolComponentLink, + CoreFilepoolFileOptions, + CoreFilepoolLinksRecord, + CoreFilepoolPackageEntry, + CoreFilepoolQueueEntry, + CoreFilepoolQueueDBEntry, +} from '@services/filepool.db'; /* * Factory for handling downloading files and retrieve downloaded files. @@ -60,182 +74,6 @@ export class CoreFilepoolProvider { protected static readonly FILE_UPDATE_UNKNOWN_WHERE_CLAUSE = 'isexternalfile = 1 OR ((revision IS NULL OR revision = 0) AND (timemodified IS NULL OR timemodified = 0))'; - // Variables for database. - protected static readonly QUEUE_TABLE = 'filepool_files_queue'; // Queue of files to download. - protected static readonly FILES_TABLE = 'filepool_files'; // Downloaded files. - protected static readonly LINKS_TABLE = 'filepool_files_links'; // Links between downloaded files and components. - protected static readonly PACKAGES_TABLE = 'filepool_packages'; // Downloaded packages (sets of files). - protected appTablesSchema: CoreAppSchema = { - name: 'CoreFilepoolProvider', - version: 1, - tables: [ - { - name: CoreFilepoolProvider.QUEUE_TABLE, - columns: [ - { - name: 'siteId', - type: 'TEXT', - }, - { - name: 'fileId', - type: 'TEXT', - }, - { - name: 'added', - type: 'INTEGER', - }, - { - name: 'priority', - type: 'INTEGER', - }, - { - name: 'url', - type: 'TEXT', - }, - { - name: 'revision', - type: 'INTEGER', - }, - { - name: 'timemodified', - type: 'INTEGER', - }, - { - name: 'isexternalfile', - type: 'INTEGER', - }, - { - name: 'repositorytype', - type: 'TEXT', - }, - { - name: 'path', - type: 'TEXT', - }, - { - name: 'links', - type: 'TEXT', - }, - ], - primaryKeys: ['siteId', 'fileId'], - }, - ], - }; - - protected siteSchema: CoreSiteSchema = { - name: 'CoreFilepoolProvider', - version: 1, - tables: [ - { - name: CoreFilepoolProvider.FILES_TABLE, - columns: [ - { - name: 'fileId', - type: 'TEXT', - primaryKey: true, - }, - { - name: 'url', - type: 'TEXT', - notNull: true, - }, - { - name: 'revision', - type: 'INTEGER', - }, - { - name: 'timemodified', - type: 'INTEGER', - }, - { - name: 'stale', - type: 'INTEGER', - }, - { - name: 'downloadTime', - type: 'INTEGER', - }, - { - name: 'isexternalfile', - type: 'INTEGER', - }, - { - name: 'repositorytype', - type: 'TEXT', - }, - { - name: 'path', - type: 'TEXT', - }, - { - name: 'extension', - type: 'TEXT', - }, - ], - }, - { - name: CoreFilepoolProvider.LINKS_TABLE, - columns: [ - { - name: 'fileId', - type: 'TEXT', - }, - { - name: 'component', - type: 'TEXT', - }, - { - name: 'componentId', - type: 'TEXT', - }, - ], - primaryKeys: ['fileId', 'component', 'componentId'], - }, - { - name: CoreFilepoolProvider.PACKAGES_TABLE, - columns: [ - { - name: 'id', - type: 'TEXT', - primaryKey: true, - }, - { - name: 'component', - type: 'TEXT', - }, - { - name: 'componentId', - type: 'TEXT', - }, - { - name: 'status', - type: 'TEXT', - }, - { - name: 'previous', - type: 'TEXT', - }, - { - name: 'updated', - type: 'INTEGER', - }, - { - name: 'downloadTime', - type: 'INTEGER', - }, - { - name: 'previousDownloadTime', - type: 'INTEGER', - }, - { - name: 'extra', - type: 'TEXT', - }, - ], - }, - ], - }; - protected logger: CoreLogger; protected appDB: SQLiteDB; protected dbReady: Promise; // Promise resolved when the app DB is initialized. @@ -258,12 +96,10 @@ export class CoreFilepoolProvider { this.logger = CoreLogger.getInstance('CoreFilepoolProvider'); this.appDB = CoreApp.instance.getDB(); - this.dbReady = CoreApp.instance.createTablesFromSchema(this.appTablesSchema).catch(() => { + this.dbReady = CoreApp.instance.createTablesFromSchema(APP_SCHEMA).catch(() => { // Ignore errors. }); - CoreSites.instance.registerSiteSchema(this.siteSchema); - this.init(); } @@ -308,7 +144,7 @@ export class CoreFilepoolProvider { componentId: componentId || '', }; - await db.insertRecord(CoreFilepoolProvider.LINKS_TABLE, newEntry); + await db.insertRecord(LINKS_TABLE_NAME, newEntry); } /** @@ -373,7 +209,7 @@ export class CoreFilepoolProvider { const db = await CoreSites.instance.getSiteDb(siteId); - await db.insertRecord(CoreFilepoolProvider.FILES_TABLE, record); + await db.insertRecord(FILES_TABLE_NAME, record); } /** @@ -433,7 +269,7 @@ export class CoreFilepoolProvider { this.logger.debug(`Adding ${fileId} to the queue`); - await this.appDB.insertRecord(CoreFilepoolProvider.QUEUE_TABLE, { + await this.appDB.insertRecord(QUEUE_TABLE_NAME, { siteId, fileId, url, @@ -563,7 +399,7 @@ export class CoreFilepoolProvider { // Update only when required. this.logger.debug(`Updating file ${fileId} which is already in queue`); - return this.appDB.updateRecords(CoreFilepoolProvider.QUEUE_TABLE, newData, primaryKey).then(() => + return this.appDB.updateRecords(QUEUE_TABLE_NAME, newData, primaryKey).then(() => this.getQueuePromise(siteId, fileId, true, onProgress)); } @@ -692,9 +528,9 @@ export class CoreFilepoolProvider { const site = await CoreSites.instance.getSite(siteId); // Get all the packages to be able to "notify" the change in the status. - const entries: CoreFilepoolPackageEntry[] = await site.getDb().getAllRecords(CoreFilepoolProvider.PACKAGES_TABLE); + const entries: CoreFilepoolPackageEntry[] = await site.getDb().getAllRecords(PACKAGES_TABLE_NAME); // Delete all the entries. - await site.getDb().deleteRecords(CoreFilepoolProvider.PACKAGES_TABLE); + await site.getDb().deleteRecords(PACKAGES_TABLE_NAME); entries.forEach((entry) => { // Trigger module status changed, setting it as not downloaded. @@ -712,8 +548,8 @@ export class CoreFilepoolProvider { const db = await CoreSites.instance.getSiteDb(siteId); await Promise.all([ - db.deleteRecords(CoreFilepoolProvider.FILES_TABLE), - db.deleteRecords(CoreFilepoolProvider.LINKS_TABLE), + db.deleteRecords(FILES_TABLE_NAME), + db.deleteRecords(LINKS_TABLE_NAME), ]); } @@ -732,7 +568,7 @@ export class CoreFilepoolProvider { componentId: this.fixComponentId(componentId), }; - const count = await db.countRecords(CoreFilepoolProvider.LINKS_TABLE, conditions); + const count = await db.countRecords(LINKS_TABLE_NAME, conditions); if (count <= 0) { throw new CoreError('Component doesn\'t have files'); } @@ -1257,7 +1093,7 @@ export class CoreFilepoolProvider { // Minor problem: file will remain in the filesystem once downloaded again. this.logger.debug('Staled file with no extension ' + entry.fileId); - await db.updateRecords(CoreFilepoolProvider.FILES_TABLE, { stale: 1 }, { fileId: entry.fileId }); + await db.updateRecords(FILES_TABLE_NAME, { stale: 1 }, { fileId: entry.fileId }); return; } @@ -1267,7 +1103,7 @@ export class CoreFilepoolProvider { entry.fileId = CoreMimetypeUtils.instance.removeExtension(fileId); entry.extension = extension; - await db.updateRecords(CoreFilepoolProvider.FILES_TABLE, entry, { fileId }); + await db.updateRecords(FILES_TABLE_NAME, entry, { fileId }); if (entry.fileId == fileId) { // File ID hasn't changed, we're done. this.logger.debug('Removed extesion ' + extension + ' from file ' + entry.fileId); @@ -1276,7 +1112,7 @@ export class CoreFilepoolProvider { } // Now update the links. - await db.updateRecords(CoreFilepoolProvider.LINKS_TABLE, { fileId: entry.fileId }, { fileId }); + await db.updateRecords(LINKS_TABLE_NAME, { fileId: entry.fileId }, { fileId }); } /** @@ -1339,7 +1175,7 @@ export class CoreFilepoolProvider { componentId: this.fixComponentId(componentId), }; - const items = await db.getRecords(CoreFilepoolProvider.LINKS_TABLE, conditions); + const items = await db.getRecords(LINKS_TABLE_NAME, conditions); items.forEach((item) => { item.componentId = this.fixComponentId(item.componentId); }); @@ -1449,7 +1285,7 @@ export class CoreFilepoolProvider { */ protected async getFileLinks(siteId: string, fileId: string): Promise { const db = await CoreSites.instance.getSiteDb(siteId); - const items = await db.getRecords(CoreFilepoolProvider.LINKS_TABLE, { fileId }); + const items = await db.getRecords(LINKS_TABLE_NAME, { fileId }); items.forEach((item) => { item.componentId = this.fixComponentId(item.componentId); @@ -1527,7 +1363,7 @@ export class CoreFilepoolProvider { await Promise.all(items.map(async (item) => { try { const fileEntry = await db.getRecord( - CoreFilepoolProvider.FILES_TABLE, + FILES_TABLE_NAME, { fileId: item.fileId }, ); @@ -1808,7 +1644,7 @@ export class CoreFilepoolProvider { const site = await CoreSites.instance.getSite(siteId); const packageId = this.getPackageId(component, componentId); - return site.getDb().getRecord(CoreFilepoolProvider.PACKAGES_TABLE, { id: packageId }); + return site.getDb().getRecord(PACKAGES_TABLE_NAME, { id: packageId }); } /** @@ -2258,7 +2094,7 @@ export class CoreFilepoolProvider { */ protected async hasFileInPool(siteId: string, fileId: string): Promise { const db = await CoreSites.instance.getSiteDb(siteId); - const entry = await db.getRecord(CoreFilepoolProvider.FILES_TABLE, { fileId }); + const entry = await db.getRecord(FILES_TABLE_NAME, { fileId }); if (typeof entry === 'undefined') { throw new CoreError('File not found in filepool.'); @@ -2277,7 +2113,7 @@ export class CoreFilepoolProvider { protected async hasFileInQueue(siteId: string, fileId: string): Promise { await this.dbReady; - const entry = await this.appDB.getRecord(CoreFilepoolProvider.QUEUE_TABLE, { siteId, fileId }); + const entry = await this.appDB.getRecord(QUEUE_TABLE_NAME, { siteId, fileId }); if (typeof entry === 'undefined') { throw new CoreError('File not found in queue.'); @@ -2301,7 +2137,7 @@ export class CoreFilepoolProvider { const where = onlyUnknown ? CoreFilepoolProvider.FILE_UPDATE_UNKNOWN_WHERE_CLAUSE : undefined; - await db.updateRecordsWhere(CoreFilepoolProvider.FILES_TABLE, { stale: 1 }, where); + await db.updateRecordsWhere(FILES_TABLE_NAME, { stale: 1 }, where); } /** @@ -2322,7 +2158,7 @@ export class CoreFilepoolProvider { const db = await CoreSites.instance.getSiteDb(siteId); - await db.updateRecords(CoreFilepoolProvider.FILES_TABLE, { stale: 1 }, { fileId }); + await db.updateRecords(FILES_TABLE_NAME, { stale: 1 }, { fileId }); } /** @@ -2359,7 +2195,7 @@ export class CoreFilepoolProvider { whereAndParams[0] += ' AND (' + CoreFilepoolProvider.FILE_UPDATE_UNKNOWN_WHERE_CLAUSE + ')'; } - await db.updateRecordsWhere(CoreFilepoolProvider.FILES_TABLE, { stale: 1 }, whereAndParams[0], whereAndParams[1]); + await db.updateRecordsWhere(FILES_TABLE_NAME, { stale: 1 }, whereAndParams[0], whereAndParams[1]); } /** @@ -2615,7 +2451,7 @@ export class CoreFilepoolProvider { try { items = await this.appDB.getRecords( - CoreFilepoolProvider.QUEUE_TABLE, + QUEUE_TABLE_NAME, undefined, 'priority DESC, added ASC', undefined, @@ -2760,7 +2596,7 @@ export class CoreFilepoolProvider { protected async removeFromQueue(siteId: string, fileId: string): Promise { await this.dbReady; - await this.appDB.deleteRecords(CoreFilepoolProvider.QUEUE_TABLE, { siteId, fileId }); + await this.appDB.deleteRecords(QUEUE_TABLE_NAME, { siteId, fileId }); } /** @@ -2797,10 +2633,10 @@ export class CoreFilepoolProvider { const promises: Promise[] = []; // Remove entry from filepool store. - promises.push(db.deleteRecords(CoreFilepoolProvider.FILES_TABLE, conditions)); + promises.push(db.deleteRecords(FILES_TABLE_NAME, conditions)); // Remove links. - promises.push(db.deleteRecords(CoreFilepoolProvider.LINKS_TABLE, conditions)); + promises.push(db.deleteRecords(LINKS_TABLE_NAME, conditions)); // Remove the file. if (CoreFile.instance.isAvailable()) { @@ -2885,7 +2721,7 @@ export class CoreFilepoolProvider { const packageId = this.getPackageId(component, componentId); // Get current stored data, we'll only update 'status' and 'updated' fields. - const entry = site.getDb().getRecord(CoreFilepoolProvider.PACKAGES_TABLE, { id: packageId }); + const entry = site.getDb().getRecord(PACKAGES_TABLE_NAME, { id: packageId }); const newData: CoreFilepoolPackageEntry = {}; if (entry.status == CoreConstants.DOWNLOADING) { // Going back from downloading to previous status, restore previous download time. @@ -2895,7 +2731,7 @@ export class CoreFilepoolProvider { newData.updated = Date.now(); this.logger.debug(`Set previous status '${entry.status}' for package ${component} ${componentId}`); - await site.getDb().updateRecords(CoreFilepoolProvider.PACKAGES_TABLE, newData, { id: packageId }); + await site.getDb().updateRecords(PACKAGES_TABLE_NAME, newData, { id: packageId }); // Success updating, trigger event. this.triggerPackageStatusChanged(site.id!, newData.status, component, componentId); @@ -2973,7 +2809,7 @@ export class CoreFilepoolProvider { let previousStatus: string | undefined; // Search current status to set it as previous status. try { - const entry = site.getDb().getRecord(CoreFilepoolProvider.PACKAGES_TABLE, { id: packageId }); + const entry = site.getDb().getRecord(PACKAGES_TABLE_NAME, { id: packageId }); if (typeof extra == 'undefined' || extra === null) { extra = entry.extra; } @@ -3008,7 +2844,7 @@ export class CoreFilepoolProvider { return; } - await site.getDb().insertRecord(CoreFilepoolProvider.PACKAGES_TABLE, packageEntry); + await site.getDb().insertRecord(PACKAGES_TABLE_NAME, packageEntry); // Success inserting, trigger event. this.triggerPackageStatusChanged(siteId, status, component, componentId); @@ -3132,7 +2968,7 @@ export class CoreFilepoolProvider { const packageId = this.getPackageId(component, componentId); await site.getDb().updateRecords( - CoreFilepoolProvider.PACKAGES_TABLE, + PACKAGES_TABLE_NAME, { downloadTime: CoreTimeUtils.instance.timestamp() }, { id: packageId }, ); @@ -3142,166 +2978,6 @@ export class CoreFilepoolProvider { export class CoreFilepool extends makeSingleton(CoreFilepoolProvider) {} -/** - * File options. - */ -type CoreFilepoolFileOptions = { - revision?: number; // File's revision. - timemodified?: number; // File's timemodified. - isexternalfile?: number; // 1 if it's a external file (from an external repository), 0 otherwise. - repositorytype?: string; // Type of the repository this file belongs to. -}; - -/** - * Entry from filepool. - */ -export type CoreFilepoolFileEntry = CoreFilepoolFileOptions & { - /** - * The fileId to identify the file. - */ - fileId: string; - - /** - * File's URL. - */ - url: string; - - /** - * 1 if file is stale (needs to be updated), 0 otherwise. - */ - stale: number; - - /** - * Timestamp when this file was downloaded. - */ - downloadTime: number; - - /** - * File's path. - */ - path: string; - - /** - * File's extension. - */ - extension: string; -}; - -/** - * DB data for entry from file's queue. - */ -export type CoreFilepoolQueueDBEntry = CoreFilepoolFileOptions & { - /** - * The site the file belongs to. - */ - siteId: string; - - /** - * The fileId to identify the file. - */ - fileId: string; - - /** - * Timestamp when the file was added to the queue. - */ - added: number; - - /** - * The priority of the file. - */ - priority: number; - - /** - * File's URL. - */ - url: string; - - /** - * File's path. - */ - path?: string; - - /** - * File links (to link the file to components and componentIds). Serialized to store on DB. - */ - links: string; -}; - -/** - * Entry from the file's queue. - */ -export type CoreFilepoolQueueEntry = CoreFilepoolQueueDBEntry & { - /** - * File links (to link the file to components and componentIds). - */ - linksUnserialized?: CoreFilepoolComponentLink[]; -}; - -/** - * Entry from packages table. - */ -export type CoreFilepoolPackageEntry = { - /** - * Package id. - */ - id?: string; - - /** - * The component to link the files to. - */ - component?: string; - - /** - * An ID to use in conjunction with the component. - */ - componentId?: string | number; - - /** - * Package status. - */ - status?: string; - - /** - * Package previous status. - */ - previous?: string; - - /** - * Timestamp when this package was updated. - */ - updated?: number; - - /** - * Timestamp when this package was downloaded. - */ - downloadTime?: number; - - /** - * Previous download time. - */ - previousDownloadTime?: number; - - /** - * Extra data stored by the package. - */ - extra?: string; -}; - -/** - * A component link. - */ -export type CoreFilepoolComponentLink = { - /** - * Link's component. - */ - component: string; - - /** - * Link's componentId. - */ - componentId?: string | number; -}; - /** * File actions. */ @@ -3359,14 +3035,5 @@ type CoreFilepoolPromiseDefer = PromiseDefer & { onProgress?: CoreFilepoolOnProgressCallback; // On Progress function. }; -/** - * Links table record type. - */ -type CoreFilepoolLinksRecord = { - fileId: string; // File Id. - component: string; // Component name. - componentId: number | string; // Component Id. -}; - type AnchorOrMediaElement = HTMLAnchorElement | HTMLImageElement | HTMLAudioElement | HTMLVideoElement | HTMLSourceElement | HTMLTrackElement; diff --git a/src/app/services/local-notifications.db.ts b/src/app/services/local-notifications.db.ts new file mode 100644 index 000000000..4eb333077 --- /dev/null +++ b/src/app/services/local-notifications.db.ts @@ -0,0 +1,80 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { CoreAppSchema } from '@services/app'; +import { PromiseDefer } from '@services/utils/utils'; + +/** + * Database variables for CoreLocalNotifications service. + */ +export const SITES_TABLE_NAME = 'notification_sites'; // Store to asigne unique codes to each site. +export const COMPONENTS_TABLE_NAME = 'notification_components'; // Store to asigne unique codes to each component. +export const TRIGGERED_TABLE_NAME = 'notifications_triggered'; // Store to prevent re-triggering notifications. +export const APP_SCHEMA: CoreAppSchema = { + name: 'CoreLocalNotificationsProvider', + version: 1, + tables: [ + { + name: SITES_TABLE_NAME, + columns: [ + { + name: 'id', + type: 'TEXT', + primaryKey: true, + }, + { + name: 'code', + type: 'INTEGER', + notNull: true, + }, + ], + }, + { + name: COMPONENTS_TABLE_NAME, + columns: [ + { + name: 'id', + type: 'TEXT', + primaryKey: true, + }, + { + name: 'code', + type: 'INTEGER', + notNull: true, + }, + ], + }, + { + name: TRIGGERED_TABLE_NAME, + columns: [ + { + name: 'id', + type: 'INTEGER', + primaryKey: true, + }, + { + name: 'at', + type: 'INTEGER', + notNull: true, + }, + ], + }, + ], +}; + +export type CodeRequestsQueueItem = { + table: string; + id: string; + deferreds: PromiseDefer[]; +}; diff --git a/src/app/services/local-notifications.ts b/src/app/services/local-notifications.ts index 5f1c7e3d7..def7f011c 100644 --- a/src/app/services/local-notifications.ts +++ b/src/app/services/local-notifications.ts @@ -16,11 +16,11 @@ import { Injectable } from '@angular/core'; import { Subject, Subscription } from 'rxjs'; import { ILocalNotification } from '@ionic-native/local-notifications'; -import { CoreApp, CoreAppSchema } from '@services/app'; +import { CoreApp } from '@services/app'; import { CoreConfig } from '@services/config'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreTextUtils } from '@services/utils/text'; -import { CoreUtils, PromiseDefer } from '@services/utils/utils'; +import { CoreUtils } from '@services/utils/utils'; import { SQLiteDB } from '@classes/sqlitedb'; import { CoreSite } from '@classes/site'; import { CoreQueueRunner } from '@classes/queue-runner'; @@ -28,6 +28,13 @@ import { CoreError } from '@classes/errors/error'; import { CoreConstants } from '@core/constants'; import { makeSingleton, NgZone, Platform, Translate, LocalNotifications, Push, Device } from '@singletons/core.singletons'; import { CoreLogger } from '@singletons/logger'; +import { + APP_SCHEMA, + TRIGGERED_TABLE_NAME, + COMPONENTS_TABLE_NAME, + SITES_TABLE_NAME, + CodeRequestsQueueItem, +} from '@services/local-notifications.db'; /** * Service to handle local notifications. @@ -35,62 +42,6 @@ import { CoreLogger } from '@singletons/logger'; @Injectable() export class CoreLocalNotificationsProvider { - // Variables for the database. - protected static readonly SITES_TABLE = 'notification_sites'; // Store to asigne unique codes to each site. - protected static readonly COMPONENTS_TABLE = 'notification_components'; // Store to asigne unique codes to each component. - protected static readonly TRIGGERED_TABLE = 'notifications_triggered'; // Store to prevent re-triggering notifications. - protected tablesSchema: CoreAppSchema = { - name: 'CoreLocalNotificationsProvider', - version: 1, - tables: [ - { - name: CoreLocalNotificationsProvider.SITES_TABLE, - columns: [ - { - name: 'id', - type: 'TEXT', - primaryKey: true, - }, - { - name: 'code', - type: 'INTEGER', - notNull: true, - }, - ], - }, - { - name: CoreLocalNotificationsProvider.COMPONENTS_TABLE, - columns: [ - { - name: 'id', - type: 'TEXT', - primaryKey: true, - }, - { - name: 'code', - type: 'INTEGER', - notNull: true, - }, - ], - }, - { - name: CoreLocalNotificationsProvider.TRIGGERED_TABLE, - columns: [ - { - name: 'id', - type: 'INTEGER', - primaryKey: true, - }, - { - name: 'at', - type: 'INTEGER', - notNull: true, - }, - ], - }, - ], - }; - protected logger: CoreLogger; protected appDB: SQLiteDB; protected dbReady: Promise; // Promise resolved when the app DB is initialized. @@ -111,7 +62,7 @@ export class CoreLocalNotificationsProvider { this.logger = CoreLogger.getInstance('CoreLocalNotificationsProvider'); this.queueRunner = new CoreQueueRunner(10); this.appDB = CoreApp.instance.getDB(); - this.dbReady = CoreApp.instance.createTablesFromSchema(this.tablesSchema).catch(() => { + this.dbReady = CoreApp.instance.createTablesFromSchema(APP_SCHEMA).catch(() => { // Ignore errors. }); @@ -301,7 +252,7 @@ export class CoreLocalNotificationsProvider { * @return Promise resolved when the component code is retrieved. */ protected getComponentCode(component: string): Promise { - return this.requestCode(CoreLocalNotificationsProvider.COMPONENTS_TABLE, component); + return this.requestCode(COMPONENTS_TABLE_NAME, component); } /** @@ -312,7 +263,7 @@ export class CoreLocalNotificationsProvider { * @return Promise resolved when the site code is retrieved. */ protected getSiteCode(siteId: string): Promise { - return this.requestCode(CoreLocalNotificationsProvider.SITES_TABLE, siteId); + return this.requestCode(SITES_TABLE_NAME, siteId); } /** @@ -377,7 +328,7 @@ export class CoreLocalNotificationsProvider { try { const stored = await this.appDB.getRecord<{ id: number; at: number }>( - CoreLocalNotificationsProvider.TRIGGERED_TABLE, + TRIGGERED_TABLE_NAME, { id: notification.id }, ); @@ -532,7 +483,7 @@ export class CoreLocalNotificationsProvider { async removeTriggered(id: number): Promise { await this.dbReady; - await this.appDB.deleteRecords(CoreLocalNotificationsProvider.TRIGGERED_TABLE, { id: id }); + await this.appDB.deleteRecords(TRIGGERED_TABLE_NAME, { id: id }); } /** @@ -695,7 +646,7 @@ export class CoreLocalNotificationsProvider { at: notification.trigger && notification.trigger.at ? notification.trigger.at.getTime() : Date.now(), }; - return this.appDB.insertRecord(CoreLocalNotificationsProvider.TRIGGERED_TABLE, entry); + return this.appDB.insertRecord(TRIGGERED_TABLE_NAME, entry); } /** @@ -708,10 +659,10 @@ export class CoreLocalNotificationsProvider { async updateComponentName(oldName: string, newName: string): Promise { await this.dbReady; - const oldId = CoreLocalNotificationsProvider.COMPONENTS_TABLE + '#' + oldName; - const newId = CoreLocalNotificationsProvider.COMPONENTS_TABLE + '#' + newName; + const oldId = COMPONENTS_TABLE_NAME + '#' + oldName; + const newId = COMPONENTS_TABLE_NAME + '#' + newName; - await this.appDB.updateRecords(CoreLocalNotificationsProvider.COMPONENTS_TABLE, { id: newId }, { id: oldId }); + await this.appDB.updateRecords(COMPONENTS_TABLE_NAME, { id: newId }, { id: oldId }); } } @@ -719,9 +670,3 @@ export class CoreLocalNotificationsProvider { export class CoreLocalNotifications extends makeSingleton(CoreLocalNotificationsProvider) {} export type CoreLocalNotificationsClickCallback = (value: T) => void; - -type CodeRequestsQueueItem = { - table: string; - id: string; - deferreds: PromiseDefer[]; -}; diff --git a/src/app/services/sites.db.ts b/src/app/services/sites.db.ts new file mode 100644 index 000000000..83c6e8027 --- /dev/null +++ b/src/app/services/sites.db.ts @@ -0,0 +1,233 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { CoreAppSchema } from '@services/app'; +import { CoreSiteSchema, registerSiteSchema } from '@services/sites'; +import { SQLiteDB, SQLiteDBTableSchema } from '@classes/sqlitedb'; +import { CoreSite } from '@classes/site'; + +/** + * Database variables for CoreSites service. + */ +export const SITES_TABLE_NAME = 'sites_2'; +export const CURRENT_SITE_TABLE_NAME = 'current_site'; +export const SCHEMA_VERSIONS_TABLE_NAME = 'schema_versions'; + +// Schema to register in App DB. +export const APP_SCHEMA: CoreAppSchema = { + name: 'CoreSitesProvider', + version: 2, + tables: [ + { + name: SITES_TABLE_NAME, + columns: [ + { + name: 'id', + type: 'TEXT', + primaryKey: true, + }, + { + name: 'siteUrl', + type: 'TEXT', + notNull: true, + }, + { + name: 'token', + type: 'TEXT', + }, + { + name: 'info', + type: 'TEXT', + }, + { + name: 'privateToken', + type: 'TEXT', + }, + { + name: 'config', + type: 'TEXT', + }, + { + name: 'loggedOut', + type: 'INTEGER', + }, + { + name: 'oauthId', + type: 'INTEGER', + }, + ], + }, + { + name: CURRENT_SITE_TABLE_NAME, + columns: [ + { + name: 'id', + type: 'INTEGER', + primaryKey: true, + }, + { + name: 'siteId', + type: 'TEXT', + notNull: true, + unique: true, + }, + ], + }, + ], + async migrate(db: SQLiteDB, oldVersion: number): Promise { + if (oldVersion < 2) { + const newTable = SITES_TABLE_NAME; + const oldTable = 'sites'; + + try { + // Check if V1 table exists. + await db.tableExists(oldTable); + + // Move the records from the old table. + const sites = await db.getAllRecords(oldTable); + const promises: Promise[] = []; + + sites.forEach((site) => { + promises.push(db.insertRecord(newTable, site)); + }); + + await Promise.all(promises); + + // Data moved, drop the old table. + await db.dropTable(oldTable); + } catch (error) { + // Old table does not exist, ignore. + } + } + }, +}; + +// Schema to register for Site DB. +export const SITE_SCHEMA: CoreSiteSchema = { + name: 'CoreSitesProvider', + version: 2, + canBeCleared: [CoreSite.WS_CACHE_TABLE], + tables: [ + { + name: CoreSite.WS_CACHE_TABLE, + columns: [ + { + name: 'id', + type: 'TEXT', + primaryKey: true, + }, + { + name: 'data', + type: 'TEXT', + }, + { + name: 'key', + type: 'TEXT', + }, + { + name: 'expirationTime', + type: 'INTEGER', + }, + { + name: 'component', + type: 'TEXT', + }, + { + name: 'componentId', + type: 'INTEGER', + }, + ], + }, + { + name: CoreSite.CONFIG_TABLE, + columns: [ + { + name: 'name', + type: 'TEXT', + unique: true, + notNull: true, + }, + { + name: 'value', + }, + ], + }, + ], + async migrate(db: SQLiteDB, oldVersion: number): Promise { + if (oldVersion && oldVersion < 2) { + const newTable = CoreSite.WS_CACHE_TABLE; + const oldTable = 'wscache'; + + try { + await db.tableExists(oldTable); + } catch (error) { + // Old table does not exist, ignore. + return; + } + // Cannot use insertRecordsFrom because there are extra fields, so manually code INSERT INTO. + await db.execute( + 'INSERT INTO ' + newTable + ' ' + + 'SELECT id, data, key, expirationTime, NULL as component, NULL as componentId ' + + 'FROM ' + oldTable, + ); + + try { + await db.dropTable(oldTable); + } catch (error) { + // Error deleting old table, ignore. + } + } + }, +}; + +// Table for site DB to include the schema versions. It's not part of SITE_SCHEMA because it needs to be created first. +export const SCHEMA_VERSIONS_TABLE_SCHEMA: SQLiteDBTableSchema = { + name: SCHEMA_VERSIONS_TABLE_NAME, + columns: [ + { + name: 'name', + type: 'TEXT', + primaryKey: true, + }, + { + name: 'version', + type: 'INTEGER', + }, + ], +}; + +export type SiteDBEntry = { + id: string; + siteUrl: string; + token: string; + info: string; + privateToken: string; + config: string; + loggedOut: number; + oauthId: number; +}; + +export type CurrentSiteDBEntry = { + id: number; + siteId: string; +}; + +export type SchemaVersionsDBEntry = { + name: string; + version: number; +}; + +export const initCoreSitesDB = (): void => { + registerSiteSchema(SITE_SCHEMA); +}; diff --git a/src/app/services/sites.ts b/src/app/services/sites.ts index 71604d276..7bcccfc8b 100644 --- a/src/app/services/sites.ts +++ b/src/app/services/sites.ts @@ -16,7 +16,7 @@ import { Injectable } from '@angular/core'; import { Md5 } from 'ts-md5/dist/md5'; import { timeout } from 'rxjs/operators'; -import { CoreApp, CoreAppSchema, CoreStoreConfig } from '@services/app'; +import { CoreApp, CoreStoreConfig } from '@services/app'; import { CoreEvents } from '@singletons/events'; import { CoreWS } from '@services/ws'; import { CoreDomUtils } from '@services/utils/dom'; @@ -38,114 +38,36 @@ import { CoreError } from '@classes/errors/error'; import { CoreSiteError } from '@classes/errors/siteerror'; import { makeSingleton, Translate, Http } from '@singletons/core.singletons'; import { CoreLogger } from '@singletons/logger'; +import { + APP_SCHEMA, + SCHEMA_VERSIONS_TABLE_SCHEMA, + SITES_TABLE_NAME, + CURRENT_SITE_TABLE_NAME, + SCHEMA_VERSIONS_TABLE_NAME, + SiteDBEntry, + CurrentSiteDBEntry, + SchemaVersionsDBEntry, +} from '@services/sites.db'; -const SITES_TABLE = 'sites_2'; -const CURRENT_SITE_TABLE = 'current_site'; -const SCHEMA_VERSIONS_TABLE = 'schema_versions'; + +// Schemas for site tables. Other providers can add schemas in here using the registerSiteSchema function. +const siteSchemas: { [name: string]: CoreRegisteredSiteSchema } = {}; +export const registerSiteSchema = (schema: CoreSiteSchema): void => { + siteSchemas[schema.name] = schema; +}; /* * Service to manage and interact with sites. * It allows creating tables in the databases of all sites. Each service or component should be responsible of creating * their own database tables. Example: * - * constructor(sitesProvider: CoreSitesProvider) { - * this.sitesProvider.registerSiteSchema(this.tableSchema); + * import { registerSiteSchema } from '@services/sites'; * - * This provider will automatically create the tables in the databases of all the instantiated sites, and also to the - * databases of sites instantiated from now on. + * registerSiteSchema(tableSchema); */ @Injectable() export class CoreSitesProvider { - // Variables for the database. - protected appTablesSchema: CoreAppSchema = { - name: 'CoreSitesProvider', - version: 2, - tables: [ - { - name: SITES_TABLE, - columns: [ - { - name: 'id', - type: 'TEXT', - primaryKey: true, - }, - { - name: 'siteUrl', - type: 'TEXT', - notNull: true, - }, - { - name: 'token', - type: 'TEXT', - }, - { - name: 'info', - type: 'TEXT', - }, - { - name: 'privateToken', - type: 'TEXT', - }, - { - name: 'config', - type: 'TEXT', - }, - { - name: 'loggedOut', - type: 'INTEGER', - }, - { - name: 'oauthId', - type: 'INTEGER', - }, - ], - }, - { - name: CURRENT_SITE_TABLE, - columns: [ - { - name: 'id', - type: 'INTEGER', - primaryKey: true, - }, - { - name: 'siteId', - type: 'TEXT', - notNull: true, - unique: true, - }, - ], - }, - ], - async migrate(db: SQLiteDB, oldVersion: number): Promise { - if (oldVersion < 2) { - const newTable = SITES_TABLE; - const oldTable = 'sites'; - - try { - // Check if V1 table exists. - await db.tableExists(oldTable); - - // Move the records from the old table. - const sites = await db.getAllRecords(oldTable); - const promises: Promise[] = []; - - sites.forEach((site) => { - promises.push(db.insertRecord(newTable, site)); - }); - - await Promise.all(promises); - - // Data moved, drop the old table. - await db.dropTable(oldTable); - } catch (error) { - // Old table does not exist, ignore. - } - } - }, - }; - // Constants to validate a site version. protected readonly WORKPLACE_APP = 3; protected readonly MOODLE_APP = 2; @@ -162,112 +84,15 @@ export class CoreSitesProvider { protected appDB: SQLiteDB; protected dbReady: Promise; // Promise resolved when the app DB is initialized. protected siteSchemasMigration: { [siteId: string]: Promise } = {}; - - // Schemas for site tables. Other providers can add schemas in here. - protected siteSchemas: { [name: string]: CoreRegisteredSiteSchema } = {}; - protected siteTablesSchemas: SQLiteDBTableSchema[] = [ - { - name: SCHEMA_VERSIONS_TABLE, - columns: [ - { - name: 'name', - type: 'TEXT', - primaryKey: true, - }, - { - name: 'version', - type: 'INTEGER', - }, - ], - }, - ]; - - // Site schema for this provider. - protected siteSchema: CoreSiteSchema = { - name: 'CoreSitesProvider', - version: 2, - canBeCleared: [CoreSite.WS_CACHE_TABLE], - tables: [ - { - name: CoreSite.WS_CACHE_TABLE, - columns: [ - { - name: 'id', - type: 'TEXT', - primaryKey: true, - }, - { - name: 'data', - type: 'TEXT', - }, - { - name: 'key', - type: 'TEXT', - }, - { - name: 'expirationTime', - type: 'INTEGER', - }, - { - name: 'component', - type: 'TEXT', - }, - { - name: 'componentId', - type: 'INTEGER', - }, - ], - }, - { - name: CoreSite.CONFIG_TABLE, - columns: [ - { - name: 'name', - type: 'TEXT', - unique: true, - notNull: true, - }, - { - name: 'value', - }, - ], - }, - ], - async migrate(db: SQLiteDB, oldVersion: number): Promise { - if (oldVersion && oldVersion < 2) { - const newTable = CoreSite.WS_CACHE_TABLE; - const oldTable = 'wscache'; - - try { - await db.tableExists(oldTable); - } catch (error) { - // Old table does not exist, ignore. - return; - } - // Cannot use insertRecordsFrom because there are extra fields, so manually code INSERT INTO. - await db.execute( - 'INSERT INTO ' + newTable + ' ' + - 'SELECT id, data, key, expirationTime, NULL as component, NULL as componentId ' + - 'FROM ' + oldTable, - ); - - try { - await db.dropTable(oldTable); - } catch (error) { - // Error deleting old table, ignore. - } - } - }, - }; + protected pluginsSiteSchemas: { [name: string]: CoreRegisteredSiteSchema } = {}; constructor() { this.logger = CoreLogger.getInstance('CoreSitesProvider'); this.appDB = CoreApp.instance.getDB(); - this.dbReady = CoreApp.instance.createTablesFromSchema(this.appTablesSchema).catch(() => { + this.dbReady = CoreApp.instance.createTablesFromSchema(APP_SCHEMA).catch(() => { // Ignore errors. }); - this.registerSiteSchema(this.siteSchema); } /** @@ -857,7 +682,7 @@ export class CoreSitesProvider { oauthId, }; - await this.appDB.insertRecord(SITES_TABLE, entry); + await this.appDB.insertRecord(SITES_TABLE_NAME, entry); } /** @@ -1084,7 +909,7 @@ export class CoreSitesProvider { delete this.sites[siteId]; try { - await this.appDB.deleteRecords(SITES_TABLE, { id: siteId }); + await this.appDB.deleteRecords(SITES_TABLE_NAME, { id: siteId }); } catch (err) { // DB remove shouldn't fail, but we'll go ahead even if it does. } @@ -1103,7 +928,7 @@ export class CoreSitesProvider { async hasSites(): Promise { await this.dbReady; - const count = await this.appDB.countRecords(SITES_TABLE); + const count = await this.appDB.countRecords(SITES_TABLE_NAME); return count > 0; } @@ -1129,7 +954,7 @@ export class CoreSitesProvider { return this.sites[siteId]; } else { // Retrieve and create the site. - const data = await this.appDB.getRecord(SITES_TABLE, { id: siteId }); + const data = await this.appDB.getRecord(SITES_TABLE_NAME, { id: siteId }); return this.makeSiteFromSiteListEntry(data); } @@ -1202,7 +1027,7 @@ export class CoreSitesProvider { async getSites(ids?: string[]): Promise { await this.dbReady; - const sites = await this.appDB.getAllRecords(SITES_TABLE); + const sites = await this.appDB.getAllRecords(SITES_TABLE_NAME); const formattedSites: CoreSiteBasicInfo[] = []; sites.forEach((site) => { @@ -1266,7 +1091,7 @@ export class CoreSitesProvider { async getLoggedInSitesIds(): Promise { await this.dbReady; - const sites = await this.appDB.getRecords(SITES_TABLE, { loggedOut : 0 }); + const sites = await this.appDB.getRecords(SITES_TABLE_NAME, { loggedOut : 0 }); return sites.map((site) => site.id); } @@ -1279,7 +1104,7 @@ export class CoreSitesProvider { async getSitesIds(): Promise { await this.dbReady; - const sites = await this.appDB.getAllRecords(SITES_TABLE); + const sites = await this.appDB.getAllRecords(SITES_TABLE_NAME); return sites.map((site) => site.id); } @@ -1298,7 +1123,7 @@ export class CoreSitesProvider { siteId, }; - await this.appDB.insertRecord(CURRENT_SITE_TABLE, entry); + await this.appDB.insertRecord(CURRENT_SITE_TABLE_NAME, entry); CoreEvents.trigger(CoreEvents.LOGIN, {}, siteId); } @@ -1324,7 +1149,7 @@ export class CoreSitesProvider { promises.push(this.setSiteLoggedOut(siteId, true)); } - promises.push(this.appDB.deleteRecords(CURRENT_SITE_TABLE, { id: 1 })); + promises.push(this.appDB.deleteRecords(CURRENT_SITE_TABLE_NAME, { id: 1 })); } try { @@ -1349,7 +1174,7 @@ export class CoreSitesProvider { this.sessionRestored = true; try { - const currentSite = await this.appDB.getRecord(CURRENT_SITE_TABLE, { id: 1 }); + const currentSite = await this.appDB.getRecord(CURRENT_SITE_TABLE_NAME, { id: 1 }); const siteId = currentSite.siteId; this.logger.debug(`Restore session in site ${siteId}`); @@ -1377,7 +1202,7 @@ export class CoreSitesProvider { site.setLoggedOut(loggedOut); - await this.appDB.updateRecords(SITES_TABLE, newValues, { id: siteId }); + await this.appDB.updateRecords(SITES_TABLE_NAME, newValues, { id: siteId }); } /** @@ -1426,7 +1251,7 @@ export class CoreSitesProvider { site.privateToken = privateToken; site.setLoggedOut(false); // Token updated means the user authenticated again, not logged out anymore. - await this.appDB.updateRecords(SITES_TABLE, newValues, { id: siteId }); + await this.appDB.updateRecords(SITES_TABLE_NAME, newValues, { id: siteId }); } /** @@ -1470,7 +1295,7 @@ export class CoreSitesProvider { } try { - await this.appDB.updateRecords(SITES_TABLE, newValues, { id: siteId }); + await this.appDB.updateRecords(SITES_TABLE_NAME, newValues, { id: siteId }); } finally { CoreEvents.trigger(CoreEvents.SITE_UPDATED, info, siteId); } @@ -1529,7 +1354,7 @@ export class CoreSitesProvider { } try { - const siteEntries = await this.appDB.getAllRecords(SITES_TABLE); + const siteEntries = await this.appDB.getAllRecords(SITES_TABLE_NAME); const ids: string[] = []; const promises: Promise[] = []; @@ -1562,7 +1387,7 @@ export class CoreSitesProvider { async getStoredCurrentSiteId(): Promise { await this.dbReady; - const currentSite = await this.appDB.getRecord(CURRENT_SITE_TABLE, { id: 1 }); + const currentSite = await this.appDB.getRecord(CURRENT_SITE_TABLE_NAME, { id: 1 }); return currentSite.siteId; } @@ -1605,32 +1430,6 @@ export class CoreSitesProvider { return this.getSite(siteId).then((site) => site.isFeatureDisabled(name)); } - /** - * Create a table in all the sites databases. - * - * @param table Table schema. - * @deprecated. Please use registerSiteSchema instead. - */ - createTableFromSchema(table: SQLiteDBTableSchema): void { - this.createTablesFromSchema([table]); - } - - /** - * Create several tables in all the sites databases. - * - * @param tables List of tables schema. - * @deprecated. Please use registerSiteSchema instead. - */ - createTablesFromSchema(tables: SQLiteDBTableSchema[]): void { - // Add the tables to the list of schemas. This list is to create all the tables in new sites. - this.siteTablesSchemas = this.siteTablesSchemas.concat(tables); - - // Now create these tables in current sites. - for (const id in this.sites) { - this.sites[id].getDb().createTablesFromSchema(tables); - } - } - /** * Check if a WS is available in the current site, if any. * @@ -1645,40 +1444,29 @@ export class CoreSitesProvider { } /** - * Register a site schema. + * Register a site schema in current site. + * This function is meant for site plugins to create DB tables in current site. Tables created from within the app + * whould use the registerSiteSchema function exported in this same file. * * @param schema The schema to register. * @return Promise resolved when done. */ async registerSiteSchema(schema: CoreSiteSchema): Promise { - if (this.currentSite) { - try { - // Site has already been created, apply the schema directly. - const schemas: {[name: string]: CoreRegisteredSiteSchema} = {}; - schemas[schema.name] = schema; + if (!this.currentSite) { + return; + } - if (!schema.onlyCurrentSite) { - // Apply it to all sites. - const siteIds = await this.getSitesIds(); + try { + // Site has already been created, apply the schema directly. + const schemas: {[name: string]: CoreRegisteredSiteSchema} = {}; + schemas[schema.name] = schema; - await Promise.all(siteIds.map(async (siteId) => { - const site = await this.getSite(siteId); + // Apply it to the specified site only. + (schema as CoreRegisteredSiteSchema).siteId = this.currentSite.getId(); - return this.applySiteSchemas(site, schemas); - })); - } else { - // Apply it to the specified site only. - (schema as CoreRegisteredSiteSchema).siteId = this.currentSite.getId(); - - await this.applySiteSchemas(this.currentSite, schemas); - } - } finally { - // Add the schema to the list. It's done in the end to prevent a schema being applied twice. - this.siteSchemas[schema.name] = schema; - } - } else if (!schema.onlyCurrentSite) { - // Add the schema to the list, it will be applied when the sites are created. - this.siteSchemas[schema.name] = schema; + await this.applySiteSchemas(this.currentSite, schemas); + } finally { + this.pluginsSiteSchemas[schema.name] = schema; } } @@ -1700,8 +1488,8 @@ export class CoreSitesProvider { this.logger.debug(`Migrating all schemas of ${site.id}`); // First create tables not registerd with name/version. - const promise = site.getDb().createTablesFromSchema(this.siteTablesSchemas) - .then(() => this.applySiteSchemas(site, this.siteSchemas)); + const promise = site.getDb().createTableFromSchema(SCHEMA_VERSIONS_TABLE_SCHEMA) + .then(() => this.applySiteSchemas(site, siteSchemas)); this.siteSchemasMigration[site.id] = promise; @@ -1721,7 +1509,7 @@ export class CoreSitesProvider { const db = site.getDb(); // Fetch installed versions of the schema. - const records = await db.getAllRecords(SCHEMA_VERSIONS_TABLE); + const records = await db.getAllRecords(SCHEMA_VERSIONS_TABLE_NAME); const versions: {[name: string]: number} = {}; records.forEach((record) => { @@ -1768,7 +1556,7 @@ export class CoreSitesProvider { } // Set installed version. - await db.insertRecord(SCHEMA_VERSIONS_TABLE, { name, version: schema.version }); + await db.insertRecord(SCHEMA_VERSIONS_TABLE_NAME, { name, version: schema.version }); } /** @@ -1814,13 +1602,13 @@ export class CoreSitesProvider { */ getSiteTableSchemasToClear(site: CoreSite): string[] { let reset: string[] = []; - for (const name in this.siteSchemas) { - const schema = this.siteSchemas[name]; + const schemas = Object.values(siteSchemas).concat(Object.values(this.pluginsSiteSchemas)); + schemas.forEach((schema) => { if (schema.canBeCleared && (!schema.siteId || site.getId() == schema.siteId)) { reset = reset.concat(schema.canBeCleared); } - } + }); return reset; } @@ -1980,12 +1768,6 @@ export type CoreSiteSchema = { */ canBeCleared?: string[]; - /** - * If true, the schema will only be applied to the current site. Otherwise it will be applied to all sites. - * If you're implementing a site plugin, please set it to true. - */ - onlyCurrentSite?: boolean; - /** * Tables to create when installing or upgrading the schema. */ @@ -2088,24 +1870,3 @@ export type CoreSitesLoginTokenResponse = { debuginfo?: string; reproductionlink?: string; }; - -type SiteDBEntry = { - id: string; - siteUrl: string; - token: string; - info: string; - privateToken: string; - config: string; - loggedOut: number; - oauthId: number; -}; - -type CurrentSiteDBEntry = { - id: number; - siteId: string; -}; - -type SchemaVersionsDBEntry = { - name: string; - version: number; -}; diff --git a/src/app/services/sync.db.ts b/src/app/services/sync.db.ts new file mode 100644 index 000000000..0f30c0a12 --- /dev/null +++ b/src/app/services/sync.db.ts @@ -0,0 +1,62 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { CoreSiteSchema, registerSiteSchema } from '@services/sites'; + +/** + * Database variables for CoreSync service. + */ +export const SYNC_TABLE_NAME = 'sync'; +export const SITE_SCHEMA: CoreSiteSchema = { + name: 'CoreSyncProvider', + version: 1, + tables: [ + { + name: SYNC_TABLE_NAME, + columns: [ + { + name: 'component', + type: 'TEXT', + notNull: true, + }, + { + name: 'id', + type: 'TEXT', + notNull: true, + }, + { + name: 'time', + type: 'INTEGER', + }, + { + name: 'warnings', + type: 'TEXT', + }, + ], + primaryKeys: ['component', 'id'], + }, + ], +}; + +export type CoreSyncRecord = { + component: string; + id: string; + time: number; + warnings: string; +}; + +export const initCoreSyncDB = (): void => { + registerSiteSchema(SITE_SCHEMA); +}; + diff --git a/src/app/services/sync.ts b/src/app/services/sync.ts index 2246f5cbe..efbe00ab4 100644 --- a/src/app/services/sync.ts +++ b/src/app/services/sync.ts @@ -16,8 +16,7 @@ import { Injectable } from '@angular/core'; import { CoreEvents } from '@singletons/events'; import { CoreSites, CoreSiteSchema } from '@services/sites'; import { makeSingleton } from '@singletons/core.singletons'; - -const SYNC_TABLE = 'sync'; +import { SYNC_TABLE_NAME, CoreSyncRecord } from '@services/sync.db'; /* * Service that provides some features regarding synchronization. @@ -31,7 +30,7 @@ export class CoreSyncProvider { version: 1, tables: [ { - name: SYNC_TABLE, + name: SYNC_TABLE_NAME, columns: [ { name: 'component', @@ -61,8 +60,6 @@ export class CoreSyncProvider { protected blockedItems: { [siteId: string]: { [blockId: string]: { [operation: string]: boolean } } } = {}; constructor() { - CoreSites.instance.registerSiteSchema(this.siteSchema); - // Unblock all blocks on logout. CoreEvents.on(CoreEvents.LOGOUT, (data: {siteId: string}) => { this.clearAllBlocks(data.siteId); @@ -133,7 +130,7 @@ export class CoreSyncProvider { * @return Record if found or reject. */ getSyncRecord(component: string, id: string | number, siteId?: string): Promise { - return CoreSites.instance.getSiteDb(siteId).then((db) => db.getRecord(SYNC_TABLE, { component: component, id: id })); + return CoreSites.instance.getSiteDb(siteId).then((db) => db.getRecord(SYNC_TABLE_NAME, { component: component, id: id })); } /** @@ -151,7 +148,7 @@ export class CoreSyncProvider { data.component = component; data.id = id; - await db.insertRecord(SYNC_TABLE, data); + await db.insertRecord(SYNC_TABLE_NAME, data); } /** @@ -211,10 +208,3 @@ export class CoreSyncProvider { } export class CoreSync extends makeSingleton(CoreSyncProvider) {} - -export type CoreSyncRecord = { - component: string; - id: string; - time: number; - warnings: string; -};