MOBILE-4304 core: Return database records rowId

main
Noel De Martin 2024-01-17 12:19:18 +01:00
parent b6f32dfddd
commit 869f08eee7
18 changed files with 219 additions and 101 deletions

View File

@ -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];

View File

@ -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<CoreDatabaseTable<AddonModScormTrackDBRecord, 'scormid' | 'userid' | 'attempt' | 'scoid' | 'element'>>
AsyncInstance<CoreDatabaseTable<AddonModScormTrackDBRecord, AddonModScormTrackDBPrimaryKeys, never>>
>;
protected attemptsTables: LazyMap<
AsyncInstance<CoreDatabaseTable<AddonModScormAttemptDBRecord, 'scormid' | 'userid' | 'attempt'>>
AsyncInstance<CoreDatabaseTable<AddonModScormAttemptDBRecord, AddonModScormAttemptDBPrimaryKeys, never>>
>;
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<AddonModScormTrackDBRecord, AddonModScormTrackDBPrimaryKeys, never>(
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<AddonModScormAttemptDBRecord, AddonModScormAttemptDBPrimaryKeys, never>(
ATTEMPTS_TABLE_NAME,
{
siteId,
primaryKeyColumns: [...ATTEMPTS_TABLE_PRIMARY_KEYS],
rowIdColumn: null,
config: { cachingStrategy: CoreDatabaseCachingStrategy.None },
onDestroy: () => delete this.tracksTables[siteId],
},
),
),
);
}

View File

