Skip to content

Commit

Permalink
Merge pull request #573 from CBIIT/CRDCDH-2098
Browse files Browse the repository at this point in the history
CRDCDH-2098 PBAC - Data Submission Button Visibility
  • Loading branch information
amattu2 authored Dec 31, 2024
2 parents 421d99a + 8cf513c commit 29463e1
Show file tree
Hide file tree
Showing 17 changed files with 629 additions and 285 deletions.
12 changes: 0 additions & 12 deletions src/components/APITokenDialog/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -270,16 +270,4 @@ describe("Implementation Requirements", () => {

expect(mockWriteText).not.toHaveBeenCalled();
});

it("should show an error when the user without the required permission tries to generate a token", async () => {
const { getByText } = render(<ApiTokenDialog open />, {
wrapper: (p) => <TestParent {...p} permissions={[]} />,
});

userEvent.click(getByText(/Create Token/, { selector: "button" }));

await waitFor(() => {
expect(getByText(/Token was unable to be created./)).toBeInTheDocument();
});
});
});
9 changes: 0 additions & 9 deletions src/components/APITokenDialog/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ import { GRANT_TOKEN, GrantTokenResp } from "../../graphql";
import GenericAlert, { AlertState } from "../GenericAlert";
import { ReactComponent as CopyIconSvg } from "../../assets/icons/copy_icon.svg";
import { ReactComponent as CloseIconSvg } from "../../assets/icons/close_icon.svg";
import { useAuthContext } from "../Contexts/AuthContext";
import { hasPermission } from "../../config/AuthPermissions";

