From 869f08eee7d1b8ddab0ea70bc191cfd1f09fe56c Mon Sep 17 00:00:00 2001 From: Noel De Martin Date: Wed, 17 Jan 2024 12:19:18 +0100 Subject: [PATCH] MOBILE-4304 core: Return database records rowId --- .../mod/scorm/services/database/scorm.ts | 10 +++- .../mod/scorm/services/scorm-offline.ts | 40 ++++++++++------ .../classes/database/database-table-proxy.ts | 15 +++--- src/core/classes/database/database-table.ts | 23 +++++++--- .../classes/database/debug-database-table.ts | 13 +++--- .../classes/database/eager-database-table.ts | 13 +++--- .../database/inmemory-database-table.ts | 29 +++++++++++- .../classes/database/lazy-database-table.ts | 11 +++-- src/core/classes/sites/site.ts | 11 +++-- src/core/features/course/services/course.ts | 27 +++++++---- .../course/services/database/course.ts | 5 +- .../services/database/pushnotifications.ts | 10 +++- .../services/pushnotifications.ts | 31 ++++++++++--- .../features/usertours/services/user-tours.ts | 1 - src/core/services/database/filepool.ts | 16 +++++-- src/core/services/database/sites.ts | 5 +- src/core/services/filepool.ts | 46 +++++++++++-------- src/core/services/sites.ts | 14 ++++-- 18 files changed, 219 insertions(+), 101 deletions(-) diff --git a/src/addons/mod/scorm/services/database/scorm.ts b/src/addons/mod/scorm/services/database/scorm.ts index e6e356fe6..39830b2e4 100644 --- a/src/addons/mod/scorm/services/database/scorm.ts +++ b/src/addons/mod/scorm/services/database/scorm.ts @@ -18,7 +18,9 @@ import { CoreSiteSchema } from '@services/sites'; * Database variables for AddonModScormOfflineProvider. */ export const ATTEMPTS_TABLE_NAME = 'addon_mod_scorm_offline_attempts'; +export const ATTEMPTS_TABLE_PRIMARY_KEYS = ['scormid', 'userid', 'attempt'] as const; export const TRACKS_TABLE_NAME = 'addon_mod_scorm_offline_scos_tracks'; +export const TRACKS_TABLE_PRIMARY_KEYS = ['scormid', 'userid', 'attempt', 'scoid', 'element'] as const; export const OFFLINE_SITE_SCHEMA: CoreSiteSchema = { name: 'AddonModScormOfflineProvider', version: 1, @@ -58,7 +60,7 @@ export const OFFLINE_SITE_SCHEMA: CoreSiteSchema = { type: 'TEXT', }, ], - primaryKeys: ['scormid', 'userid', 'attempt'], + primaryKeys: [...ATTEMPTS_TABLE_PRIMARY_KEYS], }, { name: TRACKS_TABLE_NAME, @@ -101,7 +103,7 @@ export const OFFLINE_SITE_SCHEMA: CoreSiteSchema = { type: 'INTEGER', }, ], - primaryKeys: ['scormid', 'userid', 'attempt', 'scoid', 'element'], + primaryKeys: [...TRACKS_TABLE_PRIMARY_KEYS], }, ], }; @@ -125,6 +127,8 @@ export type AddonModScormAttemptDBRecord = AddonModScormOfflineDBCommonData & { snapshot?: string | null; }; +export type AddonModScormAttemptDBPrimaryKeys = typeof ATTEMPTS_TABLE_PRIMARY_KEYS[number]; + /** * SCORM track data. */ @@ -135,3 +139,5 @@ export type AddonModScormTrackDBRecord = AddonModScormOfflineDBCommonData & { timemodified: number; synced: number; }; + +export type AddonModScormTrackDBPrimaryKeys = typeof TRACKS_TABLE_PRIMARY_KEYS[number]; diff --git a/src/addons/mod/scorm/services/scorm-offline.ts b/src/addons/mod/scorm/services/scorm-offline.ts index 343da11c5..907b103ce 100644 --- a/src/addons/mod/scorm/services/scorm-offline.ts +++ b/src/addons/mod/scorm/services/scorm-offline.ts @@ -22,11 +22,15 @@ import { CoreUtils } from '@services/utils/utils'; import { makeSingleton } from '@singletons'; import { CoreLogger } from '@singletons/logger'; import { + AddonModScormAttemptDBPrimaryKeys, AddonModScormAttemptDBRecord, AddonModScormOfflineDBCommonData, + AddonModScormTrackDBPrimaryKeys, AddonModScormTrackDBRecord, ATTEMPTS_TABLE_NAME, + ATTEMPTS_TABLE_PRIMARY_KEYS, TRACKS_TABLE_NAME, + TRACKS_TABLE_PRIMARY_KEYS, } from './database/scorm'; import { AddonModScormDataEntry, @@ -51,33 +55,41 @@ export class AddonModScormOfflineProvider { protected logger: CoreLogger; protected tracksTables: LazyMap< - AsyncInstance> + AsyncInstance> >; protected attemptsTables: LazyMap< - AsyncInstance> + AsyncInstance> >; constructor() { this.logger = CoreLogger.getInstance('AddonModScormOfflineProvider'); this.tracksTables = lazyMap( siteId => asyncInstance( - () => CoreSites.getSiteTable(TRACKS_TABLE_NAME, { - siteId, - primaryKeyColumns: ['scormid', 'userid', 'attempt', 'scoid', 'element'], - config: { cachingStrategy: CoreDatabaseCachingStrategy.None }, - onDestroy: () => delete this.tracksTables[siteId], - }), + () => CoreSites.getSiteTable( + TRACKS_TABLE_NAME, + { + siteId, + primaryKeyColumns: [...TRACKS_TABLE_PRIMARY_KEYS], + rowIdColumn: null, + config: { cachingStrategy: CoreDatabaseCachingStrategy.None }, + onDestroy: () => delete this.tracksTables[siteId], + }, + ), ), ); this.attemptsTables = lazyMap( siteId => asyncInstance( - () => CoreSites.getSiteTable(ATTEMPTS_TABLE_NAME, { - siteId, - primaryKeyColumns: ['scormid', 'userid', 'attempt'], - config: { cachingStrategy: CoreDatabaseCachingStrategy.None }, - onDestroy: () => delete this.tracksTables[siteId], - }), + () => CoreSites.getSiteTable( + ATTEMPTS_TABLE_NAME, + { + siteId, + primaryKeyColumns: [...ATTEMPTS_TABLE_PRIMARY_KEYS], + rowIdColumn: null, + config: { cachingStrategy: CoreDatabaseCachingStrategy.None }, + onDestroy: () => delete this.tracksTables[siteId], + }, + ), ), ); } diff --git a/src/core/classes/database/database-table-proxy.ts b/src/core/classes/database/database-table-proxy.ts index ea7bffd96..4bf1ede74 100644 --- a/src/core/classes/database/database-table-proxy.ts +++ b/src/core/classes/database/database-table-proxy.ts @@ -38,16 +38,17 @@ import { CoreLazyDatabaseTable } from './lazy-database-table'; export class CoreDatabaseTableProxy< DBRecord extends SQLiteDBRecordValues = SQLiteDBRecordValues, PrimaryKeyColumn extends keyof DBRecord = 'id', - PrimaryKey extends GetDBRecordPrimaryKey = GetDBRecordPrimaryKey -> extends CoreDatabaseTable { + RowIdColumn extends PrimaryKeyColumn = PrimaryKeyColumn, + PrimaryKey extends GetDBRecordPrimaryKey = GetDBRecordPrimaryKey, +> extends CoreDatabaseTable { protected readonly DEFAULT_CACHING_STRATEGY = CoreDatabaseCachingStrategy.None; - protected target = asyncInstance>(); + protected target = asyncInstance>(); protected environmentObserver?: CoreEventObserver; protected targetConstructors: Record< CoreDatabaseCachingStrategy, - CoreDatabaseTableConstructor + CoreDatabaseTableConstructor > = { [CoreDatabaseCachingStrategy.Eager]: CoreEagerDatabaseTable, [CoreDatabaseCachingStrategy.Lazy]: CoreLazyDatabaseTable, @@ -154,7 +155,7 @@ export class CoreDatabaseTableProxy< /** * @inheritdoc */ - async insert(record: DBRecord): Promise { + async insert(record: Omit & Partial>): Promise { return this.target.insert(record); } @@ -239,7 +240,7 @@ export class CoreDatabaseTableProxy< * * @returns Target instance. */ - protected async createTarget(): Promise> { + protected async createTarget(): Promise> { const config = await this.getRuntimeConfig(); const table = this.createTable(config); @@ -252,7 +253,7 @@ export class CoreDatabaseTableProxy< * @param config Database configuration. * @returns Database table. */ - protected createTable(config: Partial): CoreDatabaseTable { + protected createTable(config: Partial): CoreDatabaseTable { const DatabaseTable = this.targetConstructors[config.cachingStrategy ?? this.DEFAULT_CACHING_STRATEGY]; return new DatabaseTable(config, this.database, this.tableName, this.primaryKeyColumns); diff --git a/src/core/classes/database/database-table.ts b/src/core/classes/database/database-table.ts index f78d28756..5cb2131fb 100644 --- a/src/core/classes/database/database-table.ts +++ b/src/core/classes/database/database-table.ts @@ -21,13 +21,15 @@ import { SQLiteDB, SQLiteDBRecordValue, SQLiteDBRecordValues } from '@classes/sq export class CoreDatabaseTable< DBRecord extends SQLiteDBRecordValues = SQLiteDBRecordValues, PrimaryKeyColumn extends keyof DBRecord = 'id', - PrimaryKey extends GetDBRecordPrimaryKey = GetDBRecordPrimaryKey + RowIdColumn extends PrimaryKeyColumn = PrimaryKeyColumn, + PrimaryKey extends GetDBRecordPrimaryKey = GetDBRecordPrimaryKey, > { protected config: Partial; protected database: SQLiteDB; protected tableName: string; protected primaryKeyColumns: PrimaryKeyColumn[]; + protected rowIdColumn: RowIdColumn | null; protected listeners: CoreDatabaseTableListener[] = []; constructor( @@ -35,11 +37,13 @@ export class CoreDatabaseTable< database: SQLiteDB, tableName: string, primaryKeyColumns?: PrimaryKeyColumn[], + rowIdColumn?: RowIdColumn | null, ) { this.config = config; this.database = database; this.tableName = tableName; this.primaryKeyColumns = primaryKeyColumns ?? ['id'] as PrimaryKeyColumn[]; + this.rowIdColumn = rowIdColumn === null ? null : (rowIdColumn ?? 'id') as RowIdColumn; } /** @@ -253,9 +257,12 @@ export class CoreDatabaseTable< * Insert a new record. * * @param record Database record. + * @returns New record row id. */ - async insert(record: DBRecord): Promise { - await this.database.insertRecord(this.tableName, record); + async insert(record: Omit & Partial>): Promise { + const rowId = await this.database.insertRecord(this.tableName, record); + + return rowId; } /** @@ -263,7 +270,7 @@ export class CoreDatabaseTable< * * @param record Database record. */ - syncInsert(record: DBRecord): void { + syncInsert(record: Omit & Partial>): void { // The current database architecture does not support synchronous operations, // so calling this method will mean that errors will be silenced. Because of that, // this should only be called if using the asynchronous alternatives is not possible. @@ -423,15 +430,17 @@ export interface CoreDatabaseTableListener { export type CoreDatabaseTableConstructor< DBRecord extends SQLiteDBRecordValues = SQLiteDBRecordValues, PrimaryKeyColumn extends keyof DBRecord = 'id', - PrimaryKey extends GetDBRecordPrimaryKey = GetDBRecordPrimaryKey + RowIdColumn extends PrimaryKeyColumn = PrimaryKeyColumn, + PrimaryKey extends GetDBRecordPrimaryKey = GetDBRecordPrimaryKey, > = { new ( config: Partial, database: SQLiteDB, tableName: string, - primaryKeyColumns?: PrimaryKeyColumn[] - ): CoreDatabaseTable; + primaryKeyColumns?: PrimaryKeyColumn[], + rowIdColumn?: RowIdColumn | null, + ): CoreDatabaseTable; }; diff --git a/src/core/classes/database/debug-database-table.ts b/src/core/classes/database/debug-database-table.ts index 1328a8eea..c3906df25 100644 --- a/src/core/classes/database/debug-database-table.ts +++ b/src/core/classes/database/debug-database-table.ts @@ -30,13 +30,14 @@ import { export class CoreDebugDatabaseTable< DBRecord extends SQLiteDBRecordValues = SQLiteDBRecordValues, PrimaryKeyColumn extends keyof DBRecord = 'id', - PrimaryKey extends GetDBRecordPrimaryKey = GetDBRecordPrimaryKey -> extends CoreDatabaseTable { + RowIdColumn extends PrimaryKeyColumn = PrimaryKeyColumn, + PrimaryKey extends GetDBRecordPrimaryKey = GetDBRecordPrimaryKey, +> extends CoreDatabaseTable { - protected target: CoreDatabaseTable; + protected target: CoreDatabaseTable; protected logger: CoreLogger; - constructor(target: CoreDatabaseTable) { + constructor(target: CoreDatabaseTable) { super(target.getConfig(), target.getDatabase(), target.getTableName(), target.getPrimaryKeyColumns()); this.target = target; @@ -48,7 +49,7 @@ export class CoreDebugDatabaseTable< * * @returns Table instance. */ - getTarget(): CoreDatabaseTable { + getTarget(): CoreDatabaseTable { return this.target; } @@ -152,7 +153,7 @@ export class CoreDebugDatabaseTable< /** * @inheritdoc */ - insert(record: DBRecord): Promise { + insert(record: Omit & Partial>): Promise { this.logger.log('insert', record); return this.target.insert(record); diff --git a/src/core/classes/database/eager-database-table.ts b/src/core/classes/database/eager-database-table.ts index 5db503162..3ed457c50 100644 --- a/src/core/classes/database/eager-database-table.ts +++ b/src/core/classes/database/eager-database-table.ts @@ -31,8 +31,9 @@ import { export class CoreEagerDatabaseTable< DBRecord extends SQLiteDBRecordValues = SQLiteDBRecordValues, PrimaryKeyColumn extends keyof DBRecord = 'id', - PrimaryKey extends GetDBRecordPrimaryKey = GetDBRecordPrimaryKey -> extends CoreInMemoryDatabaseTable { + RowIdColumn extends PrimaryKeyColumn = PrimaryKeyColumn, + PrimaryKey extends GetDBRecordPrimaryKey = GetDBRecordPrimaryKey, +> extends CoreInMemoryDatabaseTable { protected records: Record = {}; @@ -153,12 +154,10 @@ export class CoreEagerDatabaseTable< /** * @inheritdoc */ - async insert(record: DBRecord): Promise { - await super.insert(record); + async insert(record: Omit & Partial>): Promise { + const rowId = await this.insertAndRemember(record, this.records); - const primaryKey = this.serializePrimaryKey(this.getPrimaryKeyFromRecord(record)); - - this.records[primaryKey] = record; + return rowId; } /** diff --git a/src/core/classes/database/inmemory-database-table.ts b/src/core/classes/database/inmemory-database-table.ts index 6637f1f54..3af78a470 100644 --- a/src/core/classes/database/inmemory-database-table.ts +++ b/src/core/classes/database/inmemory-database-table.ts @@ -26,8 +26,9 @@ import { CoreDatabaseTable, GetDBRecordPrimaryKey } from './database-table'; export abstract class CoreInMemoryDatabaseTable< DBRecord extends SQLiteDBRecordValues = SQLiteDBRecordValues, PrimaryKeyColumn extends keyof DBRecord = 'id', - PrimaryKey extends GetDBRecordPrimaryKey = GetDBRecordPrimaryKey -> extends CoreDatabaseTable { + RowIdColumn extends PrimaryKeyColumn = PrimaryKeyColumn, + PrimaryKey extends GetDBRecordPrimaryKey = GetDBRecordPrimaryKey, +> extends CoreDatabaseTable { private static readonly ACTIVE_TABLES: WeakMap> = new WeakMap(); private static readonly LOGGER: CoreLogger = CoreLogger.getInstance('CoreInMemoryDatabaseTable'); @@ -70,4 +71,28 @@ export abstract class CoreInMemoryDatabaseTable< } } + /** + * Insert a new record and store it in the given object. + * + * @param record Database record. + * @param records Records object. + * @returns New record row id. + */ + protected async insertAndRemember( + record: Omit & Partial>, + records: Record, + ): Promise { + const rowId = await super.insert(record); + + const completeRecord = (this.rowIdColumn && !(this.rowIdColumn in record)) + ? Object.assign({ [this.rowIdColumn]: rowId }, record) as DBRecord + : record as DBRecord; + + const primaryKey = this.serializePrimaryKey(this.getPrimaryKeyFromRecord(completeRecord)); + + records[primaryKey] = completeRecord; + + return rowId; + } + } diff --git a/src/core/classes/database/lazy-database-table.ts b/src/core/classes/database/lazy-database-table.ts index faee315d0..c8dd76c31 100644 --- a/src/core/classes/database/lazy-database-table.ts +++ b/src/core/classes/database/lazy-database-table.ts @@ -31,8 +31,9 @@ import { export class CoreLazyDatabaseTable< DBRecord extends SQLiteDBRecordValues = SQLiteDBRecordValues, PrimaryKeyColumn extends keyof DBRecord = 'id', - PrimaryKey extends GetDBRecordPrimaryKey = GetDBRecordPrimaryKey -> extends CoreInMemoryDatabaseTable { + RowIdColumn extends PrimaryKeyColumn = PrimaryKeyColumn, + PrimaryKey extends GetDBRecordPrimaryKey = GetDBRecordPrimaryKey, +> extends CoreInMemoryDatabaseTable { protected readonly DEFAULT_CACHE_LIFETIME = 60000; @@ -137,10 +138,10 @@ export class CoreLazyDatabaseTable< /** * @inheritdoc */ - async insert(record: DBRecord): Promise { - await super.insert(record); + async insert(record: Omit & Partial>): Promise { + const rowId = await this.insertAndRemember(record, this.records); - this.records[this.serializePrimaryKey(this.getPrimaryKeyFromRecord(record))] = record; + return rowId; } /** diff --git a/src/core/classes/sites/site.ts b/src/core/classes/sites/site.ts index b19afa532..da6267c7c 100644 --- a/src/core/classes/sites/site.ts +++ b/src/core/classes/sites/site.ts @@ -42,8 +42,10 @@ import { CoreDatabaseCachingStrategy } from '../database/database-table-proxy'; import { CONFIG_TABLE, CoreSiteConfigDBRecord, + CoreSiteLastViewedDBPrimaryKeys, CoreSiteLastViewedDBRecord, CoreSiteWSCacheRecord, + LAST_VIEWED_PRIMARY_KEYS, LAST_VIEWED_TABLE, WS_CACHE_TABLE, } from '@services/database/sites'; @@ -65,8 +67,8 @@ export class CoreSite extends CoreAuthenticatedSite { protected db!: SQLiteDB; protected cacheTable: AsyncInstance>; - protected configTable: AsyncInstance>; - protected lastViewedTable: AsyncInstance>; + protected configTable: AsyncInstance>; + protected lastViewedTable: AsyncInstance>; protected lastAutoLogin = 0; protected tokenPluginFileWorks?: boolean; protected tokenPluginFileWorksPromise?: Promise; @@ -99,18 +101,19 @@ export class CoreSite extends CoreAuthenticatedSite { config: { cachingStrategy: CoreDatabaseCachingStrategy.None }, })); - this.configTable = asyncInstance(() => CoreSites.getSiteTable(CONFIG_TABLE, { + this.configTable = asyncInstance(() => CoreSites.getSiteTable(CONFIG_TABLE, { siteId: this.getId(), database: this.getDb(), config: { cachingStrategy: CoreDatabaseCachingStrategy.Eager }, primaryKeyColumns: ['name'], + rowIdColumn: null, })); this.lastViewedTable = asyncInstance(() => CoreSites.getSiteTable(LAST_VIEWED_TABLE, { siteId: this.getId(), database: this.getDb(), config: { cachingStrategy: CoreDatabaseCachingStrategy.Eager }, - primaryKeyColumns: ['component', 'id'], + primaryKeyColumns: [...LAST_VIEWED_PRIMARY_KEYS], })); this.setInfo(otherData.info); this.calculateOfflineDisabled(); diff --git a/src/core/features/course/services/course.ts b/src/core/features/course/services/course.ts index 2f0491916..b2cb8d0c6 100644 --- a/src/core/features/course/services/course.ts +++ b/src/core/features/course/services/course.ts @@ -27,7 +27,12 @@ import { makeSingleton, Translate } from '@singletons'; import { CoreStatusWithWarningsWSResponse, CoreWSExternalFile, CoreWSExternalWarning } from '@services/ws'; import { - CoreCourseStatusDBRecord, CoreCourseViewedModulesDBRecord, COURSE_STATUS_TABLE, COURSE_VIEWED_MODULES_TABLE , + CoreCourseStatusDBRecord, + CoreCourseViewedModulesDBPrimaryKeys, + CoreCourseViewedModulesDBRecord, + COURSE_STATUS_TABLE, + COURSE_VIEWED_MODULES_PRIMARY_KEYS, + COURSE_VIEWED_MODULES_TABLE, } from './database/course'; import { CoreCourseOffline } from './course-offline'; import { CoreError } from '@classes/errors/error'; @@ -121,7 +126,9 @@ export class CoreCourseProvider { protected logger: CoreLogger; protected statusTables: LazyMap>>; - protected viewedModulesTables: LazyMap>>; + protected viewedModulesTables: LazyMap< + AsyncInstance> + >; constructor() { this.logger = CoreLogger.getInstance('CoreCourseProvider'); @@ -137,12 +144,16 @@ export class CoreCourseProvider { this.viewedModulesTables = lazyMap( siteId => asyncInstance( - () => CoreSites.getSiteTable(COURSE_VIEWED_MODULES_TABLE, { - siteId, - config: { cachingStrategy: CoreDatabaseCachingStrategy.None }, - primaryKeyColumns: ['courseId', 'cmId'], - onDestroy: () => delete this.viewedModulesTables[siteId], - }), + () => CoreSites.getSiteTable( + COURSE_VIEWED_MODULES_TABLE, + { + siteId, + config: { cachingStrategy: CoreDatabaseCachingStrategy.None }, + primaryKeyColumns: [...COURSE_VIEWED_MODULES_PRIMARY_KEYS], + rowIdColumn: null, + onDestroy: () => delete this.viewedModulesTables[siteId], + }, + ), ), ); } diff --git a/src/core/features/course/services/database/course.ts b/src/core/features/course/services/database/course.ts index 7762d0b91..0c114e1e0 100644 --- a/src/core/features/course/services/database/course.ts +++ b/src/core/features/course/services/database/course.ts @@ -19,6 +19,7 @@ import { CoreSiteSchema } from '@services/sites'; */ export const COURSE_STATUS_TABLE = 'course_status'; export const COURSE_VIEWED_MODULES_TABLE = 'course_viewed_modules'; +export const COURSE_VIEWED_MODULES_PRIMARY_KEYS = ['courseId', 'cmId'] as const; export const SITE_SCHEMA: CoreSiteSchema = { name: 'CoreCourseProvider', version: 2, @@ -75,7 +76,7 @@ export const SITE_SCHEMA: CoreSiteSchema = { type: 'INTEGER', }, ], - primaryKeys: ['courseId', 'cmId'], + primaryKeys: [...COURSE_VIEWED_MODULES_PRIMARY_KEYS], }, ], }; @@ -133,6 +134,8 @@ export type CoreCourseViewedModulesDBRecord = { sectionId?: number; }; +export type CoreCourseViewedModulesDBPrimaryKeys = typeof COURSE_VIEWED_MODULES_PRIMARY_KEYS[number]; + export type CoreCourseManualCompletionDBRecord = { cmid: number; completed: number; diff --git a/src/core/features/pushnotifications/services/database/pushnotifications.ts b/src/core/features/pushnotifications/services/database/pushnotifications.ts index d1e32a531..37d851bba 100644 --- a/src/core/features/pushnotifications/services/database/pushnotifications.ts +++ b/src/core/features/pushnotifications/services/database/pushnotifications.ts @@ -21,8 +21,10 @@ import { CoreSiteSchema } from '@services/sites'; * Keep "addon" in some names for backwards compatibility. */ export const BADGE_TABLE_NAME = 'addon_pushnotifications_badge'; +export const BADGE_TABLE_PRIMARY_KEYS = ['siteid', 'addon'] as const; export const PENDING_UNREGISTER_TABLE_NAME = 'addon_pushnotifications_pending_unregister'; export const REGISTERED_DEVICES_TABLE_NAME = 'addon_pushnotifications_registered_devices_2'; +export const REGISTERED_DEVICES_TABLE_PRIMARY_KEYS = ['appid', 'uuid'] as const; export const APP_SCHEMA: CoreAppSchema = { name: 'CorePushNotificationsProvider', version: 1, @@ -43,7 +45,7 @@ export const APP_SCHEMA: CoreAppSchema = { type: 'INTEGER', }, ], - primaryKeys: ['siteid', 'addon'], + primaryKeys: [...BADGE_TABLE_PRIMARY_KEYS], }, { name: PENDING_UNREGISTER_TABLE_NAME, @@ -109,7 +111,7 @@ export const SITE_SCHEMA: CoreSiteSchema = { type: 'TEXT', }, ], - primaryKeys: ['appid', 'uuid'], + primaryKeys: [...REGISTERED_DEVICES_TABLE_PRIMARY_KEYS], }, ], async migrate(db: SQLiteDB, oldVersion: number): Promise { @@ -129,6 +131,8 @@ export type CorePushNotificationsBadgeDBRecord = { number: number; // eslint-disable-line id-blacklist }; +export type CorePushNotificationsBadgeDBPrimaryKeys = typeof BADGE_TABLE_PRIMARY_KEYS[number]; + /** * Data stored in DB for pending unregisters. */ @@ -152,3 +156,5 @@ export type CorePushNotificationsRegisteredDeviceDBRecord = { pushid: string; // Push ID. publickey?: string; // Public key. }; + +export type CorePushNotificationsRegisteredDeviceDBPrimaryKeys = typeof REGISTERED_DEVICES_TABLE_PRIMARY_KEYS[number]; diff --git a/src/core/features/pushnotifications/services/pushnotifications.ts b/src/core/features/pushnotifications/services/pushnotifications.ts index bfcb5dbca..8a5a9d66e 100644 --- a/src/core/features/pushnotifications/services/pushnotifications.ts +++ b/src/core/features/pushnotifications/services/pushnotifications.ts @@ -36,6 +36,10 @@ import { CorePushNotificationsPendingUnregisterDBRecord, CorePushNotificationsRegisteredDeviceDBRecord, CorePushNotificationsBadgeDBRecord, + REGISTERED_DEVICES_TABLE_PRIMARY_KEYS, + CorePushNotificationsRegisteredDeviceDBPrimaryKeys, + CorePushNotificationsBadgeDBPrimaryKeys, + BADGE_TABLE_PRIMARY_KEYS, } from './database/pushnotifications'; import { CoreError } from '@classes/errors/error'; import { CoreWSExternalWarning } from '@services/ws'; @@ -61,23 +65,38 @@ export class CorePushNotificationsProvider { protected logger: CoreLogger; protected pushID?: string; - protected badgesTable = asyncInstance>(); + protected badgesTable = + asyncInstance>(); + protected pendingUnregistersTable = asyncInstance>(); protected registeredDevicesTables: - LazyMap>>; + LazyMap< + AsyncInstance< + CoreDatabaseTable< + CorePushNotificationsRegisteredDeviceDBRecord, + CorePushNotificationsRegisteredDeviceDBPrimaryKeys, + never + > + > + >; constructor() { this.logger = CoreLogger.getInstance('CorePushNotificationsProvider'); this.registeredDevicesTables = lazyMap( siteId => asyncInstance( - () => CoreSites.getSiteTable( + () => CoreSites.getSiteTable< + CorePushNotificationsRegisteredDeviceDBRecord, + CorePushNotificationsRegisteredDeviceDBPrimaryKeys, + never + >( REGISTERED_DEVICES_TABLE_NAME, { siteId, config: { cachingStrategy: CoreDatabaseCachingStrategy.None }, - primaryKeyColumns: ['appid', 'uuid'], + primaryKeyColumns: [...REGISTERED_DEVICES_TABLE_PRIMARY_KEYS], + rowIdColumn: null, onDestroy: () => delete this.registeredDevicesTables[siteId], }, ), @@ -190,11 +209,11 @@ export class CorePushNotificationsProvider { } const database = CoreApp.getDB(); - const badgesTable = new CoreDatabaseTableProxy( + const badgesTable = new CoreDatabaseTableProxy( { cachingStrategy: CoreDatabaseCachingStrategy.Eager }, database, BADGE_TABLE_NAME, - ['siteid', 'addon'], + [...BADGE_TABLE_PRIMARY_KEYS], ); const pendingUnregistersTable = new CoreDatabaseTableProxy( { cachingStrategy: CoreDatabaseCachingStrategy.Eager }, diff --git a/src/core/features/usertours/services/user-tours.ts b/src/core/features/usertours/services/user-tours.ts index 2dfd3fde6..3afbe03c4 100644 --- a/src/core/features/usertours/services/user-tours.ts +++ b/src/core/features/usertours/services/user-tours.ts @@ -49,7 +49,6 @@ export class CoreUserToursService { { cachingStrategy: CoreDatabaseCachingStrategy.Eager }, CoreApp.getDB(), USER_TOURS_TABLE_NAME, - ['id'], ); await table.initialize(); diff --git a/src/core/services/database/filepool.ts b/src/core/services/database/filepool.ts index 710584800..af9224245 100644 --- a/src/core/services/database/filepool.ts +++ b/src/core/services/database/filepool.ts @@ -19,8 +19,10 @@ import { CoreSiteSchema } from '@services/sites'; * Database variables for CoreFilepool service. */ export const QUEUE_TABLE_NAME = 'filepool_files_queue'; // Queue of files to download. +export const QUEUE_TABLE_PRIMARY_KEYS = ['siteId', 'fileId'] as const; 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 LINKS_TABLE_PRIMARY_KEYS = ['fileId', 'component', 'componentId'] as const; export const PACKAGES_TABLE_NAME = 'filepool_packages'; // Downloaded packages (sets of files). export const APP_SCHEMA: CoreAppSchema = { name: 'CoreFilepoolProvider', @@ -74,7 +76,7 @@ export const APP_SCHEMA: CoreAppSchema = { type: 'TEXT', }, ], - primaryKeys: ['siteId', 'fileId'], + primaryKeys: [...QUEUE_TABLE_PRIMARY_KEYS], }, ], }; @@ -146,7 +148,7 @@ export const SITE_SCHEMA: CoreSiteSchema = { type: 'TEXT', }, ], - primaryKeys: ['fileId', 'component', 'componentId'], + primaryKeys: [...LINKS_TABLE_PRIMARY_KEYS], }, { name: PACKAGES_TABLE_NAME, @@ -241,7 +243,7 @@ export type CoreFilepoolFileEntry = CoreFilepoolFileOptions & { /** * DB data for entry from file's queue. */ -export type CoreFilepoolQueueDBEntry = CoreFilepoolFileOptions & { +export type CoreFilepoolQueueDBRecord = CoreFilepoolFileOptions & { /** * The site the file belongs to. */ @@ -278,10 +280,12 @@ export type CoreFilepoolQueueDBEntry = CoreFilepoolFileOptions & { links: string; }; +export type CoreFilepoolQueueDBPrimaryKeys = typeof QUEUE_TABLE_PRIMARY_KEYS[number]; + /** * Entry from the file's queue. */ -export type CoreFilepoolQueueEntry = CoreFilepoolQueueDBEntry & { +export type CoreFilepoolQueueEntry = CoreFilepoolQueueDBRecord & { /** * File links (to link the file to components and componentIds). */ @@ -356,8 +360,10 @@ export type CoreFilepoolComponentLink = { /** * Links table record type. */ -export type CoreFilepoolLinksRecord = { +export type CoreFilepoolLinksDBRecord = { fileId: string; // File Id. component: string; // Component name. componentId: number | string; // Component Id. }; + +export type CoreFilepoolLinksDBPrimaryKeys = typeof LINKS_TABLE_PRIMARY_KEYS[number]; diff --git a/src/core/services/database/sites.ts b/src/core/services/database/sites.ts index 7460b3480..8f5bb55b1 100644 --- a/src/core/services/database/sites.ts +++ b/src/core/services/database/sites.ts @@ -28,6 +28,7 @@ export const SCHEMA_VERSIONS_TABLE_NAME = 'schema_versions'; export const WS_CACHE_TABLE = 'wscache_2'; export const CONFIG_TABLE = 'core_site_config'; export const LAST_VIEWED_TABLE = 'core_site_last_viewed'; +export const LAST_VIEWED_PRIMARY_KEYS = ['component', 'id'] as const; // Schema to register in App DB. export const APP_SCHEMA: CoreAppSchema = { @@ -156,7 +157,7 @@ export const SITE_SCHEMA: CoreSiteSchema = { type: 'INTEGER', }, ], - primaryKeys: ['component', 'id'], + primaryKeys: [...LAST_VIEWED_PRIMARY_KEYS], }, ], }; @@ -214,3 +215,5 @@ export type CoreSiteLastViewedDBRecord = { timeaccess: number; data?: string; }; + +export type CoreSiteLastViewedDBPrimaryKeys = typeof LAST_VIEWED_PRIMARY_KEYS[number]; diff --git a/src/core/services/filepool.ts b/src/core/services/filepool.ts index f4f1fc764..2e4874e06 100644 --- a/src/core/services/filepool.ts +++ b/src/core/services/filepool.ts @@ -42,10 +42,14 @@ import { CoreFilepoolFileEntry, CoreFilepoolComponentLink, CoreFilepoolFileOptions, - CoreFilepoolLinksRecord, + CoreFilepoolLinksDBRecord, CoreFilepoolPackageEntry, CoreFilepoolQueueEntry, - CoreFilepoolQueueDBEntry, + CoreFilepoolQueueDBRecord, + CoreFilepoolLinksDBPrimaryKeys, + LINKS_TABLE_PRIMARY_KEYS, + CoreFilepoolQueueDBPrimaryKeys, + QUEUE_TABLE_PRIMARY_KEYS, } from '@services/database/filepool'; import { CoreFileHelper } from './file-helper'; import { CoreUrl } from '@singletons/url'; @@ -102,33 +106,39 @@ export class CoreFilepoolProvider { // Variables to prevent downloading packages/files twice at the same time. protected packagesPromises: { [s: string]: { [s: string]: Promise } } = {}; protected filePromises: { [s: string]: { [s: string]: Promise } } = {}; - protected filesTables: LazyMap>>; - protected linksTables: - LazyMap>>; + protected filesTables: LazyMap>>; + protected linksTables: LazyMap< + AsyncInstance> + >; protected packagesTables: LazyMap>>; - protected queueTable = asyncInstance>(); + protected queueTable = asyncInstance>(); constructor() { this.logger = CoreLogger.getInstance('CoreFilepoolProvider'); this.filesTables = lazyMap( siteId => asyncInstance( - () => CoreSites.getSiteTable(FILES_TABLE_NAME, { + () => CoreSites.getSiteTable(FILES_TABLE_NAME, { siteId, config: { cachingStrategy: CoreDatabaseCachingStrategy.Lazy }, primaryKeyColumns: ['fileId'], + rowIdColumn: null, onDestroy: () => delete this.filesTables[siteId], }), ), ); this.linksTables = lazyMap( siteId => asyncInstance( - () => CoreSites.getSiteTable(LINKS_TABLE_NAME, { - siteId, - config: { cachingStrategy: CoreDatabaseCachingStrategy.Lazy }, - primaryKeyColumns: ['fileId', 'component', 'componentId'], - onDestroy: () => delete this.linksTables[siteId], - }), + () => CoreSites.getSiteTable( + LINKS_TABLE_NAME, + { + siteId, + config: { cachingStrategy: CoreDatabaseCachingStrategy.Lazy }, + primaryKeyColumns: [...LINKS_TABLE_PRIMARY_KEYS], + rowIdColumn: null, + onDestroy: () => delete this.linksTables[siteId], + }, + ), ), ); this.packagesTables = lazyMap( @@ -168,11 +178,11 @@ export class CoreFilepoolProvider { // Ignore errors. } - const queueTable = new CoreDatabaseTableProxy( + const queueTable = new CoreDatabaseTableProxy( { cachingStrategy: CoreDatabaseCachingStrategy.Lazy }, CoreApp.getDB(), QUEUE_TABLE_NAME, - ['siteId','fileId'], + [...QUEUE_TABLE_PRIMARY_KEYS], ); await queueTable.initialize(); @@ -406,7 +416,7 @@ export class CoreFilepoolProvider { return this.addToQueue(siteId, fileId, fileUrl, priority, revision, timemodified, filePath, onProgress, options, link); } - const newData: Partial = {}; + const newData: Partial = {}; let foundLink = false; // We already have the file in queue, we update the priority and links. @@ -1245,7 +1255,7 @@ export class CoreFilepoolProvider { siteId: string | undefined, component: string, componentId?: string | number, - ): Promise { + ): Promise { siteId = siteId ?? CoreSites.getCurrentSiteId(); const conditions = { component, @@ -1364,7 +1374,7 @@ export class CoreFilepoolProvider { * @param fileId The file ID. * @returns Promise resolved with the links. */ - protected async getFileLinks(siteId: string, fileId: string): Promise { + protected async getFileLinks(siteId: string, fileId: string): Promise { const items = await this.linksTables[siteId].getMany({ fileId }); items.forEach((item) => { diff --git a/src/core/services/sites.ts b/src/core/services/sites.ts index d5dd45603..4119500de 100644 --- a/src/core/services/sites.ts +++ b/src/core/services/sites.ts @@ -93,7 +93,7 @@ export class CoreSitesProvider { protected siteSchemas: { [name: string]: CoreRegisteredSiteSchema } = {}; protected pluginsSiteSchemas: { [name: string]: CoreRegisteredSiteSchema } = {}; protected siteTables: Record>> = {}; - protected schemasTables: Record>> = {}; + protected schemasTables: Record>> = {}; protected sitesTable = asyncInstance>(); constructor(@Optional() @Inject(CORE_SITE_SCHEMAS) siteSchemas: CoreSiteSchema[][] | null) { @@ -211,7 +211,8 @@ export class CoreSitesProvider { */ async getSiteTable< DBRecord extends SQLiteDBRecordValues, - PrimaryKeyColumn extends keyof DBRecord + PrimaryKeyColumn extends keyof DBRecord, + RowIdColumn extends PrimaryKeyColumn, >( tableName: string, options: Partial<{ @@ -219,9 +220,10 @@ export class CoreSitesProvider { config: Partial; database: SQLiteDB; primaryKeyColumns: PrimaryKeyColumn[]; + rowIdColumn: RowIdColumn | null; onDestroy(): void; }> = {}, - ): Promise> { + ): Promise> { const siteId = options.siteId ?? this.getCurrentSiteId(); if (!(siteId in this.siteTables)) { @@ -231,11 +233,12 @@ export class CoreSitesProvider { if (!(tableName in this.siteTables[siteId])) { const promisedTable = this.siteTables[siteId][tableName] = new CorePromisedValue(); const database = options.database ?? await this.getSiteDb(siteId); - const table = new CoreDatabaseTableProxy( + const table = new CoreDatabaseTableProxy( options.config ?? {}, database, tableName, options.primaryKeyColumns, + options.rowIdColumn, ); options.onDestroy && table.addListener({ onDestroy: options.onDestroy }); @@ -245,7 +248,7 @@ export class CoreSitesProvider { promisedTable.resolve(table as unknown as CoreDatabaseTable); } - return this.siteTables[siteId][tableName] as unknown as Promise>; + return this.siteTables[siteId][tableName] as unknown as Promise>; } /** @@ -2033,6 +2036,7 @@ export class CoreSitesProvider { database: site.getDb(), config: { cachingStrategy: CoreDatabaseCachingStrategy.Eager }, primaryKeyColumns: ['name'], + rowIdColumn: null, onDestroy: () => delete this.schemasTables[siteId], }), );