From 2b1c610344b65fbac9c7cca80142301d6654d952 Mon Sep 17 00:00:00 2001 From: Justin Littman Date: Mon, 8 Aug 2022 14:18:08 -0700 Subject: [PATCH] Support local IDs for overlay of MARC records. refs #3555 --- __tests__/actionCreators/transfer.test.js | 7 +- __tests__/feature/editing/transfer.test.js | 33 ++++- __tests__/sinopiaApi.test.js | 23 +++- src/actionCreators/transfer.js | 5 +- .../editor/actions/TransferButton.jsx | 113 +++++++++++++++--- .../editor/actions/TransferButtons.jsx | 9 +- src/sinopiaApi.js | 10 +- 7 files changed, 168 insertions(+), 32 deletions(-) diff --git a/__tests__/actionCreators/transfer.test.js b/__tests__/actionCreators/transfer.test.js index 63a026df8..b5f0448d4 100644 --- a/__tests__/actionCreators/transfer.test.js +++ b/__tests__/actionCreators/transfer.test.js @@ -16,14 +16,15 @@ describe("transfer", () => { sinopiaApi.postTransfer = jest.fn().mockResolvedValue() const store = mockStore(createState()) await store.dispatch( - transfer(resourceUri, "stanford", "ils", "testerrorkey") + transfer(resourceUri, "stanford", "ils", "abc123", "testerrorkey") ) expect(store.getActions()).toHaveLength(0) expect(sinopiaApi.postTransfer).toHaveBeenCalledWith( resourceUri, "stanford", - "ils" + "ils", + "abc123" ) }) }) @@ -32,7 +33,7 @@ describe("transfer", () => { sinopiaApi.postTransfer = jest.fn().mockRejectedValue("Ooops!") const store = mockStore(createState()) await store.dispatch( - transfer(resourceUri, "stanford", "ils", "testerrorkey") + transfer(resourceUri, "stanford", "ils", "abc123", "testerrorkey") ) expect(store.getActions()).toHaveAction("ADD_ERROR", { diff --git a/__tests__/feature/editing/transfer.test.js b/__tests__/feature/editing/transfer.test.js index fb5dee059..bd4b009ea 100644 --- a/__tests__/feature/editing/transfer.test.js +++ b/__tests__/feature/editing/transfer.test.js @@ -20,7 +20,7 @@ jest.spyOn(Config, "transferConfig", "get").mockReturnValue({ }, }) -describe("transfer saved bf:Instance when user belongs to a transfer group", () => { +describe("transfer saved bf:Instance when user belongs to a transfer group and no local ID", () => { it("allows transfer", async () => { const state = createState() const store = createStore(state) @@ -42,6 +42,37 @@ describe("transfer saved bf:Instance when user belongs to a transfer group", () const transferBtn = screen.getByText("Export to Catalog") fireEvent.click(transferBtn) + fireEvent.click(await screen.findByText("Create a new record in catalog.")) + await screen.findByText("Requesting") + }, 15000) +}) + +describe("transfer saved bf:Instance when user belongs to a transfer group and provided local ID", () => { + it("allows transfer", async () => { + const state = createState() + const store = createStore(state) + renderApp(store) + + fireEvent.click(screen.getByText("Linked Data Editor", { selector: "a" })) + + fireEvent.change(screen.getByLabelText("Search"), { + target: { value: bfUri }, + }) + fireEvent.click(screen.getByTestId("Submit search")) + + await screen.findByText(bfUri) + fireEvent.click(screen.getByRole("button", { name: `Edit ${bfUri}` })) + + await screen.findByText("The Practitioner's Guide to Graph Data", { + selector: resourceHeaderSelector, + }) + + const transferBtn = screen.getByText("Export to Catalog") + fireEvent.click(transferBtn) + fireEvent.change(await screen.findByLabelText("Enter local system id"), { + target: { value: "abc123" }, + }) + fireEvent.click(await screen.findByText("Go")) await screen.findByText("Requesting") }, 15000) }) diff --git a/__tests__/sinopiaApi.test.js b/__tests__/sinopiaApi.test.js index 548f19d81..eb3ba9171 100644 --- a/__tests__/sinopiaApi.test.js +++ b/__tests__/sinopiaApi.test.js @@ -431,13 +431,13 @@ describe("putUserHistory", () => { }) describe("postTransfer", () => { - describe("success", () => { + describe("success without localId", () => { it("returns", async () => { global.fetch = jest.fn().mockResolvedValue({ ok: true, }) - await postTransfer(resourceUri, "stanford", "ils") + await postTransfer(resourceUri, "stanford", "ils", null) expect(global.fetch).toHaveBeenCalledWith( "https://api.development.sinopia.io/transfer/7b4c275d-b0c7-40a4-80b3-e95a0d9d987c/stanford/ils", @@ -450,6 +450,25 @@ describe("postTransfer", () => { ) }) }) + describe("success with localId", () => { + it("returns", async () => { + global.fetch = jest.fn().mockResolvedValue({ + ok: true, + }) + + await postTransfer(resourceUri, "stanford", "ils", "abc123") + + expect(global.fetch).toHaveBeenCalledWith( + "https://api.development.sinopia.io/transfer/7b4c275d-b0c7-40a4-80b3-e95a0d9d987c/stanford/ils/abc123", + { + method: "POST", + headers: { + Authorization: "Bearer Secret-Token", + }, + } + ) + }) + }) }) describe("fetchResourceRelationships", () => { diff --git a/src/actionCreators/transfer.js b/src/actionCreators/transfer.js index 333e683f8..d67740c60 100644 --- a/src/actionCreators/transfer.js +++ b/src/actionCreators/transfer.js @@ -2,12 +2,11 @@ import { postTransfer } from "../sinopiaApi" import { addError } from "actions/errors" export const transfer = - (resourceUri, group, target, errorKey) => (dispatch) => { - postTransfer(resourceUri, group, target).catch((err) => { + (resourceUri, group, target, localId, errorKey) => (dispatch) => + postTransfer(resourceUri, group, target, localId).catch((err) => { dispatch( addError(errorKey, `Error requesting transfer: ${err.message || err}`) ) }) - } export const noop = () => {} diff --git a/src/components/editor/actions/TransferButton.jsx b/src/components/editor/actions/TransferButton.jsx index 1d5a8936f..d1a5a32c1 100644 --- a/src/components/editor/actions/TransferButton.jsx +++ b/src/components/editor/actions/TransferButton.jsx @@ -1,8 +1,10 @@ import React, { useState, useEffect, useRef } from "react" import PropTypes from "prop-types" +import _ from "lodash" -const TransferButton = ({ label, handleClick }) => { - const [btnText, setBtnText] = useState(label) +const TransferButton = ({ label, localId, group, target, handleTransfer }) => { + const [requesting, setRequesting] = useState(false) + const [providedLocalId, setProvidedLocalId] = useState("") const timerRef = useRef(null) useEffect( @@ -12,27 +14,110 @@ const TransferButton = ({ label, handleClick }) => { [] ) - const handleBtnClick = (event) => { - setBtnText(Requesting) - timerRef.current = setTimeout(() => setBtnText(label), 3000) - handleClick(event) + const handleExistingLocalIdClick = (event) => { + handleTransfer(localId) + notify() event.preventDefault() } + const handleProvidedLocalIdClick = (event) => { + handleTransfer(providedLocalId) + notify() + event.preventDefault() + } + + const handleNoLocalIdClick = (event) => { + handleTransfer(null) + notify() + event.preventDefault() + } + + const notify = () => { + setRequesting(true) + timerRef.current = setTimeout(() => setRequesting(false), 3000) + } + + const handleChangeProvidedLocalId = (event) => { + setProvidedLocalId(event.target.value) + event.preventDefault() + } + + if (requesting) { + return ( + + ) + } + + const btnId = `transferBtn-${group}-${target}` + const btnClasses = ["btn", "dropdown-toggle", "btn-no-outline"] + const dropDownItemBtnClasses = ["btn", "btn-secondary", "dropdown-item"] + return ( - +
+ +
+ {localId ? ( + + + + ) : ( + +
+ Overlay record with local system ID: +
+ + +
+
+ +
+ )} +
+
) } TransferButton.propTypes = { label: PropTypes.string.isRequired, - handleClick: PropTypes.func.isRequired, + group: PropTypes.string.isRequired, + target: PropTypes.string.isRequired, + localId: PropTypes.string, + handleTransfer: PropTypes.func.isRequired, } export default TransferButton diff --git a/src/components/editor/actions/TransferButtons.jsx b/src/components/editor/actions/TransferButtons.jsx index 9d74f8144..690edce53 100644 --- a/src/components/editor/actions/TransferButtons.jsx +++ b/src/components/editor/actions/TransferButtons.jsx @@ -35,16 +35,17 @@ const TransferButtons = ({ resourceKey }) => { // Must be targets if (_.isEmpty(transferTargets)) return null - const handleClick = (event, group, target) => { - dispatch(transfer(resource.uri, group, target, errorKey)) - event.preventDefault() + const handleTransfer = (group, target, localId) => { + dispatch(transfer(resource.uri, group, target, localId, errorKey)) } const buttons = transferTargets.map(([target, group, label]) => ( handleClick(event, group, target)} + handleTransfer={(localId) => handleTransfer(group, target, localId)} /> )) diff --git a/src/sinopiaApi.js b/src/sinopiaApi.js index 2c03e12b9..adfa0a845 100644 --- a/src/sinopiaApi.js +++ b/src/sinopiaApi.js @@ -212,11 +212,11 @@ export const putUserHistory = ( ) } -export const postTransfer = (resourceUri, group, target) => { - const url = `${resourceUri.replace( - "resource", - "transfer" - )}/${group}/${target}` +export const postTransfer = (resourceUri, group, target, localId) => { + let url = `${resourceUri.replace("resource", "transfer")}/${group}/${target}` + if (localId) { + url += `/${localId}` + } return getJwt() .then((jwt) => fetch(url, {