Skip to content

Commit

Permalink
feat(docs): references/additional-request-properties #604
Browse files Browse the repository at this point in the history
  • Loading branch information
fengelniederhammer committed Feb 21, 2024
1 parent 5b02ca8 commit 837e474
Show file tree
Hide file tree
Showing 16 changed files with 254 additions and 47 deletions.
4 changes: 4 additions & 0 deletions lapis2-docs/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ export default defineConfig({
label: 'Filters',
link: '/references/filters/',
},
{
label: 'Additional Request Properties',
link: '/references/additional-request-properties/',
},
{
label: 'Open API / Swagger',
link: '/references/open-api-definition/',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type Props = {

export const OutputFormatSelection = ({ queryType, format, onFormatChange }: Props) => {
return (
<div>
<div className='mt-4'>
{queryType.selection === 'nucleotideSequences' || queryType.selection === 'aminoAcidSequences' ? (
<>
For sequences, only <b>FASTA</b> is available as output format.
Expand Down
24 changes: 13 additions & 11 deletions lapis2-docs/src/components/QueryGenerator/QueryTypeSelection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,17 +113,19 @@ const AggregatedStratified = ({ config, state, onStateChange }: Props) => {
<div>
<LabelWrapper>By which field(s) would you like to stratify?</LabelWrapper>
<CheckBoxesWrapper>
{config.schema.metadata.map((m) => (
<LabeledCheckBox
label={m.name}
key={m.name}
type='checkbox'
className='w-80'
disabled={state.selection !== 'aggregatedStratified'}
checked={state.aggregatedStratified.fields.has(m.name)}
onChange={() => changeAggregatedStratifiedField(m.name)}
/>
))}
{config.schema.metadata
.filter((metadata) => metadata.type !== 'insertion' && metadata.type !== 'aaInsertion')
.map((metadata) => (
<LabeledCheckBox
label={metadata.name}
key={metadata.name}
type='checkbox'
className='w-80'
disabled={state.selection !== 'aggregatedStratified'}
checked={state.aggregatedStratified.fields.has(metadata.name)}
onChange={() => changeAggregatedStratifiedField(metadata.name)}
/>
))}
</CheckBoxesWrapper>
</div>
</ContainerWrapper>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,5 +125,12 @@ function mapMetadataTypeToResultFieldType(type: MetadataType): ResultFieldType {
case 'date':
case 'string':
return 'string';
case 'int':
return 'integer';
case 'float':
return 'float';
case 'insertion':
case 'aaInsertion':
throw Error('Insertion and aaInsertion are not supported as result field types');
}
}
120 changes: 94 additions & 26 deletions lapis2-docs/src/components/QueryGenerator/Result.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,26 @@ import { CodeBlock } from '../CodeBlock';
import { Tab, TabsBox } from '../TabsBox/react/TabsBox';
import { generateNonFastaQuery } from '../../utils/code-generators/python/generator';
import type { Config } from '../../config';
import { useState } from 'react';
import React, { useState } from 'react';
import type { ResultField } from '../../utils/code-generators/types';
import { ContainerWrapper, LabelWrapper } from './styled-components';
import { CheckBoxesWrapper, ContainerWrapper, LabeledCheckBox, LabelWrapper } from './styled-components';
import { getResultFields, MULTI_SEGMENTED, type QueryTypeSelectionState } from './QueryTypeSelectionState.ts';
import type { OrderByLimitOffset } from './OrderLimitOffsetSelection.tsx';

const compressionOptions = [
{ value: undefined, label: 'No compression' },
{ value: 'gzip', label: 'gzip' },
{ value: 'zstd', label: 'zstd' },
];

type CompressionValues = (typeof compressionOptions)[number]['value'];

type AdditionalProperties = {
downloadAsFile: boolean;
tabularOutputFormat: TabularOutputFormat;
compression: CompressionValues;
};

type Props = {
queryType: QueryTypeSelectionState;
filters: Filters;
Expand All @@ -19,23 +33,60 @@ type Props = {
lapisUrl: string;
};

type TabProps = Props & AdditionalProperties;

export const Result = (props: Props) => {
const [additionalProperties, setAdditionalProperties] = useState<AdditionalProperties>({
downloadAsFile: false,
tabularOutputFormat: 'json',
compression: undefined,
});

const tabProps = { ...props, ...additionalProperties };

const tabs = [
{ name: 'Query URL', content: <QueryUrlTab {...props} /> },
{ name: 'R code', content: <RTab {...props} /> },
{ name: 'Python code', content: <PythonTab {...props} /> },
{ name: 'Query URL', content: <QueryUrlTab {...tabProps} /> },
{ name: 'R code', content: <RTab {...tabProps} /> },
{ name: 'Python code', content: <PythonTab {...tabProps} /> },
];

return (
<TabsBox>
{tabs.map((tab) => (
<Tab label={tab.name}>{tab.content}</Tab>
))}
</TabsBox>
<>
<LabeledCheckBox
label='Download as file'
type='checkbox'
checked={additionalProperties.downloadAsFile}
onChange={() => setAdditionalProperties((prev) => ({ ...prev, downloadAsFile: !prev.downloadAsFile }))}
/>
<OutputFormatSelection
queryType={props.queryType}
format={additionalProperties.tabularOutputFormat}
onFormatChange={(value) => setAdditionalProperties((prev) => ({ ...prev, tabularOutputFormat: value }))}
/>
<div className='mt-4'>
<LabelWrapper>Do you want to fetch compressed data?</LabelWrapper>
<CheckBoxesWrapper>
{compressionOptions.map(({ value, label }) => (
<LabeledCheckBox
label={label}
type='checkbox'
className='w-40'
checked={value === additionalProperties.compression}
onChange={() => setAdditionalProperties((prev) => ({ ...prev, compression: value }))}
/>
))}
</CheckBoxesWrapper>
</div>
<TabsBox>
{tabs.map((tab) => (
<Tab label={tab.name}>{tab.content}</Tab>
))}
</TabsBox>
</>
);
};

function constructGetQueryUrl(props: Props, tabularOutputFormat: TabularOutputFormat) {
function constructGetQueryUrl(props: TabProps) {
const { lapisUrl, endpoint, body } = constructPostQuery(props);
const params = new URLSearchParams();
for (let [name, value] of Object.entries(body)) {
Expand All @@ -51,25 +102,24 @@ function constructGetQueryUrl(props: Props, tabularOutputFormat: TabularOutputFo
if (
queryType.selection !== 'nucleotideSequences' &&
queryType.selection !== 'aminoAcidSequences' &&
tabularOutputFormat !== 'json'
props.tabularOutputFormat !== 'json'
) {
params.set('dataFormat', tabularOutputFormat);
params.set('dataFormat', props.tabularOutputFormat);
}
if (props.downloadAsFile) {
params.set('downloadAsFile', 'true');
}
if (props.compression !== undefined) {
params.set('compression', props.compression);
}
return `${lapisUrl}${endpoint}?${params}`;
}

const QueryUrlTab = (props: Props) => {
const [tabularOutputFormat, setTabularOutputFormat] = useState<TabularOutputFormat>('json');

const queryUrl = constructGetQueryUrl(props, tabularOutputFormat);
const QueryUrlTab = (props: TabProps) => {
const queryUrl = constructGetQueryUrl(props);

return (
<ContainerWrapper>
<OutputFormatSelection
queryType={props.queryType}
format={tabularOutputFormat}
onFormatChange={setTabularOutputFormat}
/>
<div>
<LabelWrapper>Query URL:</LabelWrapper>
<input
Expand All @@ -87,23 +137,32 @@ const QueryUrlTab = (props: Props) => {
);
};

const RTab = (props: Props) => {
const RTab = (props: TabProps) => {
return <CodeBlock>TODO R code</CodeBlock>;
};

const PythonTab = (props: Props) => {
const PythonTab = (props: TabProps) => {
if (props.queryType.selection === 'nucleotideSequences' || props.queryType.selection === 'aminoAcidSequences') {
return <CodeBlock>TODO Code for fetching sequences</CodeBlock>;
}
const propsWithJson: Props = {
const propsWithJson: TabProps = {
...props,
};
const { lapisUrl, endpoint, body, resultFields } = constructPostQuery(propsWithJson);
const code = generateNonFastaQuery(lapisUrl, endpoint, body, resultFields);
return <CodeBlock>{code}</CodeBlock>;
};

function constructPostQuery({ queryType, filters, config, lapisUrl, orderByLimitOffset }: Props): {
function constructPostQuery({
queryType,
filters,
config,
lapisUrl,
orderByLimitOffset,
downloadAsFile,
tabularOutputFormat,
compression,
}: TabProps): {
lapisUrl: string;
endpoint: string;
body: object;
Expand Down Expand Up @@ -165,5 +224,14 @@ function constructPostQuery({ queryType, filters, config, lapisUrl, orderByLimit
if (orderByLimitOffset.offset !== undefined) {
body.offset = orderByLimitOffset.offset;
}
if (downloadAsFile) {
body.downloadAsFile = true;
}
if (tabularOutputFormat !== 'json') {
body.dataFormat = tabularOutputFormat;
}
if (compression !== undefined) {
body.compression = compression;
}
return { lapisUrl, endpoint, body, resultFields };
}
2 changes: 1 addition & 1 deletion lapis2-docs/src/components/TabsBox/react/TabsBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const TabsBox = ({ children }: Props) => {
const [activeTab, setActiveTab] = useState(0);

return (
<div>
<div className='mt-4'>
<div className='tabs'>
{tabs.map((tab, index) => (
<a
Expand Down
4 changes: 2 additions & 2 deletions lapis2-docs/src/content/docs/concepts/response-format.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ Genomic sequences (such as those from `unalignedNucleotideSequences`, `alignedAm

To understand the response from the `nucleotideMutation` endpoint, refer to the relevant section in the Swagger UI:

![Example nucleotide mutation response](/images/docs/concepts/example_nucleotide_mutation_response_200.png)
![Example nucleotide mutation response](../../../images/concepts/example_nucleotide_mutation_response_200.png)

At the top, you'll find a plain text description of the response.
The middle section displays a JSON format response example.
For detailed information about the response data, click on 'Schema'.
This reveals the response structure and descriptions for each field, accessible by clicking the three dots next to them.

![Example nucleotide mutation response schema](/images/docs/concepts/example_nucleotide_mutation_schema.png)
![Example nucleotide mutation response schema](../../../images/concepts/example_nucleotide_mutation_schema.png)

Fields marked with an asterisk (\*) are always included in the response; unmarked fields may be omitted.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
---
title: Additional Request Properties
description: Request properties that are not sequence filters
---

Most of the request properties are sequence filters.
However, there are some properties that influence the response data in other ways.

Check the [Swagger UI](/references/open-api-definition/) for the full specification.

## Ordering The Results

You can specify the response fields by which the results should be ordered.
LAPIS by default sorts in ascending order.

```http
GET /sample/aggregated?orderBy=responseField1,responseField2
```

To specify descending order, you need to send a POST request:

```http
POST /sample/aggregated
{
"orderBy": [
"responseField1",
{ "field": "responseField2", "type": "descending" }
]
}
```

:::caution
LAPIS will throw an error if you try to order by a field that is not in the response.
:::

## Pagination

LAPIS supports pagination. You can specify the number of results to return and the offset to start from.

```http
GET /sample/aggregated?limit=10&offset=20
```

Pagination respects the ordering of the results.

## Data Format

By default, LAPIS returns the data in JSON format.
LAPIS also supports returning the data as CSV or TSV.
The format can be specified in the `Accept` header.

```http
GET /sample/aggregated
Accept: text/csv
```

```http
POST /sample/aggregated
Accept: text/tab-separated-values
```

![Media Type Selector In Swagger UI](../../../images/references/media_type.png)

You can specify the parameter `headers=false` in the accept header to exclude the headers from the CSV or TSV response.

```http
GET /sample/aggregated
Accept: text/csv;headers=false
```

:::note
Alternatively, you can use the `dataFormat` property in the request.
Refer to the Swagger UI for allowed values.
:::

## Downloading The Results From A Browser

You can set `downloadAsFile` to `true`.
LAPIS will then set the header `Content-Disposition: attachment; filename=<data.format>`.
This will prompt browsers to download the data as file.

```http
GET /sample/aggregated?downloadAsFile=true
```

## Compression

LAPIS supports gzip and Zstd compression.
You can request compressed data via the `Accept-Encoding` header.

```http
GET /sample/aggregated
Accept-Encoding: gzip
```

```http
POST /sample/aggregated
Accept-Encoding: zstd
```

LAPIS will set the `Content-Encoding` header in the response to indicate the compression used.

:::note
Alternatively, you can use the `compression` property in the request.
Refer to the Swagger UI for allowed values.

```http
GET /sample/aggregated?compression=gzip
```

:::
2 changes: 1 addition & 1 deletion lapis2-docs/src/content/docs/references/filters.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ description: Filters

import FiltersTable from '../../../components/FiltersTable/FiltersTable.astro';

This instance of LAPIS supports the following filters:
This instance of LAPIS supports the following sequence filters:

<FiltersTable />
Binary file added lapis2-docs/src/images/references/media_type.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 837e474

Please sign in to comment.