Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
1 change: 1 addition & 0 deletions website/.env.e2e
Original file line number Diff line number Diff line change
@@ -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"
1 change: 1 addition & 0 deletions website/.env.example
Original file line number Diff line number Diff line change
@@ -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

Expand Down
10 changes: 3 additions & 7 deletions website/src/components/views/wasap/Wasap.astro
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,18 @@
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 = {
wastewaterOrganism: WastewaterOrganismName;
};

const { wastewaterOrganism } = Astro.props;
const config = (isStaging() ? wastewaterOrganismStagingConfigs : wastewaterOrganismConfigs)[wastewaterOrganism];
const config = wastewaterOrganismConfigs()[wastewaterOrganism];
const backendService = new BackendService(getBackendHost());
let resistanceData: ResistanceData = { mutationAnnotations: [], displayMutationsBySet: {} };

Expand Down
2 changes: 2 additions & 0 deletions website/src/env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
24 changes: 12 additions & 12 deletions website/src/layouts/base/header/getPathogenMegaMenuSections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
Expand All @@ -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,
},
Expand Down
22 changes: 22 additions & 0 deletions website/src/types/dbIdSpace.ts
Original file line number Diff line number Diff line change
@@ -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;
}
4 changes: 2 additions & 2 deletions website/src/types/wastewaterConfig.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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');
Expand Down
44 changes: 32 additions & 12 deletions website/src/types/wastewaterConfig.ts
Original file line number Diff line number Diff line change
@@ -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<T>(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',
Expand All @@ -13,7 +25,7 @@ export type WastewaterOrganismName = (typeof wastewaterOrganisms)[keyof typeof w

export const wastewaterPathFragment = 'swiss-wastewater';

function buildWastewaterOrganismConfigs(isStaging: boolean): Record<WastewaterOrganismName, WasapPageConfig> {
function buildWastewaterOrganismConfigs(env: DbIdSpace): Record<WastewaterOrganismName, WasapPageConfig> {
return {
[wastewaterOrganisms.covid]: {
internalName: wastewaterOrganisms.covid,
Expand All @@ -34,21 +46,21 @@ function buildWastewaterOrganismConfigs(isStaging: boolean): Record<WastewaterOr
defaultAnalysisMode: 'resistance',
resistanceMutationCollections: [
{
collectionId: isStaging ? 1 : 4,
collectionId: byEnv(env, { prod: 4, staging: 1, local: 1 }),
name: '3CLpro',
annotationSymbol: 'c',
description:
'SARS-CoV-2 3C-like protease (3CLpro, or Mpro for Main protease) inhibitor resistance mutation as per <a class="link" href="https://covdb.stanford.edu/drms">Stanford Coronavirus Antiviral & Resistance database</a> (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 <a class="link" href="https://covdb.stanford.edu/drms">Stanford Coronavirus Antiviral & Resistance database</a> (last updated on 21 August 2024).',
},
{
collectionId: isStaging ? 3 : 6,
collectionId: byEnv(env, { prod: 6, staging: 3, local: 3 }),
name: 'Spike',
annotationSymbol: 's',
description:
Expand All @@ -59,7 +71,7 @@ function buildWastewaterOrganismConfigs(isStaging: boolean): Record<WastewaterOr
samplingDateField: 'samplingDate',
locationNameField: 'locationName',
predefinedVariantsSource: {
collectionsUserId: isStaging ? 1 : 3,
collectionsUserId: byEnv(env, { prod: 3, staging: 1, local: 1 }),
collectionsTag: '#pango-lineage',
variantSourceLabel: 'Nextclade',
},
Expand Down Expand Up @@ -90,7 +102,7 @@ function buildWastewaterOrganismConfigs(isStaging: boolean): Record<WastewaterOr
minCount: 15,
minJaccard: 0.75,
timeFrame: VARIANT_TIME_FRAME.all,
collectionId: isStaging ? 4944 : 4943, // XFG lineage
collectionId: byEnv(env, { prod: 4943, staging: 4944, local: 5019 }),
},
resistance: {
mode: 'resistance',
Expand Down Expand Up @@ -136,14 +148,14 @@ function buildWastewaterOrganismConfigs(isStaging: boolean): Record<WastewaterOr
clinicalSequenceCountWarningThreshold: 50,
resistanceMutationCollections: [
{
collectionId: isStaging ? 4 : 4983,
collectionId: byEnv(env, { prod: 4983, staging: 4, local: 4 }),
name: 'Nirsevimab',
annotationSymbol: 'n',
description:
'RSV-A F protein resistance mutations against Nirsevimab as per <a class="link" href="https://viralzone.expasy.org/11605">ViralZone</a>.',
},
{
collectionId: isStaging ? 5 : 4984,
collectionId: byEnv(env, { prod: 4984, staging: 5, local: 5 }),
name: 'Palivizumab',
annotationSymbol: 'p',
description:
Expand Down Expand Up @@ -201,14 +213,14 @@ function buildWastewaterOrganismConfigs(isStaging: boolean): Record<WastewaterOr
clinicalSequenceCountWarningThreshold: 50,
resistanceMutationCollections: [
{
collectionId: isStaging ? 6 : 4985,
collectionId: byEnv(env, { prod: 4985, staging: 6, local: 6 }),
name: 'Nirsevimab',
annotationSymbol: 'n',
description:
'RSV-B F protein resistance mutations against Nirsevimab as per <a class="link" href="https://viralzone.expasy.org/11605">ViralZone</a>.',
},
{
collectionId: isStaging ? 7 : 4986,
collectionId: byEnv(env, { prod: 4986, staging: 7, local: 7 }),
name: 'Palivizumab',
annotationSymbol: 'p',
description:
Expand Down Expand Up @@ -241,8 +253,16 @@ function buildWastewaterOrganismConfigs(isStaging: boolean): Record<WastewaterOr
};
}

export const wastewaterOrganismConfigs = buildWastewaterOrganismConfigs(false);
export const wastewaterOrganismStagingConfigs = buildWastewaterOrganismConfigs(true);
export function wastewaterOrganismConfigs() {
switch (getDbIdSpace()) {
case dbIdSpaces.prod:
return buildWastewaterOrganismConfigs(dbIdSpaces.prod);
case dbIdSpaces.staging:
return buildWastewaterOrganismConfigs(dbIdSpaces.staging);
case dbIdSpaces.local:
return buildWastewaterOrganismConfigs(dbIdSpaces.local);
}
}
Comment thread
fhennig marked this conversation as resolved.

export const wastewaterConfig = {
menuListEntryDecoration: 'decoration-teal',
Expand Down
2 changes: 1 addition & 1 deletion website/tests/WasapPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export class WasapPage {
constructor(public readonly page: Page) {}

public async goto(organism: WastewaterOrganismName) {
await this.page.goto(wastewaterOrganismConfigs[organism].path);
await this.page.goto(wastewaterOrganismConfigs()[organism].path);
}

public async submitFilters() {
Expand Down
2 changes: 1 addition & 1 deletion website/tests/wastewater.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ test.describe('WASAP Pages', () => {
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 }) => {
Expand Down
Loading