const StyledDialog = styled(Dialog)({
"& .MuiDialog-paper": {
Expand Down Expand Up @@ -146,8 +144,6 @@ type Props = {
} & Omit<DialogProps, "onClose">;

const APITokenDialog: FC<Props> = ({ onClose, open, ...rest }) => {
const { user } = useAuthContext();

const [tokens, setTokens] = useState<string[]>([]);
const [tokenIdx, setTokenIdx] = useState<number | null>(null);
const [changesAlert, setChangesAlert] = useState<AlertState>(null);
Expand All @@ -166,11 +162,6 @@ const APITokenDialog: FC<Props> = ({ onClose, open, ...rest }) => {
};

const generateToken = async () => {
if (!hasPermission(user, "data_submission", "create")) {
onGenerateTokenError();
return;
}

try {
const { data: d, errors } = await grantToken();
const tokens = d?.grantToken?.tokens;
Expand Down
2 changes: 1 addition & 1 deletion src/components/Collaborators/CollaboratorsDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ const CollaboratorsDialog = ({ onClose, onSave, open, ...rest }: Props) => {
const isLoading = collaboratorLoading || status === AuthStatus.LOADING;
const canModifyCollaborators = useMemo(
() =>
hasPermission(user, "data_submission", "create") &&
hasPermission(user, "data_submission", "create", null, true) &&
submission?.getSubmission?.submitterID === user?._id,
[user, submission?.getSubmission]
);
Expand Down
237 changes: 122 additions & 115 deletions src/components/DataSubmissions/CreateDataSubmissionDialog.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,14 @@ import {
ContextState as AuthCtxState,
Status as AuthStatus,
} from "../Contexts/AuthContext";
import { CREATE_SUBMISSION, CreateSubmissionResp, GetMyUserResp } from "../../graphql";
import {
CREATE_SUBMISSION,
CreateSubmissionResp,
GetMyUserResp,
LIST_APPROVED_STUDIES,
ListApprovedStudiesInput,
ListApprovedStudiesResp,
} from "../../graphql";

const baseStudies: GetMyUserResp["getMyUser"]["studies"] = [
{
Expand Down Expand Up @@ -976,118 +983,118 @@ describe("Implementation Requirements", () => {
});

// NOTE: We're just random-testing against the opposite of the RequiresStudiesAssigned variable
// it.each<UserRole>(["Data Curator", "Data Commons POC"])(
// "should fetch all of the studies if the user's role is %s",
// async (role) => {
// const mockMatcher = jest.fn().mockImplementation(() => true);
// const listApprovedStudiesMock: MockedResponse<
// ListApprovedStudiesResp,
// ListApprovedStudiesInput
// > = {
// request: {
// query: LIST_APPROVED_STUDIES,
// },
// variableMatcher: mockMatcher,
// result: {
// data: {
// listApprovedStudies: {
// total: 1,
// studies: [
// {
// _id: "study1",
// studyName: "study-1-from-api",
// studyAbbreviation: "study-1-from-api-abbr",
// dbGaPID: "",
// controlledAccess: false,
// },
// {
// _id: "study2",
// studyName: "study-2-from-api",
// studyAbbreviation: "study-2-from-api-abbr",
// dbGaPID: "",
// controlledAccess: false,
// },
// ] as ApprovedStudy[],
// },
// },
// },
// };

// const { getByRole } = render(<CreateDataSubmissionDialog onCreate={jest.fn()} />, {
// wrapper: (p) => (
// <TestParent
// mocks={[listApprovedStudiesMock]}
// authCtxState={{ ...baseAuthCtx, user: { ...baseUser, role } }}
// {...p}
// />
// ),
// });

// userEvent.click(getByRole("button", { name: "Create a Data Submission" }));

// await waitFor(() => {
// expect(mockMatcher).toHaveBeenCalledTimes(1); // Ensure the listApprovedStudies query was called
// });
// }
// );

// it("should fetch all of the studies if the user's assigned studies contains the 'All' study", async () => {
// const mockMatcher = jest.fn().mockImplementation(() => true);
// const listApprovedStudiesMock: MockedResponse<
// ListApprovedStudiesResp,
// ListApprovedStudiesInput
// > = {
// request: {
// query: LIST_APPROVED_STUDIES,
// },
// variableMatcher: mockMatcher,
// result: {
// data: {
// listApprovedStudies: {
// total: 1,
// studies: [
// {
// _id: "study1",
// studyName: "study-1-from-api",
// studyAbbreviation: "study-1-from-api-abbr",
// dbGaPID: "",
// controlledAccess: false,
// },
// ] as ApprovedStudy[],
// },
// },
// },
// };

// const { getByRole } = render(<CreateDataSubmissionDialog onCreate={jest.fn()} />, {
// wrapper: (p) => (
// <TestParent
// mocks={[listApprovedStudiesMock]}
// authCtxState={{
// ...baseAuthCtx,
// user: {
// ...baseUser,
// role: "Federal Lead",
// studies: [
// {
// _id: "All", // This is the important part
// studyAbbreviation: "",
// studyName: "",
// dbGaPID: "",
// controlledAccess: false,
// },
// ],
// },
// }}
// {...p}
// />
// ),
// });

// userEvent.click(getByRole("button", { name: "Create a Data Submission" }));

// await waitFor(() => {
// expect(mockMatcher).toHaveBeenCalledTimes(1); // Ensure the listApprovedStudies query was called
// });
// });
it.each<UserRole>(["Data Commons Personnel"])(
"should fetch all of the studies if the user's role is %s",
async (role) => {
const mockMatcher = jest.fn().mockImplementation(() => true);
const listApprovedStudiesMock: MockedResponse<
ListApprovedStudiesResp,
ListApprovedStudiesInput
> = {
request: {
query: LIST_APPROVED_STUDIES,
},
variableMatcher: mockMatcher,
result: {
data: {
listApprovedStudies: {
total: 1,
studies: [
{
_id: "study1",
studyName: "study-1-from-api",
studyAbbreviation: "study-1-from-api-abbr",
dbGaPID: "",
controlledAccess: false,
},
{
_id: "study2",
studyName: "study-2-from-api",
studyAbbreviation: "study-2-from-api-abbr",
dbGaPID: "",
controlledAccess: false,
},
] as ApprovedStudy[],
},
},
},
};

const { getByRole } = render(<CreateDataSubmissionDialog onCreate={jest.fn()} />, {
wrapper: (p) => (
<TestParent
mocks={[listApprovedStudiesMock]}
authCtxState={{ ...baseAuthCtx, user: { ...baseUser, role } }}
{...p}
/>
),
});

userEvent.click(getByRole("button", { name: "Create a Data Submission" }));

await waitFor(() => {
expect(mockMatcher).toHaveBeenCalledTimes(1); // Ensure the listApprovedStudies query was called
});
}
);

it("should fetch all of the studies if the user's assigned studies contains the 'All' study", async () => {
const mockMatcher = jest.fn().mockImplementation(() => true);
const listApprovedStudiesMock: MockedResponse<
ListApprovedStudiesResp,
ListApprovedStudiesInput
> = {
request: {
query: LIST_APPROVED_STUDIES,
},
variableMatcher: mockMatcher,
result: {
data: {
listApprovedStudies: {
total: 1,
studies: [
{
_id: "study1",
studyName: "study-1-from-api",
studyAbbreviation: "study-1-from-api-abbr",
dbGaPID: "",
controlledAccess: false,
},
] as ApprovedStudy[],
},
},
},
};

const { getByRole } = render(<CreateDataSubmissionDialog onCreate={jest.fn()} />, {
wrapper: (p) => (
<TestParent
mocks={[listApprovedStudiesMock]}
authCtxState={{
...baseAuthCtx,
user: {
...baseUser,
role: "Federal Lead",
studies: [
{
_id: "All", // This is the important part
studyAbbreviation: "",
studyName: "",
dbGaPID: "",
controlledAccess: false,
},
],
},
}}
{...p}
/>
),
});

userEvent.click(getByRole("button", { name: "Create a Data Submission" }));

await waitFor(() => {
expect(mockMatcher).toHaveBeenCalledTimes(1); // Ensure the listApprovedStudies query was called
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -574,7 +574,7 @@ const CreateDataSubmissionDialog: FC<Props> = ({ onCreate }) => {
</StyledDialogActions>
</CreateSubmissionDialog>

{hasPermission(user, "data_submission", "create") && (
{hasPermission(user, "data_submission", "create", null, true) && (
<StyledTooltipWrapper alignItems="center" justifyContent="flex-end">
<StyledButton
type="button"
Expand Down
28 changes: 2 additions & 26 deletions src/components/DataSubmissions/DataUpload.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jest.mock("../../utils", () => ({

const baseSubmission: Omit<Submission, "_id"> = {
name: "",
submitterID: "",
submitterID: "current-user",
submitterName: "",
organization: undefined,
dataCommons: "",
Expand Down Expand Up @@ -340,31 +340,7 @@ describe("Implementation Requirements", () => {
expect(getByTestId("uploader-cli-config-button")).toBeDisabled();
});

it("should disable the Uploader CLI download button when collaborator does not have 'Can Edit' permissions", async () => {
const { getByTestId } = render(
<DataUpload
submission={{
...baseSubmission,
_id: "config-download-check",
dataType: "Metadata and Data Files", // NOTE: Required for the button to show
submitterID: "some-other-user",
collaborators: [
{
collaboratorID: "current-user",
collaboratorName: "",
Organization: null,
permission: "Can View",
},
],
}}
/>,
{ wrapper: (p) => <TestParent {...p} role="Submitter" /> }
);

expect(getByTestId("uploader-cli-config-button")).toBeDisabled();
});

it("should enable the Uploader CLI download button when collaborator does have 'Can Edit' permissions", async () => {
it("should enable the Uploader CLI download button when user is a collaborator", async () => {
const { getByTestId } = render(
<DataUpload
submission={{
Expand Down
9 changes: 2 additions & 7 deletions src/components/DataSubmissions/DataUpload.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,6 @@ export const DataUpload: FC<Props> = ({ submission }: Props) => {
context: { clientName: "backend" },
});

const collaborator = submission?.collaborators?.find((c) => c.collaboratorID === user?._id);

const handleConfigDownload = async ({ manifest, dataFolder }: InputForm) => {
try {
const { data, error } = await retrieveCLIConfig({
Expand Down Expand Up @@ -110,10 +108,7 @@ export const DataUpload: FC<Props> = ({ submission }: Props) => {

return (
<StyledDownloadButton
disabled={
(collaborator && collaborator.permission !== "Can Edit") ||
!hasPermission(user, "data_submission", "create")
}
disabled={!hasPermission(user, "data_submission", "create", submission)}
onClick={() => setConfigDialogOpen(true)}
variant="contained"
color="info"
Expand All @@ -122,7 +117,7 @@ export const DataUpload: FC<Props> = ({ submission }: Props) => {
Download Configuration File
</StyledDownloadButton>
);
}, [submission?.dataType, user?.role, collaborator]);
}, [submission, user]);

return (
<FlowWrapper index={2} title="Upload Data Files" actions={Actions}>
Expand Down
Loading

0 comments on commit 29463e1

Please sign in to comment.