Skip to content
Open
Show file tree
Hide file tree
Changes from 7 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
1 change: 1 addition & 0 deletions .env.schema
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ NEXT_PUBLIC_SCORE_API_URL=
######## Jbrowse
NEXT_PUBLIC_JBROWSE_GENOME_URL_ROOT=
NEXT_PUBLIC_JBROWSE_GENOME_ALIASES_URL_ROOT=
NEXT_PUBLIC_JBROWSE_DATA_MODEL=
5 changes: 4 additions & 1 deletion .env.test
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,7 @@ 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 } }
143 changes: 86 additions & 57 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,29 @@ import {
JbrowseQueryNode,
ScoreDownloadParams,
ScoreDownloadResult,
TableData,
TableNodes,
} 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 } = getConfig();
const arrangerFetcher = createArrangerFetcher({});

// 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 {
${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 +116,94 @@ const JbrowseEl = ({ activeJbrowseType }: { activeJbrowseType: JbrowseTypeName }
console.error(error);
};

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

const mapJbrowseFiles = ({ 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,
});

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

setLoading(true);

const dataQuery = NEXT_PUBLIC_JBROWSE_DATA_MODEL || fileQuery;
const variables = {
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 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 isDefaultDataModel = !NEXT_PUBLIC_JBROWSE_DATA_MODEL;

const nodes = isDefaultDataModel
? data.file?.hits?.edges
: jsonpath
.query(data, '$..edges')[0]
Copy link
Author

@demariadaniel demariadaniel Apr 11, 2024

Choose a reason for hiding this comment

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

Given Jbrowse compatibility filter and file mapping requires specific properties, you kind of have to know what the input query is going to be.

Here I am querying edges[0] because it allows me to get all the nodes and then map them. I might be able to simplify this a bit by querying nodes, but you still need that identifier to know the name of your objects.

If your query doesn't have edges (or nodes), it won't work. The query can't be completely ambiguous.

We might be able to do a .find() to get an array of all objects with the given fields we need as an alternate solution
cc: @justincorrigible

.map(({ node }: TableNodes) => {
const files = node.files?.hits?.edges.map((data: TableData) => {
// 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 { node: jbrowseFile };
});
return files;
})
.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 = nodes
.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
2 changes: 2 additions & 0 deletions global/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ 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_SHOW_MOBILE_WARNING: publicConfig.NEXT_PUBLIC_SHOW_MOBILE_WARNING === 'true',
} as {
NEXT_PUBLIC_EGO_API_ROOT: string;
Expand All @@ -64,6 +65,7 @@ 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_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