Skip to content
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
196 changes: 196 additions & 0 deletions __tests__/viewModelBuilders/buildFileDownload.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
import { buildFileDownload } from "../../app/viewModelBuilders/azul/anvil-cmg/common/viewModelBuilders";
import { FilesResponse } from "../../app/apis/azul/anvil-cmg/common/responses";

/**
* Creates a mock FilesResponse with configurable file and dataset values.
* @param overrides - Partial overrides for the files array.
* @param datasetOverrides - Partial overrides for the datasets array.
* @returns Mock FilesResponse.
*/
const createMockFilesResponse = (
overrides: Partial<FilesResponse["files"][0]> = {},
datasetOverrides: Partial<FilesResponse["datasets"][0]> = {}
): FilesResponse => ({
activities: [
{
activity_type: ["Sequencing"],
data_modality: ["Genomic"],
},
],
biosamples: [
{
anatomical_site: ["Blood"],
biosample_type: ["Normal"],
},
],
datasets: [
{
dataset_id: ["dataset-123"],
title: ["Test Dataset"],
...datasetOverrides,
},
],
donors: [
{
organism_type: ["Homo sapiens"],
phenotypic_sex: ["Female"],
reported_ethnicity: ["Not reported"],
},
],
entryId: "entry-123",
files: [
{
accessible: true,
azul_mirror_uri: null,
azul_url: "https://service.azul.data/repository/files/file-123",
data_modality: ["Genomic"],
date_created: "2024-01-01",
document_id: "doc-123",
drs_uri: "drs://data.azul/file-123",
file_format: "bam",
file_id: "file-123",
file_name: "sample.bam",
file_size: 1024,
file_type: "Analysis",
...overrides,
},
],
libraries: [
{
prep_material_name: ["RNA"],
},
],
});

