diff --git a/docker-compose.yml b/docker-compose.yml index 16e0848a5..3844a499b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,6 +8,7 @@ services: environment: BACKEND_URL: http://backend:8080 DASHBOARDS_ENVIRONMENT: dashboards-prod + DB_ID_SPACE: local GITHUB_CLIENT_ID: "dummy" GITHUB_CLIENT_SECRET: "dummy" AUTH_SECRET: "e2e-test-auth-secret-do-not-use-in-production" diff --git a/website/.env.e2e b/website/.env.e2e index 361558c4f..4c9de97b2 100644 --- a/website/.env.e2e +++ b/website/.env.e2e @@ -1,4 +1,5 @@ CONFIG_DIR=../backend/src/main/resources/ DASHBOARDS_ENVIRONMENT=dashboards-prod +DB_ID_SPACE=local AUTH_SECRET="e2e-test-auth-secret-do-not-use-in-production" diff --git a/website/.env.example b/website/.env.example index 5d27f6ddb..ce30fcdc7 100644 --- a/website/.env.example +++ b/website/.env.example @@ -1,6 +1,7 @@ CONFIG_DIR=../backend/src/main/resources/ BACKEND_URL=http://localhost:8080 DASHBOARDS_ENVIRONMENT=dashboards-prod +# DB_ID_SPACE=local # optional override; defaults to 'prod'/'staging' based on DASHBOARDS_ENVIRONMENT LOG_DIR=./logs LOG_LEVEL=info diff --git a/website/src/components/views/wasap/Wasap.astro b/website/src/components/views/wasap/Wasap.astro index 39c86ca61..fa4e4e145 100644 --- a/website/src/components/views/wasap/Wasap.astro +++ b/website/src/components/views/wasap/Wasap.astro @@ -2,14 +2,10 @@ import { WasapPage } from './WasapPage'; import { fetchResistanceData, type ResistanceData } from './resistanceData'; import { BackendService } from '../../../backendApi/backendService.ts'; -import { isStaging, getBackendHost } from '../../../config.ts'; +import { getBackendHost } from '../../../config.ts'; import BaseLayout from '../../../layouts/base/BaseLayout.astro'; import { getInstanceLogger } from '../../../logger.ts'; -import { - wastewaterOrganismConfigs, - wastewaterOrganismStagingConfigs, - type WastewaterOrganismName, -} from '../../../types/wastewaterConfig'; +import { type WastewaterOrganismName, wastewaterOrganismConfigs } from '../../../types/wastewaterConfig'; import { getErrorLogMessage } from '../../../util/getErrorLogMessage'; type Props = { @@ -17,7 +13,7 @@ type Props = { }; const { wastewaterOrganism } = Astro.props; -const config = (isStaging() ? wastewaterOrganismStagingConfigs : wastewaterOrganismConfigs)[wastewaterOrganism]; +const config = wastewaterOrganismConfigs()[wastewaterOrganism]; const backendService = new BackendService(getBackendHost()); let resistanceData: ResistanceData = { mutationAnnotations: [], displayMutationsBySet: {} }; diff --git a/website/src/env.d.ts b/website/src/env.d.ts index 4e1118935..b4283caeb 100644 --- a/website/src/env.d.ts +++ b/website/src/env.d.ts @@ -17,6 +17,8 @@ declare namespace App { interface ImportMetaEnv { // eslint-disable-next-line @typescript-eslint/naming-convention readonly DASHBOARDS_ENVIRONMENT: 'dashboards-staging' | 'dashboards-prod'; + // eslint-disable-next-line @typescript-eslint/naming-convention + readonly DB_ID_SPACE?: import('./types/dbIdSpace').DbIdSpace; } interface ImportMeta { diff --git a/website/src/layouts/base/header/getPathogenMegaMenuSections.ts b/website/src/layouts/base/header/getPathogenMegaMenuSections.ts index 70015a43c..2429deba9 100644 --- a/website/src/layouts/base/header/getPathogenMegaMenuSections.ts +++ b/website/src/layouts/base/header/getPathogenMegaMenuSections.ts @@ -99,24 +99,24 @@ export function getPathogenMegaMenuSections(): PathogenMegaMenuSections { { label: 'SARS-CoV-2', iconType: 'table', - href: wastewaterOrganismConfigs[wastewaterOrganisms.covid].path, - description: wastewaterOrganismConfigs[wastewaterOrganisms.covid].description, + href: wastewaterOrganismConfigs()[wastewaterOrganisms.covid].path, + description: wastewaterOrganismConfigs()[wastewaterOrganisms.covid].description, underlineColor: wastewaterConfig.menuListEntryDecoration, externalLink: false, }, { label: 'RSV-A', iconType: 'table', - href: wastewaterOrganismConfigs[wastewaterOrganisms.rsvA].path, - description: wastewaterOrganismConfigs[wastewaterOrganisms.rsvA].description, + href: wastewaterOrganismConfigs()[wastewaterOrganisms.rsvA].path, + description: wastewaterOrganismConfigs()[wastewaterOrganisms.rsvA].description, underlineColor: wastewaterConfig.menuListEntryDecoration, externalLink: false, }, { label: 'RSV-B', iconType: 'table', - href: wastewaterOrganismConfigs[wastewaterOrganisms.rsvB].path, - description: wastewaterOrganismConfigs[wastewaterOrganisms.rsvB].description, + href: wastewaterOrganismConfigs()[wastewaterOrganisms.rsvB].path, + description: wastewaterOrganismConfigs()[wastewaterOrganisms.rsvB].description, underlineColor: wastewaterConfig.menuListEntryDecoration, externalLink: false, }, @@ -139,24 +139,24 @@ export function getPathogenMegaMenuSections(): PathogenMegaMenuSections { { label: 'Browse SARS-CoV-2 data', iconType: 'database', - href: wastewaterOrganismConfigs[wastewaterOrganisms.covid].browseDataUrl, - description: wastewaterOrganismConfigs[wastewaterOrganisms.covid].browseDataDescription, + href: wastewaterOrganismConfigs()[wastewaterOrganisms.covid].browseDataUrl, + description: wastewaterOrganismConfigs()[wastewaterOrganisms.covid].browseDataDescription, underlineColor: wastewaterConfig.menuListEntryDecoration, externalLink: true, }, { label: 'Browse RSV-A data', iconType: 'database', - href: wastewaterOrganismConfigs[wastewaterOrganisms.rsvA].browseDataUrl, - description: wastewaterOrganismConfigs[wastewaterOrganisms.rsvA].browseDataDescription, + href: wastewaterOrganismConfigs()[wastewaterOrganisms.rsvA].browseDataUrl, + description: wastewaterOrganismConfigs()[wastewaterOrganisms.rsvA].browseDataDescription, underlineColor: wastewaterConfig.menuListEntryDecoration, externalLink: true, }, { label: 'Browse RSV-B data', iconType: 'database', - href: wastewaterOrganismConfigs[wastewaterOrganisms.rsvB].browseDataUrl, - description: wastewaterOrganismConfigs[wastewaterOrganisms.rsvB].browseDataDescription, + href: wastewaterOrganismConfigs()[wastewaterOrganisms.rsvB].browseDataUrl, + description: wastewaterOrganismConfigs()[wastewaterOrganisms.rsvB].browseDataDescription, underlineColor: wastewaterConfig.menuListEntryDecoration, externalLink: true, }, diff --git a/website/src/types/dbIdSpace.ts b/website/src/types/dbIdSpace.ts new file mode 100644 index 000000000..be1828a16 --- /dev/null +++ b/website/src/types/dbIdSpace.ts @@ -0,0 +1,22 @@ +export const dbIdSpaces = { + prod: 'prod', + staging: 'staging', + local: 'local', +} as const; + +export type DbIdSpace = (typeof dbIdSpaces)[keyof typeof dbIdSpaces]; + +export function getDbIdSpace(): DbIdSpace { + const envValue = process.env.DB_ID_SPACE ?? import.meta.env.DB_ID_SPACE; + if (envValue) { + const validValues: string[] = [dbIdSpaces.prod, dbIdSpaces.staging, dbIdSpaces.local]; + if (validValues.includes(envValue)) { + return envValue as DbIdSpace; + } + throw new Error( + `Environment variable DB_ID_SPACE (value '${envValue}') is not valid. Expected one of: ${validValues.join(', ')}`, + ); + } + const dashboardsEnv = process.env.DASHBOARDS_ENVIRONMENT ?? import.meta.env.DASHBOARDS_ENVIRONMENT; + return dashboardsEnv === 'dashboards-staging' ? dbIdSpaces.staging : dbIdSpaces.prod; +} diff --git a/website/src/types/wastewaterConfig.spec.ts b/website/src/types/wastewaterConfig.spec.ts index 4671138a5..0991adf03 100644 --- a/website/src/types/wastewaterConfig.spec.ts +++ b/website/src/types/wastewaterConfig.spec.ts @@ -3,7 +3,7 @@ import { describe, expect, test } from 'vitest'; import { wastewaterOrganismConfigs, wastewaterOrganisms } from './wastewaterConfig'; import { enabledAnalysisModes } from '../components/views/wasap/wasapPageConfig'; -describe.each(Object.entries(wastewaterOrganismConfigs))('wastewaterConfig %s', (_configName, config) => { +describe.each(Object.entries(wastewaterOrganismConfigs()))('wastewaterConfig %s', (_configName, config) => { test('default resistance set name is valid', () => { if (config.resistanceAnalysisModeEnabled) { const resistanceSetNames = config.resistanceMutationCollections.map((s) => s.name); @@ -24,7 +24,7 @@ describe.each(Object.entries(wastewaterOrganismConfigs))('wastewaterConfig %s', }); test('COVID wastewater opens on Spike resistance mutations by default', () => { - const covidConfig = wastewaterOrganismConfigs[wastewaterOrganisms.covid]; + const covidConfig = wastewaterOrganismConfigs()[wastewaterOrganisms.covid]; // This pins the default requested for the COVID wastewater dashboard landing state. expect(covidConfig.defaultAnalysisMode).toBe('resistance'); diff --git a/website/src/types/wastewaterConfig.ts b/website/src/types/wastewaterConfig.ts index 85381d4cd..cfb5f3a35 100644 --- a/website/src/types/wastewaterConfig.ts +++ b/website/src/types/wastewaterConfig.ts @@ -1,8 +1,20 @@ import type { MutationAnnotation } from '@genspectrum/dashboard-components/util'; +import { getDbIdSpace, dbIdSpaces, type DbIdSpace } from './dbIdSpace'; import type { ResistanceMutationCollectionConfig } from '../components/views/wasap/wasapPageConfig'; import { VARIANT_TIME_FRAME, type WasapPageConfig } from '../components/views/wasap/wasapPageConfig'; +function byEnv(env: DbIdSpace, vars: { prod: T; staging: T; local: T }): T { + switch (env) { + case dbIdSpaces.prod: + return vars.prod; + case dbIdSpaces.staging: + return vars.staging; + case dbIdSpaces.local: + return vars.local; + } +} + export const wastewaterOrganisms = { covid: 'covid', rsvA: 'rsv-a', @@ -13,7 +25,7 @@ export type WastewaterOrganismName = (typeof wastewaterOrganisms)[keyof typeof w export const wastewaterPathFragment = 'swiss-wastewater'; -function buildWastewaterOrganismConfigs(isStaging: boolean): Record { +function buildWastewaterOrganismConfigs(env: DbIdSpace): Record { return { [wastewaterOrganisms.covid]: { internalName: wastewaterOrganisms.covid, @@ -34,21 +46,21 @@ function buildWastewaterOrganismConfigs(isStaging: boolean): RecordStanford Coronavirus Antiviral & Resistance database (last updated on 21 August 2024).', }, { - collectionId: isStaging ? 2 : 5, + collectionId: byEnv(env, { prod: 5, staging: 2, local: 2 }), name: 'RdRp', annotationSymbol: 'r', description: 'SARS-CoV-2 RNA-dependent RNA polymerase (RdRP) inhibitor resistance mutation as per Stanford Coronavirus Antiviral & Resistance database (last updated on 21 August 2024).', }, { - collectionId: isStaging ? 3 : 6, + collectionId: byEnv(env, { prod: 6, staging: 3, local: 3 }), name: 'Spike', annotationSymbol: 's', description: @@ -59,7 +71,7 @@ function buildWastewaterOrganismConfigs(isStaging: boolean): RecordViralZone.', }, { - collectionId: isStaging ? 5 : 4984, + collectionId: byEnv(env, { prod: 4984, staging: 5, local: 5 }), name: 'Palivizumab', annotationSymbol: 'p', description: @@ -201,14 +213,14 @@ function buildWastewaterOrganismConfigs(isStaging: boolean): RecordViralZone.', }, { - collectionId: isStaging ? 7 : 4986, + collectionId: byEnv(env, { prod: 4986, staging: 7, local: 7 }), name: 'Palivizumab', annotationSymbol: 'p', description: @@ -241,8 +253,16 @@ function buildWastewaterOrganismConfigs(isStaging: boolean): Record { test.setTimeout(60_000); for (const organism of Object.values(wastewaterOrganisms)) { - const { name } = wastewaterOrganismConfigs[organism]; + const { name } = wastewaterOrganismConfigs()[organism]; test.describe(name, () => { test('should load with default filters and show mutation data', async ({ wasapPage }) => {