Skip to content

Commit

Permalink
Use genome details and genome slug explain endpoints (#1047)
Browse files Browse the repository at this point in the history
Switched to using:

- The url explainer endpoint of the metadata api, which receives a string that can be either a genome tag
   or genome uuid, and returns brief information about the genome that tells the client all it needs to know
   for further api requests
- The genome details endpoint to dynamically fetch data that is displayed in right-hand sidebar on the species page.
  • Loading branch information
azangru authored Nov 7, 2023
1 parent bf6e83b commit 3305bc9
Show file tree
Hide file tree
Showing 23 changed files with 373 additions and 648 deletions.
4 changes: 3 additions & 1 deletion config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,9 @@ const getBaseApiUrls = (): BaseApiUrls => {
variationApiUrl:
process.env.SSR_VARIATION_GRAPHQL_API_URL ??
`${defaultServerHost}${defaultApiUrls.variationApiUrl}`,
metadataApiBaseUrl: defaultApiUrls.metadataApiBaseUrl, // irrelevant for server-side rendering
metadataApiBaseUrl:
process.env.SSR_METADATA_API_URL ??
`${defaultServerHost}${defaultApiUrls.metadataApiBaseUrl}`,
genomeBrowserBackendBaseUrl: defaultApiUrls.genomeBrowserBackendBaseUrl, // irrelevant for server-side rendering
refgetBaseUrl: defaultApiUrls.refgetBaseUrl, // irrelevant for server-side rendering
tracksApiBaseUrl: defaultApiUrls.tracksApiBaseUrl, // irrelevant for server-side rendering
Expand Down
4 changes: 4 additions & 0 deletions gitlab-ci-templates/.setup-review-template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@
- sed -i "s#<DEPLOYMENT_ENV>#${TOOLS_API_SERVICE_SLUG}#g" ensembl_tools_api_service.yaml
- sed -i "s#<DEPLOYMENT_ENV>#${TOOLS_API_SERVICE_SLUG}#g" ensembl_tools_api_ingress.yaml
- sed -i "s#<SUB_DOMAIN>#${CI_COMMIT_REF_SLUG}#g" ensembl_tools_api_ingress.yaml
# ensembl-search-api
- sed -i "s#<SUB_DOMAIN>#${CI_COMMIT_REF_SLUG}#g" ensembl_search_hub_ingress.yaml
# metadata api
- sed -i "s#<SUB_DOMAIN>#${CI_COMMIT_REF_SLUG}#g" metadata_api_ingress.yaml
script:
Expand Down Expand Up @@ -93,5 +95,7 @@
- kubectl apply -f ensembl_client_node_service.yaml
- kubectl apply -f ensembl_client_node_ingress.yaml
- kubectl apply -f ensembl_client_node_cm.yaml
# ensembl searchapi ingress
- kubectl apply -f ensembl_search_hub_ingress.yaml
# metadata api ingress
- kubectl apply -f metadata_api_ingress.yaml
6 changes: 3 additions & 3 deletions src/content/app/entity-viewer/EntityViewerPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { getPathParameters } from 'src/shared/hooks/useUrlParams';
import useHasMounted from 'src/shared/hooks/useHasMounted';

import {
fetchGenomeInfo,
fetchGenomeSummary,
isGenomeNotFoundError
} from 'src/shared/state/genome/genomeApiSlice';
import { updatePageMeta } from 'src/shared/state/page-meta/pageMetaSlice';
Expand Down Expand Up @@ -92,7 +92,7 @@ export const serverFetch: ServerFetch = async (params) => {
}

const genomeInfoResponsePromise = dispatch(
fetchGenomeInfo.initiate(genomeIdFromUrl)
fetchGenomeSummary.initiate(genomeIdFromUrl)
);
const { data: genomeInfoData, error: genomeInfoError } =
await genomeInfoResponsePromise;
Expand All @@ -103,7 +103,7 @@ export const serverFetch: ServerFetch = async (params) => {
};
}

const genomeId = genomeInfoData?.genomeId as string; // by this point, genomeId clearly exists
const genomeId = genomeInfoData?.genome_id as string; // by this point, genomeId clearly exists

// If the url is just /entity-viewer/:genomeId, update page meta and exit
if (!entityId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import {
import { useAppSelector } from 'src/store';
import usePrevious from 'src/shared/hooks/usePrevious';
import { useUrlParams } from 'src/shared/hooks/useUrlParams';
import { useGenomeInfoQuery } from 'src/shared/state/genome/genomeApiSlice';
import { useGenomeSummaryByGenomeSlugQuery } from 'src/shared/state/genome/genomeApiSlice';

import {
getEntityViewerActiveGenomeId,
Expand Down Expand Up @@ -59,18 +59,16 @@ const EntityViewerIdsContextProvider = (props: {
]);
const { genomeId: genomeIdInUrl, entityId: entityIdInUrl } = params;

const { currentData: genomeInfo, error } = useGenomeInfoQuery(
genomeIdInUrl ?? '',
{
const { currentData: genomeSummary, error } =
useGenomeSummaryByGenomeSlugQuery(genomeIdInUrl ?? '', {
skip: !genomeIdInUrl
}
);
});

const genomeId = genomeInfo?.genomeId;
const genomeId = genomeSummary?.genome_id;
const genomeIdForUrl =
genomeIdInUrl ??
genomeInfo?.genomeTag ??
genomeInfo?.genomeId ??
genomeSummary?.genome_tag ??
genomeSummary?.genome_id ??
savedGenomeInfo?.genome_tag ??
savedGenomeInfo?.genome_id;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { useUrlParams } from 'src/shared/hooks/useUrlParams';

import {
isGenomeNotFoundError,
useGenomeInfoQuery
useGenomeSummaryByGenomeSlugQuery
} from 'src/shared/state/genome/genomeApiSlice';

const useParsedGenomeBrowserUrl = () => {
Expand All @@ -39,14 +39,14 @@ const useParsedGenomeBrowserUrl = () => {
const locationInUrl = urlSearchParams.get('location');

const {
currentData: genomeInfo,
currentData: genomeSummary,
isFetching,
isError,
error
} = useGenomeInfoQuery(genomeIdInUrl ?? '', {
} = useGenomeSummaryByGenomeSlugQuery(genomeIdInUrl ?? '', {
skip: !genomeIdInUrl
});
const genomeId = genomeInfo?.genomeId;
const genomeId = genomeSummary?.genome_id;
const isMissingGenomeId = isError && isGenomeNotFoundError(error);

let focusObjectId;
Expand Down
55 changes: 23 additions & 32 deletions src/content/app/genome-browser/hooks/useBrowserRouting.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,10 @@ import { GenomeBrowserIdsProvider } from '../contexts/genome-browser-ids-context
const mockChangeFocusObject = jest.fn();
const mockChangeBrowserLocation = jest.fn();

const mockGenomeSearchApiBaseUrl = 'http://genome-search-api';
const mockMetadataApiBaseUrl = 'http://metadata-api';
const mockGenomeBrowserObj = {};

jest.mock('config', () => ({
genomeSearchBaseUrl: 'http://genome-search-api',
metadataApiBaseUrl: 'http://metadata-api',
coreApiUrl: 'http://graphql-api'
}));
Expand All @@ -66,19 +64,17 @@ jest.mock(
);

const humanGenomeInfo = {
assembly_name: 'GRCh38.p13',
common_name: 'Human',
genome_id: 'homo_sapiens_GCA_000001405_28',
scientific_name: 'Homo sapiens',
genome_tag: 'grch38'
genome_tag: 'grch38',
common_name: 'Human',
scientific_name: 'Homo sapiens'
};

const wheatGenomeInfo = {
assembly_name: 'IWGSC',
common_name: null,
genome_id: 'triticum_aestivum_GCA_900519105_1',
scientific_name: 'Triticum aestivum',
genome_tag: 'iwgsc'
genome_tag: 'iwgsc',
common_name: null,
scientific_name: 'Triticum aestivum'
};

const committedHuman = {
Expand Down Expand Up @@ -177,29 +173,24 @@ const renderComponent = ({
};

const server = setupServer(
rest.get(`${mockGenomeSearchApiBaseUrl}/genome/info`, (req, res, ctx) => {
const genomeId = req.url.searchParams.get('genome_id');

if (
genomeId === humanGenomeInfo.genome_id ||
genomeId === humanGenomeInfo.genome_tag
) {
return res(
ctx.json({
genome_info: [humanGenomeInfo]
})
);
} else if (
genomeId === wheatGenomeInfo.genome_id ||
genomeId === wheatGenomeInfo.genome_tag
) {
return res(
ctx.json({
genome_info: [wheatGenomeInfo]
})
);
rest.get(
`${mockMetadataApiBaseUrl}/genome/:slug/explain`,
(req, res, ctx) => {
const { slug } = req.params;

if (
slug === humanGenomeInfo.genome_id ||
slug === humanGenomeInfo.genome_tag
) {
return res(ctx.json(humanGenomeInfo));
} else if (
slug === wheatGenomeInfo.genome_id ||
slug === wheatGenomeInfo.genome_tag
) {
return res(ctx.json(wheatGenomeInfo));
}
}
}),
),
rest.get(`${mockMetadataApiBaseUrl}/validate_location`, (req, res, ctx) => {
const location = req.url.searchParams.get('location');

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,11 @@
* limitations under the License.
*/

import {
createAsyncThunk,
createSlice,
type Action,
type ThunkAction
} from '@reduxjs/toolkit';
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';

import isGeneFocusObject from './isGeneFocusObject';
import * as focusObjectStorageService from 'src/content/app/genome-browser/services/focus-objects/focusObjectStorageService';

import { fetchGenomeInfo } from 'src/shared/state/genome/genomeApiSlice';
import { getTrackPanelGene } from 'src/content/app/genome-browser/state/api/genomeBrowserApiSlice';

import { shouldFetch } from 'src/shared/helpers/fetchHelper';
Expand Down Expand Up @@ -147,28 +141,6 @@ const buildFocusVariantObject = (payload: {
};
};

export const fetchExampleFocusObjects =
(genomeId: string): ThunkAction<void, RootState, void, Action<string>> =>
async (dispatch) => {
const genomeInfoResponsePromise = dispatch(
fetchGenomeInfo.initiate(genomeId)
);
const { data: genomeInfoResponse } = await genomeInfoResponsePromise;
genomeInfoResponsePromise.unsubscribe();

if (!genomeInfoResponse) {
// failed network request; nothing to do
return;
}

const { genomeInfo } = genomeInfoResponse;
const exampleFocusObjects = genomeInfo.example_objects;

exampleFocusObjects.forEach(({ id, type }) => {
dispatch(fetchFocusObject({ genomeId, type, objectId: id }));
});
};

export const fetchFocusObject = createAsyncThunk(
'genome-browser/fetch-focus-object',
async (payload: string | FocusObjectIdConstituents, thunkAPI) => {
Expand Down
30 changes: 15 additions & 15 deletions src/content/app/new-species-selector/types/speciesSearchMatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,21 @@
* limitations under the License.
*/

export type SpeciesSearchMatch = {
genome_id: string;
genome_tag: string | null;
common_name: string | null;
scientific_name: string;
type: {
kind: string; // e.g. "population"
value: string; // e.g. "European"
} | null;
is_reference: boolean;
assembly: {
accession_id: string;
name: string;
url: string;
};
import type { GenomeInfo } from 'src/shared/state/genome/genomeTypes';

type SearchMatchFieldsFromGenomeInfo =
| 'genome_id'
| 'genome_tag'
| 'common_name'
| 'scientific_name'
| 'type'
| 'is_reference'
| 'assembly';

export type SpeciesSearchMatch = Pick<
GenomeInfo,
SearchMatchFieldsFromGenomeInfo
> & {
coding_genes_count: number;
contig_n50: number | null; // E.coli doesn't have contig n50 in species stats
has_variation: boolean;
Expand Down
12 changes: 6 additions & 6 deletions src/content/app/species-selector/state/speciesSelectorEpics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import {
updateCommittedSpecies,
loadStoredSpecies
} from 'src/content/app/species-selector/state/speciesSelectorSlice';
import { fetchGenomeInfo } from 'src/shared/state/genome/genomeApiSlice';
import { fetchGenomeSummary } from 'src/shared/state/genome/genomeApiSlice';

import type { RootState } from 'src/store';
import type {
Expand Down Expand Up @@ -104,7 +104,7 @@ export const ensureCommittedSpeciesEpic: Epic<Action, Action, RootState> = (
state$
) =>
action$.pipe(
filter(fetchGenomeInfo.matchFulfilled),
filter(fetchGenomeSummary.matchFulfilled),
map((action) => {
const state = state$.value;
return {
Expand All @@ -114,17 +114,17 @@ export const ensureCommittedSpeciesEpic: Epic<Action, Action, RootState> = (
}),
filter(({ action, state }) => {
// check that the genome is not among committed species
const genomeId = action.payload.genomeId;
const genomeId = action.payload.genome_id;
const speciesAlreadyCommitted = getCommittedSpeciesById(state, genomeId);
return !speciesAlreadyCommitted;
}),
map(({ action, state }) => {
const { genomeInfo } = action.payload;
const genomeInfo = action.payload;
const newSpecies: CommittedItem = {
genome_id: genomeInfo.genome_id,
common_name: genomeInfo.common_name,
scientific_name: genomeInfo.scientific_name,
assembly_name: genomeInfo.assembly_name,
assembly_name: genomeInfo.assembly.name,
genome_tag: genomeInfo.genome_tag,
isEnabled: true
};
Expand Down Expand Up @@ -180,7 +180,7 @@ export const checkLoadedSpeciesEpic: Epic<Action, Action, RootState> = (
genome_id: genome.genome_id,
common_name: genome.common_name,
scientific_name: genome.scientific_name,
assembly_name: genome.assembly_name,
assembly_name: genome.assembly.name,
genome_tag: genome.genome_tag,
isEnabled: true
}));
Expand Down
4 changes: 0 additions & 4 deletions src/content/app/species/SpeciesPage.scss
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,3 @@
font-weight: $bold;
margin-left: 17px;
}

.dataForSpecies {
color: $medium-dark-grey;
}
7 changes: 3 additions & 4 deletions src/content/app/species/SpeciesPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { updatePageMeta } from 'src/shared/state/page-meta/pageMetaSlice';
import useHasMounted from 'src/shared/hooks/useHasMounted';

import {
fetchGenomeInfo,
fetchGenomeSummary,
isGenomeNotFoundError
} from 'src/shared/state/genome/genomeApiSlice';
import { getPathParameters, useUrlParams } from 'src/shared/hooks/useUrlParams';
Expand Down Expand Up @@ -79,17 +79,16 @@ export const serverFetch: ServerFetch = async (params) => {
) as { genomeId: string };

const genomeInfoResponsePromise = dispatch(
fetchGenomeInfo.initiate(genomeIdFromUrl)
fetchGenomeSummary.initiate(genomeIdFromUrl)
);
const { data: genomeInfoData, error: genomeInfoError } =
const { data: genomeInfo, error: genomeInfoError } =
await genomeInfoResponsePromise;

if (isGenomeNotFoundError(genomeInfoError)) {
return {
status: 404
};
} else {
const genomeInfo = genomeInfoData?.genomeInfo;
const title = genomeInfo ? getDisplayName(genomeInfo) : defaultTitle;
dispatch(
updatePageMeta({
Expand Down
Loading

0 comments on commit 3305bc9

Please sign in to comment.