Skip to content

Commit

Permalink
feat(docs): generate correct URLs for nucleotide sequences endpoints …
Browse files Browse the repository at this point in the history
…in multi-segmented case #521
  • Loading branch information
fengelniederhammer committed Dec 20, 2023
1 parent 305e91c commit 6058b99
Show file tree
Hide file tree
Showing 16 changed files with 162 additions and 31 deletions.
3 changes: 2 additions & 1 deletion lapis2-docs/.env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# Create a .env file with this input might NOT work. You might need to properly set CONFIG_FILE as an
# environment variable.
CONFIG_FILE=../lapis2/src/test/resources/config/testDatabaseConfig.yaml
CONFIG_FILE=../siloLapisTests/testData/testDatabaseConfig.yaml
REFERENCE_GENOMES_FILE=../siloLapisTests/testData/reference_genomes.json
1 change: 1 addition & 0 deletions lapis2-docs/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ RUN apk update && apk add curl

COPY . .
ENV CONFIG_FILE=/config/database_config.yaml
ENV REFERENCE_GENOMES_FILE=/config/reference_genomes.json

EXPOSE 3000
VOLUME /config
Expand Down
2 changes: 1 addition & 1 deletion lapis2-docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ This documentation is a website built with
For running and building the website, the environment variables `LAPIS_URL` and `CONFIG_FILE` must be set, e.g.:

```shell
CONFIG_FILE=../siloLapisTests/testData/testDatabaseConfig.yaml LAPIS_URL=http://localhost:8080 npm run dev
CONFIG_FILE=../siloLapisTests/testData/testDatabaseConfig.yaml REFERENCE_GENOMES_FILE=../siloLapisTests/testData/reference_genomes.json LAPIS_URL=http://localhost:8080 npm run dev
```

