Skip to content

Commit 02fcb00

Browse files
fhennigclaude
andcommitted
feat(website): remove lapisUrls from runtime config
`lapisUrls` is no longer injected into `runtime_config.json` — the LAPIS URL for any organism is now derived as `{backendUrl}/{organism}/lapis`, matching the backend proxy routes added in the previous commit. Changes: - `runtimeConfig.ts`: remove `lapisUrls` field from `serviceUrls` Zod schema - `config.ts`: rewrite `getLapisUrl()` to compute from `backendUrl` instead of reading a config map; no more runtime error on unknown organisms - `GroupPage.tsx`: replace `clientConfig.lapisUrls[key]` (with null guard) with `getLapisUrl(clientConfig, key)` - `SeqSetRecordsTableWithMetadata.tsx`: add `organisms: string[]` prop; iterate that list and compute each URL via `getLapisUrl` — removes the hacky `lapisUrls` dummy-organism filter - `SeqSetItem.tsx`: thread `organisms` prop through to `SeqSetRecordsTableWithMetadata` - `seqsets/[seqSetId].[version].astro`: pass `Object.keys(websiteConfig.organisms)` as `organisms` - `api-documentation/index.astro`: compute LAPIS doc links from `backendUrl` + organism keys (already available as `organismKeys`) - `loculus-info/index.ts`: compute `lapis` response field from `backendUrl` + organism keys instead of reading `lapisUrls` - `_common-metadata.tpl`: remove `lapisUrls` from `publicRuntimeConfig` - `loculus-website-config.yaml`: remove `lapisUrls` from serverSide block (URL is now computed client-side) - test files: replace `testConfig.serverSide.lapisUrls[testOrganism]` with `getLapisUrl(testConfig.serverSide, testOrganism)` Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 78c6cae commit 02fcb00

13 files changed

Lines changed: 49 additions & 59 deletions

File tree

kubernetes/loculus/templates/_common-metadata.tpl

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -611,10 +611,7 @@ fields:
611611

612612
{{- define "loculus.publicRuntimeConfig" }}
613613
{{- $backendUrl := include "loculus.backendUrl" . }}
614-
{{- $lapisUrlTemplate := printf "%s/%%organism%%/lapis" $backendUrl }}
615-
{{- $externalLapisUrlConfig := dict "lapisUrlTemplate" $lapisUrlTemplate "config" $.Values }}
616614
"backendUrl": "{{ $backendUrl }}",
617-
"lapisUrls": {{- include "loculus.generateExternalLapisUrls" $externalLapisUrlConfig | fromYaml | toJson }},
618615
"keycloakUrl": "{{ include "loculus.keycloakUrl" . }}"
619616
{{- end }}
620617

kubernetes/loculus/templates/loculus-website-config.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ data:
1919
{{- else -}}
2020
"backendUrl": "http://loculus-backend-service:8079",
2121
{{- end }}
22-
"lapisUrls": {{- include "loculus.generateInternalLapisUrlsViaBackend" . | fromYaml | toJson }},
2322
"keycloakUrl": "{{ if not .Values.disableWebsite -}}http://loculus-keycloak-service:8083{{ else -}}http://{{ $.Values.localHost }}:8083{{ end }}"
2423
{{- end }}
2524
},

