From b262128acc11efc9b22a113c252852ac7754f755 Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Wed, 11 Sep 2024 12:02:45 -0700 Subject: [PATCH 1/9] refactor: runtime deployment services --- src/app/app.component.html | 21 +++++++++++-------- src/app/app.component.ts | 20 +++++++++++------- src/app/feature/feedback/feedback.service.ts | 10 +++++---- .../services/app-config/app-config.service.ts | 14 ++++++------- src/app/shared/services/auth/auth.service.ts | 7 ++++--- .../crashlytics/crashlytics.service.ts | 6 +++--- src/app/shared/services/db/db-sync.service.ts | 7 +++++-- .../dynamic-data/adapters/persistedMemory.ts | 7 ++++--- .../dynamic-data/adapters/reactiveMemory.ts | 6 ++++-- .../file-manager/file-manager.service.ts | 7 ++++--- .../services/firebase/firebase.service.ts | 8 +++---- .../remote-asset/remote-asset.service.ts | 7 ++++--- .../shared/services/server/server.service.ts | 7 +++++-- src/environments/environment.prod.ts | 1 - src/environments/environment.ts | 1 - 15 files changed, 74 insertions(+), 55 deletions(-) diff --git a/src/app/app.component.html b/src/app/app.component.html index f6931026ed..50f45c6c03 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -16,16 +16,19 @@ {{ sideMenuDefaults.title }}
- - - {{ CONTENT_VERSION }} - - - {{ APP_VERSION }} - + @if (sideMenuDefaults.should_show_version) { + @if (deploymentService.config()._content_version; as CONTENT_VERSION) { + + + {{ CONTENT_VERSION }} + + + } @else { + {{ this.deploymentService.config()._app_builder_version }} + } + } ({{ DEPLOYMENT_NAME }})({{ this.deploymentService.config().name }})
diff --git a/src/app/app.component.ts b/src/app/app.component.ts index bcbf94702c..27e8a8a984 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -41,6 +41,7 @@ import { SeoService } from "./shared/services/seo/seo.service"; import { FeedbackService } from "./feature/feedback/feedback.service"; import { ShareService } from "./shared/services/share/share.service"; import { LocalStorageService } from "./shared/services/local-storage/local-storage.service"; +import { DeploymentService } from "./shared/services/deployment/deployment.service"; @Component({ selector: "app-root", @@ -48,15 +49,13 @@ import { LocalStorageService } from "./shared/services/local-storage/local-stora styleUrls: ["app.component.scss"], }) export class AppComponent { - APP_VERSION = environment.version; - CONTENT_VERSION = environment.deploymentConfig.git.content_tag_latest; - DEPLOYMENT_NAME = environment.deploymentName; appConfig: IAppConfig; appAuthenticationDefaults: IAppConfig["APP_AUTHENTICATION_DEFAULTS"]; sideMenuDefaults: IAppConfig["APP_SIDEMENU_DEFAULTS"]; footerDefaults: IAppConfig["APP_FOOTER_DEFAULTS"]; /** Track when app ready to render sidebar and route templates */ public renderAppTemplates = false; + /** * A space-separated list of values, hierarchically representing the current platform, * e.g. on iPhone the value would be "mobile ios iphone". @@ -66,6 +65,9 @@ export class AppComponent { platforms: string; constructor( + // Public services (for UI) + public deploymentService: DeploymentService, + // 3rd Party Services private platform: Platform, private cdr: ChangeDetectorRef, @@ -111,6 +113,9 @@ export class AppComponent { } private async initializeApp() { + // eagerly load deployment config to make available throughout app + await this.deploymentService.ready(); + this.platform.ready().then(async () => { this.platforms = this.platform.platforms().join(" "); this.subscribeToAppConfigChanges(); @@ -148,9 +153,10 @@ export class AppComponent { } /** Populate contact fields that may be used by other services during initialisation */ private async populateAppInitFields() { - this.localStorageService.setProtected("DEPLOYMENT_NAME", this.DEPLOYMENT_NAME); - this.localStorageService.setProtected("APP_VERSION", this.APP_VERSION); - this.localStorageService.setProtected("CONTENT_VERSION", this.CONTENT_VERSION); + const { _content_version, _app_builder_version, name } = this.deploymentService.config(); + this.localStorageService.setProtected("DEPLOYMENT_NAME", name); + this.localStorageService.setProtected("APP_VERSION", _app_builder_version); + this.localStorageService.setProtected("CONTENT_VERSION", _content_version); // HACK - ensure first_app_launch migrated from event service if (!this.localStorageService.getProtected("APP_FIRST_LAUNCH")) { await this.appEventService.ready(); @@ -164,7 +170,7 @@ export class AppComponent { * Currently only run on native where specified (but can comment out for testing locally) */ private async loadAuthConfig() { - const { firebase } = environment.deploymentConfig; + const { firebase } = this.deploymentService.config(); const { enforceLogin } = this.appAuthenticationDefaults; const ensureLogin = firebase.config && enforceLogin && Capacitor.isNativePlatform(); if (ensureLogin) { diff --git a/src/app/feature/feedback/feedback.service.ts b/src/app/feature/feedback/feedback.service.ts index afcb4b49b0..6936297986 100644 --- a/src/app/feature/feedback/feedback.service.ts +++ b/src/app/feature/feedback/feedback.service.ts @@ -16,7 +16,6 @@ import { import { UserMetaService } from "src/app/shared/services/userMeta/userMeta.service"; import { TemplateService } from "src/app/shared/components/template/services/template.service"; import { generateTimestamp } from "src/app/shared/utils"; -import { environment } from "src/environments/environment"; import { DbService } from "src/app/shared/services/db/db.service"; import { DBSyncService } from "src/app/shared/services/db/db-sync.service"; import { @@ -38,6 +37,7 @@ import { } from "src/app/shared/components/template/services/instance/template-action.registry"; import { SyncServiceBase } from "src/app/shared/services/syncService.base"; import { LocalStorageService } from "src/app/shared/services/local-storage/local-storage.service"; +import { DeploymentService } from "src/app/shared/services/deployment/deployment.service"; @Injectable({ providedIn: "root", @@ -80,7 +80,8 @@ export class FeedbackService extends SyncServiceBase { private router: Router, private themeService: ThemeService, private skinService: SkinService, - private templateActionRegistry: TemplateActionRegistry + private templateActionRegistry: TemplateActionRegistry, + private deploymentService: DeploymentService ) { super("Feedback"); this.subscribeToAppConfigChanges(); @@ -367,13 +368,14 @@ export class FeedbackService extends SyncServiceBase { * device info and user uuid */ public generateFeedbackMetadata() { + const { _app_builder_version, name } = this.deploymentService.config(); const metadata: IFeedbackMetadata = { deviceInfo: this.deviceInfo, pathname: location.pathname, uuid: this.userMetaService.getUserMeta("uuid"), timestamp: generateTimestamp(), - app_version: environment.version, - app_deployment_name: environment.deploymentName, + app_version: _app_builder_version, + app_deployment_name: name, app_theme: this.themeService.getCurrentTheme(), app_skin: this.skinService.getActiveSkinName(), }; diff --git a/src/app/shared/services/app-config/app-config.service.ts b/src/app/shared/services/app-config/app-config.service.ts index 25a8a3ea65..25052fe363 100644 --- a/src/app/shared/services/app-config/app-config.service.ts +++ b/src/app/shared/services/app-config/app-config.service.ts @@ -1,18 +1,17 @@ import { Injectable } from "@angular/core"; import { getDefaultAppConfig, IAppConfig, IAppConfigOverride } from "data-models"; import { BehaviorSubject } from "rxjs"; -import { environment } from "src/environments/environment"; import { deepMergeObjects, RecursivePartial, trackObservableObjectChanges } from "../../utils"; import clone from "clone"; import { SyncServiceBase } from "../syncService.base"; import { startWith } from "rxjs/operators"; import { Observable } from "rxjs"; +import { DeploymentService } from "../deployment/deployment.service"; @Injectable({ providedIn: "root", }) export class AppConfigService extends SyncServiceBase { - deploymentOverrides: IAppConfigOverride = (environment.deploymentConfig as any).app_config || {}; /** List of constants provided by data-models combined with deployment-specific overrides and skin-specific overrides */ appConfig$ = new BehaviorSubject(undefined as any); @@ -42,19 +41,18 @@ export class AppConfigService extends SyncServiceBase { return this.changes$.pipe(startWith(this.value)); } - constructor() { + constructor(private deploymentService: DeploymentService) { super("AppConfig"); this.initialise(); } private initialise() { + const deploymentOverrides: IAppConfigOverride = + this.deploymentService.config().app_config || {}; this.APP_CONFIG = getDefaultAppConfig(); // Store app config with deployment overrides applied, to be merged with additional overrides when applied - this.deploymentAppConfig = this.applyAppConfigOverrides( - this.APP_CONFIG, - this.deploymentOverrides - ); - this.updateAppConfig(this.deploymentOverrides); + this.deploymentAppConfig = this.applyAppConfigOverrides(this.APP_CONFIG, deploymentOverrides); + this.updateAppConfig(deploymentOverrides); } public updateAppConfig(overrides: IAppConfigOverride) { diff --git a/src/app/shared/services/auth/auth.service.ts b/src/app/shared/services/auth/auth.service.ts index 2f85b1ae32..3b65d589d1 100644 --- a/src/app/shared/services/auth/auth.service.ts +++ b/src/app/shared/services/auth/auth.service.ts @@ -2,11 +2,11 @@ import { Injectable } from "@angular/core"; import { FirebaseAuthentication, User } from "@capacitor-firebase/authentication"; import { BehaviorSubject, firstValueFrom } from "rxjs"; import { filter } from "rxjs/operators"; -import { environment } from "src/environments/environment"; import { SyncServiceBase } from "../syncService.base"; import { TemplateActionRegistry } from "../../components/template/services/instance/template-action.registry"; import { FirebaseService } from "../firebase/firebase.service"; import { LocalStorageService } from "../local-storage/local-storage.service"; +import { DeploymentService } from "../deployment/deployment.service"; @Injectable({ providedIn: "root", @@ -18,13 +18,14 @@ export class AuthService extends SyncServiceBase { constructor( private templateActionRegistry: TemplateActionRegistry, private firebaseService: FirebaseService, - private localStorageService: LocalStorageService + private localStorageService: LocalStorageService, + private deploymentService: DeploymentService ) { super("Auth"); this.initialise(); } private initialise() { - const { firebase } = environment.deploymentConfig; + const { firebase } = this.deploymentService.config(); if (firebase?.auth?.enabled && this.firebaseService.app) { this.addAuthListeners(); this.registerTemplateActionHandlers(); diff --git a/src/app/shared/services/crashlytics/crashlytics.service.ts b/src/app/shared/services/crashlytics/crashlytics.service.ts index 9e6b2f0ad7..ac5616d977 100644 --- a/src/app/shared/services/crashlytics/crashlytics.service.ts +++ b/src/app/shared/services/crashlytics/crashlytics.service.ts @@ -3,7 +3,7 @@ import { FirebaseCrashlytics } from "@capacitor-firebase/crashlytics"; import { Capacitor } from "@capacitor/core"; import { Device } from "@capacitor/device"; import { AsyncServiceBase } from "../asyncService.base"; -import { environment } from "src/environments/environment"; +import { DeploymentService } from "../deployment/deployment.service"; @Injectable({ providedIn: "root", @@ -14,13 +14,13 @@ import { environment } from "src/environments/environment"; * https://github.com/capawesome-team/capacitor-firebase/tree/main/packages/crashlytics */ export class CrashlyticsService extends AsyncServiceBase { - constructor() { + constructor(private deploymentService: DeploymentService) { super("Crashlytics"); this.registerInitFunction(this.initialise); } private async initialise() { if (Capacitor.isNativePlatform()) { - const { firebase } = environment.deploymentConfig; + const { firebase } = this.deploymentService.config(); // Crashlytics is still supported on native device without firebase config (uses google-services.json) // so use config property to toggle enabled instead await this.setEnabled({ enabled: firebase?.crashlytics?.enabled }); diff --git a/src/app/shared/services/db/db-sync.service.ts b/src/app/shared/services/db/db-sync.service.ts index dd6f3e8e25..e5110e1854 100644 --- a/src/app/shared/services/db/db-sync.service.ts +++ b/src/app/shared/services/db/db-sync.service.ts @@ -14,6 +14,7 @@ import { AppConfigService } from "../app-config/app-config.service"; import { AsyncServiceBase } from "../asyncService.base"; import { UserMetaService } from "../userMeta/userMeta.service"; import { DbService } from "./db.service"; +import { DeploymentService } from "../deployment/deployment.service"; @Injectable({ providedIn: "root" }) /** @@ -29,7 +30,8 @@ export class DBSyncService extends AsyncServiceBase { private dbService: DbService, private http: HttpClient, private userMetaService: UserMetaService, - private appConfigService: AppConfigService + private appConfigService: AppConfigService, + private deploymentService: DeploymentService ) { super("DB Sync"); this.registerInitFunction(this.inititialise); @@ -81,12 +83,13 @@ export class DBSyncService extends AsyncServiceBase { /** Populate common app_meta to local record */ private generateServerRecord(record: any, mapping: IDBServerMapping) { + const { name } = this.deploymentService.config(); const { is_user_record, user_record_id_field } = mapping; if (is_user_record && user_record_id_field) { const serverRecord: IDBServerUserRecord = { app_user_id: this.userMetaService.getUserMeta("uuid"), app_user_record_id: record[user_record_id_field], - app_deployment_name: environment.deploymentName, + app_deployment_name: name, app_version: environment.version, data: record, }; diff --git a/src/app/shared/services/dynamic-data/adapters/persistedMemory.ts b/src/app/shared/services/dynamic-data/adapters/persistedMemory.ts index 73d78a4c2d..bdd93774b7 100644 --- a/src/app/shared/services/dynamic-data/adapters/persistedMemory.ts +++ b/src/app/shared/services/dynamic-data/adapters/persistedMemory.ts @@ -17,8 +17,8 @@ addRxPlugin(RxDBUpdatePlugin); import { debounceTime, filter, firstValueFrom, Subject } from "rxjs"; import { FlowTypes } from "data-models"; -import { environment } from "src/environments/environment"; import { deepMergeObjects, compareObjectKeys } from "../../../utils"; +import { DeploymentService } from "../../deployment/deployment.service"; /** * All persisted docs are stored in the same format with a standard set of meta fields and doc data @@ -78,7 +78,7 @@ export class PersistedMemoryAdapter { [key: string]: RxCollection; }>; - constructor() { + constructor(private deploymentService: DeploymentService) { this.subscribeToStatePersist(); } @@ -98,8 +98,9 @@ export class PersistedMemoryAdapter { } public async create() { + const { name } = this.deploymentService.config(); this.db = await createRxDatabase({ - name: `${environment.deploymentName}_user`, + name: `${name}_user`, storage: getRxStorageDexie({ autoOpen: true }), ignoreDuplicate: true, }); diff --git a/src/app/shared/services/dynamic-data/adapters/reactiveMemory.ts b/src/app/shared/services/dynamic-data/adapters/reactiveMemory.ts index 1d788dd47d..4085f09124 100644 --- a/src/app/shared/services/dynamic-data/adapters/reactiveMemory.ts +++ b/src/app/shared/services/dynamic-data/adapters/reactiveMemory.ts @@ -22,7 +22,7 @@ import { RxDBUpdatePlugin } from "rxdb/plugins/update"; addRxPlugin(RxDBUpdatePlugin); import { BehaviorSubject } from "rxjs"; -import { environment } from "src/environments/environment"; +import { DeploymentService } from "../../deployment/deployment.service"; /** * Create a base schema for data @@ -67,10 +67,12 @@ export class ReactiveMemoryAdapater { MemoryStorageInternals, RxStorageMemoryInstanceCreationOptions >; + constructor(private deploymentService: DeploymentService) {} public async createDB() { + const { name } = this.deploymentService.config(); this.db = await createRxDatabase({ - name: `${environment.deploymentName}`, + name, storage: getRxStorageMemory(), ignoreDuplicate: true, }); diff --git a/src/app/shared/services/file-manager/file-manager.service.ts b/src/app/shared/services/file-manager/file-manager.service.ts index 6e59d18e0a..d9582ae730 100644 --- a/src/app/shared/services/file-manager/file-manager.service.ts +++ b/src/app/shared/services/file-manager/file-manager.service.ts @@ -5,10 +5,10 @@ import { Capacitor } from "@capacitor/core"; import write_blob from "capacitor-blob-writer"; import { saveAs } from "file-saver"; import { SyncServiceBase } from "../syncService.base"; -import { environment } from "src/environments/environment"; import { TemplateActionRegistry } from "../../components/template/services/instance/template-action.registry"; import { TemplateAssetService } from "../../components/template/services/template-asset.service"; import { ErrorHandlerService } from "../error-handler/error-handler.service"; +import { DeploymentService } from "../deployment/deployment.service"; @Injectable({ providedIn: "root", @@ -19,14 +19,15 @@ export class FileManagerService extends SyncServiceBase { constructor( private errorHandler: ErrorHandlerService, private templateActionRegistry: TemplateActionRegistry, - private templateAssetService: TemplateAssetService + private templateAssetService: TemplateAssetService, + private deploymentService: DeploymentService ) { super("FileManager"); this.initialise(); } private initialise() { - this.cacheName = environment.deploymentConfig.name; + this.cacheName = this.deploymentService.config().name; this.registerTemplateActionHandlers(); } diff --git a/src/app/shared/services/firebase/firebase.service.ts b/src/app/shared/services/firebase/firebase.service.ts index e17f6097c1..39fddb5a5b 100644 --- a/src/app/shared/services/firebase/firebase.service.ts +++ b/src/app/shared/services/firebase/firebase.service.ts @@ -1,7 +1,7 @@ import { Injectable } from "@angular/core"; import { initializeApp, FirebaseApp } from "firebase/app"; -import { environment } from "src/environments/environment"; import { SyncServiceBase } from "../syncService.base"; +import { DeploymentService } from "../deployment/deployment.service"; /** Service used to configure initialize firebase app core configuration */ @Injectable({ providedIn: "root" }) @@ -9,7 +9,7 @@ export class FirebaseService extends SyncServiceBase { /** Initialised firebase app. Will be undefined if firebase config unavailable */ app: FirebaseApp | undefined; - constructor() { + constructor(private deploymentService: DeploymentService) { super("Firebase"); this.initialise(); } @@ -18,7 +18,7 @@ export class FirebaseService extends SyncServiceBase { * Configure app module imports dependent on what firebase features should be enabled */ private initialise() { - const { firebase } = environment.deploymentConfig; + const { firebase } = this.deploymentService.config(); // Check if any services are enabled, simply return if not const enabledServices = Object.entries(firebase) @@ -32,6 +32,6 @@ export class FirebaseService extends SyncServiceBase { return; } - this.app = initializeApp(environment.deploymentConfig.firebase.config); + this.app = initializeApp(firebase.config); } } diff --git a/src/app/shared/services/remote-asset/remote-asset.service.ts b/src/app/shared/services/remote-asset/remote-asset.service.ts index 1bd645f0ff..fddff64d3d 100644 --- a/src/app/shared/services/remote-asset/remote-asset.service.ts +++ b/src/app/shared/services/remote-asset/remote-asset.service.ts @@ -3,7 +3,6 @@ import { HttpClient, HttpEventType } from "@angular/common/http"; import { Capacitor } from "@capacitor/core"; import { createClient, SupabaseClient } from "@supabase/supabase-js"; import { FileObject } from "@supabase/storage-js"; -import { environment } from "src/environments/environment"; import { TemplateActionRegistry } from "../../components/template/services/instance/template-action.registry"; import { FlowTypes, IAppConfig } from "../../model"; import { AppConfigService } from "../app-config/app-config.service"; @@ -16,6 +15,7 @@ import { AsyncServiceBase } from "../asyncService.base"; import { IAssetEntry } from "packages/data-models/deployment.model"; import { DynamicDataService } from "../dynamic-data/dynamic-data.service"; import { arrayToHashmap, convertBlobToBase64 } from "../../utils"; +import { DeploymentService } from "../deployment/deployment.service"; const CORE_ASSET_PACK_NAME = "core_assets"; @@ -39,7 +39,8 @@ export class RemoteAssetService extends AsyncServiceBase { private fileManagerService: FileManagerService, private templateAssetService: TemplateAssetService, private templateActionRegistry: TemplateActionRegistry, - private http: HttpClient + private http: HttpClient, + private deploymentService: DeploymentService ) { super("RemoteAsset"); this.registerInitFunction(this.initialise); @@ -48,7 +49,7 @@ export class RemoteAssetService extends AsyncServiceBase { private async initialise() { this.registerTemplateActionHandlers(); // require supabase to be configured to use remote asset service - const { enabled, publicApiKey, url } = environment.deploymentConfig.supabase; + const { enabled, publicApiKey, url } = this.deploymentService.config().supabase; this.supabaseEnabled = enabled; if (this.supabaseEnabled) { await this.ensureAsyncServicesReady([this.templateAssetService, this.dynamicDataService]); diff --git a/src/app/shared/services/server/server.service.ts b/src/app/shared/services/server/server.service.ts index 0edd998021..42df072504 100644 --- a/src/app/shared/services/server/server.service.ts +++ b/src/app/shared/services/server/server.service.ts @@ -10,6 +10,7 @@ import { AppConfigService } from "../app-config/app-config.service"; import { SyncServiceBase } from "../syncService.base"; import { LocalStorageService } from "../local-storage/local-storage.service"; import { DynamicDataService } from "../dynamic-data/dynamic-data.service"; +import { DeploymentService } from "../deployment/deployment.service"; /** * Backend API @@ -31,7 +32,8 @@ export class ServerService extends SyncServiceBase { private http: HttpClient, private appConfigService: AppConfigService, private localStorageService: LocalStorageService, - private dynamicDataService: DynamicDataService + private dynamicDataService: DynamicDataService, + private deploymentService: DeploymentService ) { super("Server"); this.initialise(); @@ -57,6 +59,7 @@ export class ServerService extends SyncServiceBase { } public async syncUserData() { + const { name } = this.deploymentService.config(); await this.dynamicDataService.ready(); if (!this.device_info) { this.device_info = await Device.getInfo(); @@ -78,7 +81,7 @@ export class ServerService extends SyncServiceBase { contact_fields, app_version: environment.version, device_info: this.device_info, - app_deployment_name: environment.deploymentName, + app_deployment_name: name, dynamic_data, }; console.log("[SERVER] sync data", data); diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts index 161077181d..b7e290ad64 100644 --- a/src/environments/environment.prod.ts +++ b/src/environments/environment.prod.ts @@ -4,7 +4,6 @@ import type { IDeploymentConfig } from "data-models"; export const environment = { version: packageJson.version, - deploymentName: deploymentJson.name, // HACK - json config converts functions to strings, not strongly typed deploymentConfig: deploymentJson as any as IDeploymentConfig, production: true, diff --git a/src/environments/environment.ts b/src/environments/environment.ts index 27059abb45..9ac4ab5a0a 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -5,7 +5,6 @@ import type { IDeploymentConfig } from "data-models"; export const environment = { /** App version, as provided by package.json */ version: packageJson.version, - deploymentName: deploymentJson.name, // HACK - json config converts functions to strings, not strongly typed deploymentConfig: deploymentJson as any as IDeploymentConfig, production: false, From 5b017f6aec3ffa11951097fb3179f2a2137e675e Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Wed, 11 Sep 2024 12:04:53 -0700 Subject: [PATCH 2/9] chore: update deployment runtime web config --- packages/data-models/deployment.model.ts | 10 +++++----- src/app/shared/services/seo/seo.service.ts | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/data-models/deployment.model.ts b/packages/data-models/deployment.model.ts index bd093f1ef3..165763c024 100644 --- a/packages/data-models/deployment.model.ts +++ b/packages/data-models/deployment.model.ts @@ -59,6 +59,10 @@ export interface IDeploymentRuntimeConfig { url?: string; publicApiKey?: string; }; + web: { + /** Relative path of custom favicon asset to load from app_data assets */ + favicon_asset?: string; + }; } /** Deployment settings not available at runtime */ @@ -124,10 +128,6 @@ interface IDeploymentCoreConfig { /** translated string for import. Default `./app_data/translations_source/translated_strings */ translated_strings_path?: string; }; - web: { - /** Relative path of custom favicon asset to load from app_data assets */ - favicon_asset?: string; - }; workflows: { /** path to custom workflow files to include */ custom_ts_files: string[]; @@ -169,6 +169,7 @@ export const DEPLOYMENT_RUNTIME_CONFIG_DEFAULTS: IDeploymentRuntimeConfig = { supabase: { enabled: false, }, + web: {}, }; /** Full example of just all config once merged with defaults */ @@ -198,7 +199,6 @@ export const DEPLOYMENT_CONFIG_EXAMPLE_DEFAULTS: IDeploymentConfig = { source_strings_path: "./app_data/translations_source/source_strings", translated_strings_path: "./app_data/translations_source/translated_strings", }, - web: {}, workflows: { custom_ts_files: [], task_cache_path: "./tasks", diff --git a/src/app/shared/services/seo/seo.service.ts b/src/app/shared/services/seo/seo.service.ts index 61a20b9072..95e120414c 100644 --- a/src/app/shared/services/seo/seo.service.ts +++ b/src/app/shared/services/seo/seo.service.ts @@ -1,6 +1,6 @@ import { Injectable } from "@angular/core"; -import { environment } from "src/environments/environment"; import { SyncServiceBase } from "../syncService.base"; +import { DeploymentService } from "../deployment/deployment.service"; interface ISEOMeta { title: string; @@ -21,7 +21,7 @@ type IMetaName = providedIn: "root", }) export class SeoService extends SyncServiceBase { - constructor() { + constructor(private deploymentService: DeploymentService) { super("SEO Service"); // call after init to apply defaults this.updateMeta({}); @@ -65,7 +65,7 @@ export class SeoService extends SyncServiceBase { private getDefaultSEOTags(): ISEOMeta { const PUBLIC_URL = location.origin; let faviconUrl = `${PUBLIC_URL}/assets/icon/favicon.svg`; - const { web, app_config } = environment.deploymentConfig; + const { web, app_config } = this.deploymentService.config(); if (web?.favicon_asset) { faviconUrl = `${PUBLIC_URL}/assets/app_data/assets/${web.favicon_asset}`; } From 06746ad8556841791854c49cad7b6c0fa0ee75ca Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Wed, 11 Sep 2024 12:30:04 -0700 Subject: [PATCH 3/9] refactor: environment version references --- packages/data-models/deployment.model.ts | 2 +- src/app/shared/services/db/db-sync.service.ts | 4 ++-- .../error-handler/error-handler.service.ts | 21 +++++++++++-------- .../shared/services/server/server.service.ts | 4 ++-- .../services/task/task-action.service.ts | 10 ++++++--- src/environments/environment.prod.ts | 2 -- src/environments/environment.ts | 3 --- 7 files changed, 24 insertions(+), 22 deletions(-) diff --git a/packages/data-models/deployment.model.ts b/packages/data-models/deployment.model.ts index 165763c024..473d4b6018 100644 --- a/packages/data-models/deployment.model.ts +++ b/packages/data-models/deployment.model.ts @@ -6,7 +6,7 @@ export const DEPLOYMENT_CONFIG_VERSION = 20240910.0; /** Configuration settings available to runtime application */ export interface IDeploymentRuntimeConfig { - /** version of open-app-builder used to compile */ + /** version of open-app-builder used to compile, read from builder repo package.json */ _app_builder_version: string; /** tag of content version provided by content git repo*/ _content_version: string; diff --git a/src/app/shared/services/db/db-sync.service.ts b/src/app/shared/services/db/db-sync.service.ts index e5110e1854..964a8868f4 100644 --- a/src/app/shared/services/db/db-sync.service.ts +++ b/src/app/shared/services/db/db-sync.service.ts @@ -83,14 +83,14 @@ export class DBSyncService extends AsyncServiceBase { /** Populate common app_meta to local record */ private generateServerRecord(record: any, mapping: IDBServerMapping) { - const { name } = this.deploymentService.config(); + const { name, _app_builder_version } = this.deploymentService.config(); const { is_user_record, user_record_id_field } = mapping; if (is_user_record && user_record_id_field) { const serverRecord: IDBServerUserRecord = { app_user_id: this.userMetaService.getUserMeta("uuid"), app_user_record_id: record[user_record_id_field], app_deployment_name: name, - app_version: environment.version, + app_version: _app_builder_version, data: record, }; return serverRecord; diff --git a/src/app/shared/services/error-handler/error-handler.service.ts b/src/app/shared/services/error-handler/error-handler.service.ts index 5c7b55adce..0e6f2f2290 100644 --- a/src/app/shared/services/error-handler/error-handler.service.ts +++ b/src/app/shared/services/error-handler/error-handler.service.ts @@ -7,6 +7,7 @@ import { GIT_SHA } from "src/environments/sha"; import { fromError as getStacktraceFromError } from "stacktrace-js"; import { CrashlyticsService } from "../crashlytics/crashlytics.service"; import { FirebaseService } from "../firebase/firebase.service"; +import { DeploymentService } from "../deployment/deployment.service"; @Injectable({ providedIn: "root", @@ -18,7 +19,11 @@ export class ErrorHandlerService extends ErrorHandler { // Error handling is important and needs to be loaded first. // Because of this we should manually inject the services with Injector. - constructor(private injector: Injector, private firebaseService: FirebaseService) { + constructor( + private injector: Injector, + private firebaseService: FirebaseService, + private deploymentService: DeploymentService + ) { super(); } @@ -30,13 +35,12 @@ export class ErrorHandlerService extends ErrorHandler { * (although workaround required as cannot extend multiple services) */ private async initialise() { - const { production, deploymentConfig } = environment; - const { error_logging, firebase } = deploymentConfig; - if (production && error_logging?.dsn) { + const { error_logging, firebase } = this.deploymentService.config(); + if (environment.production && error_logging?.dsn) { await this.initialiseSentry(); this.sentryEnabled = true; } - if (production && this.firebaseService.app && Capacitor.isNativePlatform()) { + if (environment.production && this.firebaseService.app && Capacitor.isNativePlatform()) { // crashlytics initialised in app component so omitted here this.crashlyticsEnabled = firebase.crashlytics.enabled; } @@ -74,12 +78,11 @@ export class ErrorHandlerService extends ErrorHandler { * https://docs.sentry.io/platforms/javascript/guides/capacitor/ */ private async initialiseSentry() { - const { deploymentConfig, version, production } = environment; - const { error_logging, name } = deploymentConfig; + const { error_logging, name, _app_builder_version } = this.deploymentService.config(); Sentry.init({ dsn: error_logging?.dsn, - environment: production ? "production" : "development", - release: `${name}-${version}-${GIT_SHA}`, + environment: environment.production ? "production" : "development", + release: `${name}-${_app_builder_version}-${GIT_SHA}`, autoSessionTracking: false, attachStacktrace: true, enabled: true, diff --git a/src/app/shared/services/server/server.service.ts b/src/app/shared/services/server/server.service.ts index 42df072504..076034ca57 100644 --- a/src/app/shared/services/server/server.service.ts +++ b/src/app/shared/services/server/server.service.ts @@ -59,7 +59,7 @@ export class ServerService extends SyncServiceBase { } public async syncUserData() { - const { name } = this.deploymentService.config(); + const { name, _app_builder_version } = this.deploymentService.config(); await this.dynamicDataService.ready(); if (!this.device_info) { this.device_info = await Device.getInfo(); @@ -79,7 +79,7 @@ export class ServerService extends SyncServiceBase { // TODO - get DTO from api (?) const data = { contact_fields, - app_version: environment.version, + app_version: _app_builder_version, device_info: this.device_info, app_deployment_name: name, dynamic_data, diff --git a/src/app/shared/services/task/task-action.service.ts b/src/app/shared/services/task/task-action.service.ts index a80da37711..a52ad252b0 100644 --- a/src/app/shared/services/task/task-action.service.ts +++ b/src/app/shared/services/task/task-action.service.ts @@ -1,10 +1,10 @@ import { Injectable } from "@angular/core"; import { Subject } from "rxjs"; -import { environment } from "src/environments/environment"; import { generateTimestamp } from "../../utils"; import { AsyncServiceBase } from "../asyncService.base"; import { DbService } from "../db/db.service"; +import { DeploymentService } from "../deployment/deployment.service"; @Injectable({ providedIn: "root" }) /** @@ -25,7 +25,10 @@ export class TaskActionService extends AsyncServiceBase { private appInactiveStartTime = new Date().getTime(); /** Don't log inactivity periods lower than this number (30000ms = 30s) */ private readonly INACTIVITY_THRESHOLD = 30000; - constructor(private db: DbService) { + constructor( + private db: DbService, + private deploymentService: DeploymentService + ) { super("TaskActions"); this.registerInitFunction(this.initialise); } @@ -130,13 +133,14 @@ export class TaskActionService extends AsyncServiceBase { } private createNewEntry(task_id: string) { + const { _app_builder_version } = this.deploymentService.config(); const timestamp = generateTimestamp(); const entry: ITaskEntry = { id: `${task_id}_${timestamp}`, task_id, actions: [], _created: timestamp, - _appVersion: environment.version, + _appVersion: _app_builder_version, _completed: false, _duration: 0, }; diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts index b7e290ad64..9b5eec46a6 100644 --- a/src/environments/environment.prod.ts +++ b/src/environments/environment.prod.ts @@ -1,9 +1,7 @@ -import packageJson from "../../package.json"; import deploymentJson from "../../.idems_app/deployments/activeDeployment.json"; import type { IDeploymentConfig } from "data-models"; export const environment = { - version: packageJson.version, // HACK - json config converts functions to strings, not strongly typed deploymentConfig: deploymentJson as any as IDeploymentConfig, production: true, diff --git a/src/environments/environment.ts b/src/environments/environment.ts index 9ac4ab5a0a..6825f94d3a 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -1,10 +1,7 @@ -import packageJson from "../../package.json"; import deploymentJson from "../../.idems_app/deployments/activeDeployment.json"; import type { IDeploymentConfig } from "data-models"; export const environment = { - /** App version, as provided by package.json */ - version: packageJson.version, // HACK - json config converts functions to strings, not strongly typed deploymentConfig: deploymentJson as any as IDeploymentConfig, production: false, From 298a1b2e4890c771db893dc03544903932082651 Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Wed, 11 Sep 2024 12:40:08 -0700 Subject: [PATCH 4/9] fix: dynamic data adapter db names --- .../dynamic-data/adapters/persistedMemory.ts | 6 ++---- .../dynamic-data/adapters/reactiveMemory.ts | 9 +++------ .../services/dynamic-data/dynamic-data.service.ts | 13 ++++++++----- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/app/shared/services/dynamic-data/adapters/persistedMemory.ts b/src/app/shared/services/dynamic-data/adapters/persistedMemory.ts index bdd93774b7..4378dd7a81 100644 --- a/src/app/shared/services/dynamic-data/adapters/persistedMemory.ts +++ b/src/app/shared/services/dynamic-data/adapters/persistedMemory.ts @@ -18,7 +18,6 @@ import { debounceTime, filter, firstValueFrom, Subject } from "rxjs"; import { FlowTypes } from "data-models"; import { deepMergeObjects, compareObjectKeys } from "../../../utils"; -import { DeploymentService } from "../../deployment/deployment.service"; /** * All persisted docs are stored in the same format with a standard set of meta fields and doc data @@ -78,7 +77,7 @@ export class PersistedMemoryAdapter { [key: string]: RxCollection; }>; - constructor(private deploymentService: DeploymentService) { + constructor(private dbName: string) { this.subscribeToStatePersist(); } @@ -98,9 +97,8 @@ export class PersistedMemoryAdapter { } public async create() { - const { name } = this.deploymentService.config(); this.db = await createRxDatabase({ - name: `${name}_user`, + name: `${this.dbName}_user`, storage: getRxStorageDexie({ autoOpen: true }), ignoreDuplicate: true, }); diff --git a/src/app/shared/services/dynamic-data/adapters/reactiveMemory.ts b/src/app/shared/services/dynamic-data/adapters/reactiveMemory.ts index 4085f09124..59d8f62882 100644 --- a/src/app/shared/services/dynamic-data/adapters/reactiveMemory.ts +++ b/src/app/shared/services/dynamic-data/adapters/reactiveMemory.ts @@ -22,8 +22,6 @@ import { RxDBUpdatePlugin } from "rxdb/plugins/update"; addRxPlugin(RxDBUpdatePlugin); import { BehaviorSubject } from "rxjs"; -import { DeploymentService } from "../../deployment/deployment.service"; - /** * Create a base schema for data * NOTE - by default assumes data has an id field which will be used as primary key @@ -59,7 +57,7 @@ interface IDataUpdate { data?: Record; } -export class ReactiveMemoryAdapater { +export class ReactiveMemoryAdapter { private db: RxDatabase< { [key: string]: RxCollection; @@ -67,12 +65,11 @@ export class ReactiveMemoryAdapater { MemoryStorageInternals, RxStorageMemoryInstanceCreationOptions >; - constructor(private deploymentService: DeploymentService) {} + constructor(private dbName: string) {} public async createDB() { - const { name } = this.deploymentService.config(); this.db = await createRxDatabase({ - name, + name: this.dbName, storage: getRxStorageMemory(), ignoreDuplicate: true, }); diff --git a/src/app/shared/services/dynamic-data/dynamic-data.service.ts b/src/app/shared/services/dynamic-data/dynamic-data.service.ts index 5fd7c6138e..e49574cbbc 100644 --- a/src/app/shared/services/dynamic-data/dynamic-data.service.ts +++ b/src/app/shared/services/dynamic-data/dynamic-data.service.ts @@ -8,9 +8,10 @@ import { AppDataService } from "../data/app-data.service"; import { AsyncServiceBase } from "../asyncService.base"; import { arrayToHashmap, deepMergeObjects } from "../../utils"; import { PersistedMemoryAdapter } from "./adapters/persistedMemory"; -import { ReactiveMemoryAdapater, REACTIVE_SCHEMA_BASE } from "./adapters/reactiveMemory"; +import { ReactiveMemoryAdapter, REACTIVE_SCHEMA_BASE } from "./adapters/reactiveMemory"; import { TemplateActionRegistry } from "../../components/template/services/instance/template-action.registry"; import { TopLevelProperty } from "rxdb/dist/types/types"; +import { DeploymentService } from "../deployment/deployment.service"; type IDocWithMeta = { id: string; APP_META?: Record }; @@ -27,7 +28,7 @@ export class DynamicDataService extends AsyncServiceBase { * Each flow is represented in its own collection, and populated as requested. * This allows users to query and subscribe to data changes in an efficient way */ - private db: ReactiveMemoryAdapater; + private db: ReactiveMemoryAdapter; /** * A separate cache stores user edits flow data, initially in memory @@ -46,7 +47,8 @@ export class DynamicDataService extends AsyncServiceBase { constructor( private appDataService: AppDataService, - private templateActionRegistry: TemplateActionRegistry + private templateActionRegistry: TemplateActionRegistry, + private deploymentService: DeploymentService ) { super("Dynamic Data"); this.registerInitFunction(this.initialise); @@ -54,6 +56,7 @@ export class DynamicDataService extends AsyncServiceBase { } private async initialise() { + const { name } = this.deploymentService.config(); // Enable dev mode when not in production // NOTE - calls 'global' so requires polyfill if (!environment.production) { @@ -61,8 +64,8 @@ export class DynamicDataService extends AsyncServiceBase { addRxPlugin(module.RxDBDevModePlugin); }); } - this.writeCache = await new PersistedMemoryAdapter().create(); - this.db = await new ReactiveMemoryAdapater().createDB(); + this.writeCache = await new PersistedMemoryAdapter(name).create(); + this.db = await new ReactiveMemoryAdapter(name).createDB(); } private registerTemplateActionHandlers() { this.templateActionRegistry.register({ From fca4107f690ff1ac9ec5b6539e40d1b6dbc563e5 Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Wed, 11 Sep 2024 13:16:09 -0700 Subject: [PATCH 5/9] fix: deployment init --- src/app/app.component.html | 8 ++++---- src/app/app.component.ts | 11 ++++++----- src/app/app.module.ts | 12 +++++++++++- .../shared/services/deployment/deployment.service.ts | 6 +++--- 4 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/app/app.component.html b/src/app/app.component.html index 50f45c6c03..13b532e1f6 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -17,18 +17,18 @@ {{ sideMenuDefaults.title }}
@if (sideMenuDefaults.should_show_version) { - @if (deploymentService.config()._content_version; as CONTENT_VERSION) { + @if (deploymentConfig()._content_version; as CONTENT_VERSION) { - + {{ CONTENT_VERSION }} } @else { - {{ this.deploymentService.config()._app_builder_version }} + {{ deploymentConfig()._app_builder_version }} } } ({{ this.deploymentService.config().name }})({{ deploymentConfig().name }})
diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 27e8a8a984..81273b264b 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -56,6 +56,10 @@ export class AppComponent { /** Track when app ready to render sidebar and route templates */ public renderAppTemplates = false; + public get deploymentConfig() { + return this.deploymentService.config; + } + /** * A space-separated list of values, hierarchically representing the current platform, * e.g. on iPhone the value would be "mobile ios iphone". @@ -65,8 +69,8 @@ export class AppComponent { platforms: string; constructor( - // Public services (for UI) - public deploymentService: DeploymentService, + // Component UI + private deploymentService: DeploymentService, // 3rd Party Services private platform: Platform, @@ -113,9 +117,6 @@ export class AppComponent { } private async initializeApp() { - // eagerly load deployment config to make available throughout app - await this.deploymentService.ready(); - this.platform.ready().then(async () => { this.platforms = this.platform.platforms().join(" "); this.subscribeToAppConfigChanges(); diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 02bfbf6edb..0adbeb589a 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,4 +1,4 @@ -import { ErrorHandler, NgModule } from "@angular/core"; +import { APP_INITIALIZER, ErrorHandler, NgModule } from "@angular/core"; import { BrowserModule } from "@angular/platform-browser"; import { FormsModule } from "@angular/forms"; import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; @@ -25,6 +25,7 @@ import { TemplateComponentsModule } from "./shared/components/template/template. import { ContextMenuModule } from "./shared/modules/context-menu/context-menu.module"; import { TourModule } from "./feature/tour/tour.module"; import { ErrorHandlerService } from "./shared/services/error-handler/error-handler.service"; +import { DeploymentService } from "./shared/services/deployment/deployment.service"; // Note we need a separate function as it's required // by the AOT compiler. @@ -55,6 +56,15 @@ export function lottiePlayerFactory() { ContextMenuModule, ], providers: [ + // ensure deployment service initialized before app component load + { + provide: APP_INITIALIZER, + multi: true, + useFactory: (deploymentService: DeploymentService) => { + return () => deploymentService.ready(); + }, + deps: [DeploymentService], + }, { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }, HTTP, Device, diff --git a/src/app/shared/services/deployment/deployment.service.ts b/src/app/shared/services/deployment/deployment.service.ts index e0b0a5110e..87adccaf75 100644 --- a/src/app/shared/services/deployment/deployment.service.ts +++ b/src/app/shared/services/deployment/deployment.service.ts @@ -8,8 +8,8 @@ import { DEPLOYMENT_RUNTIME_CONFIG_DEFAULTS, IDeploymentRuntimeConfig } from "pa /** * Deployment runtime config settings * - * NOTE - this is populated as a blocking service during init, - * so no need to await service initialisation before access + * NOTE - this is intialized using an `APP_INITIALIZER` token within + * the main app.module.ts */ export class DeploymentService extends AsyncServiceBase { constructor(private http: HttpClient) { @@ -32,7 +32,7 @@ export class DeploymentService extends AsyncServiceBase { } private loadDeployment() { - return this.http.get("assets/deployment.json").pipe( + return this.http.get("assets/app_data/deployment.json").pipe( catchError(() => { console.warn("No deployment config available"); return of(null); From 0741fabb61498aacf58ae4dff1129e4292e01eb4 Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Wed, 11 Sep 2024 13:36:57 -0700 Subject: [PATCH 6/9] refactor: revert deployment config signals --- src/app/app.component.html | 8 ++++---- src/app/app.component.ts | 4 ++-- src/app/feature/feedback/feedback.service.ts | 2 +- .../shared/services/app-config/app-config.service.ts | 3 +-- src/app/shared/services/auth/auth.service.ts | 2 +- .../shared/services/crashlytics/crashlytics.service.ts | 2 +- src/app/shared/services/db/db-sync.service.ts | 2 +- .../shared/services/deployment/deployment.service.ts | 10 ++++++---- .../services/dynamic-data/dynamic-data.service.ts | 2 +- .../services/error-handler/error-handler.service.ts | 4 ++-- .../services/file-manager/file-manager.service.ts | 2 +- src/app/shared/services/firebase/firebase.service.ts | 2 +- .../services/remote-asset/remote-asset.service.ts | 2 +- src/app/shared/services/seo/seo.service.ts | 2 +- src/app/shared/services/server/server.service.ts | 2 +- src/app/shared/services/task/task-action.service.ts | 2 +- 16 files changed, 26 insertions(+), 25 deletions(-) diff --git a/src/app/app.component.html b/src/app/app.component.html index 13b532e1f6..27bacab8d8 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -17,18 +17,18 @@ {{ sideMenuDefaults.title }}
@if (sideMenuDefaults.should_show_version) { - @if (deploymentConfig()._content_version; as CONTENT_VERSION) { + @if (deploymentConfig._content_version; as CONTENT_VERSION) { - + {{ CONTENT_VERSION }} } @else { - {{ deploymentConfig()._app_builder_version }} + {{ deploymentConfig._app_builder_version }} } } ({{ deploymentConfig().name }})({{ deploymentConfig.name }})
diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 81273b264b..b16b1adf05 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -154,7 +154,7 @@ export class AppComponent { } /** Populate contact fields that may be used by other services during initialisation */ private async populateAppInitFields() { - const { _content_version, _app_builder_version, name } = this.deploymentService.config(); + const { _content_version, _app_builder_version, name } = this.deploymentService.config; this.localStorageService.setProtected("DEPLOYMENT_NAME", name); this.localStorageService.setProtected("APP_VERSION", _app_builder_version); this.localStorageService.setProtected("CONTENT_VERSION", _content_version); @@ -171,7 +171,7 @@ export class AppComponent { * Currently only run on native where specified (but can comment out for testing locally) */ private async loadAuthConfig() { - const { firebase } = this.deploymentService.config(); + const { firebase } = this.deploymentService.config; const { enforceLogin } = this.appAuthenticationDefaults; const ensureLogin = firebase.config && enforceLogin && Capacitor.isNativePlatform(); if (ensureLogin) { diff --git a/src/app/feature/feedback/feedback.service.ts b/src/app/feature/feedback/feedback.service.ts index 6936297986..3058b28d29 100644 --- a/src/app/feature/feedback/feedback.service.ts +++ b/src/app/feature/feedback/feedback.service.ts @@ -368,7 +368,7 @@ export class FeedbackService extends SyncServiceBase { * device info and user uuid */ public generateFeedbackMetadata() { - const { _app_builder_version, name } = this.deploymentService.config(); + const { _app_builder_version, name } = this.deploymentService.config; const metadata: IFeedbackMetadata = { deviceInfo: this.deviceInfo, pathname: location.pathname, diff --git a/src/app/shared/services/app-config/app-config.service.ts b/src/app/shared/services/app-config/app-config.service.ts index 25052fe363..5c9bde8d82 100644 --- a/src/app/shared/services/app-config/app-config.service.ts +++ b/src/app/shared/services/app-config/app-config.service.ts @@ -47,8 +47,7 @@ export class AppConfigService extends SyncServiceBase { } private initialise() { - const deploymentOverrides: IAppConfigOverride = - this.deploymentService.config().app_config || {}; + const deploymentOverrides: IAppConfigOverride = this.deploymentService.config.app_config || {}; this.APP_CONFIG = getDefaultAppConfig(); // Store app config with deployment overrides applied, to be merged with additional overrides when applied this.deploymentAppConfig = this.applyAppConfigOverrides(this.APP_CONFIG, deploymentOverrides); diff --git a/src/app/shared/services/auth/auth.service.ts b/src/app/shared/services/auth/auth.service.ts index 3b65d589d1..affb1e16f9 100644 --- a/src/app/shared/services/auth/auth.service.ts +++ b/src/app/shared/services/auth/auth.service.ts @@ -25,7 +25,7 @@ export class AuthService extends SyncServiceBase { this.initialise(); } private initialise() { - const { firebase } = this.deploymentService.config(); + const { firebase } = this.deploymentService.config; if (firebase?.auth?.enabled && this.firebaseService.app) { this.addAuthListeners(); this.registerTemplateActionHandlers(); diff --git a/src/app/shared/services/crashlytics/crashlytics.service.ts b/src/app/shared/services/crashlytics/crashlytics.service.ts index ac5616d977..a6f7010f7a 100644 --- a/src/app/shared/services/crashlytics/crashlytics.service.ts +++ b/src/app/shared/services/crashlytics/crashlytics.service.ts @@ -20,7 +20,7 @@ export class CrashlyticsService extends AsyncServiceBase { } private async initialise() { if (Capacitor.isNativePlatform()) { - const { firebase } = this.deploymentService.config(); + const { firebase } = this.deploymentService.config; // Crashlytics is still supported on native device without firebase config (uses google-services.json) // so use config property to toggle enabled instead await this.setEnabled({ enabled: firebase?.crashlytics?.enabled }); diff --git a/src/app/shared/services/db/db-sync.service.ts b/src/app/shared/services/db/db-sync.service.ts index 964a8868f4..ea8ac1b998 100644 --- a/src/app/shared/services/db/db-sync.service.ts +++ b/src/app/shared/services/db/db-sync.service.ts @@ -83,7 +83,7 @@ export class DBSyncService extends AsyncServiceBase { /** Populate common app_meta to local record */ private generateServerRecord(record: any, mapping: IDBServerMapping) { - const { name, _app_builder_version } = this.deploymentService.config(); + const { name, _app_builder_version } = this.deploymentService.config; const { is_user_record, user_record_id_field } = mapping; if (is_user_record && user_record_id_field) { const serverRecord: IDBServerUserRecord = { diff --git a/src/app/shared/services/deployment/deployment.service.ts b/src/app/shared/services/deployment/deployment.service.ts index 87adccaf75..eb82ae9ac7 100644 --- a/src/app/shared/services/deployment/deployment.service.ts +++ b/src/app/shared/services/deployment/deployment.service.ts @@ -1,4 +1,4 @@ -import { Injectable, signal } from "@angular/core"; +import { Injectable } from "@angular/core"; import { AsyncServiceBase } from "../asyncService.base"; import { HttpClient } from "@angular/common/http"; import { catchError, map, of, firstValueFrom } from "rxjs"; @@ -18,16 +18,18 @@ export class DeploymentService extends AsyncServiceBase { } /** Private writeable config to allow population from JSON */ - private _config = signal(DEPLOYMENT_RUNTIME_CONFIG_DEFAULTS); + private _config = DEPLOYMENT_RUNTIME_CONFIG_DEFAULTS; /** Read-only access to deployment runtime config */ - public config = this._config.asReadonly(); + public get config() { + return this._config; + } /** Load active deployment configuration from JSON file */ private async initialise() { const deployment = await firstValueFrom(this.loadDeployment()); if (deployment) { - this._config.set(deployment); + this._config = deployment; } } diff --git a/src/app/shared/services/dynamic-data/dynamic-data.service.ts b/src/app/shared/services/dynamic-data/dynamic-data.service.ts index e49574cbbc..0785bf10bd 100644 --- a/src/app/shared/services/dynamic-data/dynamic-data.service.ts +++ b/src/app/shared/services/dynamic-data/dynamic-data.service.ts @@ -56,7 +56,7 @@ export class DynamicDataService extends AsyncServiceBase { } private async initialise() { - const { name } = this.deploymentService.config(); + const { name } = this.deploymentService.config; // Enable dev mode when not in production // NOTE - calls 'global' so requires polyfill if (!environment.production) { diff --git a/src/app/shared/services/error-handler/error-handler.service.ts b/src/app/shared/services/error-handler/error-handler.service.ts index 0e6f2f2290..45e557fe0a 100644 --- a/src/app/shared/services/error-handler/error-handler.service.ts +++ b/src/app/shared/services/error-handler/error-handler.service.ts @@ -35,7 +35,7 @@ export class ErrorHandlerService extends ErrorHandler { * (although workaround required as cannot extend multiple services) */ private async initialise() { - const { error_logging, firebase } = this.deploymentService.config(); + const { error_logging, firebase } = this.deploymentService.config; if (environment.production && error_logging?.dsn) { await this.initialiseSentry(); this.sentryEnabled = true; @@ -78,7 +78,7 @@ export class ErrorHandlerService extends ErrorHandler { * https://docs.sentry.io/platforms/javascript/guides/capacitor/ */ private async initialiseSentry() { - const { error_logging, name, _app_builder_version } = this.deploymentService.config(); + const { error_logging, name, _app_builder_version } = this.deploymentService.config; Sentry.init({ dsn: error_logging?.dsn, environment: environment.production ? "production" : "development", diff --git a/src/app/shared/services/file-manager/file-manager.service.ts b/src/app/shared/services/file-manager/file-manager.service.ts index d9582ae730..be0bf3c48e 100644 --- a/src/app/shared/services/file-manager/file-manager.service.ts +++ b/src/app/shared/services/file-manager/file-manager.service.ts @@ -27,7 +27,7 @@ export class FileManagerService extends SyncServiceBase { } private initialise() { - this.cacheName = this.deploymentService.config().name; + this.cacheName = this.deploymentService.config.name; this.registerTemplateActionHandlers(); } diff --git a/src/app/shared/services/firebase/firebase.service.ts b/src/app/shared/services/firebase/firebase.service.ts index 39fddb5a5b..e8ca57e734 100644 --- a/src/app/shared/services/firebase/firebase.service.ts +++ b/src/app/shared/services/firebase/firebase.service.ts @@ -18,7 +18,7 @@ export class FirebaseService extends SyncServiceBase { * Configure app module imports dependent on what firebase features should be enabled */ private initialise() { - const { firebase } = this.deploymentService.config(); + const { firebase } = this.deploymentService.config; // Check if any services are enabled, simply return if not const enabledServices = Object.entries(firebase) diff --git a/src/app/shared/services/remote-asset/remote-asset.service.ts b/src/app/shared/services/remote-asset/remote-asset.service.ts index fddff64d3d..f097fb85fa 100644 --- a/src/app/shared/services/remote-asset/remote-asset.service.ts +++ b/src/app/shared/services/remote-asset/remote-asset.service.ts @@ -49,7 +49,7 @@ export class RemoteAssetService extends AsyncServiceBase { private async initialise() { this.registerTemplateActionHandlers(); // require supabase to be configured to use remote asset service - const { enabled, publicApiKey, url } = this.deploymentService.config().supabase; + const { enabled, publicApiKey, url } = this.deploymentService.config.supabase; this.supabaseEnabled = enabled; if (this.supabaseEnabled) { await this.ensureAsyncServicesReady([this.templateAssetService, this.dynamicDataService]); diff --git a/src/app/shared/services/seo/seo.service.ts b/src/app/shared/services/seo/seo.service.ts index 95e120414c..6e9b254d15 100644 --- a/src/app/shared/services/seo/seo.service.ts +++ b/src/app/shared/services/seo/seo.service.ts @@ -65,7 +65,7 @@ export class SeoService extends SyncServiceBase { private getDefaultSEOTags(): ISEOMeta { const PUBLIC_URL = location.origin; let faviconUrl = `${PUBLIC_URL}/assets/icon/favicon.svg`; - const { web, app_config } = this.deploymentService.config(); + const { web, app_config } = this.deploymentService.config; if (web?.favicon_asset) { faviconUrl = `${PUBLIC_URL}/assets/app_data/assets/${web.favicon_asset}`; } diff --git a/src/app/shared/services/server/server.service.ts b/src/app/shared/services/server/server.service.ts index 076034ca57..fc0c04a6d7 100644 --- a/src/app/shared/services/server/server.service.ts +++ b/src/app/shared/services/server/server.service.ts @@ -59,7 +59,7 @@ export class ServerService extends SyncServiceBase { } public async syncUserData() { - const { name, _app_builder_version } = this.deploymentService.config(); + const { name, _app_builder_version } = this.deploymentService.config; await this.dynamicDataService.ready(); if (!this.device_info) { this.device_info = await Device.getInfo(); diff --git a/src/app/shared/services/task/task-action.service.ts b/src/app/shared/services/task/task-action.service.ts index a52ad252b0..5cec8a66ed 100644 --- a/src/app/shared/services/task/task-action.service.ts +++ b/src/app/shared/services/task/task-action.service.ts @@ -133,7 +133,7 @@ export class TaskActionService extends AsyncServiceBase { } private createNewEntry(task_id: string) { - const { _app_builder_version } = this.deploymentService.config(); + const { _app_builder_version } = this.deploymentService.config; const timestamp = generateTimestamp(); const entry: ITaskEntry = { id: `${task_id}_${timestamp}`, From adca32bb09a9468aa5e55cc47eea31a40af5cfd7 Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Wed, 11 Sep 2024 13:39:19 -0700 Subject: [PATCH 7/9] chore: code tidying --- src/app/app.module.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 0adbeb589a..322d875069 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -56,6 +56,9 @@ export function lottiePlayerFactory() { ContextMenuModule, ], providers: [ + { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }, + HTTP, + Device, // ensure deployment service initialized before app component load { provide: APP_INITIALIZER, @@ -65,9 +68,6 @@ export function lottiePlayerFactory() { }, deps: [DeploymentService], }, - { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }, - HTTP, - Device, httpInterceptorProviders, { provide: ErrorHandler, useClass: ErrorHandlerService }, ], From 0cb211b0ed9f0ff765ec27647a141527e7fa1505 Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Thu, 12 Sep 2024 11:58:44 -0700 Subject: [PATCH 8/9] chore: update comment --- src/app/shared/services/deployment/deployment.service.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/app/shared/services/deployment/deployment.service.ts b/src/app/shared/services/deployment/deployment.service.ts index eb82ae9ac7..eb897ab8b2 100644 --- a/src/app/shared/services/deployment/deployment.service.ts +++ b/src/app/shared/services/deployment/deployment.service.ts @@ -9,7 +9,11 @@ import { DEPLOYMENT_RUNTIME_CONFIG_DEFAULTS, IDeploymentRuntimeConfig } from "pa * Deployment runtime config settings * * NOTE - this is intialized using an `APP_INITIALIZER` token within - * the main app.module.ts + * the main app.module.ts and will block all other services from loading until + * it is fully initialised + * + * Services that access the deployment config therefore do not need to await + * DeploymentService init/ready methods. */ export class DeploymentService extends AsyncServiceBase { constructor(private http: HttpClient) { From 1a47cb3950b59ad08fc240b42c68d29eb6e99f50 Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Thu, 12 Sep 2024 12:05:01 -0700 Subject: [PATCH 9/9] chore: update comments --- src/app/shared/services/dynamic-data/dynamic-data.service.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/app/shared/services/dynamic-data/dynamic-data.service.ts b/src/app/shared/services/dynamic-data/dynamic-data.service.ts index 0785bf10bd..356ccfd645 100644 --- a/src/app/shared/services/dynamic-data/dynamic-data.service.ts +++ b/src/app/shared/services/dynamic-data/dynamic-data.service.ts @@ -56,6 +56,9 @@ export class DynamicDataService extends AsyncServiceBase { } private async initialise() { + // Use the deployment name as unique database identifier + // This will allow multiple databases to be used on the same origin + // for different deployments (e.g. dev sites running on localhost) const { name } = this.deploymentService.config; // Enable dev mode when not in production // NOTE - calls 'global' so requires polyfill