Skip to content

Commit

Permalink
Support local IDs for overlay of MARC records.
Browse files Browse the repository at this point in the history
refs #3555
  • Loading branch information
justinlittman committed Sep 17, 2022
1 parent e38de3d commit 0e9debc
Show file tree
Hide file tree
Showing 18 changed files with 384 additions and 39 deletions.
55 changes: 55 additions & 0 deletions __tests__/__resource_fixtures__/local_admin_metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
[
{
"@id": "http://localhost:3000/resource/ae93cff4-d272-43b2-a4ee-fb8651907e51",
"@type": [
"http://sinopia.io/vocabulary/LocalAdminMetadata"
],
"http://id.loc.gov/ontologies/bibframe/identifier": [
{
"@id": "_:Nfe4b29df32004cc1b097a218f69df09f"
}
],
"http://sinopia.io/vocabulary/exportDate": [
{
"@value": "2022-08-01T15:49:44.558203"
}
],
"http://sinopia.io/vocabulary/hasResourceTemplate": [
{
"@value": "pcc:sinopia:localAdminMetadata"
}
],
"http://sinopia.io/vocabulary/localAdminMetadataFor": [
{
"@id": "http://localhost:3000/resource/a5c5f4c0-e7cd-4ca5-a20f-2a37fe1080d5"
}
]
},
{
"@id": "_:Nfe4b29df32004cc1b097a218f69df09f",
"@type": [
"http://id.loc.gov/ontologies/bibframe/Local"
],
"http://id.loc.gov/ontologies/bibframe/source": [
{
"@id": "_:Nf65f353d6fb64adeb6aa6040d21fb88c"
}
],
"http://www.w3.org/1999/02/22-rdf-syntax-ns#value": [
{
"@value": "13714202"
}
]
},
{
"@id": "_:Nf65f353d6fb64adeb6aa6040d21fb88c",
"@type": [
"http://id.loc.gov/ontologies/bibframe/Source"
],
"http://www.w3.org/2000/01/rdf-schema#label": [
{
"@value": "SIRSI"
}
]
}
]
7 changes: 4 additions & 3 deletions __tests__/actionCreators/transfer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
})
})
Expand All @@ -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", {
Expand Down
33 changes: 32 additions & 1 deletion __tests__/feature/editing/transfer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
})
Expand Down
23 changes: 21 additions & 2 deletions __tests__/sinopiaApi.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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", () => {
Expand Down
4 changes: 4 additions & 0 deletions __tests__/testUtilities/fixtureLoaderHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const resourceFilenames = {
"a5c5f4c0-e7cd-4ca5-a20f-2a37fe1080d5": "instance_with_refs.json",
"b6c5f4c0-e7cd-4ca5-a20f-2a37fe1080d6": "test-inputs.json",
"c7c5f4c0-e7cd-4ca5-a20f-2a37fe1080d7": "test-multiple_property_uris.json",
"ae93cff4-d272-43b2-a4ee-fb8651907e51": "local_admin_metadata.json",
}

