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

WIP: Feat / 48 Configurable Jbrowse Wrapper #197

Open
wants to merge 10 commits into
base: feat/jbrowsemvp-arranger3
Choose a base branch
from
Open
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: 3 additions & 0 deletions .env.schema
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,6 @@ NEXT_PUBLIC_SCORE_API_URL=
######## Jbrowse
NEXT_PUBLIC_JBROWSE_GENOME_URL_ROOT=
NEXT_PUBLIC_JBROWSE_GENOME_ALIASES_URL_ROOT=
NEXT_PUBLIC_JBROWSE_DATA_MODEL=
NEXT_PUBLIC_JBROWSE_NODE_QUERY=
NEXT_PUBLIC_JBROWSE_FILE_QUERY=
7 changes: 6 additions & 1 deletion .env.test
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,9 @@ NEXT_PUBLIC_ARRANGER_DOCUMENT_TYPE=file
NEXT_PUBLIC_ARRANGER_INDEX=file-centric

# ######## Optional features/functionalities
NEXT_PUBLIC_DEBUG=true
NEXT_PUBLIC_DEBUG=true

# Testing Jbrowse Env Queries
NEXT_PUBLIC_JBROWSE_DATA_MODEL=analysis { hits { edges { node { analysis_id files { hits { edges { node { data_type object_id name size fileType file_access } } } } id } } total } }
NEXT_PUBLIC_JBROWSE_NODE_QUERY=edges
NEXT_PUBLIC_JBROWSE_FILE_QUERY=edges
161 changes: 103 additions & 58 deletions components/pages/explorer/Jbrowse/JbrowseWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { css, useTheme } from '@emotion/react';
import { useTableContext } from '@overture-stack/arranger-components';
import { JbrowseCircular, JbrowseLinear } from '@overture-stack/dms-jbrowse-components';
import SQON from '@overture-stack/sqon-builder';
import jsonpath from 'jsonpath';
import { find } from 'lodash';
import { useEffect, useState } from 'react';
import urlJoin from 'url-join';
Expand All @@ -40,42 +41,43 @@ import {
JbrowseQueryNode,
ScoreDownloadParams,
ScoreDownloadResult,
TableData,
} from './types';
import useJbrowseCompatibility from './useJbrowseCompatibility';
import {
checkJbrowseCompatibility,
fileQuery,
jbrowseAssemblyName,
jbrowseErrors,
JbrowseFileAccess,
JbrowseFileTypes,
JbrowseTypeName,
JbrowseTypeNames,
} from './utils';
const { NEXT_PUBLIC_SCORE_API_URL } = getConfig();

const {
NEXT_PUBLIC_SCORE_API_URL,
NEXT_PUBLIC_JBROWSE_DATA_MODEL,
NEXT_PUBLIC_JBROWSE_NODE_QUERY,
NEXT_PUBLIC_JBROWSE_FILE_QUERY,
} = getConfig();
const arrangerFetcher = createArrangerFetcher({});

type Filters = {
filters: {
first: number;
offset: number;
score: string;
sort: [{ fieldName: string; order: string }];
sqon: SQON;
};
};