## Deploying
Expand Down
3 changes: 2 additions & 1 deletion lapis2-docs/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion lapis2-docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
"swagger-ui": "^5.10.5",
"swagger-ui-react": "^5.10.5",
"tailwindcss": "^3.3.7",
"yaml": "^2.3.4"
"yaml": "^2.3.4",
"zod": "^3.22.4"
},
"devDependencies": {
"@astrojs/check": "^0.3.2",
Expand Down
15 changes: 12 additions & 3 deletions lapis2-docs/src/components/QueryGenerator/QueryGenerator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,17 @@ import { Result } from './Result';
import { type Config } from '../../config';
import { type OrderByLimitOffset, OrderLimitOffsetSelection } from './OrderLimitOffsetSelection.tsx';
import { getInitialQueryState, type QueryTypeSelectionState } from './QueryTypeSelectionState.ts';
import { type ReferenceGenomes } from '../../reference_genomes.ts';

type Props = {
config: Config;
referenceGenomes: ReferenceGenomes;
lapisUrl: string;
};

export const QueryGenerator = ({ config, lapisUrl }: Props) => {
export const QueryGenerator = ({ config, referenceGenomes, lapisUrl }: Props) => {
const [step, setStep] = useState(0);
const [queryType, setQueryType] = useState<QueryTypeSelectionState>(getInitialQueryState());
const [queryType, setQueryType] = useState<QueryTypeSelectionState>(getInitialQueryState(referenceGenomes));
const [filters, setFilters] = useState<Filters>(new Map());
const [orderByLimitOffset, setOrderByLimitOffset] = useState<OrderByLimitOffset>({
orderBy: [],
Expand All @@ -33,7 +35,14 @@ export const QueryGenerator = ({ config, lapisUrl }: Props) => {
</div>

<div className='mt-8'>
{step === 0 && <QueryTypeSelection config={config} state={queryType} onStateChange={setQueryType} />}
{step === 0 && (
<QueryTypeSelection
config={config}
referenceGenomes={referenceGenomes}
state={queryType}
onStateChange={setQueryType}
/>
)}
{step === 1 && <FiltersSelection config={config} filters={filters} onFiltersChange={setFilters} />}
{step === 2 && (
<OrderLimitOffsetSelection
Expand Down
51 changes: 44 additions & 7 deletions lapis2-docs/src/components/QueryGenerator/QueryTypeSelection.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
import type { ReactNode } from 'react';
import type { Dispatch, ReactNode, SetStateAction } from 'react';
import type { Config } from '../../config';
import { CheckBoxesWrapper, ContainerWrapper, LabeledCheckBox, LabelWrapper } from './styled-components';
import {
type AlignmentType,
alignmentTypes,
type DetailsType,
MULTI_SEGMENTED,
type QueryTypeSelectionState,
type Selection,
type SequenceType,
sequenceTypes,
} from './QueryTypeSelectionState.ts';
import { type ReferenceGenomes } from '../../reference_genomes.ts';

type Props = {
config: Config;
referenceGenomes: ReferenceGenomes;
state: QueryTypeSelectionState;
onStateChange: (state: QueryTypeSelectionState) => void;
onStateChange: Dispatch<SetStateAction<QueryTypeSelectionState>>;
};

export const QueryTypeSelection = (props: Props) => {
Expand Down Expand Up @@ -274,14 +277,26 @@ const Insertions = ({ state, onStateChange }: Props) => {
);
};

const NucleotideSequences = ({ state, onStateChange }: Props) => {
const NucleotideSequences = ({ referenceGenomes, state, onStateChange }: Props) => {
const changeType = (type: AlignmentType) => {
onStateChange({
...state,
nucleotideSequences: { ...state.nucleotideSequences, type },
});
};

const changeSegment = (segmentName: string) =>
onStateChange((prev) => ({
...prev,
nucleotideSequences: {
...prev.nucleotideSequences,
segment: {
type: MULTI_SEGMENTED,
segmentName,
},
},
}));

return (
<ContainerWrapper>
<div>
Expand All @@ -299,12 +314,29 @@ const NucleotideSequences = ({ state, onStateChange }: Props) => {
/>
))}
</CheckBoxesWrapper>
{state.nucleotideSequences.segment.type === MULTI_SEGMENTED && (
<>
<LabelWrapper>Which segments are you interested in?</LabelWrapper>
<select
className='input input-bordered w-full max-w-xs'
disabled={state.selection !== 'nucleotideSequences'}
value={state.nucleotideSequences.segment.segmentName}
onChange={(e) => changeSegment(e.target.value)}
>
{referenceGenomes.nucleotideSequences.map((segment) => (
<option value={segment.name} key={segment.name}>
{segment.name}
</option>
))}
</select>
</>
)}
</div>
</ContainerWrapper>
);
};

const AminoAcidSequences = ({ state, onStateChange }: Props) => {
const AminoAcidSequences = ({ referenceGenomes, state, onStateChange }: Props) => {
const changeGene = (gene: string) => {
onStateChange({
...state,
Expand All @@ -316,13 +348,18 @@ const AminoAcidSequences = ({ state, onStateChange }: Props) => {
<ContainerWrapper>
<div>
<LabelWrapper>Which gene/reading frame are you interested in?</LabelWrapper>
<input
type='text'
<select
className='input input-bordered w-full max-w-xs'
disabled={state.selection !== 'aminoAcidSequences'}
value={state.aminoAcidSequences.gene}
onChange={(e) => changeGene(e.target.value)}
/>
>
{referenceGenomes.genes.map((gene) => (
<option value={gene.name} key={gene.name}>
{gene.name}
</option>
))}
</select>
</div>
</ContainerWrapper>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Config, MetadataType } from '../../config.ts';
import type { ResultField, ResultFieldType } from '../../utils/code-generators/types.ts';
import { isMultiSegmented, type ReferenceGenomes } from '../../reference_genomes.ts';

export type Selection = keyof Omit<QueryTypeSelectionState, 'selection'>;

Expand All @@ -9,6 +10,9 @@ export type DetailsType = 'all' | 'selected';
export const alignmentTypes = ['unaligned', 'aligned'] as const;
export type AlignmentType = (typeof alignmentTypes)[number];

export const SINGLE_SEGMENTED = 'singleSegmented' as const;
export const MULTI_SEGMENTED = 'multiSegmented' as const;

export type QueryTypeSelectionState = {
selection: Selection;
aggregatedAll: {};
Expand All @@ -28,13 +32,21 @@ export type QueryTypeSelectionState = {
};
nucleotideSequences: {
type: AlignmentType;
segment:
| {
type: typeof SINGLE_SEGMENTED;
}
| {
type: typeof MULTI_SEGMENTED;
segmentName: string;
};
};
aminoAcidSequences: {
gene: string;
};
};

export function getInitialQueryState(): QueryTypeSelectionState {
export function getInitialQueryState(referenceGenomes: ReferenceGenomes): QueryTypeSelectionState {
return {
selection: 'aggregatedAll',
aggregatedAll: {},
Expand All @@ -54,9 +66,12 @@ export function getInitialQueryState(): QueryTypeSelectionState {
},
nucleotideSequences: {
type: 'unaligned',
segment: isMultiSegmented(referenceGenomes)
? { type: MULTI_SEGMENTED, segmentName: referenceGenomes.nucleotideSequences[0].name }
: { type: SINGLE_SEGMENTED },
},
aminoAcidSequences: {
gene: '',
gene: referenceGenomes.genes[0].name,
},
};
}
Expand Down Expand Up @@ -93,9 +108,7 @@ function getFieldsThatAreAlwaysPresent(selection: Selection): ResultField[] {
{ name: 'count', type: 'integer', nullable: false },
];
case 'details':
return [];
case 'nucleotideSequences':
return [];
case 'aminoAcidSequences':
return [];
}
Expand Down
17 changes: 11 additions & 6 deletions lapis2-docs/src/components/QueryGenerator/Result.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type { Config } from '../../config';
import { useState } from 'react';
import type { ResultField } from '../../utils/code-generators/types';
import { ContainerWrapper, LabelWrapper } from './styled-components';
import { getResultFields, type QueryTypeSelectionState } from './QueryTypeSelectionState.ts';
import { getResultFields, MULTI_SEGMENTED, type QueryTypeSelectionState } from './QueryTypeSelectionState.ts';
import type { OrderByLimitOffset } from './OrderLimitOffsetSelection.tsx';

type Props = {
Expand Down Expand Up @@ -125,11 +125,11 @@ function constructPostQuery({ queryType, filters, config, lapisUrl, orderByLimit
}
break;
case 'mutations':
endpoint += queryType.mutations.type === 'nucleotide' ? 'nuc-mutations' : 'aa-mutations';
endpoint += queryType.mutations.type === 'nucleotide' ? 'nucleotideMutations' : 'aminoAcidMutations';
body.minProportion = queryType.mutations.minProportion;
break;
case 'insertions':
endpoint += queryType.insertions.type === 'nucleotide' ? 'nuc-insertions' : 'aa-insertions';
endpoint += queryType.insertions.type === 'nucleotide' ? 'nucleotideInsertions' : 'aminoAcidInsertions';
break;
case 'details':
endpoint += 'details';
Expand All @@ -139,11 +139,16 @@ function constructPostQuery({ queryType, filters, config, lapisUrl, orderByLimit
}
break;
case 'nucleotideSequences':
// TODO(#521): multi segment case
endpoint += queryType.nucleotideSequences.type === 'unaligned' ? 'nuc-sequences' : 'nuc-sequences-aligned';
endpoint +=
queryType.nucleotideSequences.type === 'unaligned'
? 'nucleotideSequences'
: 'alignedNucleotideSequences';
if (queryType.nucleotideSequences.segment.type === MULTI_SEGMENTED) {
endpoint += `/${queryType.nucleotideSequences.segment.segmentName}`;
}
break;
case 'aminoAcidSequences':
endpoint += `aa-sequences-aligned/${queryType.aminoAcidSequences.gene}`;
endpoint += `alignedAminoAcidSequences/${queryType.aminoAcidSequences.gene}`;
break;
}
for (let [name, value] of filters) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ description: A step-by-step wizard to help you generate a request

import { getConfig } from '../../../config.ts';
import { getLapisUrl } from '../../../lapisUrl.js';

import { getReferenceGenomes } from "../../../reference_genomes.js";
import { QueryGenerator } from '../../../components/QueryGenerator/QueryGenerator.tsx';

<QueryGenerator client:load config={getConfig()} lapisUrl={getLapisUrl()} />
<QueryGenerator client:load config={getConfig()} referenceGenomes={getReferenceGenomes()} lapisUrl={getLapisUrl()} />
31 changes: 31 additions & 0 deletions lapis2-docs/src/reference_genomes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import fs from 'fs';
import { z } from 'zod';

const referenceSequenceSchema = z.object({
name: z.string(),
sequence: z.string(),
});

const referenceGenomesSchema = z.object({
nucleotideSequences: z.array(referenceSequenceSchema),
genes: z.array(referenceSequenceSchema),
});

export type ReferenceGenomes = z.infer<typeof referenceGenomesSchema>;

let _referenceGenomes: ReferenceGenomes | null = null;

export function getReferenceGenomes(): ReferenceGenomes {
if (_referenceGenomes === null) {
const configFilePath = process.env.REFERENCE_GENOMES_FILE;
if (configFilePath === undefined) {
throw new Error('Please set the environment variable REFERENCE_GENOMES_FILE.');
}
_referenceGenomes = referenceGenomesSchema.parse(JSON.parse(fs.readFileSync(configFilePath, 'utf8')));
}
return _referenceGenomes;
}

export function isMultiSegmented(referenceGenomes: ReferenceGenomes): boolean {
return referenceGenomes.nucleotideSequences.length > 1;
}
1 change: 1 addition & 0 deletions lapis2-docs/test-docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ services:
- "3000:3000"
volumes:
- ../siloLapisTests/testData/testDatabaseConfig.yaml:/config/database_config.yaml
- ../siloLapisTests/testData/reference_genomes.json:/config/reference_genomes.json
environment:
LAPIS_URL: localhost:8080
BASE_URL: /docs/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import org.genspectrum.lapis.openApi.DataFormat
import org.genspectrum.lapis.openApi.DetailsFields
import org.genspectrum.lapis.openApi.DetailsOrderByFields
import org.genspectrum.lapis.openApi.FieldsToAggregateBy
import org.genspectrum.lapis.openApi.Gene
import org.genspectrum.lapis.openApi.INSERTIONS_REQUEST_SCHEMA
import org.genspectrum.lapis.openApi.InsertionsOrderByFields
import org.genspectrum.lapis.openApi.LapisAggregatedResponse
Expand Down Expand Up @@ -1269,7 +1270,9 @@ class LapisController(
@GetMapping("$ALIGNED_AMINO_ACID_SEQUENCES_ROUTE/{gene}", produces = ["text/x-fasta"])
@LapisAlignedAminoAcidSequenceResponse
fun getAlignedAminoAcidSequence(
@PathVariable(name = "gene", required = true) gene: String,
@PathVariable(name = "gene", required = true)
@Gene
gene: String,
@PrimitiveFieldFilters
@RequestParam
sequenceFilters: GetRequestSequenceFilters?,
Expand Down Expand Up @@ -1314,7 +1317,9 @@ class LapisController(
@PostMapping("$ALIGNED_AMINO_ACID_SEQUENCES_ROUTE/{gene}", produces = ["text/x-fasta"])
@LapisAlignedAminoAcidSequenceResponse
fun postAlignedAminoAcidSequence(
@PathVariable(name = "gene", required = true) gene: String,
@PathVariable(name = "gene", required = true)
@Gene
gene: String,
@Parameter(schema = Schema(ref = "#/components/schemas/$ALIGNED_AMINO_ACID_SEQUENCE_REQUEST_SCHEMA"))
@RequestBody
request: SequenceFiltersRequest,
Expand Down
Loading

0 comments on commit 6058b99

Please sign in to comment.