const templateFilenames = {
Expand Down Expand Up @@ -201,5 +202,8 @@ export const getFixtureResourceRelationships = () => {
bfItemInferredRefs: [],
bfInstanceInferredRefs: [],
bfWorkInferredRefs: [],
sinopiaHasLocalAdminMetadataInferredRefs: [
"http://localhost:3000/resource/ae93cff4-d272-43b2-a4ee-fb8651907e51",
],
}
}
12 changes: 9 additions & 3 deletions src/Config.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,13 +140,19 @@ class Config {

static get transferConfig() {
return {
ils: {
SIRSI: {
// group: label
stanford: "Catalog",
stanford: "Symphony",
cornell: "Catalog",
},
FOLIO: {
stanford: "Folio",
cornell: "Catalog",
},
ALMA: {
penn: "Catalog",
},
// Can add additional transfer targets, e.g., discovery
// Can add additional transfer targets.
}
}

Expand Down
6 changes: 4 additions & 2 deletions src/actionCreators/relationships.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { fetchResourceRelationships } from "sinopiaApi"

/**
* A thunk that loads inferred relationships from the Sinopia API and adds to state.
* @return true if successful
* @return relationships if successful or false if not
*/
export const loadRelationships = (resourceKey, uri, errorKey) => (dispatch) => {
dispatch(clearErrors(errorKey))
Expand All @@ -15,12 +15,14 @@ export const loadRelationships = (resourceKey, uri, errorKey) => (dispatch) => {
dispatch(
setRelationships(resourceKey, {
bfAdminMetadataRefs: relationships.bfAdminMetadataInferredRefs,
sinopiaLocalAdminMetadataRefs:
relationships.sinopiaHasLocalAdminMetadataInferredRefs,
bfItemRefs: relationships.bfItemInferredRefs,
bfInstanceRefs: relationships.bfInstanceInferredRefs,
bfWorkRefs: relationships.bfWorkInferredRefs,
})
)
return true
return relationships
})
.catch((err) => {
console.error(err)
Expand Down
15 changes: 14 additions & 1 deletion src/actionCreators/resources.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import { addResourceHistory } from "actionCreators/history"
import _ from "lodash"
import { setCurrentComponent } from "actions/index"
import { loadRelationships } from "./relationships"
import { loadLocalIds } from "./transfer"

/**
* A thunk that loads an existing resource from Sinopia API and adds to state.
Expand Down Expand Up @@ -72,7 +73,19 @@ export const loadResource =
unusedDataset.size > 0 ? unusedDataset.toCanonical() : null
)
)
dispatch(loadRelationships(resource.key, uri, errorKey))
dispatch(loadRelationships(resource.key, uri, errorKey)).then(
(relationships) => {
if (relationships) {
dispatch(
loadLocalIds(
resource.key,
relationships.sinopiaHasLocalAdminMetadataInferredRefs,
errorKey
)
)
}
}
)
return [response, resource, unusedDataset]
})
.catch((err) => {
Expand Down
70 changes: 66 additions & 4 deletions src/actionCreators/transfer.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,75 @@
import { postTransfer } from "../sinopiaApi"
import { postTransfer, fetchResource } from "../sinopiaApi"
import { addError } from "actions/errors"
import { clearLocalIds, setLocalId } from "actions/transfer"
import rdf from "rdf-ext"

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 loadLocalIds =
(resourceKey, sinopiaLocalAdminMetadataRefs, errorKey) => (dispatch) => {
dispatch(clearLocalIds(resourceKey))
sinopiaLocalAdminMetadataRefs.forEach((resourceUri) => {
dispatch(fetchLocalId(resourceUri, errorKey)).then(
([target, group, localId]) => {
if (!target) {
return
}
dispatch(setLocalId(resourceKey, target, group, localId))
}
)
})
}

export const noop = () => {}
const fetchLocalId = (uri, errorKey) => (dispatch) =>
fetchResource(uri)
.then(([dataset, response]) => {
if (!dataset) return [false, false, false]
const identifierNode = identifierNodeFromDataset(uri, dataset)
if (!identifierNode) return [false, false, false]
const localId = localIdFromIdentifierNode(identifierNode, dataset)
const target = targetFromIdentifierNode(identifierNode, dataset)
return [target, response.group, localId]
})
.catch((err) => {
dispatch(
addError(errorKey, `Error retrieving ${uri}: ${err.message || err}`)
)
return [false, false, false]
})

const localIdFromIdentifierNode = (identifierNode, dataset) =>
dataset
.match(
identifierNode,
rdf.namedNode("http://www.w3.org/1999/02/22-rdf-syntax-ns#value")
)
.toArray()[0].object.value

const targetFromIdentifierNode = (identifierNode, dataset) => {
const sourceNode = dataset
.match(
identifierNode,
rdf.namedNode("http://id.loc.gov/ontologies/bibframe/source")
)
.toArray()[0].object
return dataset
.match(
sourceNode,
rdf.namedNode("http://www.w3.org/2000/01/rdf-schema#label")
)
.toArray()[0].object.value
}

const identifierNodeFromDataset = (uri, dataset) =>
dataset
.match(
rdf.namedNode(uri),
rdf.namedNode("http://id.loc.gov/ontologies/bibframe/identifier")
)
.toArray()[0].object
9 changes: 9 additions & 0 deletions src/actions/transfer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export const clearLocalIds = (resourceKey) => ({
type: "CLEAR_LOCAL_IDS",
payload: resourceKey,
})

export const setLocalId = (resourceKey, target, group, localId) => ({
type: "SET_LOCAL_ID",
payload: { resourceKey, target, group, localId },
})
Loading

0 comments on commit 0e9debc

Please sign in to comment.