diff --git a/src/core/classes/database/database-table-proxy.ts b/src/core/classes/database/database-table-proxy.ts index 58393f943..bda6135bc 100644 --- a/src/core/classes/database/database-table-proxy.ts +++ b/src/core/classes/database/database-table-proxy.ts @@ -15,7 +15,7 @@ import { CoreConstants } from '@/core/constants'; import { asyncInstance } from '@/core/utils/async-instance'; import { SQLiteDB, SQLiteDBRecordValues } from '@classes/sqlitedb'; -import { CoreConfigProvider } from '@services/config'; +import { CoreConfig, CoreConfigProvider } from '@services/config'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreDatabaseReducer, CoreDatabaseTable, CoreDatabaseConditions, GetDBRecordPrimaryKey } from './database-table'; import { CoreDebugDatabaseTable } from './debug-database-table'; @@ -144,7 +144,9 @@ export class CoreDatabaseTableProxy< * * @returns Database configuration. */ - protected getRuntimeConfig(): CoreDatabaseConfiguration { + protected async getRuntimeConfig(): Promise { + await CoreConfig.ready(); + return { ...this.config, ...CoreConstants.CONFIG.databaseOptimizations, @@ -157,7 +159,7 @@ export class CoreDatabaseTableProxy< */ protected async updateTarget(): Promise { const oldTarget = this.target.instance; - const newTarget = this.createTarget(); + const newTarget = await this.createTarget(); if (oldTarget) { await oldTarget.destroy(); @@ -175,8 +177,8 @@ export class CoreDatabaseTableProxy< * * @returns Target instance. */ - protected createTarget(): CoreDatabaseTable { - const config = this.getRuntimeConfig(); + protected async createTarget(): Promise> { + const config = await this.getRuntimeConfig(); const table = this.createTable(config.cachingStrategy); return config.debug ? new CoreDebugDatabaseTable(table) : table; diff --git a/src/core/classes/tests/database-table.test.ts b/src/core/classes/tests/database-table.test.ts index 9c7f1757f..a942959c1 100644 --- a/src/core/classes/tests/database-table.test.ts +++ b/src/core/classes/tests/database-table.test.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { mock } from '@/testing/utils'; +import { mock, mockSingleton } from '@/testing/utils'; import { CoreDatabaseTable } from '@classes/database/database-table'; import { CoreDatabaseCachingStrategy, @@ -20,6 +20,7 @@ import { CoreDatabaseTableProxy, } from '@classes/database/database-table-proxy'; import { SQLiteDB, SQLiteDBRecordValues } from '@classes/sqlitedb'; +import { CoreConfig } from '@services/config'; interface User extends SQLiteDBRecordValues { id: number; @@ -66,6 +67,8 @@ function prepareStubs(config: Partial = {}): [User[], }); const table = new CoreDatabaseTableProxy(config, database, 'users'); + mockSingleton(CoreConfig, { isReady: () => Promise.resolve() }); + return [records, database, table]; } diff --git a/src/core/constants.ts b/src/core/constants.ts index b83e896a8..7113598cc 100644 --- a/src/core/constants.ts +++ b/src/core/constants.ts @@ -145,6 +145,17 @@ export class CoreConstants { static readonly CONFIG = { ...envJson.config } as unknown as EnvironmentConfig; // Data parsed from config.json files. static readonly BUILD = envJson.build as unknown as EnvironmentBuild; // Build info. + /** + * Check whether devtools should be enabled. + * + * @returns Whether devtools should be enabled. + */ + static enableDevTools(): boolean { + // @todo [4.0] This is not the proper way to check for development tools, we should rely only on the BUILD variable. + return this.BUILD.isDevelopment + || this.CONFIG.versionname.includes('-dev'); + } + } interface EnvironmentBuild { diff --git a/src/core/initializers/initialize-services.ts b/src/core/initializers/initialize-services.ts index 6b172b93c..0806badd5 100644 --- a/src/core/initializers/initialize-services.ts +++ b/src/core/initializers/initialize-services.ts @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +import { CoreConfig } from '@services/config'; import { CoreFilepool } from '@services/filepool'; import { CoreLang } from '@services/lang'; import { CoreLocalNotifications } from '@services/local-notifications'; @@ -20,6 +21,7 @@ import { CoreUpdateManager } from '@services/update-manager'; export default async function(): Promise { await Promise.all([ + CoreConfig.initialize(), CoreFilepool.initialize(), CoreSites.initialize(), CoreLang.initialize(), diff --git a/src/core/initializers/export-data.ts b/src/core/initializers/prepare-devtools.ts similarity index 69% rename from src/core/initializers/export-data.ts rename to src/core/initializers/prepare-devtools.ts index 1abdb4239..ffed96e55 100644 --- a/src/core/initializers/export-data.ts +++ b/src/core/initializers/prepare-devtools.ts @@ -12,22 +12,21 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { EnvironmentConfig } from '@/types/config'; +import { CoreConfig, CoreConfigProvider } from '@services/config'; import { CoreConstants } from '../constants'; type DevelopmentWindow = Window & { - coreConstantsConfig?: EnvironmentConfig; + configProvider?: CoreConfigProvider; }; -function exportData(window: DevelopmentWindow) { - window.coreConstantsConfig = CoreConstants.CONFIG; +function initializeDevelopmentWindow(window: DevelopmentWindow) { + window.configProvider = CoreConfig.instance; } export default function(): void { - if (!CoreConstants.CONFIG.versionname.includes('-dev')) { - // Only export data in development. + if (!CoreConstants.enableDevTools()) { return; } - exportData(window); + initializeDevelopmentWindow(window); } diff --git a/src/core/services/config.ts b/src/core/services/config.ts index ef0f65d5a..b203690a3 100644 --- a/src/core/services/config.ts +++ b/src/core/services/config.ts @@ -22,6 +22,8 @@ import { CoreConstants } from '../constants'; import { CoreEvents } from '@singletons/events'; import { CoreDatabaseTable } from '@classes/database/database-table'; import { asyncInstance } from '../utils/async-instance'; +import { CorePromisedValue } from '@classes/promised-value'; +import { CoreUtils } from './utils/utils'; declare module '@singletons/events' { @@ -47,6 +49,23 @@ export class CoreConfigProvider { protected table = asyncInstance>(); protected defaultEnvironment?: EnvironmentConfig; + protected isReady = new CorePromisedValue(); + + /** + * Wait until configuration is ready for use. + */ + ready(): Promise { + return this.isReady; + } + + /** + * Initialize. + */ + async initialize(): Promise { + this.loadDevelopmentConfig(); + + this.isReady.resolve(); + } /** * Initialize database. @@ -139,6 +158,17 @@ export class CoreConfigProvider { CoreEvents.trigger(CoreConfigProvider.ENVIRONMENT_UPDATED, CoreConstants.CONFIG); } + /** + * Load development config overrides. + */ + protected loadDevelopmentConfig(): void { + if (!CoreConstants.enableDevTools() || !CoreUtils.hasCookie('MoodleAppConfig')) { + return; + } + + this.patchEnvironment(JSON.parse(CoreUtils.getCookie('MoodleAppConfig') ?? '{}')); + } + } export const CoreConfig = makeSingleton(CoreConfigProvider); diff --git a/src/core/services/utils/utils.ts b/src/core/services/utils/utils.ts index 3ddee12d1..4808ccca5 100644 --- a/src/core/services/utils/utils.ts +++ b/src/core/services/utils/utils.ts @@ -1748,6 +1748,34 @@ export class CoreUtilsProvider { return CoreApp.isIOS() && openFileAction == OpenFileAction.OPEN_WITH; } + /** + * Check whether the given cookie is set. + * + * @param name Cookie name. + * @returns Whether the cookie is set. + */ + hasCookie(name: string): boolean { + return new RegExp(`(\\s|;|^)${name}=`).test(document.cookie ?? ''); + } + + /** + * Read a cookie. + * + * @param name Cookie name. + * @return Cookie value. + */ + getCookie(name: string): string | null { + const cookies = (document.cookie ?? '').split(';').reduce((cookies, cookie) => { + const [name, value] = cookie.trim().split('='); + + cookies[name] = value; + + return cookies; + }, {}); + + return cookies[name] ?? null; + } + } export const CoreUtils = makeSingleton(CoreUtilsProvider);