Skip to content

Commit d7324b4

Browse files
MillenniumFalconMechanicclaudeCopilot
authored
feat: always show curl export option in non-production environments (#4768)
* feat: always show curl export option in non-production environments #4767 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor: extract shared NRES consent group check #4767 - Add isNRESOrUnrestrictedAccess to transformers.ts - Update isNRESDataset to check for both NRES and Unrestricted access - Remove isNRESConsentGroup wrapper, use hasNRESConsentGroup directly Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: remove dead code #4767 Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
1 parent 7f9fd4e commit d7324b4

6 files changed

Lines changed: 77 additions & 21 deletions

File tree

app/apis/azul/anvil-cmg/common/transformers.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,18 @@ export function getConsentGroup(response: DatasetsResponse): string[] {
7171
return processAggregatedOrArrayValue(response.datasets, "consent_group");
7272
}
7373

74+
/**
75+
* Returns true if consent groups include NRES or Unrestricted access.
76+
* @param consentGroups - Array of consent group strings.
77+
* @returns true if NRES or Unrestricted access is present.
78+
*/
79+
export function isNRESOrUnrestrictedAccess(consentGroups: string[]): boolean {
80+
return (
81+
consentGroups.includes("NRES") ||
82+
consentGroups.includes("Unrestricted access")
83+
);
84+
}
85+
7486
/**
7587
* Maps biosample type from an aggregated biosamples value returned from endpoints other than index/biosamples.
7688
* @param response - Response model return from Azul that includes aggregated biosamples.

app/components/Export/components/AnVILExplorer/components/ExportCohort/components/DownloadSection/downloadSection.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { TYPOGRAPHY_PROPS } from "@databiosphere/findable-ui/lib/styles/common/mui/typography";
22
import { Stack, Typography } from "@mui/material";
33
import type { JSX } from "react";
4-
import { isNRESConsentGroup } from "../../../../../../../../viewModelBuilders/azul/anvil-cmg/common/viewModelBuilders";
4+
import { hasNRESConsentGroup } from "../../../../../../../../viewModelBuilders/azul/anvil-cmg/common/viewModelBuilders";
55
import { Props } from "./types";
66

77
/**
@@ -11,7 +11,7 @@ import { Props } from "./types";
1111
* @returns Download section title and description.
1212
*/
1313
export const DownloadSection = ({ viewContext }: Props): JSX.Element => {
14-
const isNRES = isNRESConsentGroup(viewContext);
14+
const isNRES = hasNRESConsentGroup(viewContext.fileManifestState);
1515
return (
1616
<Stack gap={1}>
1717
<Typography

app/config/utils.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
import { SelectCategoryValue } from "@databiosphere/findable-ui/lib/common/entities";
22

3+
/**
4+
* Returns true if the current environment is production.
5+
* Determined by checking if NEXT_PUBLIC_SITE_CONFIG ends with "-prod".
6+
* @returns true if production environment.
7+
*/
8+
export function isProductionEnvironment(): boolean {
9+
const config = process.env.NEXT_PUBLIC_SITE_CONFIG ?? "";
10+
return config.endsWith("-prod");
11+
}
12+
313
/**
414
* Returns select category value with formatted label.
515
* @param formatLabel - Function to format label.

app/viewModelBuilders/azul/anvil-cmg/common/viewModelBuilders.tsx

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ import {
8080
getBioSampleId,
8181
getBioSampleType,
8282
getConsentGroup,
83+
isNRESOrUnrestrictedAccess,
8384
getDatasetDetails,
8485
getDatasetEntryId,
8586
getDocumentId,
@@ -109,6 +110,7 @@ import { METADATA_KEY } from "../../../../components/Index/common/entities";
109110
import { getPluralizedMetadataLabel } from "../../../../components/Index/common/indexTransformer";
110111
import { SUMMARY_DISPLAY_TEXT } from "./summaryMapper/constants";
111112
import { mapExportSummary } from "./summaryMapper/summaryMapper";
113+
import { isProductionEnvironment } from "../../../../config/utils";
112114

113115
/**
114116
* Build props for activity type BasicCell component from the given activities response.
@@ -1790,25 +1792,23 @@ function isFileManifestSummaryFileCountValid(
17901792
}
17911793

17921794
/**
1793-
* Returns true if the dataset has NRES or Unrestricted access consent group.
1794-
* @param viewContext - View context.
1795-
* @returns true if the dataset has NRES or Unrestricted access consent group.
1795+
* Returns true if the file manifest state includes NRES or Unrestricted access consent group.
1796+
* @param fileManifestState - File manifest state.
1797+
* @returns true if NRES or Unrestricted access consent group is present.
17961798
*/
1797-
export function isNRESConsentGroup(
1798-
viewContext: ViewContext<DatasetsResponse>
1799+
export function hasNRESConsentGroup(
1800+
fileManifestState: FileManifestState
17991801
): boolean {
1800-
const { fileManifestState } = viewContext;
1801-
18021802
const facet = findFacet(
18031803
fileManifestState.filesFacets,
18041804
ANVIL_CMG_CATEGORY_KEY.DATASET_CONSENT_GROUP
18051805
);
18061806

18071807
if (!facet) return false;
18081808

1809-
const termsByName = facet.termsByName;
1809+
const consentGroups = [...facet.termsByName.keys()];
18101810

1811-
return termsByName.has("NRES") || termsByName.has("Unrestricted access");
1811+
return isNRESOrUnrestrictedAccess(consentGroups);
18121812
}
18131813

18141814
/**
@@ -1882,7 +1882,8 @@ export const renderWhenUnAuthenticated = (
18821882
};
18831883

18841884
/**
1885-
* Renders cohort curl download component when the current query includes NRES or Unrestricted access consent groups.
1885+
* Renders cohort curl download component when the current query includes NRES or Unrestricted access consent groups,
1886+
* or when the environment is non-production.
18861887
* @param _ - Unused.
18871888
* @param viewContext - View context.
18881889
* @returns model to be used as props for the ConditionalComponent component.
@@ -1891,19 +1892,25 @@ export const renderCohortCurlDownload = (
18911892
_: unknown,
18921893
viewContext: ViewContext<DatasetsResponse>
18931894
): ComponentProps<typeof C.ConditionalComponent> => {
1894-
return { isIn: isNRESConsentGroup(viewContext) };
1895+
return {
1896+
isIn:
1897+
!isProductionEnvironment() ||
1898+
hasNRESConsentGroup(viewContext.fileManifestState),
1899+
};
18951900
};
18961901

18971902
/**
1898-
* Renders dataset curl download components when the given dataset has NRES consent group.
1903+
* Renders dataset curl download components when the given dataset has NRES consent group,
1904+
* or when the environment is non-production.
18991905
* @param datasetsResponse - Response model return from datasets API.
19001906
* @returns model to be used as props for the ConditionalComponent component.
19011907
*/
19021908
export const renderDatasetCurlDownload = (
19031909
datasetsResponse: DatasetsResponse
19041910
): React.ComponentProps<typeof C.ConditionalComponent> => {
19051911
return {
1906-
isIn: isDatasetNRESConsentGroup(datasetsResponse),
1912+
isIn:
1913+
!isProductionEnvironment() || isDatasetNRESConsentGroup(datasetsResponse),
19071914
};
19081915
};
19091916

pages/[entityListType]/[...params].tsx

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,12 @@ import { useConfig } from "@databiosphere/findable-ui/lib/hooks/useConfig";
3333
import NextError from "next/error";
3434
import { ROUTES } from "../../site-config/anvil-cmg/dev/export/routes";
3535

36-
import { getConsentGroup } from "../../app/apis/azul/anvil-cmg/common/transformers";
36+
import {
37+
getConsentGroup,
38+
isNRESOrUnrestrictedAccess,
39+
} from "../../app/apis/azul/anvil-cmg/common/transformers";
3740
import { DatasetsResponse } from "../../app/apis/azul/anvil-cmg/common/responses";
41+
import { isProductionEnvironment } from "../../app/config/utils";
3842

3943
const setOfProcessedIds = new Set<string>();
4044

@@ -66,8 +70,13 @@ const EntityDetailPage = (props: EntityDetailPageProps): JSX.Element => {
6670
const { query } = useRouter();
6771
if (!props.entityListType) return <></>;
6872
if (props.override) return <EntityGuard override={props.override} />;
69-
// Curl download requires NRES consent group (AnVIL only)
70-
if (isAnVIL && isCurlDownloadRoute(query) && !isNRESDataset(props.data)) {
73+
// Curl download requires NRES consent group (AnVIL production only)
74+
if (
75+
isAnVIL &&
76+
isProductionEnvironment() &&
77+
isCurlDownloadRoute(query) &&
78+
!isNRESDataset(props.data)
79+
) {
7180
return <NextError statusCode={404} />;
7281
}
7382
if (isChooseExportView(query)) return <EntityExportView {...props} />;
@@ -103,14 +112,14 @@ function isCurlDownloadRoute(query: ParsedUrlQuery): boolean {
103112
}
104113

105114
/**
106-
* Returns true if the dataset has NRES consent group.
115+
* Returns true if the dataset has NRES or Unrestricted access consent group.
107116
* @param data - Entity response data.
108-
* @returns True if the dataset has NRES consent group.
117+
* @returns True if the dataset has NRES or Unrestricted access consent group.
109118
*/
110119
function isNRESDataset(data: AzulEntityStaticResponse | undefined): boolean {
111120
if (!data) return false;
112121
const consentGroups = getConsentGroup(data as DatasetsResponse);
113-
return consentGroups.includes("NRES");
122+
return isNRESOrUnrestrictedAccess(consentGroups);
114123
}
115124

116125
/**

pages/export/get-curl-command.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import { JSX } from "react";
22
import { ExportMethodView } from "@databiosphere/findable-ui/lib/views/ExportMethodView/exportMethodView";
33
import { GetStaticProps } from "next";
4+
import { useFileManifestState } from "@databiosphere/findable-ui/lib/hooks/useFileManifestState";
5+
import { useConfig } from "@databiosphere/findable-ui/lib/hooks/useConfig";
6+
import NextError from "next/error";
7+
import { isProductionEnvironment } from "../../app/config/utils";
8+
import { hasNRESConsentGroup } from "../../app/viewModelBuilders/azul/anvil-cmg/common/viewModelBuilders";
49

510
export const getStaticProps: GetStaticProps = async () => {
611
return {
@@ -15,6 +20,19 @@ export const getStaticProps: GetStaticProps = async () => {
1520
* @returns download curl command view component.
1621
*/
1722
const GetCurlCommandPage = (): JSX.Element => {
23+
const { config } = useConfig();
24+
const { fileManifestState } = useFileManifestState();
25+
const isAnVIL = config.appTitle?.includes("AnVIL");
26+
27+
// Curl download requires NRES consent group (AnVIL production only)
28+
if (
29+
isAnVIL &&
30+
isProductionEnvironment() &&
31+
!hasNRESConsentGroup(fileManifestState)
32+
) {
33+
return <NextError statusCode={404} />;
34+
}
35+
1836
return <ExportMethodView />;
1937
};
2038

0 commit comments

Comments
 (0)