diff --git a/src/businessRules/roleBasedAccess.ts b/src/businessRules/roleBasedAccess.ts
index 1098fd29f..4bd327d74 100644
--- a/src/businessRules/roleBasedAccess.ts
+++ b/src/businessRules/roleBasedAccess.ts
@@ -8,6 +8,14 @@ type HasLibraryKeyProps = {
[key: string]: unknown;
};
+// If the `quicksightOnlyForSysadmins` feature flag is set, only system
+// admins should see the QuickSight link.
+export const useMaySeeQuickSightLink = (_: HasLibraryKeyProps): boolean => {
+ const admin = useAppAdmin();
+ const onlyForSysAdmins = useAppFeatureFlags().quicksightOnlyForSysadmins;
+ return !onlyForSysAdmins || admin.isSystemAdmin();
+};
+
// If the `reportsOnlyForSysadmins` feature flag is set, only system admins
// may request inventory reports.
export const useMayRequestInventoryReports = (
diff --git a/src/components/LibraryStats.tsx b/src/components/LibraryStats.tsx
index 04823a8b9..5cdea404c 100644
--- a/src/components/LibraryStats.tsx
+++ b/src/components/LibraryStats.tsx
@@ -2,6 +2,7 @@ import * as React from "react";
import { LibraryStatistics } from "../interfaces";
import {
useMayRequestInventoryReports,
+ useMaySeeQuickSightLink,
useMayViewCollectionBarChart,
} from "../businessRules/roleBasedAccess";
import StatsTotalCirculationsGroup from "./StatsTotalCirculationsGroup";
@@ -45,6 +46,7 @@ const LibraryStats = ({ stats, library }: LibraryStatsProps) => {
const inventoryReportRequestEnabled = useMayRequestInventoryReports({
library,
});
+ const quicksightLinkEnabled = useMaySeeQuickSightLink({ library });
const quicksightPageUrl = useAppContext().quicksightPagePath;
let statsLayoutClass: string, dashboardTitle: string, implementation: string;
@@ -74,6 +76,7 @@ const LibraryStats = ({ stats, library }: LibraryStatsProps) => {
diff --git a/src/components/StatsCollectionsGroup.tsx b/src/components/StatsCollectionsGroup.tsx
index 797790336..5e682b530 100644
--- a/src/components/StatsCollectionsGroup.tsx
+++ b/src/components/StatsCollectionsGroup.tsx
@@ -20,7 +20,7 @@ const StatsCollectionsGroup = ({
}: Props) => {
const content =
collections.length === 0 ? (
- No associated collections.
+ No associated collections.
) : showBarChart ? (
) : (
diff --git a/src/components/StatsUsageReportsGroup.tsx b/src/components/StatsUsageReportsGroup.tsx
index 0313adda9..5b7773114 100644
--- a/src/components/StatsUsageReportsGroup.tsx
+++ b/src/components/StatsUsageReportsGroup.tsx
@@ -8,6 +8,7 @@ type Props = {
heading?: string;
description?: string;
inventoryReportsEnabled: boolean;
+ quicksightLinkEnabled: boolean;
library?: string;
usageDataHref?: string;
usageDataLabel?: string;
@@ -24,6 +25,7 @@ const StatsUsageReportsGroup = ({
usageDataLabel = "View Usage",
usageDataTarget = "_self",
inventoryReportsEnabled,
+ quicksightLinkEnabled,
library = undefined,
}: Props) => {
const [showReportForm, setShowReportForm] = useState(false);
@@ -41,6 +43,11 @@ const StatsUsageReportsGroup = ({
<>
+ {!inventoryReportsEnabled && !quicksightLinkEnabled && (
+
+ Usage reporting is not available.
+
+ )}
{inventoryReportsEnabled && library && (
<>
-
-
-
+ {quicksightLinkEnabled && (
+
+
+
+ )}
>
);
diff --git a/src/interfaces.ts b/src/interfaces.ts
index b12a6942b..3783e1389 100644
--- a/src/interfaces.ts
+++ b/src/interfaces.ts
@@ -48,6 +48,7 @@ export interface TestingFlags {
export interface FeatureFlags {
enableAutoList?: boolean;
reportsOnlyForSysadmins?: boolean;
+ quicksightOnlyForSysadmins?: boolean;
}
export interface Navigate {
diff --git a/src/stylesheets/stats.scss b/src/stylesheets/stats.scss
index c5bf99f79..ab65c8cd0 100644
--- a/src/stylesheets/stats.scss
+++ b/src/stylesheets/stats.scss
@@ -61,12 +61,13 @@
margin: 0;
overflow-wrap: normal;
}
+ }
- .no-collections {
- margin: 10px;
- font-style: italic;
- color: $medium-dark-gray;
- }
+ .no-content {
+ margin: 10px;
+ font-style: italic;
+ font-weight: bolder;
+ color: $medium-dark-gray;
}
.stat-group-description {
diff --git a/src/utils/featureFlags.ts b/src/utils/featureFlags.ts
index 6224f50d9..73f8fba90 100644
--- a/src/utils/featureFlags.ts
+++ b/src/utils/featureFlags.ts
@@ -6,4 +6,5 @@ import { FeatureFlags } from "../interfaces";
export const defaultFeatureFlags: FeatureFlags = {
enableAutoList: true,
reportsOnlyForSysadmins: true,
+ quicksightOnlyForSysadmins: true,
};
diff --git a/tests/jest/businessRules/roleBasedAccess.test.ts b/tests/jest/businessRules/roleBasedAccess.test.ts
index f2f889db8..352250ec2 100644
--- a/tests/jest/businessRules/roleBasedAccess.test.ts
+++ b/tests/jest/businessRules/roleBasedAccess.test.ts
@@ -4,6 +4,7 @@ import { ContextProviderProps } from "../../../src/components/ContextProvider";
import { ConfigurationSettings, FeatureFlags } from "../../../src/interfaces";
import {
useMayRequestInventoryReports,
+ useMaySeeQuickSightLink,
useMayViewCollectionBarChart,
} from "../../../src/businessRules/roleBasedAccess";
@@ -116,6 +117,98 @@ describe("Business rules for role-based access", () => {
});
});
+ describe("controls access to the quicksight link", () => {
+ const testAccess = (
+ expectedResult: boolean,
+ config: Partial
+ ) => {
+ const wrapper = setupWrapper(config);
+ const { result } = renderHook(
+ () => useMaySeeQuickSightLink({ library: libraryMatch }),
+ { wrapper }
+ );
+ expect(result.current).toBe(expectedResult);
+ };
+
+ it("restricts access to only sysadmins, if the restriction feature flag is true", () => {
+ const featureFlags: FeatureFlags = { quicksightOnlyForSysadmins: true };
+
+ testAccess(true, { roles: [{ role: "system" }], featureFlags });
+
+ testAccess(false, { roles: [{ role: "manager-all" }], featureFlags });
+ testAccess(false, { roles: [{ role: "librarian-all" }], featureFlags });
+
+ testAccess(false, {
+ roles: [{ role: "manager", library: libraryMatch }],
+ featureFlags,
+ });
+ testAccess(false, {
+ roles: [{ role: "manager", library: libraryMismatch }],
+ featureFlags,
+ });
+ testAccess(false, {
+ roles: [{ role: "librarian", library: libraryMatch }],
+ featureFlags,
+ });
+ testAccess(false, {
+ roles: [{ role: "librarian", library: libraryMismatch }],
+ featureFlags,
+ });
+ });
+
+ it("allows all users, if the restriction feature flag is is false", () => {
+ const featureFlags: FeatureFlags = { quicksightOnlyForSysadmins: false };
+
+ testAccess(true, { roles: [{ role: "system" }], featureFlags });
+
+ testAccess(true, { roles: [{ role: "manager-all" }], featureFlags });
+ testAccess(true, { roles: [{ role: "librarian-all" }], featureFlags });
+
+ testAccess(true, {
+ roles: [{ role: "manager", library: libraryMatch }],
+ featureFlags,
+ });
+ testAccess(true, {
+ roles: [{ role: "manager", library: libraryMismatch }],
+ featureFlags,
+ });
+ testAccess(true, {
+ roles: [{ role: "librarian", library: libraryMatch }],
+ featureFlags,
+ });
+ testAccess(true, {
+ roles: [{ role: "librarian", library: libraryMismatch }],
+ featureFlags,
+ });
+ });
+
+ it("allows all users, if the restriction feature flag is not set", () => {
+ const featureFlags: FeatureFlags = {};
+
+ testAccess(true, { roles: [{ role: "system" }], featureFlags });
+
+ testAccess(true, { roles: [{ role: "manager-all" }], featureFlags });
+ testAccess(true, { roles: [{ role: "librarian-all" }], featureFlags });
+
+ testAccess(true, {
+ roles: [{ role: "manager", library: libraryMatch }],
+ featureFlags,
+ });
+ testAccess(true, {
+ roles: [{ role: "manager", library: libraryMismatch }],
+ featureFlags,
+ });
+ testAccess(true, {
+ roles: [{ role: "librarian", library: libraryMatch }],
+ featureFlags,
+ });
+ testAccess(true, {
+ roles: [{ role: "librarian", library: libraryMismatch }],
+ featureFlags,
+ });
+ });
+ });
+
describe("controls access to the collection statistics barchart", () => {
const testAccess = (
expectedResult: boolean,
diff --git a/tests/jest/components/Stats.test.tsx b/tests/jest/components/Stats.test.tsx
index 135da76a8..087a65356 100644
--- a/tests/jest/components/Stats.test.tsx
+++ b/tests/jest/components/Stats.test.tsx
@@ -434,6 +434,53 @@ describe("Dashboard Statistics", () => {
expect(renderFor(false, managerAll)).not.toBeNull();
expect(renderFor(false, librarianAll)).not.toBeNull();
});
+
+ it("shows quicksight link only for sysadmins, if sysadmin-only flag set", () => {
+ const fakeQuickSightHref = "https://example.com/fakeQS";
+
+ // We'll use this function to test multiple scenarios.
+ const renderFor = (
+ onlySysadmins: boolean,
+ roles: { role: string; library?: string }[]
+ ) => {
+ const contextProviderProps: Partial = {
+ featureFlags: { quicksightOnlyForSysadmins: onlySysadmins },
+ roles,
+ quicksightPagePath: fakeQuickSightHref,
+ };
+ const {
+ container,
+ getByRole,
+ queryByRole,
+ queryByText,
+ } = renderWithProviders(, {
+ contextProviderProps,
+ });
+
+ // We should always render a Usage reports group when a library is specified.
+ getByRole("heading", {
+ level: 3,
+ name: statGroupToHeading.usageReports,
+ });
+ const usageReportLink = queryByRole("link", { name: /View Usage/i });
+ if (usageReportLink) {
+ expect(usageReportLink).toHaveAttribute("href", fakeQuickSightHref);
+ }
+
+ // Clean up the container after each render.
+ document.body.removeChild(container);
+ return usageReportLink;
+ };
+
+ // If the feature flag is set, the link should be visible only to sysadmins.
+ expect(renderFor(true, systemAdmin)).not.toBeNull();
+ expect(renderFor(true, managerAll)).toBeNull();
+ expect(renderFor(true, librarianAll)).toBeNull();
+ // If the feature flag is false, the button should be visible to all users.
+ expect(renderFor(false, systemAdmin)).not.toBeNull();
+ expect(renderFor(false, managerAll)).not.toBeNull();
+ expect(renderFor(false, librarianAll)).not.toBeNull();
+ });
});
describe("charting - custom tooltip", () => {