forked from EVOgeek/Vmeda.Online
		
	MOBILE-3977 core: Reuse databases initialization
This commit is contained in:
		
							parent
							
								
									7a2a8c3e98
								
							
						
					
					
						commit
						359d7ab5a5
					
				| @ -44,6 +44,7 @@ import { CoreLang } from '@services/lang'; | ||||
| import { CoreSites } from '@services/sites'; | ||||
| import { asyncInstance, AsyncInstance } from '../utils/async-instance'; | ||||
| import { CoreDatabaseTable } from './database/database-table'; | ||||
| import { CoreDatabaseCachingStrategy } from './database/database-table-proxy'; | ||||
| 
 | ||||
| /** | ||||
|  * QR Code type enumeration. | ||||
| @ -140,7 +141,11 @@ export class CoreSite { | ||||
|     ) { | ||||
|         this.logger = CoreLogger.getInstance('CoreSite'); | ||||
|         this.siteUrl = CoreUrlUtils.removeUrlParams(this.siteUrl); // Make sure the URL doesn't have params.
 | ||||
|         this.cacheTable = asyncInstance(() => CoreSites.getCacheTable(this)); | ||||
|         this.cacheTable = asyncInstance(() => CoreSites.getSiteTable(CoreSite.WS_CACHE_TABLE, { | ||||
|             siteId: this.getId(), | ||||
|             database: this.getDb(), | ||||
|             config: { cachingStrategy: CoreDatabaseCachingStrategy.None }, | ||||
|         })); | ||||
|         this.setInfo(infos); | ||||
|         this.calculateOfflineDisabled(); | ||||
| 
 | ||||
|  | ||||
| @ -48,9 +48,10 @@ import { | ||||
| } from '@services/database/filepool'; | ||||
| import { CoreFileHelper } from './file-helper'; | ||||
| import { CoreUrl } from '@singletons/url'; | ||||
| import { CorePromisedValue } from '@classes/promised-value'; | ||||
| import { CoreDatabaseTable } from '@classes/database/database-table'; | ||||
| import { CoreDatabaseCachingStrategy, CoreDatabaseTableProxy } from '@classes/database/database-table-proxy'; | ||||
| import { CoreDatabaseCachingStrategy } from '@classes/database/database-table-proxy'; | ||||
| import { lazyMap, LazyMap } from '../utils/lazy-map'; | ||||
| import { asyncInstance, AsyncInstance } from '../utils/async-instance'; | ||||
| 
 | ||||
| /* | ||||
|  * Factory for handling downloading files and retrieve downloaded files. | ||||
| @ -101,11 +102,20 @@ export class CoreFilepoolProvider { | ||||
|     // Variables for DB.
 | ||||
|     protected appDB: Promise<SQLiteDB>; | ||||
|     protected resolveAppDB!: (appDB: SQLiteDB) => void; | ||||
|     protected filesTables: Record<string, CorePromisedValue<CoreDatabaseTable<CoreFilepoolFileEntry, 'fileId'>>> = {}; | ||||
|     protected filesTables: LazyMap<AsyncInstance<CoreDatabaseTable<CoreFilepoolFileEntry, 'fileId'>>>; | ||||
| 
 | ||||
|     constructor() { | ||||
|         this.appDB = new Promise(resolve => this.resolveAppDB = resolve); | ||||
|         this.logger = CoreLogger.getInstance('CoreFilepoolProvider'); | ||||
|         this.filesTables = lazyMap( | ||||
|             siteId => asyncInstance( | ||||
|                 () => CoreSites.getSiteTable<CoreFilepoolFileEntry, 'fileId'>(FILES_TABLE_NAME, { | ||||
|                     siteId, | ||||
|                     config: { cachingStrategy: CoreDatabaseCachingStrategy.Lazy }, | ||||
|                     primaryKeyColumns: ['fileId'], | ||||
|                 }), | ||||
|             ), | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -128,11 +138,9 @@ export class CoreFilepoolProvider { | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             const filesTable = await this.filesTables[siteId]; | ||||
|             await this.filesTables[siteId].destroy(); | ||||
| 
 | ||||
|             delete this.filesTables[siteId]; | ||||
| 
 | ||||
|             await filesTable.destroy(); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
| @ -149,33 +157,6 @@ export class CoreFilepoolProvider { | ||||
|         this.resolveAppDB(CoreApp.getDB()); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get files table. | ||||
|      * | ||||
|      * @param siteId Site id. | ||||
|      * @returns Files table. | ||||
|      */ | ||||
|     async getFilesTable(siteId?: string): Promise<CoreDatabaseTable<CoreFilepoolFileEntry, 'fileId'>> { | ||||
|         siteId = siteId ?? CoreSites.getCurrentSiteId(); | ||||
| 
 | ||||
|         if (!(siteId in this.filesTables)) { | ||||
|             const filesTable = this.filesTables[siteId] = new CorePromisedValue(); | ||||
|             const database = await CoreSites.getSiteDb(siteId); | ||||
|             const table = new CoreDatabaseTableProxy<CoreFilepoolFileEntry, 'fileId'>( | ||||
|                 { cachingStrategy: CoreDatabaseCachingStrategy.Lazy }, | ||||
|                 database, | ||||
|                 FILES_TABLE_NAME, | ||||
|                 ['fileId'], | ||||
|             ); | ||||
| 
 | ||||
|             await table.initialize(); | ||||
| 
 | ||||
|             filesTable.resolve(table); | ||||
|         } | ||||
| 
 | ||||
|         return this.filesTables[siteId]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Link a file with a component. | ||||
|      * | ||||
| @ -262,9 +243,7 @@ export class CoreFilepoolProvider { | ||||
|             ...data, | ||||
|         }; | ||||
| 
 | ||||
|         const filesTable = await this.getFilesTable(siteId); | ||||
| 
 | ||||
|         await filesTable.insert(record); | ||||
|         await this.filesTables[siteId].insert(record); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -605,14 +584,13 @@ export class CoreFilepoolProvider { | ||||
|      */ | ||||
|     async clearFilepool(siteId: string): Promise<void> { | ||||
|         const db = await CoreSites.getSiteDb(siteId); | ||||
|         const filesTable = await this.getFilesTable(siteId); | ||||
| 
 | ||||
|         // Read the data first to be able to notify the deletions.
 | ||||
|         const filesEntries = await filesTable.all(); | ||||
|         const filesEntries = await this.filesTables[siteId].all(); | ||||
|         const filesLinks = await db.getAllRecords<CoreFilepoolLinksRecord>(LINKS_TABLE_NAME); | ||||
| 
 | ||||
|         await Promise.all([ | ||||
|             filesTable.delete(), | ||||
|             this.filesTables[siteId].delete(), | ||||
|             db.deleteRecords(LINKS_TABLE_NAME), | ||||
|         ]); | ||||
| 
 | ||||
| @ -1167,14 +1145,13 @@ export class CoreFilepoolProvider { | ||||
|         } | ||||
| 
 | ||||
|         const db = await CoreSites.getSiteDb(siteId); | ||||
|         const filesTable = await this.getFilesTable(siteId); | ||||
|         const extension = CoreMimetypeUtils.getFileExtension(entry.path); | ||||
|         if (!extension) { | ||||
|             // Files does not have extension. Invalidate file (stale = true).
 | ||||
|             // Minor problem: file will remain in the filesystem once downloaded again.
 | ||||
|             this.logger.debug('Staled file with no extension ' + entry.fileId); | ||||
| 
 | ||||
|             await filesTable.update({ stale: 1 }, { fileId: entry.fileId }); | ||||
|             await this.filesTables[siteId].update({ stale: 1 }, { fileId: entry.fileId }); | ||||
| 
 | ||||
|             return; | ||||
|         } | ||||
| @ -1184,7 +1161,7 @@ export class CoreFilepoolProvider { | ||||
|         entry.fileId = CoreMimetypeUtils.removeExtension(fileId); | ||||
|         entry.extension = extension; | ||||
| 
 | ||||
|         await filesTable.update(entry, { fileId }); | ||||
|         await this.filesTables[siteId].update(entry, { fileId }); | ||||
|         if (entry.fileId == fileId) { | ||||
|             // File ID hasn't changed, we're done.
 | ||||
|             this.logger.debug('Removed extesion ' + extension + ' from file ' + entry.fileId); | ||||
| @ -1445,13 +1422,12 @@ export class CoreFilepoolProvider { | ||||
|      */ | ||||
|     async getFilesByComponent(siteId: string, component: string, componentId?: string | number): Promise<CoreFilepoolFileEntry[]> { | ||||
|         const db = await CoreSites.getSiteDb(siteId); | ||||
|         const filesTable = await this.getFilesTable(siteId); | ||||
|         const items = await this.getComponentFiles(db, component, componentId); | ||||
|         const files: CoreFilepoolFileEntry[] = []; | ||||
| 
 | ||||
|         await Promise.all(items.map(async (item) => { | ||||
|             try { | ||||
|                 const fileEntry = await filesTable.findByPrimaryKey({ fileId: item.fileId }); | ||||
|                 const fileEntry = await this.filesTables[siteId].findByPrimaryKey({ fileId: item.fileId }); | ||||
| 
 | ||||
|                 if (!fileEntry) { | ||||
|                     return; | ||||
| @ -2184,9 +2160,7 @@ export class CoreFilepoolProvider { | ||||
|      * @return Resolved with file object from DB on success, rejected otherwise. | ||||
|      */ | ||||
|     protected async hasFileInPool(siteId: string, fileId: string): Promise<CoreFilepoolFileEntry> { | ||||
|         const filesTable = await this.getFilesTable(siteId); | ||||
| 
 | ||||
|         return filesTable.findByPrimaryKey({ fileId }); | ||||
|         return this.filesTables[siteId].findByPrimaryKey({ fileId }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -2218,17 +2192,15 @@ export class CoreFilepoolProvider { | ||||
|      * @return Resolved on success. | ||||
|      */ | ||||
|     async invalidateAllFiles(siteId: string, onlyUnknown: boolean = true): Promise<void> { | ||||
|         const filesTable = await this.getFilesTable(siteId); | ||||
| 
 | ||||
|         onlyUnknown | ||||
|             ? await filesTable.updateWhere( | ||||
|             ? await this.filesTables[siteId].updateWhere( | ||||
|                 { stale: 1 }, | ||||
|                 { | ||||
|                     sql: CoreFilepoolProvider.FILE_IS_UNKNOWN_SQL, | ||||
|                     js: CoreFilepoolProvider.FILE_IS_UNKNOWN_JS, | ||||
|                 }, | ||||
|             ) | ||||
|             : await filesTable.update({ stale: 1 }); | ||||
|             : await this.filesTables[siteId].update({ stale: 1 }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -2247,9 +2219,7 @@ export class CoreFilepoolProvider { | ||||
|         const file = await this.fixPluginfileURL(siteId, fileUrl); | ||||
|         const fileId = this.getFileIdByUrl(CoreFileHelper.getFileUrl(file)); | ||||
| 
 | ||||
|         const filesTable = await this.getFilesTable(siteId); | ||||
| 
 | ||||
|         await filesTable.update({ stale: 1 }, { fileId }); | ||||
|         await this.filesTables[siteId].update({ stale: 1 }, { fileId }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -2269,7 +2239,6 @@ export class CoreFilepoolProvider { | ||||
|         onlyUnknown: boolean = true, | ||||
|     ): Promise<void> { | ||||
|         const db = await CoreSites.getSiteDb(siteId); | ||||
|         const filesTable = await this.getFilesTable(siteId); | ||||
|         const items = await this.getComponentFiles(db, component, componentId); | ||||
| 
 | ||||
|         if (!items.length) { | ||||
| @ -2277,6 +2246,8 @@ export class CoreFilepoolProvider { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         siteId = siteId ?? CoreSites.getCurrentSiteId(); | ||||
| 
 | ||||
|         const fileIds = items.map((item) => item.fileId); | ||||
| 
 | ||||
|         const whereAndParams = db.getInOrEqual(fileIds); | ||||
| @ -2287,7 +2258,7 @@ export class CoreFilepoolProvider { | ||||
|             whereAndParams.sql += ' AND (' + CoreFilepoolProvider.FILE_IS_UNKNOWN_SQL + ')'; | ||||
|         } | ||||
| 
 | ||||
|         await filesTable.updateWhere( | ||||
|         await this.filesTables[siteId].updateWhere( | ||||
|             { stale: 1 }, | ||||
|             { | ||||
|                 sql: whereAndParams.sql, | ||||
| @ -2714,7 +2685,6 @@ export class CoreFilepoolProvider { | ||||
|      */ | ||||
|     protected async removeFileById(siteId: string, fileId: string): Promise<void> { | ||||
|         const db = await CoreSites.getSiteDb(siteId); | ||||
|         const filesTable = await this.getFilesTable(siteId); | ||||
| 
 | ||||
|         // Get the path to the file first since it relies on the file object stored in the pool.
 | ||||
|         // Don't use getFilePath to prevent performing 2 DB requests.
 | ||||
| @ -2741,7 +2711,7 @@ export class CoreFilepoolProvider { | ||||
|         const promises: Promise<unknown>[] = []; | ||||
| 
 | ||||
|         // Remove entry from filepool store.
 | ||||
|         promises.push(filesTable.delete(conditions)); | ||||
|         promises.push(this.filesTables[siteId].delete(conditions)); | ||||
| 
 | ||||
|         // Remove links.
 | ||||
|         promises.push(db.deleteRecords(LINKS_TABLE_NAME, conditions)); | ||||
|  | ||||
| @ -31,9 +31,8 @@ import { | ||||
|     CoreSiteConfig, | ||||
|     CoreSitePublicConfigResponse, | ||||
|     CoreSiteInfoResponse, | ||||
|     CoreSiteWSCacheRecord, | ||||
| } from '@classes/site'; | ||||
| import { SQLiteDB, SQLiteDBTableSchema } from '@classes/sqlitedb'; | ||||
| import { SQLiteDB, SQLiteDBRecordValues, SQLiteDBTableSchema } from '@classes/sqlitedb'; | ||||
| import { CoreError } from '@classes/errors/error'; | ||||
| import { CoreSiteError } from '@classes/errors/siteerror'; | ||||
| import { makeSingleton, Translate, Http } from '@singletons'; | ||||
| @ -60,7 +59,7 @@ import { CoreAjaxWSError } from '@classes/errors/ajaxwserror'; | ||||
| import { CoreSitePlugins } from '@features/siteplugins/services/siteplugins'; | ||||
| import { CorePromisedValue } from '@classes/promised-value'; | ||||
| import { CoreDatabaseTable } from '@classes/database/database-table'; | ||||
| import { CoreDatabaseCachingStrategy, CoreDatabaseTableProxy } from '@classes/database/database-table-proxy'; | ||||
| import { CoreDatabaseConfiguration, CoreDatabaseTableProxy } from '@classes/database/database-table-proxy'; | ||||
| 
 | ||||
| export const CORE_SITE_SCHEMAS = new InjectionToken<CoreSiteSchema[]>('CORE_SITE_SCHEMAS'); | ||||
| 
 | ||||
| @ -89,7 +88,7 @@ export class CoreSitesProvider { | ||||
|     // Variables for DB.
 | ||||
|     protected appDB: Promise<SQLiteDB>; | ||||
|     protected resolveAppDB!: (appDB: SQLiteDB) => void; | ||||
|     protected cacheTables: Record<string, CorePromisedValue<CoreDatabaseTable<CoreSiteWSCacheRecord>>> = {}; | ||||
|     protected siteTables: Record<string, Record<string, CorePromisedValue<CoreDatabaseTable>>> = {}; | ||||
| 
 | ||||
|     constructor(@Optional() @Inject(CORE_SITE_SCHEMAS) siteSchemas: CoreSiteSchema[][] = []) { | ||||
|         this.appDB = new Promise(resolve => this.resolveAppDB = resolve); | ||||
| @ -109,15 +108,17 @@ export class CoreSitesProvider { | ||||
|      */ | ||||
|     initialize(): void { | ||||
|         CoreEvents.on(CoreEvents.SITE_DELETED, async ({ siteId }) => { | ||||
|             if (!siteId || !(siteId in this.cacheTables)) { | ||||
|             if (!siteId || !(siteId in this.siteTables)) { | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             const cacheTable = await this.cacheTables[siteId]; | ||||
|             await Promise.all( | ||||
|                 Object | ||||
|                     .values(this.siteTables[siteId]) | ||||
|                     .map(promisedTable => promisedTable.then(table => table.destroy())), | ||||
|             ); | ||||
| 
 | ||||
|             delete this.cacheTables[siteId]; | ||||
| 
 | ||||
|             await cacheTable.destroy(); | ||||
|             delete this.siteTables[siteId]; | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
| @ -135,30 +136,46 @@ export class CoreSitesProvider { | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get cache table. | ||||
|      * Get site table. | ||||
|      * | ||||
|      * @param siteId Site id. | ||||
|      * @returns cache table. | ||||
|      * @param tableName Site table name. | ||||
|      * @param options Options to configure table initialization. | ||||
|      * @returns Site table. | ||||
|      */ | ||||
|     async getCacheTable(site: CoreSite): Promise<CoreDatabaseTable<CoreSiteWSCacheRecord>> { | ||||
|         if (!site.id) { | ||||
|             throw new CoreError('Can\'t get cache table for site without id'); | ||||
|     async getSiteTable< | ||||
|         DBRecord extends SQLiteDBRecordValues, | ||||
|         PrimaryKeyColumn extends keyof DBRecord | ||||
|     >( | ||||
|         tableName: string, | ||||
|         options: Partial<{ | ||||
|             siteId: string; | ||||
|             config: Partial<CoreDatabaseConfiguration>; | ||||
|             database: SQLiteDB; | ||||
|             primaryKeyColumns: PrimaryKeyColumn[]; | ||||
|         }> = {}, | ||||
|     ): Promise<CoreDatabaseTable<DBRecord, PrimaryKeyColumn>> { | ||||
|         const siteId = options.siteId ?? this.getCurrentSiteId(); | ||||
| 
 | ||||
|         if (!(siteId in this.siteTables)) { | ||||
|             this.siteTables[siteId] = {}; | ||||
|         } | ||||
| 
 | ||||
|         if (!(site.id in this.cacheTables)) { | ||||
|             const promisedTable = this.cacheTables[site.id] = new CorePromisedValue(); | ||||
|             const table = new CoreDatabaseTableProxy<CoreSiteWSCacheRecord>( | ||||
|                 { cachingStrategy: CoreDatabaseCachingStrategy.None }, | ||||
|                 site.getDb(), | ||||
|                 CoreSite.WS_CACHE_TABLE, | ||||
|         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>( | ||||
|                 options.config ?? {}, | ||||
|                 database, | ||||
|                 tableName, | ||||
|                 options.primaryKeyColumns, | ||||
|             ); | ||||
| 
 | ||||
|             await table.initialize(); | ||||
| 
 | ||||
|             promisedTable.resolve(table); | ||||
|             promisedTable.resolve(table as unknown as CoreDatabaseTable); | ||||
|         } | ||||
| 
 | ||||
|         return this.cacheTables[site.id]; | ||||
|         return this.siteTables[siteId][tableName] as unknown as Promise<CoreDatabaseTable<DBRecord, PrimaryKeyColumn>>; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  | ||||
							
								
								
									
										40
									
								
								src/core/utils/lazy-map.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/core/utils/lazy-map.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,40 @@ | ||||
| // (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.
 | ||||
| 
 | ||||
| /** | ||||
|  * Lazy map. | ||||
|  * | ||||
|  * Lazy maps are empty by default, but entries are generated lazily when accessed. | ||||
|  */ | ||||
| export type LazyMap<T> = Record<string, T>; | ||||
| 
 | ||||
| /** | ||||
|  * Create a map that will initialize entries lazily with the given constructor. | ||||
|  * | ||||
|  * @param lazyConstructor Constructor to use the first time an entry is accessed. | ||||
|  * @returns Lazy map. | ||||
|  */ | ||||
| export function lazyMap<T>(lazyConstructor: (key: string) => T): LazyMap<T> { | ||||
|     const instances = {}; | ||||
| 
 | ||||
|     return new Proxy(instances, { | ||||
|         get(target, property, receiver) { | ||||
|             if (!(property in instances)) { | ||||
|                 target[property] = lazyConstructor(property.toString()); | ||||
|             } | ||||
| 
 | ||||
|             return Reflect.get(target, property, receiver); | ||||
|         }, | ||||
|     }); | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user