@ -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<DBRecord, PrimaryKeyColumn> = GetDBRecordPrimaryKey<DBRecord, PrimaryKeyColumn>
> extends CoreDatabaseTable<DBRecord, PrimaryKeyColumn, PrimaryKey> {
RowIdColumn extends PrimaryKeyColumn = PrimaryKeyColumn,
PrimaryKey extends GetDBRecordPrimaryKey<DBRecord, PrimaryKeyColumn> = GetDBRecordPrimaryKey<DBRecord, PrimaryKeyColumn>,
> extends CoreDatabaseTable<DBRecord, PrimaryKeyColumn, RowIdColumn, PrimaryKey> {
protected readonly DEFAULT_CACHING_STRATEGY = CoreDatabaseCachingStrategy.None;
protected target = asyncInstance<CoreDatabaseTable<DBRecord, PrimaryKeyColumn>>();
protected target = asyncInstance<CoreDatabaseTable<DBRecord, PrimaryKeyColumn, RowIdColumn>>();
protected environmentObserver?: CoreEventObserver;
protected targetConstructors: Record<
CoreDatabaseCachingStrategy,
CoreDatabaseTableConstructor<DBRecord, PrimaryKeyColumn, PrimaryKey>
CoreDatabaseTableConstructor<DBRecord, PrimaryKeyColumn, RowIdColumn>
> = {
[CoreDatabaseCachingStrategy.Eager]: CoreEagerDatabaseTable,
[CoreDatabaseCachingStrategy.Lazy]: CoreLazyDatabaseTable,
@ -154,7 +155,7 @@ export class CoreDatabaseTableProxy<
/**
* @inheritdoc
*/
async insert(record: DBRecord): Promise<void> {
async insert(record: Omit<DBRecord, RowIdColumn> & Partial<Pick<DBRecord, RowIdColumn>>): Promise<number> {
return this.target.insert(record);
}
@ -239,7 +240,7 @@ export class CoreDatabaseTableProxy<
*
* @returns Target instance.
*/
protected async createTarget(): Promise<CoreDatabaseTable<DBRecord, PrimaryKeyColumn>> {
protected async createTarget(): Promise<CoreDatabaseTable<DBRecord, PrimaryKeyColumn, RowIdColumn>> {
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<CoreDatabaseConfiguration>): CoreDatabaseTable<DBRecord, PrimaryKeyColumn> {
protected createTable(config: Partial<CoreDatabaseConfiguration>): CoreDatabaseTable<DBRecord, PrimaryKeyColumn, RowIdColumn> {
const DatabaseTable = this.targetConstructors[config.cachingStrategy ?? this.DEFAULT_CACHING_STRATEGY];
return new DatabaseTable(config, this.database, this.tableName, this.primaryKeyColumns);

View File

@ -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<DBRecord, PrimaryKeyColumn> = GetDBRecordPrimaryKey<DBRecord, PrimaryKeyColumn>
RowIdColumn extends PrimaryKeyColumn = PrimaryKeyColumn,
PrimaryKey extends GetDBRecordPrimaryKey<DBRecord, PrimaryKeyColumn> = GetDBRecordPrimaryKey<DBRecord, PrimaryKeyColumn>,
> {
protected config: Partial<CoreDatabaseConfiguration>;
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<void> {
await this.database.insertRecord(this.tableName, record);
async insert(record: Omit<DBRecord, RowIdColumn> & Partial<Pick<DBRecord, RowIdColumn>>): Promise<number> {
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<DBRecord, RowIdColumn> & Partial<Pick<DBRecord, RowIdColumn>>): 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<DBRecord, PrimaryKeyColumn> = GetDBRecordPrimaryKey<DBRecord, PrimaryKeyColumn>
RowIdColumn extends PrimaryKeyColumn = PrimaryKeyColumn,
PrimaryKey extends GetDBRecordPrimaryKey<DBRecord, PrimaryKeyColumn> = GetDBRecordPrimaryKey<DBRecord, PrimaryKeyColumn>,
> = {
new (
config: Partial<CoreDatabaseConfiguration>,
database: SQLiteDB,
tableName: string,
primaryKeyColumns?: PrimaryKeyColumn[]
): CoreDatabaseTable<DBRecord, PrimaryKeyColumn, PrimaryKey>;
primaryKeyColumns?: PrimaryKeyColumn[],
rowIdColumn?: RowIdColumn | null,
): CoreDatabaseTable<DBRecord, PrimaryKeyColumn, RowIdColumn, PrimaryKey>;
};

View File

@ -30,13 +30,14 @@ import {
export class CoreDebugDatabaseTable<
DBRecord extends SQLiteDBRecordValues = SQLiteDBRecordValues,
PrimaryKeyColumn extends keyof DBRecord = 'id',
PrimaryKey extends GetDBRecordPrimaryKey<DBRecord, PrimaryKeyColumn> = GetDBRecordPrimaryKey<DBRecord, PrimaryKeyColumn>
> extends CoreDatabaseTable<DBRecord, PrimaryKeyColumn, PrimaryKey> {
RowIdColumn extends PrimaryKeyColumn = PrimaryKeyColumn,
PrimaryKey extends GetDBRecordPrimaryKey<DBRecord, PrimaryKeyColumn> = GetDBRecordPrimaryKey<DBRecord, PrimaryKeyColumn>,
> extends CoreDatabaseTable<DBRecord, PrimaryKeyColumn, RowIdColumn, PrimaryKey> {
protected target: CoreDatabaseTable<DBRecord, PrimaryKeyColumn, PrimaryKey>;
protected target: CoreDatabaseTable<DBRecord, PrimaryKeyColumn, RowIdColumn, PrimaryKey>;
protected logger: CoreLogger;
constructor(target: CoreDatabaseTable<DBRecord, PrimaryKeyColumn, PrimaryKey>) {
constructor(target: CoreDatabaseTable<DBRecord, PrimaryKeyColumn, RowIdColumn, PrimaryKey>) {
super(target.getConfig(), target.getDatabase(), target.getTableName(), target.getPrimaryKeyColumns());
this.target = target;
@ -48,7 +49,7 @@ export class CoreDebugDatabaseTable<
*
* @returns Table instance.
*/
getTarget(): CoreDatabaseTable<DBRecord, PrimaryKeyColumn, PrimaryKey> {
getTarget(): CoreDatabaseTable<DBRecord, PrimaryKeyColumn, RowIdColumn, PrimaryKey> {
return this.target;
}
@ -152,7 +153,7 @@ export class CoreDebugDatabaseTable<
/**
* @inheritdoc
*/
insert(record: DBRecord): Promise<void> {
insert(record: Omit<DBRecord, RowIdColumn> & Partial<Pick<DBRecord, RowIdColumn>>): Promise<number> {
this.logger.log('insert', record);
return this.target.insert(record);

View File

@ -31,8 +31,9 @@ import {
export class CoreEagerDatabaseTable<
DBRecord extends SQLiteDBRecordValues = SQLiteDBRecordValues,
PrimaryKeyColumn extends keyof DBRecord = 'id',
PrimaryKey extends GetDBRecordPrimaryKey<DBRecord, PrimaryKeyColumn> = GetDBRecordPrimaryKey<DBRecord, PrimaryKeyColumn>
> extends CoreInMemoryDatabaseTable<DBRecord, PrimaryKeyColumn, PrimaryKey> {
RowIdColumn extends PrimaryKeyColumn = PrimaryKeyColumn,
PrimaryKey extends GetDBRecordPrimaryKey<DBRecord, PrimaryKeyColumn> = GetDBRecordPrimaryKey<DBRecord, PrimaryKeyColumn>,
> extends CoreInMemoryDatabaseTable<DBRecord, PrimaryKeyColumn, RowIdColumn, PrimaryKey> {
protected records: Record<string, DBRecord> = {};
@ -153,12 +154,10 @@ export class CoreEagerDatabaseTable<
/**
* @inheritdoc
*/
async insert(record: DBRecord): Promise<void> {
await super.insert(record);
async insert(record: Omit<DBRecord, RowIdColumn> & Partial<Pick<DBRecord, RowIdColumn>>): Promise<number> {
const rowId = await this.insertAndRemember(record, this.records);
const primaryKey = this.serializePrimaryKey(this.getPrimaryKeyFromRecord(record));
this.records[primaryKey] = record;
return rowId;
}
/**

View File

@ -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<DBRecord, PrimaryKeyColumn> = GetDBRecordPrimaryKey<DBRecord, PrimaryKeyColumn>
> extends CoreDatabaseTable<DBRecord, PrimaryKeyColumn, PrimaryKey> {
RowIdColumn extends PrimaryKeyColumn = PrimaryKeyColumn,
PrimaryKey extends GetDBRecordPrimaryKey<DBRecord, PrimaryKeyColumn> = GetDBRecordPrimaryKey<DBRecord, PrimaryKeyColumn>,
> extends CoreDatabaseTable<DBRecord, PrimaryKeyColumn, RowIdColumn, PrimaryKey> {
private static readonly ACTIVE_TABLES: WeakMap<SQLiteDB, Set<string>> = 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<DBRecord, RowIdColumn> & Partial<Pick<DBRecord, RowIdColumn>>,
records: Record<string, DBRecord | null>,
): Promise<number> {
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;
}
}

View File

@ -31,8 +31,9 @@ import {
export class CoreLazyDatabaseTable<
DBRecord extends SQLiteDBRecordValues = SQLiteDBRecordValues,
PrimaryKeyColumn extends keyof DBRecord = 'id',
PrimaryKey extends GetDBRecordPrimaryKey<DBRecord, PrimaryKeyColumn> = GetDBRecordPrimaryKey<DBRecord, PrimaryKeyColumn>
> extends CoreInMemoryDatabaseTable<DBRecord, PrimaryKeyColumn, PrimaryKey> {
RowIdColumn extends PrimaryKeyColumn = PrimaryKeyColumn,
PrimaryKey extends GetDBRecordPrimaryKey<DBRecord, PrimaryKeyColumn> = GetDBRecordPrimaryKey<DBRecord, PrimaryKeyColumn>,
> extends CoreInMemoryDatabaseTable<DBRecord, PrimaryKeyColumn, RowIdColumn, PrimaryKey> {
protected readonly DEFAULT_CACHE_LIFETIME = 60000;
@ -137,10 +138,10 @@ export class CoreLazyDatabaseTable<
/**
* @inheritdoc
*/
async insert(record: DBRecord): Promise<void> {
await super.insert(record);
async insert(record: Omit<DBRecord, RowIdColumn> & Partial<Pick<DBRecord, RowIdColumn>>): Promise<number> {
const rowId = await this.insertAndRemember(record, this.records);
this.records[this.serializePrimaryKey(this.getPrimaryKeyFromRecord(record))] = record;
return rowId;
}
/**

View File

@ -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<CoreDatabaseTable<CoreSiteWSCacheRecord>>;
protected configTable: AsyncInstance<CoreDatabaseTable<CoreSiteConfigDBRecord, 'name'>>;
protected lastViewedTable: AsyncInstance<CoreDatabaseTable<CoreSiteLastViewedDBRecord, 'component' | 'id'>>;
protected configTable: AsyncInstance<CoreDatabaseTable<CoreSiteConfigDBRecord, 'name', never>>;
protected lastViewedTable: AsyncInstance<CoreDatabaseTable<CoreSiteLastViewedDBRecord, CoreSiteLastViewedDBPrimaryKeys>>;
protected lastAutoLogin = 0;
protected tokenPluginFileWorks?: boolean;
protected tokenPluginFileWorksPromise?: Promise<boolean>;
@ -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<CoreSiteConfigDBRecord, 'name', never>(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();

View File

@ -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<AsyncInstance<CoreDatabaseTable<CoreCourseStatusDBRecord>>>;
protected viewedModulesTables: LazyMap<AsyncInstance<CoreDatabaseTable<CoreCourseViewedModulesDBRecord, 'courseId' | 'cmId'>>>;
protected viewedModulesTables: LazyMap<
AsyncInstance<CoreDatabaseTable<CoreCourseViewedModulesDBRecord, CoreCourseViewedModulesDBPrimaryKeys, never>>
>;
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<CoreCourseViewedModulesDBRecord, CoreCourseViewedModulesDBPrimaryKeys, never>(
COURSE_VIEWED_MODULES_TABLE,
{
siteId,
config: { cachingStrategy: CoreDatabaseCachingStrategy.None },
primaryKeyColumns: [...COURSE_VIEWED_MODULES_PRIMARY_KEYS],
rowIdColumn: null,
onDestroy: () => delete this.viewedModulesTables[siteId],
},
),
),
);
}

View File

@ -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;

View File

@ -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<void> {
@ -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];

View File

@ -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<CoreDatabaseTable<CorePushNotificationsBadgeDBRecord, 'siteid' | 'addon'>>();
protected badgesTable =
asyncInstance<CoreDatabaseTable<CorePushNotificationsBadgeDBRecord, CorePushNotificationsBadgeDBPrimaryKeys>>();
protected pendingUnregistersTable =
asyncInstance<CoreDatabaseTable<CorePushNotificationsPendingUnregisterDBRecord, 'siteid'>>();
protected registeredDevicesTables:
LazyMap<AsyncInstance<CoreDatabaseTable<CorePushNotificationsRegisteredDeviceDBRecord, 'appid' | 'uuid'>>>;
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<CorePushNotificationsBadgeDBRecord, 'siteid' | 'addon'>(
const badgesTable = new CoreDatabaseTableProxy<CorePushNotificationsBadgeDBRecord, CorePushNotificationsBadgeDBPrimaryKeys>(
{ cachingStrategy: CoreDatabaseCachingStrategy.Eager },
database,
BADGE_TABLE_NAME,
['siteid', 'addon'],
[...BADGE_TABLE_PRIMARY_KEYS],
);
const pendingUnregistersTable = new CoreDatabaseTableProxy<CorePushNotificationsPendingUnregisterDBRecord, 'siteid'>(
{ cachingStrategy: CoreDatabaseCachingStrategy.Eager },

View File

@ -49,7 +49,6 @@ export class CoreUserToursService {
{ cachingStrategy: CoreDatabaseCachingStrategy.Eager },
CoreApp.getDB(),
USER_TOURS_TABLE_NAME,
['id'],
);
await table.initialize();

View File

@ -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];

View File

@ -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];

View File

@ -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<void> } } = {};
protected filePromises: { [s: string]: { [s: string]: Promise<string> } } = {};
protected filesTables: LazyMap<AsyncInstance<CoreDatabaseTable<CoreFilepoolFileEntry, 'fileId'>>>;
protected linksTables:
LazyMap<AsyncInstance<CoreDatabaseTable<CoreFilepoolLinksRecord, 'fileId' | 'component' | 'componentId'>>>;
protected filesTables: LazyMap<AsyncInstance<CoreDatabaseTable<CoreFilepoolFileEntry, 'fileId', never>>>;
protected linksTables: LazyMap<
AsyncInstance<CoreDatabaseTable<CoreFilepoolLinksDBRecord, CoreFilepoolLinksDBPrimaryKeys, never>>
>;
protected packagesTables: LazyMap<AsyncInstance<CoreDatabaseTable<CoreFilepoolPackageEntry>>>;
protected queueTable = asyncInstance<CoreDatabaseTable<CoreFilepoolQueueDBEntry, 'siteId' | 'fileId'>>();
protected queueTable = asyncInstance<CoreDatabaseTable<CoreFilepoolQueueDBRecord, CoreFilepoolQueueDBPrimaryKeys>>();
constructor() {
this.logger = CoreLogger.getInstance('CoreFilepoolProvider');
this.filesTables = lazyMap(
siteId => asyncInstance(
() => CoreSites.getSiteTable(FILES_TABLE_NAME, {
() => CoreSites.getSiteTable<CoreFilepoolFileEntry, 'fileId', never>(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<CoreFilepoolLinksDBRecord, CoreFilepoolLinksDBPrimaryKeys, never>(
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<CoreFilepoolQueueDBEntry, 'siteId' | 'fileId'>(
const queueTable = new CoreDatabaseTableProxy<CoreFilepoolQueueDBRecord, CoreFilepoolQueueDBPrimaryKeys>(
{ 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<CoreFilepoolQueueDBEntry> = {};
const newData: Partial<CoreFilepoolQueueDBRecord> = {};
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<CoreFilepoolLinksRecord[]> {
): Promise<CoreFilepoolLinksDBRecord[]> {
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<CoreFilepoolLinksRecord[]> {
protected async getFileLinks(siteId: string, fileId: string): Promise<CoreFilepoolLinksDBRecord[]> {
const items = await this.linksTables[siteId].getMany({ fileId });
items.forEach((item) => {

View File

@ -93,7 +93,7 @@ export class CoreSitesProvider {
protected siteSchemas: { [name: string]: CoreRegisteredSiteSchema } = {};
protected pluginsSiteSchemas: { [name: string]: CoreRegisteredSiteSchema } = {};
protected siteTables: Record<string, Record<string, CorePromisedValue<CoreDatabaseTable>>> = {};
protected schemasTables: Record<string, AsyncInstance<CoreDatabaseTable<SchemaVersionsDBEntry, 'name'>>> = {};
protected schemasTables: Record<string, AsyncInstance<CoreDatabaseTable<SchemaVersionsDBEntry, 'name', never>>> = {};
protected sitesTable = asyncInstance<CoreDatabaseTable<SiteDBEntry>>();
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<CoreDatabaseConfiguration>;
database: SQLiteDB;
primaryKeyColumns: PrimaryKeyColumn[];
rowIdColumn: RowIdColumn | null;
onDestroy(): void;
}> = {},
): Promise<CoreDatabaseTable<DBRecord, PrimaryKeyColumn>> {
): Promise<CoreDatabaseTable<DBRecord, PrimaryKeyColumn, RowIdColumn>> {
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<DBRecord, PrimaryKeyColumn>(
const table = new CoreDatabaseTableProxy<DBRecord, PrimaryKeyColumn, RowIdColumn>(
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<CoreDatabaseTable<DBRecord, PrimaryKeyColumn>>;
return this.siteTables[siteId][tableName] as unknown as Promise<CoreDatabaseTable<DBRecord, PrimaryKeyColumn, RowIdColumn>>;
}
/**
@ -2033,6 +2036,7 @@ export class CoreSitesProvider {
database: site.getDb(),
config: { cachingStrategy: CoreDatabaseCachingStrategy.Eager },
primaryKeyColumns: ['name'],
rowIdColumn: null,
onDestroy: () => delete this.schemasTables[siteId],
}),
);