MOBILE-3833 core: Improve database config updates
This commit is contained in:
		
							parent
							
								
									6f222fe2ab
								
							
						
					
					
						commit
						3f71825ead
					
				| @ -23,6 +23,7 @@ import { | ||||
|     CoreDatabaseConditions, | ||||
|     GetDBRecordPrimaryKey, | ||||
|     CoreDatabaseQueryOptions, | ||||
|     CoreDatabaseTableConstructor, | ||||
| } from './database-table'; | ||||
| import { CoreDebugDatabaseTable } from './debug-database-table'; | ||||
| import { CoreEagerDatabaseTable } from './eager-database-table'; | ||||
| @ -42,6 +43,14 @@ export class CoreDatabaseTableProxy< | ||||
|     protected config: CoreDatabaseConfiguration; | ||||
|     protected target = asyncInstance<CoreDatabaseTable<DBRecord, PrimaryKeyColumn>>(); | ||||
|     protected environmentObserver?: CoreEventObserver; | ||||
|     protected targetConstructors: Record< | ||||
|         CoreDatabaseCachingStrategy, | ||||
|         CoreDatabaseTableConstructor<DBRecord, PrimaryKeyColumn, PrimaryKey> | ||||
|     > = { | ||||
|         [CoreDatabaseCachingStrategy.Eager]: CoreEagerDatabaseTable, | ||||
|         [CoreDatabaseCachingStrategy.Lazy]: CoreLazyDatabaseTable, | ||||
|         [CoreDatabaseCachingStrategy.None]: CoreDatabaseTable, | ||||
|     }; | ||||
| 
 | ||||