website/src/components/SeqSetCitations/SeqSetItem.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ const SeqSetSectionEntry: FC<{ label: string; value: React.ReactNode }> = ({ lab
4949

5050
type SeqSetItemProps = {
5151
clientConfig: ClientConfig;
52+
organisms: string[];
5253
accessToken: string;
5354
seqSetAccessionVersion: string;
5455
seqSet: SeqSet;
@@ -63,6 +64,7 @@ type SeqSetItemProps = {
6364

6465
const SeqSetItemInner: FC<SeqSetItemProps> = ({
6566
clientConfig,
67+
organisms,
6668
accessToken,
6769
seqSetAccessionVersion,
6870
seqSet,
@@ -222,6 +224,7 @@ const SeqSetItemInner: FC<SeqSetItemProps> = ({
222224
<SeqSetRecordsTableWithMetadata
223225
seqSetRecords={getPaginatedSeqSetRecords()}
224226
clientConfig={clientConfig}
227+
organisms={organisms}
225228
fieldsToDisplay={fieldsToDisplay}
226229
organismDisplayNames={organismDisplayNames}
227230
/>

website/src/components/SeqSetCitations/SeqSetRecordsTableWithMetadata.tsx

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { useQuery } from '@tanstack/react-query';
22
import axios from 'axios';
33
import { type FC, useMemo } from 'react';
44

5+
import { getLapisUrl } from '../../config';
56
import { versionStatuses } from '../../types/lapis';
67
import type { ClientConfig } from '../../types/runtimeConfig';
78
import { type SeqSetRecord, SeqSetRecordType } from '../../types/seqSetCitation';
@@ -20,6 +21,7 @@ type FieldToDisplay = {
2021
type SeqSetRecordsTableWithMetadataProps = {
2122
seqSetRecords: SeqSetRecord[];
2223
clientConfig: ClientConfig;
24+
organisms: string[];
2325
fieldsToDisplay?: FieldToDisplay[];
2426
sortByKey?: keyof SeqSetRecord;
2527
organismDisplayNames?: Record<string, string>;
@@ -46,6 +48,7 @@ async function queryLapisDetails(
4648
const fetchRecordsMetadata = async (
4749
records: SeqSetRecord[],
4850
clientConfig: ClientConfig,
51+
organisms: string[],
4952
fieldsToDisplay: FieldToDisplay[],
5053
): Promise<Map<string, RecordMetadata>> => {
5154
const accessions = records.map((record) => record.accession);
@@ -61,17 +64,11 @@ const fetchRecordsMetadata = async (
6164
// Extract just the field names for the API request
6265
const fields = fieldsToDisplay.map((f) => f.field);
6366

64-
// filter out "organism" as substring in lapisUrls as a hack to remove the dummy organisms
65-
// #TODO: do this better, in a less hacky way
66-
// But if we do try to query something that doesn't have the field its no huge problem it will just lead to a console error
67-
const lapisUrlsWithoutDummies = Object.fromEntries(
68-
Object.entries(clientConfig.lapisUrls).filter(([organism]) => !organism.includes('organism')),
69-
);
70-
7167
const metadataMap = new Map<string, RecordMetadata>();
7268

7369
// Query all LAPIS instances in parallel
74-
const lapisPromises = Object.entries(lapisUrlsWithoutDummies).map(async ([organism, lapisUrl]) => {
70+
const lapisPromises = organisms.map(async (organism) => {
71+
const lapisUrl = getLapisUrl(clientConfig, organism);
7572
const queries: Promise<{ data: Record<string, unknown>[]; keyField: string }>[] = [];
7673

7774
// Query versioned accessions by accessionVersion
@@ -129,6 +126,7 @@ const SeqSetRecordsTableCell: FC<{ title: string; children: React.ReactNode }> =
129126
export const SeqSetRecordsTableWithMetadata: FC<SeqSetRecordsTableWithMetadataProps> = ({
130127
seqSetRecords,
131128
clientConfig,
129+
organisms,
132130
fieldsToDisplay = [
133131
{ field: 'geoLocCountry', displayName: 'Country' },
134132
{ field: 'sampleCollectionDate', displayName: 'Collection date' },
@@ -138,8 +136,8 @@ export const SeqSetRecordsTableWithMetadata: FC<SeqSetRecordsTableWithMetadataPr
138136
organismDisplayNames = {},
139137
}) => {
140138
const { data: metadataMap, isLoading } = useQuery({
141-
queryKey: ['seqset-records-metadata', seqSetRecords, clientConfig, fieldsToDisplay],
142-
queryFn: () => fetchRecordsMetadata(seqSetRecords, clientConfig, fieldsToDisplay),
139+
queryKey: ['seqset-records-metadata', seqSetRecords, clientConfig, organisms, fieldsToDisplay],
140+
queryFn: () => fetchRecordsMetadata(seqSetRecords, clientConfig, organisms, fieldsToDisplay),
143141
staleTime: 5 * 60 * 1000, // 5 minutes
144142
});
145143

website/src/components/SequenceDetailsPage/getTableData.spec.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { beforeEach, describe, expect, test } from 'vitest';
44
import { getTableData, type GetTableDataResult } from './getTableData.ts';
55
import { type TableDataEntry } from './types.ts';
66
import { mockRequest, testConfig, testOrganism } from '../../../vitest.setup.ts';
7+
import { getLapisUrl } from '../../config.ts';
78
import { LapisClient } from '../../services/lapisClient.ts';
89
import type { ProblemDetail } from '../../types/backend.ts';
910
import {
@@ -56,7 +57,7 @@ const info = {
5657

5758
const accessionVersion = 'accession';
5859

59-
const lapisClient = LapisClient.create(testConfig.serverSide.lapisUrls[testOrganism], schema);
60+
const lapisClient = LapisClient.create(getLapisUrl(testConfig.serverSide, testOrganism), schema);
6061

6162
describe('getTableData', () => {
6263
beforeEach(() => {

website/src/components/Submission/FileUpload/FolderUploadComponent.spec.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ const defaultProps = {
4141
},
4242
inputMode: 'bulk' as const,
4343
accessToken: 'test-token',
44-
clientConfig: { backendUrl: 'http://test-backend', lapisUrls: {} },
44+
clientConfig: { backendUrl: 'http://test-backend' },
4545
groupId: 1,
4646
setFileMapping: mockSetFileMapping,
4747
onError: mockOnError,

website/src/components/User/GroupPage.tsx

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { type FC, type FormEvent, useMemo, useState, type ReactNode } from 'reac
44

55
import { CumulativeSubmissionsChart, type TimeSeriesData } from './CumulativeSubmissionsChart.tsx';
66
import { getClientLogger } from '../../clientLogger.ts';
7-
import type { Organism } from '../../config.ts';
7+
import { getLapisUrl, type Organism } from '../../config.ts';
88
import { useGroupPageHooks } from '../../hooks/useGroupOperations.ts';
99
import { routes } from '../../routes/routes.ts';
1010
import type { ContinueSubmissionIntent } from '../../routes/routes.ts';
@@ -314,11 +314,7 @@ async function fetchSequenceCounts(groupId: number, clientConfig: ClientConfig,
314314
const counts: Record<string, number> = {};
315315
await Promise.all(
316316
organisms.map(async ({ key }) => {
317-
const url = clientConfig.lapisUrls[key];
318-
if (!url) {
319-
counts[key] = 0;
320-
return;
321-
}
317+
const url = getLapisUrl(clientConfig, key);
322318
try {
323319
const response = await axios.post(`${url}/sample/aggregated`, {
324320
[GROUP_ID_FIELD]: groupId,
@@ -346,11 +342,7 @@ async function fetchTimeSeriesData(
346342
const data: TimeSeriesData = {};
347343
await Promise.all(
348344
organisms.map(async ({ key }) => {
349-
const url = clientConfig.lapisUrls[key];
350-
if (!url) {
351-
data[key] = [];
352-
return;
353-
}
345+
const url = getLapisUrl(clientConfig, key);
354346
try {
355347
const response = await axios.post(`${url}/sample/aggregated`, {
356348
[GROUP_ID_FIELD]: groupId,

website/src/config.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -239,10 +239,7 @@ export function getRuntimeConfig(): RuntimeConfig {
239239
}
240240

241241
export function getLapisUrl(serviceConfig: ServiceUrls, organism: string): string {
242-
if (!(organism in serviceConfig.lapisUrls)) {
243-
throw new Error(`No lapis url configured for organism ${organism}`);
244-
}
245-
return serviceConfig.lapisUrls[organism];
242+
return `${serviceConfig.backendUrl}/${organism}/lapis`;
246243
}
247244

248245
export function getReferenceGenomes(organism: string): ReferenceGenomesInfo {

website/src/pages/api-documentation/index.astro

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
import { authenticationApiDocsUrl } from './authenticationApiDocsUrl';
3-
import { dataUseTermsAreEnabled, getRuntimeConfig, getWebsiteConfig } from '../../config';
3+
import { dataUseTermsAreEnabled, getLapisUrl, getRuntimeConfig, getWebsiteConfig } from '../../config';
44
import BaseLayout from '../../layouts/BaseLayout.astro';
55
import { routes } from '../../routes/routes.ts';
66
import { getAuthBaseUrl } from '../../utils/getAuthUrl';
@@ -82,22 +82,28 @@ const BUTTON_CLASS =
8282
<h2 class='text-xl font-semibold text-primary-400 mb-4'>LAPIS query engines</h2>
8383
<div class='space-y-4'>
8484
{
85-
Object.entries(clientConfig.lapisUrls).map(([organism, url]) => (
86-
<a class={BUTTON_CLASS} href={url + '/swagger-ui/index.html'}>
87-
{organismToDisplayName[organism]} LAPIS API documentation
88-
</a>
89-
))
85+
organismKeys.map((organism) => {
86+
const url = getLapisUrl(clientConfig, organism);
87+
return (
88+
<a class={BUTTON_CLASS} href={url + '/swagger-ui/index.html'}>
89+
{organismToDisplayName[organism]} LAPIS API documentation
90+
</a>
91+
);
92+
})
9093
}
9194
</div>
9295
<div class='mt-8'>
9396
<span class='font-medium'>URLs of LAPIS query engines:</span>
9497
<ul class='list-disc ml-6'>
9598
{
96-
Object.entries(clientConfig.lapisUrls).map(([organism, url]) => (
97-
<li>
98-
{organismToDisplayName[organism]}: <code>{url}</code>
99-
</li>
100-
))
99+
organismKeys.map((organism) => {
100+
const url = getLapisUrl(clientConfig, organism);
101+
return (
102+
<li>
103+
{organismToDisplayName[organism]}: <code>{url}</code>
104+
</li>
105+
);
106+
})
101107
}
102108
</ul>
103109
</div>

website/src/pages/loculus-info/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ export const GET: APIRoute = async ({ request }) => {
1919
const response = {
2020
hosts: {
2121
backend: runtime.public.backendUrl,
22-
lapis: runtime.public.lapisUrls,
22+
lapis: Object.fromEntries(
23+
Object.keys(website.organisms).map((org) => [org, `${runtime.public.backendUrl}/${org}/lapis`]),
24+
),
2325
keycloak: keycloakUrl,
2426
website: new URL(request.url).origin,
2527
},

0 commit comments

Comments
 (0)