describe("buildFileDownload", () => {
describe("url prop based on azul_mirror_uri", () => {
it("returns undefined url when azul_mirror_uri is null", () => {
const response = createMockFilesResponse({
azul_mirror_uri: null,
azul_url: "https://service.azul.data/repository/files/file-123",
});

const result = buildFileDownload(response);

expect(result.url).toBeUndefined();
});

it("returns undefined url when azul_mirror_uri is empty string", () => {
const response = createMockFilesResponse({
azul_mirror_uri: "",
azul_url: "https://service.azul.data/repository/files/file-123",
});

const result = buildFileDownload(response);

expect(result.url).toBeUndefined();
});

it("returns azul_url when azul_mirror_uri has a value", () => {
const azulUrl = "https://service.azul.data/repository/files/file-123";
const response = createMockFilesResponse({
azul_mirror_uri: "s3://bucket/path/to/file.bam",
azul_url: azulUrl,
});

const result = buildFileDownload(response);

expect(result.url).toBe(azulUrl);
});

it("returns azul_url value exactly as provided when mirror uri exists", () => {
const azulUrl =
"https://service.azul.data/repository/files/different-file-456";
const response = createMockFilesResponse({
azul_mirror_uri: "s3://other-bucket/other-path.bam",
azul_url: azulUrl,
});

const result = buildFileDownload(response);

expect(result.url).toBe(azulUrl);
});
});

describe("other props", () => {
it("returns correct entityName from file_name", () => {
const response = createMockFilesResponse({
file_name: "my-sample-file.bam",
});

const result = buildFileDownload(response);

expect(result.entityName).toBe("my-sample-file.bam");
});

it("returns correct relatedEntityId from dataset_id", () => {
const response = createMockFilesResponse(
{},
{ dataset_id: ["my-dataset-id-123"] }
);

const result = buildFileDownload(response);

expect(result.relatedEntityId).toBe("my-dataset-id-123");
});

it("returns correct relatedEntityName from dataset title", () => {
const response = createMockFilesResponse(
{},
{ title: ["My Research Dataset"] }
);

const result = buildFileDownload(response);

expect(result.relatedEntityName).toBe("My Research Dataset");
});
});

describe("integration scenarios", () => {
it("returns all props correctly when download is enabled", () => {
const response = createMockFilesResponse(
{
azul_mirror_uri: "s3://bucket/file.bam",
azul_url: "https://azul.service/files/123",
file_name: "research-data.bam",
},
{
dataset_id: ["dataset-abc"],
title: ["Genomics Research Project"],
}
);

const result = buildFileDownload(response);

expect(result).toEqual({
entityName: "research-data.bam",
relatedEntityId: "dataset-abc",
relatedEntityName: "Genomics Research Project",
url: "https://azul.service/files/123",
});
});

it("returns all props correctly when download is disabled", () => {
const response = createMockFilesResponse(
{
azul_mirror_uri: null,
azul_url: "https://azul.service/files/123",
file_name: "research-data.bam",
},
{
dataset_id: ["dataset-abc"],
title: ["Genomics Research Project"],
}
);

const result = buildFileDownload(response);

expect(result).toEqual({
entityName: "research-data.bam",
relatedEntityId: "dataset-abc",
relatedEntityName: "Genomics Research Project",
url: undefined,
});
});
});
});
1 change: 1 addition & 0 deletions app/apis/azul/anvil-cmg/common/entities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export interface DonorSpecies {
*/
export interface FileEntity {
accessible: boolean;
azul_mirror_uri: string | null;
azul_url: string;
data_modality: string[];
date_created: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { useExploreState } from "@databiosphere/findable-ui/lib/hooks/useExploreState";
import { useFeatureFlag } from "@databiosphere/findable-ui/lib/hooks/useFeatureFlag/useFeatureFlag";
import { updateVisibility } from "@databiosphere/findable-ui/lib/providers/exploreState/actions/updateVisibility/dispatch";
import { JSX, useEffect } from "react";
import { FEATURES } from "../../../../shared/entities";
import { ANVIL_CMG_CATEGORY_KEY } from "../../../../../site-config/anvil-cmg/category";

/**
* Controller component that syncs the AZUL_DOWNLOAD feature flag with column visibility.
* Renders nothing but manages the download column visibility based on the feature flag.
* @returns Empty fragment.
*/
export const AzulFileDownload = (): JSX.Element => {
const isDownloadEnabled = useFeatureFlag(FEATURES.AZUL_DOWNLOAD);
const { exploreDispatch } = useExploreState();

useEffect(() => {
exploreDispatch(
updateVisibility({
updaterOrValue: (prev) => ({
...prev,
[ANVIL_CMG_CATEGORY_KEY.AZUL_FILE_DOWNLOAD]: isDownloadEnabled,
}),
})
);
}, [isDownloadEnabled, exploreDispatch]);

return <></>;
};
1 change: 1 addition & 0 deletions app/components/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export { ManifestDownloadEntity } from "@databiosphere/findable-ui/lib/component
export { ManifestDownloadForm } from "@databiosphere/findable-ui/lib/components/Export/components/ManifestDownload/components/ManifestDownloadForm/manifestDownloadForm";
export { ManifestDownload } from "@databiosphere/findable-ui/lib/components/Export/components/ManifestDownload/manifestDownload";
export { AzulFileDownload } from "@databiosphere/findable-ui/lib/components/Index/components/AzulFileDownload/azulFileDownload";
export { AzulFileDownload as AzulFileDownloadVisibilityController } from "./Index/components/AzulFileDownload/azulFileDownload";
export {
BackPageContentMainColumn,
BackPageContentSideColumn,
Expand Down
1 change: 1 addition & 0 deletions app/shared/entities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
* Set of possible feature flags.
*/
export enum FEATURES {
AZUL_DOWNLOAD = "azuldownload",
NCPI_EXPORT = "ncpiexport",
}
12 changes: 11 additions & 1 deletion app/viewModelBuilders/azul/anvil-cmg/common/viewModelBuilders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -977,18 +977,28 @@ export const buildFileDataModality = (

/**
* Build props for file download AzulFileDownload component.
* Downloads use azul_url but are only enabled when azul_mirror_uri is present.
* @param response - Response model returned from index/files API endpoint.
* @returns model to be used as props for the AzulFileDownload component.
*/
export const buildFileDownload = (
response: FilesResponse
): React.ComponentProps<typeof C.AzulFileDownload> => {
const dataset = response.datasets[0];
const mirrorUri = processEntityValue(
response.files,
"azul_mirror_uri",
LABEL.EMPTY
);
// Only provide download URL if mirror URI exists (enables the download button)
const url = mirrorUri
? processEntityValue(response.files, "azul_url", LABEL.EMPTY)
: undefined;
return {
entityName: processEntityValue(response.files, "file_name"),
relatedEntityId: dataset.dataset_id[0],
relatedEntityName: dataset.title[0],
url: processEntityValue(response.files, "azul_url", LABEL.EMPTY),
url,
};
};

Expand Down
6 changes: 3 additions & 3 deletions site-config/anvil-cmg/dev/index/filesEntityConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import {
ANVIL_CMG_CATEGORY_KEY,
ANVIL_CMG_CATEGORY_LABEL,
} from "../../category";
import { entityListSlot } from "../ui/entityList";
import { entityViewSlot } from "../ui/entityView";
import { filesEntityListSlot } from "../ui/filesEntityList";

export const downloadColumn: ColumnConfig<FilesResponse> = {
componentConfig: {
Expand Down Expand Up @@ -44,7 +44,7 @@ export const filesEntityConfig: EntityConfig<FilesResponse> = {
label: "Files",
list: {
columns: [
// downloadColumn,
downloadColumn,
{
columnPinned: true,
componentConfig: {
Expand Down Expand Up @@ -171,7 +171,7 @@ export const filesEntityConfig: EntityConfig<FilesResponse> = {
enableSummary: true,
enableTabs: true,
slots: {
entityListSlot,
entityListSlot: filesEntityListSlot,
entityViewSlot,
},
},
Expand Down
17 changes: 17 additions & 0 deletions site-config/anvil-cmg/dev/ui/filesEntityList.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import {
ComponentConfig,
ComponentsConfig,
} from "@databiosphere/findable-ui/lib/config/entities";
import * as C from "../../../../app/components";
import * as MDX from "../../../../app/components/common/MDXContent/anvil-cmg";
import * as V from "../../../../app/viewModelBuilders/azul/anvil-cmg/common/viewModelBuilders";

export const filesEntityListSlot: ComponentsConfig = [
{
component: MDX.AlertEntityListWarning,
viewBuilder: V.buildAlertEntityListWarning,
} as ComponentConfig<typeof MDX.AlertEntityListWarning>,
{
component: C.AzulFileDownloadVisibilityController,
} as ComponentConfig<typeof C.AzulFileDownloadVisibilityController>,
];
7 changes: 0 additions & 7 deletions site-config/anvil-cmg/prod/config.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { EntityConfig } from "@databiosphere/findable-ui/lib/config/entities";
import { GIT_HUB_REPO_URL } from "../../common/constants";
import { SiteConfig } from "../../common/entities";
import { makeConfig } from "../dev/config";
import { downloadColumn } from "../dev/index/filesEntityConfig";
import { authenticationConfig } from "./authentication/authentication";

const config: SiteConfig = {
Expand All @@ -18,11 +16,6 @@ const config: SiteConfig = {

config.authentication = authenticationConfig;

const filesEntityConfig = config.entities.find(
(c) => c.apiPath == "index/files"
) as EntityConfig;
filesEntityConfig.list.columns.splice(0, 0, downloadColumn);

// Update gtmAuth for the prod environment lookup.
if (config.analytics) {
const analytics = { ...config.analytics };
Expand Down
Loading