// request data for jbrowse display and
// score /download request to get signed URLs
const jbrowseInputQuery = `
query jbrowseInput($filters:JSON){
file {
hits (filters: $filters){
total
edges {
node {
file_access
file_type
object_id
file {
name
size
index_file {
object_id
size
}
}
}
}
}
}
const jbrowseInputQuery = (dataQuery: string) => `
query jbrowseInput ($filters: JSON) {
${dataQuery}
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use interchangeable query bodies

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great start! not depending on the hard coded query, the root of the issue.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK great, wasn't sure if we were simply 'swapping queries' or if we were potentially retrieving data from a source other than Arranger

}
`;

Expand Down Expand Up @@ -128,53 +130,96 @@ const JbrowseEl = ({ activeJbrowseType }: { activeJbrowseType: JbrowseTypeName }
console.error(error);
};

const jBrowseCompatibilityFilter = ({
file_access,
file_type,
file: { index_file },
}: JbrowseQueryNode) =>
checkJbrowseCompatibility({
file_access,
file_type,
index_file,
jbrowseType: activeJbrowseType,
});

const mapJbrowseFiles = ({
object_id,
file,
file_type,
}: JbrowseQueryNode): JbrowseCompatibleFile => ({
fileId: object_id,
fileName: file.name,
fileSize: file.size,
fileType: file_type,
// files without an index were filtered out above.
// falsey handling is for typescript only.
indexId: file.index_file?.object_id || '',
indexSize: file.index_file?.size || 0,
});

useEffect(() => {
// step 1: get compatible files

setLoading(true);

const variables: Filters = {
filters: {
first: 20,
offset: 0,
score: '',
sort: [{ fieldName: 'analysis_id', order: 'asc' }],
sqon: SQON.in('object_id', selectedRows),
},
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will not be appropriate for all queries as well so this is another piece to revisit

};
const dataQuery = NEXT_PUBLIC_JBROWSE_DATA_MODEL || fileQuery;
const query = jbrowseInputQuery(dataQuery);
// fetch metadata from arranger for selected files
arrangerFetcher({
endpoint: 'graphql/JBrowseDataQuery',
body: JSON.stringify({
variables: {
filters: SQON.in('object_id', selectedRows),
},
query: jbrowseInputQuery,
variables,
query,
}),
})
.then(({ data }) => {
const nodeQuery = NEXT_PUBLIC_JBROWSE_NODE_QUERY
? `$..${NEXT_PUBLIC_JBROWSE_NODE_QUERY}`
: '$..edges';
const fileQuery = NEXT_PUBLIC_JBROWSE_FILE_QUERY
? `$..${NEXT_PUBLIC_JBROWSE_FILE_QUERY}`
: '$..edges';

const nodes = jsonpath.query(data, nodeQuery)[0];
const indexFiles = jsonpath.query(nodes, fileQuery);
const files = indexFiles
.map((files: TableData[]) => {
const mappedFiles = files.map((data) => {
// Map for Compatibility
// Based on Table Data Query
const { object_id, name, size, fileType, file_access } = data.node;
const jbrowseFile: JbrowseQueryNode = {
file_type: fileType as JbrowseFileTypes,
file_access: file_access as JbrowseFileAccess,
object_id,
file: {
name,
size,
index_file: {
object_id,
size,
},
},
};
return jbrowseFile;
});
return mappedFiles;
})
.flat();

// restructure compatible files list for jbrowse's API
const nextJbrowseCompatibleFiles = (data.file?.hits?.edges || [])
.filter(
({
node: {
file_access,
file_type,
file: { index_file },
},
}: {
node: JbrowseQueryNode;
}) =>
checkJbrowseCompatibility({
file_access,
file_type,
index_file,
jbrowseType: activeJbrowseType,
}),
)
.map(
({ node }: { node: JbrowseQueryNode }): JbrowseCompatibleFile => ({
fileId: node.object_id,
fileName: node.file.name,
fileSize: node.file.size,
fileType: node.file_type,
// files without an index were filtered out above.
// falsey handling is for typescript only.
indexId: node.file.index_file?.object_id || '',
indexSize: node.file.index_file?.size || 0,
}),
);
const nextJbrowseCompatibleFiles = files
.filter(jBrowseCompatibilityFilter)
.map(mapJbrowseFiles);

setCompatibleFiles(nextJbrowseCompatibleFiles);
})
.catch((error: Error) => handleError(error));
Expand Down
22 changes: 22 additions & 0 deletions components/pages/explorer/Jbrowse/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,25 @@ export type JbrowseInput = JbrowseFileType &
};

export type JbrowseCompatibleFile = ScoreDownloadJbrowseInput & JbrowseFileName & JbrowseFileType;

// Example Query Objects for configurable env queries
export type TableData = {
node: {
data_type: string;
object_id: string;
name: string;
size: number;
fileType: string;
file_access: string;
};
Comment on lines +100 to +107
Copy link
Member

@justincorrigible justincorrigible Apr 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the names in this properties cannot be guaranteed in the input model
e.g. fileType, vs file_type, vs file.type

};

export type TableNodes = {
node: {
files: {
hits: {
edges: TableData[];
};
};
};
};
21 changes: 21 additions & 0 deletions components/pages/explorer/Jbrowse/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,27 @@ export const jbrowseFileMetadataQuery = `
}
`;

export const fileQuery = `file {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thinking it would be useful to call this defaultArrangerFileQuery or something like that to make it more declarative

hits (filters: $filters){
total
edges {
node {
file_access
file_type
object_id
file {
name
size
index_file {
object_id
size
}
}
}
}
}
}`;

// check if file is the right type for jbrowse
// and that it has an index
// MVP: restrict controlled access files
Expand Down
6 changes: 6 additions & 0 deletions global/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ export const getConfig = () => {
NEXT_PUBLIC_JBROWSE_GENOME_URL_ROOT: publicConfig.NEXT_PUBLIC_JBROWSE_GENOME_URL_ROOT || '',
NEXT_PUBLIC_JBROWSE_GENOME_ALIASES_URL_ROOT:
publicConfig.NEXT_PUBLIC_JBROWSE_GENOME_ALIASES_URL_ROOT || '',
NEXT_PUBLIC_JBROWSE_DATA_MODEL: publicConfig.NEXT_PUBLIC_JBROWSE_DATA_MODEL || '',
NEXT_PUBLIC_JBROWSE_NODE_QUERY: publicConfig.NEXT_PUBLIC_JBROWSE_NODE_QUERY || '',
NEXT_PUBLIC_JBROWSE_FILE_QUERY: publicConfig.NEXT_PUBLIC_JBROWSE_FILE_QUERY || '',
NEXT_PUBLIC_SHOW_MOBILE_WARNING: publicConfig.NEXT_PUBLIC_SHOW_MOBILE_WARNING === 'true',
} as {
NEXT_PUBLIC_EGO_API_ROOT: string;
Expand All @@ -64,6 +67,9 @@ export const getConfig = () => {
NEXT_PUBLIC_SCORE_API_URL: string;
NEXT_PUBLIC_JBROWSE_GENOME_URL_ROOT: string;
NEXT_PUBLIC_JBROWSE_GENOME_ALIASES_URL_ROOT: string;
NEXT_PUBLIC_JBROWSE_DATA_MODEL: string;
NEXT_PUBLIC_JBROWSE_NODE_QUERY: string;
NEXT_PUBLIC_JBROWSE_FILE_QUERY: string;
NEXT_PUBLIC_SHOW_MOBILE_WARNING: boolean;
};
};
1 change: 1 addition & 0 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ module.exports = withPlugins([withTranspileModules], {
// Optional features/functionalities
NEXT_PUBLIC_DEBUG: process.env.NEXT_PUBLIC_DEBUG,
NEXT_PUBLIC_SHOW_MOBILE_WARNING: process.env.NEXT_PUBLIC_SHOW_MOBILE_WARNING,
NEXT_PUBLIC_JBROWSE_DATA_MODEL: process.env.NEXT_PUBLIC_JBROWSE_DATA_MODEL,
},
assetPrefix: process.env.ASSET_PREFIX || '',
optimizeFonts: false,
Expand Down
14 changes: 14 additions & 0 deletions package-lock.json

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

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"@overture-stack/dms-jbrowse-components": "^0.1.0-beta.4",
"@overture-stack/sqon-builder": "^0.0.0",
"axios": "^0.27.2",
"jsonpath": "^1.1.1",
"jsonwebtoken": "^8.5.1",
"jwt-decode": "^3.1.2",
"lodash": "^4.17.21",
Expand All @@ -36,6 +37,7 @@
"devDependencies": {
"@babel/preset-typescript": "^7.21.0",
"@emotion/babel-plugin": "^11.9.2",
"@types/jsonpath": "^0.2.4",
"@types/lodash": "^4.14.182",
"@types/node": "^17.0.35",
"@types/react": "^17.0.63",
Expand Down