Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(web): prevent writing large auspice json to local storage #1471

Merged
merged 2 commits into from
Jun 4, 2024
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
3 changes: 2 additions & 1 deletion packages/nextclade-web/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ module.exports = {
'no-shadow': 'off',
'no-unused-vars': 'off',
'only-ascii/only-ascii': 'warn',
'prefer-const': ['warn', { destructuring: 'all' }],
'prefer-for-of': 'off',
'prettier/prettier': 'warn',
'react-hooks/exhaustive-deps': [
Expand All @@ -148,7 +149,7 @@ module.exports = {
'react/state-in-constructor': 'off',
'security/detect-non-literal-fs-filename': 'off',
'security/detect-object-injection': 'off',
'sonarjs/cognitive-complexity': ['warn', 20],
'sonarjs/cognitive-complexity': 'off',
'unicorn/consistent-function-scoping': 'off',
'unicorn/escape-case': 'off',
'unicorn/filename-case': 'off',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ export function SuggestionAlertMainPage({ ...restProps }) {
const { numSuggestions, datasetsActive: datasetSuggestions } = useDatasetSuggestionResults()
const autodetectRunState = useRecoilValue(autodetectRunStateAtom)

// eslint-disable-next-line sonarjs/cognitive-complexity
const alert = useMemo(() => {
const hasMatch = datasetCurrent && datasetSuggestions.some((suggestion) => suggestion.path === datasetCurrent.path)

Expand Down
8 changes: 2 additions & 6 deletions packages/nextclade-web/src/io/fetchDatasets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
parseGithubRepoUrl,
} from 'src/io/fetchSingleDatasetFromGithub'

import { type AuspiceTree, Dataset } from 'src/types'
import { Dataset } from 'src/types'
import {
fetchDatasetsIndex,
filterDatasets,
Expand Down Expand Up @@ -128,11 +128,7 @@ export async function initializeDatasets(datasetServerUrl: string, urlQuery: Par
const minimizerIndexVersion = await getCompatibleMinimizerIndexVersion(datasetServerUrl, datasetsIndexJson)

// Check if URL params specify dataset params and try to find the corresponding dataset
const currentDataset:
| (Dataset & {
auspiceJson?: AuspiceTree
})
| undefined = await getDatasetFromUrlParams(urlQuery, datasets)
const currentDataset = await getDatasetFromUrlParams(urlQuery, datasets)

return { datasets, currentDataset, minimizerIndexVersion }
}
Expand Down
6 changes: 3 additions & 3 deletions packages/nextclade-web/src/io/fetchSingleDatasetAuspice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export async function fetchSingleDatasetAuspice(datasetJsonUrl_: string) {
}
}

const currentDataset: Dataset & { auspiceJson?: AuspiceTree } = {
const currentDataset: Dataset = {
path: datasetJsonUrl,
capabilities: {
primers: false,
Expand All @@ -50,8 +50,8 @@ export async function fetchSingleDatasetAuspice(datasetJsonUrl_: string) {
name,
...pathogen?.attributes,
},
type: 'auspiceJson',
version,
auspiceJson,
}

const datasets = [currentDataset]
Expand All @@ -60,5 +60,5 @@ export async function fetchSingleDatasetAuspice(datasetJsonUrl_: string) {
const defaultDatasetName = currentDatasetName
const defaultDatasetNameFriendly = attrStrMaybe(currentDataset.attributes, 'name') ?? currentDatasetName

return { datasets, defaultDataset, defaultDatasetName, defaultDatasetNameFriendly, currentDataset }
return { datasets, defaultDataset, defaultDatasetName, defaultDatasetNameFriendly, currentDataset, auspiceJson }
}
14 changes: 11 additions & 3 deletions packages/nextclade-web/src/io/fetchSingleDatasetDirectory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import axios from 'axios'
import urljoin from 'url-join'
import { mapValues } from 'lodash'
import { concurrent } from 'fasy'
import { attrStrMaybe, AuspiceTree, Dataset, VirusProperties } from 'src/types'
import { attrStrMaybe, Dataset, VirusProperties } from 'src/types'
import { removeTrailingSlash } from 'src/io/url'
import { axiosFetch, axiosHead, axiosHeadOrUndefined } from 'src/io/axiosFetch'
import { sanitizeError } from 'src/helpers/sanitizeError'
Expand All @@ -17,14 +17,15 @@ export async function fetchSingleDatasetDirectory(

const files = mapValues(pathogen.files, (file) => (file ? urljoin(datasetRootUrl, file) : file))

const currentDataset: Dataset & { auspiceJson?: AuspiceTree } = {
const currentDataset: Dataset = {
path: datasetRootUrl,
capabilities: {
primers: false,
qc: [],
},
...pathogen,
files,
type: 'directory',
}

const datasets = [currentDataset]
Expand All @@ -51,7 +52,14 @@ export async function fetchSingleDatasetDirectory(
Object.entries(files).filter(([_, key]) => !['examples', 'readme'].includes(key)),
)

return { datasets, defaultDataset, defaultDatasetName, defaultDatasetNameFriendly, currentDataset }
return {
datasets,
defaultDataset,
defaultDatasetName,
defaultDatasetNameFriendly,
currentDataset,
auspiceJson: undefined,
}
}

async function fetchPathogenJson(datasetRootUrl: string) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* eslint-disable security/detect-unsafe-regex,prefer-const */
/* eslint-disable security/detect-unsafe-regex */
import type { Optional } from 'utility-types'
import { isEmpty, isNil, trim } from 'lodash'
import pMemoize from 'p-memoize'
Expand Down
26 changes: 18 additions & 8 deletions packages/nextclade-web/src/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { RecoilEnv, RecoilRoot, useRecoilCallback, useRecoilState, useRecoilValu
import { AppProps } from 'next/app'
import { useRouter } from 'next/router'
import dynamic from 'next/dynamic'
import type { Dataset, AuspiceTree } from 'src/types'
import { sanitizeError } from 'src/helpers/sanitizeError'
import { useRunAnalysis } from 'src/hooks/useRunAnalysis'
import i18nAuspice, { changeAuspiceLocale } from 'src/i18n/i18n.auspice'
Expand Down Expand Up @@ -97,24 +96,35 @@ function RecoilStateInitializer() {

const datasetInfo = await fetchSingleDataset(urlQuery)
if (!isNil(datasetInfo)) {
const { datasets, currentDataset } = datasetInfo
return { datasets, currentDataset, minimizerIndexVersion: undefined }
const { datasets, currentDataset, auspiceJson } = datasetInfo
return { datasets, currentDataset, minimizerIndexVersion: undefined, auspiceJson }
}
return { datasets, currentDataset, minimizerIndexVersion }
return { datasets, currentDataset, minimizerIndexVersion, auspiceJson: undefined }
})
.catch((error) => {
// Dataset error is fatal and we want error to be handled in the ErrorBoundary
setInitialized(false)
set(globalErrorAtom, sanitizeError(error))
throw error
})
.then(async ({ datasets, currentDataset, minimizerIndexVersion }) => {
.then(async ({ datasets, currentDataset, minimizerIndexVersion, auspiceJson }) => {
set(datasetsAtom, { datasets })
const previousDataset = await getPromise(datasetCurrentAtom)
const dataset: (Dataset & { auspiceJson?: AuspiceTree }) | undefined = currentDataset ?? previousDataset
let previousDataset = await getPromise(datasetCurrentAtom)
if (previousDataset?.type === 'auspiceJson') {
// Disregard previously saved dataset if it's Auspice dataset, because the data is no longer available.
// We might re-fetch instead, but need to persist URL for that somehow.
previousDataset = undefined
}

const dataset = currentDataset ?? previousDataset
set(datasetCurrentAtom, dataset)
set(minimizerIndexVersionAtom, minimizerIndexVersion)
set(datasetJsonAtom, dataset?.auspiceJson)

if (dataset?.type === 'auspiceJson' && !isNil(auspiceJson)) {
set(datasetJsonAtom, auspiceJson)
} else {
set(datasetJsonAtom, undefined)
}
return dataset
})
.then(async (dataset) => {
Expand Down
11 changes: 11 additions & 0 deletions packages/nextclade/src/io/dataset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ pub struct Dataset {
#[serde(default, skip_serializing_if = "DatasetVersion::is_empty")]
pub version: DatasetVersion,

#[serde(default, skip_serializing_if = "Option::is_none")]
pub r#type: Option<DatasetType>,

#[serde(flatten)]
pub other: serde_json::Value,
}
Expand Down Expand Up @@ -386,3 +389,11 @@ pub struct MinimizerIndexVersion {
#[serde(flatten)]
pub other: serde_json::Value,
}

#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub enum DatasetType {
Directory,
AuspiceJson,
Other,
}
Loading