From 76cd2d46be95065146a4072ebf7ee60d5f665273 Mon Sep 17 00:00:00 2001 From: Jeremy Magland Date: Fri, 13 Oct 2023 10:09:33 -0400 Subject: [PATCH 1/5] update neurosift service endpoint URL --- web/src/views/FileBrowserView/FileBrowser.vue | 40 +++++++++++++++---- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/web/src/views/FileBrowserView/FileBrowser.vue b/web/src/views/FileBrowserView/FileBrowser.vue index 247916b5e..85745609f 100644 --- a/web/src/views/FileBrowserView/FileBrowser.vue +++ b/web/src/views/FileBrowserView/FileBrowser.vue @@ -309,35 +309,35 @@ const EXTERNAL_SERVICES = [ name: 'Bioimagesuite/Viewer', regex: /\.nii(\.gz)?$/, maxsize: 1e9, - endpoint: 'https://bioimagesuiteweb.github.io/unstableapp/viewer.html?image=', + endpoint: 'https://bioimagesuiteweb.github.io/unstableapp/viewer.html?image=$asset_url$', }, { name: 'MetaCell/NWBExplorer', regex: /\.nwb$/, maxsize: 1e9, - endpoint: 'http://nwbexplorer.opensourcebrain.org/nwbfile=', + endpoint: 'http://nwbexplorer.opensourcebrain.org/nwbfile=$asset_url$', }, { name: 'VTK/ITK Viewer', regex: /\.ome\.zarr$/, maxsize: Infinity, - endpoint: 'https://kitware.github.io/itk-vtk-viewer/app/?gradientOpacity=0.3&image=', + endpoint: 'https://kitware.github.io/itk-vtk-viewer/app/?gradientOpacity=0.3&image=$asset_url$', }, { name: 'OME Zarr validator', regex: /\.ome\.zarr$/, maxsize: Infinity, - endpoint: 'https://ome.github.io/ome-ngff-validator/?source=', + endpoint: 'https://ome.github.io/ome-ngff-validator/?source=$asset_url$', }, { name: 'Neurosift', regex: /\.nwb$/, maxsize: Infinity, - endpoint: 'https://flatironinstitute.github.io/neurosift?p=/nwb&url=', + endpoint: 'https://flatironinstitute.github.io/neurosift?p=/nwb&url=$base_api_url$assets/$asset_id$/download/&dandisetId=$dandiset_id$&dandisetVersion=$dandiset_version$', // eslint-disable-line max-len }, ]; type Service = typeof EXTERNAL_SERVICES[0]; @@ -378,7 +378,22 @@ const isOwner = computed(() => !!( )); const itemsNotFound = computed(() => items.value && !items.value.length); -function getExternalServices(path: AssetPath) { +function filterServiceEndpoint(endpoint: string, o: { + dandisetId: string, + dandisetVersion: string, + assetUrl: string, + assetId: string, + baseApiUrl: string +}) { + return endpoint + .replace(/\$dandiset_id\$/g, o.dandisetId) + .replace(/\$dandiset_version\$/g, o.dandisetVersion) + .replace(/\$asset_url\$/g, o.assetUrl) + .replace(/\$asset_id\$/g, o.assetId) + .replace(/\$base_api_url\$/g, o.baseApiUrl); +} + +function getExternalServices(path: AssetPath, info: {dandisetId: string, dandisetVersion: string}) { const servicePredicate = (service: Service, _path: AssetPath) => ( new RegExp(service.regex).test(path.path) && _path.asset !== null @@ -394,7 +409,13 @@ function getExternalServices(path: AssetPath) { .filter((service) => servicePredicate(service, path)) .map((service) => ({ name: service.name, - url: `${service.endpoint}${assetURL()}`, + url: filterServiceEndpoint(service.endpoint, { + dandisetId: info.dandisetId, + dandisetVersion: info.dandisetVersion, + assetUrl: assetURL(), + assetId: path.asset?.asset_id || '', + baseApiUrl: baseApiUrl || '', + }), })); } @@ -461,7 +482,10 @@ async function getItems() { // Inject relative path name: path.path.split('/').pop()!, // Inject services - services: getExternalServices(path) || undefined, + services: getExternalServices(path, { + dandisetId: props.identifier, + dandisetVersion: props.version, + }) || undefined, })) .sort(sortByFolderThenName); From 5fa2512b30cfe1adf155d9c9401a3af700827964 Mon Sep 17 00:00:00 2001 From: Roni Choudhury Date: Thu, 2 Nov 2023 12:49:13 -0400 Subject: [PATCH 2/5] Supply all forms of asset URLs to service endpoints --- web/src/views/FileBrowserView/FileBrowser.vue | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/web/src/views/FileBrowserView/FileBrowser.vue b/web/src/views/FileBrowserView/FileBrowser.vue index 85745609f..c11fb242a 100644 --- a/web/src/views/FileBrowserView/FileBrowser.vue +++ b/web/src/views/FileBrowserView/FileBrowser.vue @@ -337,7 +337,7 @@ const EXTERNAL_SERVICES = [ name: 'Neurosift', regex: /\.nwb$/, maxsize: Infinity, - endpoint: 'https://flatironinstitute.github.io/neurosift?p=/nwb&url=$base_api_url$assets/$asset_id$/download/&dandisetId=$dandiset_id$&dandisetVersion=$dandiset_version$', // eslint-disable-line max-len + endpoint: 'https://flatironinstitute.github.io/neurosift?p=/nwb&url=$asset_dandi_url$&dandisetId=$dandiset_id$&dandisetVersion=$dandiset_version$', // eslint-disable-line max-len }, ]; type Service = typeof EXTERNAL_SERVICES[0]; @@ -381,16 +381,18 @@ const itemsNotFound = computed(() => items.value && !items.value.length); function filterServiceEndpoint(endpoint: string, o: { dandisetId: string, dandisetVersion: string, - assetUrl: string, assetId: string, - baseApiUrl: string + assetUrl: string, + assetDandiUrl: string, + assetS3Url: string, }) { return endpoint .replace(/\$dandiset_id\$/g, o.dandisetId) .replace(/\$dandiset_version\$/g, o.dandisetVersion) - .replace(/\$asset_url\$/g, o.assetUrl) .replace(/\$asset_id\$/g, o.assetId) - .replace(/\$base_api_url\$/g, o.baseApiUrl); + .replace(/\$asset_url\$/g, o.assetUrl) + .replace(/\$asset_dandi_url\$/g, o.assetUrl) + .replace(/\$asset_s3_url\$/g, o.assetUrl); } function getExternalServices(path: AssetPath, info: {dandisetId: string, dandisetVersion: string}) { @@ -400,10 +402,16 @@ function getExternalServices(path: AssetPath, info: {dandisetId: string, dandise && _path.aggregate_size <= service.maxsize ); + // Formulate the two possible asset URLs -- the direct S3 link to the relevant + // object, and the DANDI URL that redirects to the S3 one. const baseApiUrl = process.env.VUE_APP_DANDI_API_ROOT; - const assetURL = () => (embargoed.value - ? `${baseApiUrl}assets/${path.asset?.asset_id}/download/` - : trimEnd((path.asset as AssetFile).url, '/')); + const assetDandiUrl = `${baseApiUrl}assets/${path.asset?.asset_id}/download/`; + const assetS3Url = trimEnd((path.asset as AssetFile).url, '/'); + + // Select the best "default" URL: the direct S3 link is better when it can be + // used, but we're forced to supply the internal DANDI URL for embargoed + // dandisets (since the ready-made S3 URL will prevent access in that case). + const assetUrl = embargoed.value ? assetDandiUrl : assetS3Url; return EXTERNAL_SERVICES .filter((service) => servicePredicate(service, path)) @@ -412,9 +420,10 @@ function getExternalServices(path: AssetPath, info: {dandisetId: string, dandise url: filterServiceEndpoint(service.endpoint, { dandisetId: info.dandisetId, dandisetVersion: info.dandisetVersion, - assetUrl: assetURL(), assetId: path.asset?.asset_id || '', - baseApiUrl: baseApiUrl || '', + assetUrl, + assetDandiUrl, + assetS3Url, }), })); } From 718469d0f132a92a357f9efced0ad07bef79322f Mon Sep 17 00:00:00 2001 From: Roni Choudhury Date: Thu, 2 Nov 2023 12:52:02 -0400 Subject: [PATCH 3/5] Use clearer name for function and arguments --- web/src/views/FileBrowserView/FileBrowser.vue | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/web/src/views/FileBrowserView/FileBrowser.vue b/web/src/views/FileBrowserView/FileBrowser.vue index c11fb242a..51f3a1cd4 100644 --- a/web/src/views/FileBrowserView/FileBrowser.vue +++ b/web/src/views/FileBrowserView/FileBrowser.vue @@ -378,7 +378,7 @@ const isOwner = computed(() => !!( )); const itemsNotFound = computed(() => items.value && !items.value.length); -function filterServiceEndpoint(endpoint: string, o: { +function serviceURL(endpoint: string, data: { dandisetId: string, dandisetVersion: string, assetId: string, @@ -387,12 +387,12 @@ function filterServiceEndpoint(endpoint: string, o: { assetS3Url: string, }) { return endpoint - .replace(/\$dandiset_id\$/g, o.dandisetId) - .replace(/\$dandiset_version\$/g, o.dandisetVersion) - .replace(/\$asset_id\$/g, o.assetId) - .replace(/\$asset_url\$/g, o.assetUrl) - .replace(/\$asset_dandi_url\$/g, o.assetUrl) - .replace(/\$asset_s3_url\$/g, o.assetUrl); + .replace(/\$dandiset_id\$/g, data.dandisetId) + .replace(/\$dandiset_version\$/g, data.dandisetVersion) + .replace(/\$asset_id\$/g, data.assetId) + .replace(/\$asset_url\$/g, data.assetUrl) + .replace(/\$asset_dandi_url\$/g, data.assetUrl) + .replace(/\$asset_s3_url\$/g, data.assetUrl); } function getExternalServices(path: AssetPath, info: {dandisetId: string, dandisetVersion: string}) { @@ -417,7 +417,7 @@ function getExternalServices(path: AssetPath, info: {dandisetId: string, dandise .filter((service) => servicePredicate(service, path)) .map((service) => ({ name: service.name, - url: filterServiceEndpoint(service.endpoint, { + url: serviceURL(service.endpoint, { dandisetId: info.dandisetId, dandisetVersion: info.dandisetVersion, assetId: path.asset?.asset_id || '', From ef2d95f27b0679a3c3e665f4b4094af3d67cc246 Mon Sep 17 00:00:00 2001 From: Roni Choudhury Date: Thu, 2 Nov 2023 12:55:07 -0400 Subject: [PATCH 4/5] Use simple string replacement instead of regex --- web/src/views/FileBrowserView/FileBrowser.vue | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/web/src/views/FileBrowserView/FileBrowser.vue b/web/src/views/FileBrowserView/FileBrowser.vue index 51f3a1cd4..8bf3be904 100644 --- a/web/src/views/FileBrowserView/FileBrowser.vue +++ b/web/src/views/FileBrowserView/FileBrowser.vue @@ -387,12 +387,12 @@ function serviceURL(endpoint: string, data: { assetS3Url: string, }) { return endpoint - .replace(/\$dandiset_id\$/g, data.dandisetId) - .replace(/\$dandiset_version\$/g, data.dandisetVersion) - .replace(/\$asset_id\$/g, data.assetId) - .replace(/\$asset_url\$/g, data.assetUrl) - .replace(/\$asset_dandi_url\$/g, data.assetUrl) - .replace(/\$asset_s3_url\$/g, data.assetUrl); + .replaceAll('$dandiset_id$', data.dandisetId) + .replaceAll('$dandiset_version$', data.dandisetVersion) + .replaceAll('$asset_id$', data.assetId) + .replaceAll('$asset_url$', data.assetUrl) + .replaceAll('$asset_dandi_url$', data.assetUrl) + .replaceAll('$asset_s3_url$', data.assetUrl); } function getExternalServices(path: AssetPath, info: {dandisetId: string, dandisetVersion: string}) { From 350c561476b9c73b3fc5dd3c2e06a1705b846c6a Mon Sep 17 00:00:00 2001 From: Roni Choudhury Date: Thu, 2 Nov 2023 13:02:10 -0400 Subject: [PATCH 5/5] Remove asset_id from transmitted data --- web/src/views/FileBrowserView/FileBrowser.vue | 3 --- 1 file changed, 3 deletions(-) diff --git a/web/src/views/FileBrowserView/FileBrowser.vue b/web/src/views/FileBrowserView/FileBrowser.vue index 8bf3be904..1a4f6d507 100644 --- a/web/src/views/FileBrowserView/FileBrowser.vue +++ b/web/src/views/FileBrowserView/FileBrowser.vue @@ -381,7 +381,6 @@ const itemsNotFound = computed(() => items.value && !items.value.length); function serviceURL(endpoint: string, data: { dandisetId: string, dandisetVersion: string, - assetId: string, assetUrl: string, assetDandiUrl: string, assetS3Url: string, @@ -389,7 +388,6 @@ function serviceURL(endpoint: string, data: { return endpoint .replaceAll('$dandiset_id$', data.dandisetId) .replaceAll('$dandiset_version$', data.dandisetVersion) - .replaceAll('$asset_id$', data.assetId) .replaceAll('$asset_url$', data.assetUrl) .replaceAll('$asset_dandi_url$', data.assetUrl) .replaceAll('$asset_s3_url$', data.assetUrl); @@ -420,7 +418,6 @@ function getExternalServices(path: AssetPath, info: {dandisetId: string, dandise url: serviceURL(service.endpoint, { dandisetId: info.dandisetId, dandisetVersion: info.dandisetVersion, - assetId: path.asset?.asset_id || '', assetUrl, assetDandiUrl, assetS3Url,