|     constructor( | ||||
|         config: Partial<CoreDatabaseConfiguration>, | ||||
| @ -58,7 +67,13 @@ export class CoreDatabaseTableProxy< | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     async initialize(): Promise<void> { | ||||
|         this.environmentObserver = CoreEvents.on(CoreConfigProvider.ENVIRONMENT_UPDATED, () => this.updateTarget()); | ||||
|         this.environmentObserver = CoreEvents.on(CoreConfigProvider.ENVIRONMENT_UPDATED, async () => { | ||||
|             if (!(await this.shouldUpdateTarget())) { | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             this.updateTarget(); | ||||
|         }); | ||||
| 
 | ||||
|         await this.updateTarget(); | ||||
|     } | ||||
| @ -202,6 +217,20 @@ export class CoreDatabaseTableProxy< | ||||
|         this.target.setInstance(newTarget); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Check whether the underlying target should be updated. | ||||
|      * | ||||
|      * @returns Whether target should be updated. | ||||
|      */ | ||||
|     protected async shouldUpdateTarget(): Promise<boolean> { | ||||
|         const config = await this.getRuntimeConfig(); | ||||
|         const target = await this.target.getInstance(); | ||||
|         const originalTarget = target instanceof CoreDebugDatabaseTable ? target.getTarget() : target; | ||||
| 
 | ||||
|         return (config.debug && target === originalTarget) | ||||
|             || originalTarget?.constructor !== this.targetConstructors[config.cachingStrategy]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Create proxy target. | ||||
|      * | ||||
| @ -221,14 +250,9 @@ export class CoreDatabaseTableProxy< | ||||
|      * @returns Database table. | ||||
|      */ | ||||
|     protected createTable(cachingStrategy: CoreDatabaseCachingStrategy): CoreDatabaseTable<DBRecord, PrimaryKeyColumn> { | ||||
|         switch (cachingStrategy) { | ||||
|             case CoreDatabaseCachingStrategy.Eager: | ||||
|                 return new CoreEagerDatabaseTable(this.database, this.tableName, this.primaryKeyColumns); | ||||
|             case CoreDatabaseCachingStrategy.Lazy: | ||||
|                 return new CoreLazyDatabaseTable(this.database, this.tableName, this.primaryKeyColumns); | ||||
|             case CoreDatabaseCachingStrategy.None: | ||||
|                 return new CoreDatabaseTable(this.database, this.tableName, this.primaryKeyColumns); | ||||
|         } | ||||
|         const DatabaseTable = this.targetConstructors[cachingStrategy]; | ||||
| 
 | ||||
|         return new DatabaseTable(this.database, this.tableName, this.primaryKeyColumns); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -336,6 +336,23 @@ export class CoreDatabaseTable< | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * CoreDatabaseTable constructor. | ||||
|  */ | ||||
| export type CoreDatabaseTableConstructor< | ||||
|     DBRecord extends SQLiteDBRecordValues = SQLiteDBRecordValues, | ||||
|     PrimaryKeyColumn extends keyof DBRecord = 'id', | ||||
|     PrimaryKey extends GetDBRecordPrimaryKey<DBRecord, PrimaryKeyColumn> = GetDBRecordPrimaryKey<DBRecord, PrimaryKeyColumn> | ||||
| > = { | ||||
| 
 | ||||
|     new ( | ||||
|         database: SQLiteDB, | ||||
|         tableName: string, | ||||
|         primaryKeyColumns?: PrimaryKeyColumn[] | ||||
|     ): CoreDatabaseTable<DBRecord, PrimaryKeyColumn, PrimaryKey>; | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| /** | ||||
|  * Infer primary key type from database record and primary key column types. | ||||
|  */ | ||||
|  | ||||
| @ -43,6 +43,13 @@ export class CoreDebugDatabaseTable< | ||||
|         this.logger = CoreLogger.getInstance(`CoreDatabase[${this.tableName}]`); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get underlying table instance. | ||||
|      */ | ||||
|     getTarget(): CoreDatabaseTable<DBRecord, PrimaryKeyColumn, PrimaryKey> { | ||||
|         return this.target; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|  | ||||
| @ -333,7 +333,14 @@ export class CoreLoginCredentialsPage implements OnInit, OnDestroy { | ||||
|      */ | ||||
|     ngOnDestroy(): void { | ||||
|         this.viewLeft = true; | ||||
|         CoreEvents.trigger(CoreEvents.LOGIN_SITE_UNCHECKED, { config: this.siteConfig }, this.siteId); | ||||
|         CoreEvents.trigger( | ||||
|             CoreEvents.LOGIN_SITE_UNCHECKED, | ||||
|             { | ||||
|                 config: this.siteConfig, | ||||
|                 loginSuccessful: !!this.siteId, | ||||
|             }, | ||||
|             this.siteId, | ||||
|         ); | ||||
|         this.valueChangeSubscription?.unsubscribe(); | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -57,6 +57,7 @@ export class CoreLoginReconnectPage implements OnInit, OnDestroy { | ||||
|     protected viewLeft = false; | ||||
|     protected eventThrown = false; | ||||
|     protected redirectData?: CoreRedirectPayload; | ||||
|     protected loginSuccessful = false; | ||||
| 
 | ||||
|     constructor( | ||||
|         protected fb: FormBuilder, | ||||
| @ -117,7 +118,14 @@ export class CoreLoginReconnectPage implements OnInit, OnDestroy { | ||||
|      */ | ||||
|     ngOnDestroy(): void { | ||||
|         this.viewLeft = true; | ||||
|         CoreEvents.trigger(CoreEvents.LOGIN_SITE_UNCHECKED, { config: this.siteConfig }, this.siteId); | ||||
|         CoreEvents.trigger( | ||||
|             CoreEvents.LOGIN_SITE_UNCHECKED, | ||||
|             { | ||||
|                 config: this.siteConfig, | ||||
|                 loginSuccessful: this.loginSuccessful, | ||||
|             }, | ||||
|             this.siteId, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -214,6 +222,8 @@ export class CoreLoginReconnectPage implements OnInit, OnDestroy { | ||||
|             this.credForm.controls['password'].reset(); | ||||
| 
 | ||||
|             // Go to the site initial page.
 | ||||
|             this.loginSuccessful = true; | ||||
| 
 | ||||
|             await CoreNavigator.navigateToSiteHome({ | ||||
|                 params: this.redirectData, | ||||
|             }); | ||||
|  | ||||
| @ -108,19 +108,11 @@ export class CoreStylesService { | ||||
|      * Listen events. | ||||
|      */ | ||||
|     protected listenEvents(): void { | ||||
|         let addingSite: string | undefined; | ||||
| 
 | ||||
|         // When a new site is added to the app, add its styles.
 | ||||
|         CoreEvents.on(CoreEvents.SITE_ADDED, async (data) => { | ||||
|             addingSite = data.siteId; | ||||
| 
 | ||||
|             try { | ||||
|                 await this.addSite(data.siteId); | ||||
| 
 | ||||
|                 if (addingSite == data.siteId) { | ||||
|                     addingSite = undefined; | ||||
|                 } | ||||
| 
 | ||||
|                 // User has logged in, remove tmp styles and enable loaded styles.
 | ||||
|                 if (data.siteId == CoreSites.getCurrentSiteId()) { | ||||
|                     this.unloadTmpStyles(); | ||||
| @ -164,10 +156,10 @@ export class CoreStylesService { | ||||
|         }); | ||||
| 
 | ||||
|         // Unload temporary styles when site config is "unchecked" in login.
 | ||||
|         CoreEvents.on(CoreEvents.LOGIN_SITE_UNCHECKED, (data) => { | ||||
|             if (data.siteId && data.siteId === addingSite) { | ||||
|                 // The tmp styles are from a site that is being added permanently.
 | ||||
|                 // Wait for the final site styles to be loaded before removing the tmp styles so there is no blink effect.
 | ||||
|         CoreEvents.on(CoreEvents.LOGIN_SITE_UNCHECKED, ({ loginSuccessful }) => { | ||||
|             if (loginSuccessful) { | ||||
|                 // The tmp styles have been added for a site we've logged into, so we'll wait for the final
 | ||||
|                 // site styles to be loaded before removing the tmp styles so there is no blink effect.
 | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|  | ||||
| @ -120,6 +120,19 @@ export class CoreConfigProvider { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get an app setting directly from the database, without using any optimizations.. | ||||
|      * | ||||
|      * @param name The config name. | ||||
|      * @return Resolves upon success along with the config data. Reject on failure. | ||||
|      */ | ||||
|     async getFromDB<T>(name: string): Promise<T> { | ||||
|         const db = CoreApp.getDB(); | ||||
|         const record = await db.getRecord<ConfigDBEntry>(CONFIG_TABLE_NAME, { name }); | ||||
| 
 | ||||
|         return record.value; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Check whether the given app setting exists. | ||||
|      * | ||||
| @ -151,9 +164,14 @@ export class CoreConfigProvider { | ||||
|      * Update config with the given values. | ||||
|      * | ||||
|      * @param config Config updates. | ||||
|      * @param reset Whether to reset environment before applying the patch. | ||||
|      */ | ||||
|     patchEnvironment(config: Partial<EnvironmentConfig>): void { | ||||
|         this.defaultEnvironment = this.defaultEnvironment ?? CoreConstants.CONFIG; | ||||
|     patchEnvironment(config: Partial<EnvironmentConfig>, reset: boolean = false): void { | ||||
|         this.defaultEnvironment = this.defaultEnvironment ?? { ...CoreConstants.CONFIG }; | ||||
| 
 | ||||
|         if (reset) { | ||||
|             this.resetEnvironmentSilently(); | ||||
|         } | ||||
| 
 | ||||
|         Object.assign(CoreConstants.CONFIG, config); | ||||
|         CoreEvents.trigger(CoreConfigProvider.ENVIRONMENT_UPDATED, CoreConstants.CONFIG); | ||||
| @ -169,8 +187,7 @@ export class CoreConfigProvider { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         Object.keys(CoreConstants.CONFIG).forEach(key => delete CoreConstants.CONFIG[key]); | ||||
|         Object.assign(CoreConstants.CONFIG, this.defaultEnvironment); | ||||
|         this.resetEnvironmentSilently(); | ||||
|         CoreEvents.trigger(CoreConfigProvider.ENVIRONMENT_UPDATED, CoreConstants.CONFIG); | ||||
|     } | ||||
| 
 | ||||
| @ -185,6 +202,20 @@ export class CoreConfigProvider { | ||||
|         this.patchEnvironment(JSON.parse(CoreUtils.getCookie('MoodleAppConfig') ?? '{}')); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Reset config values to its original state without emitting any events. | ||||
|      */ | ||||
|     protected resetEnvironmentSilently(): void { | ||||
|         if (!this.defaultEnvironment) { | ||||
|             // The environment config hasn't been modified; there's not need to reset.
 | ||||
| 
 | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         Object.keys(CoreConstants.CONFIG).forEach(key => delete CoreConstants.CONFIG[key]); | ||||
|         Object.assign(CoreConstants.CONFIG, this.defaultEnvironment); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| export const CoreConfig = makeSingleton(CoreConfigProvider); | ||||
|  | ||||
| @ -66,6 +66,7 @@ import { asyncInstance, AsyncInstance } from '../utils/async-instance'; | ||||
| import { CoreConfig } from './config'; | ||||
| 
 | ||||
| export const CORE_SITE_SCHEMAS = new InjectionToken<CoreSiteSchema[]>('CORE_SITE_SCHEMAS'); | ||||
| export const CORE_SITE_CURRENT_SITE_ID_CONFIG = 'current_site_id'; | ||||
| 
 | ||||
| /* | ||||
|  * Service to manage and interact with sites. | ||||
| @ -1032,13 +1033,31 @@ export class CoreSitesProvider { | ||||
|             try { | ||||
|                 const data = await this.sitesTable.getOneByPrimaryKey({ id: siteId }); | ||||
| 
 | ||||
|                 return this.makeSiteFromSiteListEntry(data); | ||||
|                 return this.addSiteFromSiteListEntry(data); | ||||
|             } catch { | ||||
|                 throw new CoreError('SiteId not found'); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get a site directly from the database, without using any optimizations. | ||||
|      * | ||||
|      * @param siteId Site id. | ||||
|      * @return Site. | ||||
|      */ | ||||
|     async getSiteFromDB(siteId: string): Promise<CoreSite> { | ||||
|         const db = CoreApp.getDB(); | ||||
| 
 | ||||
|         try { | ||||
|             const record = await db.getRecord<SiteDBEntry>(SITES_TABLE_NAME, { id: siteId }); | ||||
| 
 | ||||
|             return this.makeSiteFromSiteListEntry(record); | ||||
|         } catch { | ||||
|             throw new CoreError('SiteId not found'); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Finds a site with a certain URL. It will return the first site found. | ||||
|      * | ||||
| @ -1052,7 +1071,7 @@ export class CoreSitesProvider { | ||||
|             return this.sites[data.id]; | ||||
|         } | ||||
| 
 | ||||
|         return this.makeSiteFromSiteListEntry(data); | ||||
|         return this.addSiteFromSiteListEntry(data); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -1061,8 +1080,25 @@ export class CoreSitesProvider { | ||||
|      * @param entry Site list entry. | ||||
|      * @return Promised resolved with the created site. | ||||
|      */ | ||||
|     makeSiteFromSiteListEntry(entry: SiteDBEntry): Promise<CoreSite> { | ||||
|     addSiteFromSiteListEntry(entry: SiteDBEntry): Promise<CoreSite> { | ||||
|         // Parse info and config.
 | ||||
|         const site = this.makeSiteFromSiteListEntry(entry); | ||||
| 
 | ||||
|         return this.migrateSiteSchemas(site).then(() => { | ||||
|             // Set site after migrating schemas, or a call to getSite could get the site while tables are being created.
 | ||||
|             this.sites[entry.id] = site; | ||||
| 
 | ||||
|             return site; | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Make a site instance from a database entry. | ||||
|      * | ||||
|      * @param entry Site database entry. | ||||
|      * @return Site. | ||||
|      */ | ||||
|     makeSiteFromSiteListEntry(entry: SiteDBEntry): CoreSite { | ||||
|         const info = entry.info ? <CoreSiteInfo> CoreTextUtils.parseJSON(entry.info) : undefined; | ||||
|         const config = entry.config ? <CoreSiteConfig> CoreTextUtils.parseJSON(entry.config) : undefined; | ||||
| 
 | ||||
| @ -1077,12 +1113,7 @@ export class CoreSitesProvider { | ||||
|         ); | ||||
|         site.setOAuthId(entry.oauthId || undefined); | ||||
| 
 | ||||
|         return this.migrateSiteSchemas(site).then(() => { | ||||
|             // Set site after migrating schemas, or a call to getSite could get the site while tables are being created.
 | ||||
|             this.sites[entry.id] = site; | ||||
| 
 | ||||
|             return site; | ||||
|         }); | ||||
|         return site; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -1231,7 +1262,7 @@ export class CoreSitesProvider { | ||||
|      * @return Promise resolved when current site is stored. | ||||
|      */ | ||||
|     async login(siteId: string): Promise<void> { | ||||
|         await CoreConfig.set('current_site_id', siteId); | ||||
|         await CoreConfig.set(CORE_SITE_CURRENT_SITE_ID_CONFIG, siteId); | ||||
| 
 | ||||
|         CoreEvents.trigger(CoreEvents.LOGIN, {}, siteId); | ||||
|     } | ||||
| @ -1477,7 +1508,7 @@ export class CoreSitesProvider { | ||||
| 
 | ||||
|             siteEntries.forEach((site) => { | ||||
|                 if (!this.sites[site.id]) { | ||||
|                     promises.push(this.makeSiteFromSiteListEntry(site)); | ||||
|                     promises.push(this.addSiteFromSiteListEntry(site)); | ||||
|                 } | ||||
| 
 | ||||
|                 if (this.sites[site.id].containsUrl(url)) { | ||||
| @ -1504,7 +1535,7 @@ export class CoreSitesProvider { | ||||
|     async getStoredCurrentSiteId(): Promise<string> { | ||||
|         await this.migrateCurrentSiteLegacyTable(); | ||||
| 
 | ||||
|         return CoreConfig.get('current_site_id'); | ||||
|         return CoreConfig.get(CORE_SITE_CURRENT_SITE_ID_CONFIG); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -1513,7 +1544,7 @@ export class CoreSitesProvider { | ||||
|      * @return Promise resolved when done. | ||||
|      */ | ||||
|     async removeStoredCurrentSite(): Promise<void> { | ||||
|         await CoreConfig.delete('current_site_id'); | ||||
|         await CoreConfig.delete(CORE_SITE_CURRENT_SITE_ID_CONFIG); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -1789,7 +1820,7 @@ export class CoreSitesProvider { | ||||
| 
 | ||||
|             const { siteId } = await db.getRecord<{ siteId: string }>('current_site'); | ||||
| 
 | ||||
|             await CoreConfig.set('current_site_id', siteId); | ||||
|             await CoreConfig.set(CORE_SITE_CURRENT_SITE_ID_CONFIG, siteId); | ||||
|             await CoreApp.deleteTableSchema('current_site'); | ||||
|             await db.dropTable('current_site'); | ||||
|         } finally { | ||||
|  | ||||
| @ -56,6 +56,7 @@ export interface CoreEventsData { | ||||
|     [CoreEvents.ACTIVITY_DATA_SENT]: CoreEventActivityDataSentData; | ||||
|     [CoreEvents.IAB_LOAD_START]: InAppBrowserEvent; | ||||
|     [CoreEvents.LOGIN_SITE_CHECKED]: CoreEventLoginSiteCheckedData; | ||||
|     [CoreEvents.LOGIN_SITE_UNCHECKED]: CoreEventLoginSiteUncheckedData; | ||||
|     [CoreEvents.SEND_ON_ENTER_CHANGED]: CoreEventSendOnEnterChangedData; | ||||
|     [CoreEvents.COMPONENT_FILE_ACTION]: CoreFilepoolComponentFileEventData; | ||||
|     [CoreEvents.FILE_SHARED]: CoreEventFileSharedData; | ||||
| @ -400,6 +401,14 @@ export type CoreEventLoginSiteCheckedData = { | ||||
|     config: CoreSitePublicConfigResponse; | ||||
| }; | ||||
| 
 | ||||
| /** | ||||
|  * Data passed to LOGIN_SITE_UNCHECKED event. | ||||
|  */ | ||||
| export type CoreEventLoginSiteUncheckedData = { | ||||
|     config?: CoreSitePublicConfigResponse; | ||||
|     loginSuccessful: boolean; | ||||
| }; | ||||
| 
 | ||||
| /** | ||||
|  * Data passed to SEND_ON_ENTER_CHANGED event. | ||||
|  */ | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user