diff --git a/sample-data/instance-source-records/failing/6b4ae089-e1ee-431f-af83-e1133f8e3da0.json b/sample-data/instance-source-records/6b4ae089-e1ee-431f-af83-e1133f8e3da0.json similarity index 100% rename from sample-data/instance-source-records/failing/6b4ae089-e1ee-431f-af83-e1133f8e3da0.json rename to sample-data/instance-source-records/6b4ae089-e1ee-431f-af83-e1133f8e3da0.json diff --git a/src/main/java/org/folio/rest/impl/InstanceStorageAPI.java b/src/main/java/org/folio/rest/impl/InstanceStorageAPI.java index 61f2ca658..c3e532b90 100644 --- a/src/main/java/org/folio/rest/impl/InstanceStorageAPI.java +++ b/src/main/java/org/folio/rest/impl/InstanceStorageAPI.java @@ -597,18 +597,15 @@ public void putInstanceStorageInstancesByInstanceIdSourceRecordMarcJson( PostgresClient postgresClient = PostgresClient.getInstance(vertxContext.owner(), TenantTool.tenantId(okapiHeaders)); - String sql = "INSERT INTO " + TenantTool.tenantId(okapiHeaders) + "_" + MODULE + "." - + INSTANCE_SOURCE_MARC_TABLE - + " (_id,jsonb)" - + " VALUES ('" + instanceId + "', '" + PostgresClient.pojo2json(entity) + "'::JSONB)"; - postgresClient.mutate(sql, reply -> { + postgresClient.upsert(INSTANCE_SOURCE_MARC_TABLE, instanceId, entity, reply -> { if (reply.succeeded()) { asyncResultHandler.handle(Future.succeededFuture( PutInstanceStorageInstancesByInstanceIdSourceRecordMarcJsonResponse.withNoContent())); return; } Map fields = PgExceptionUtil.getBadRequestFields(reply.cause()); - if (fields != null && "23503".equals(fields.get('C'))) { // foreign key constraint violation + if (fields != null && "23503".equals(fields.get('C')) // foreign key constraint violation + && INSTANCE_SOURCE_MARC_TABLE.equals(fields.get('t'))) { asyncResultHandler.handle(Future.succeededFuture( PutInstanceStorageInstancesByInstanceIdSourceRecordMarcJsonResponse .withPlainNotFound(reply.cause().getMessage()))); diff --git a/src/test/java/org/folio/rest/api/InstanceStorageTest.java b/src/test/java/org/folio/rest/api/InstanceStorageTest.java index 8814b573f..d3c49584d 100644 --- a/src/test/java/org/folio/rest/api/InstanceStorageTest.java +++ b/src/test/java/org/folio/rest/api/InstanceStorageTest.java @@ -10,6 +10,7 @@ import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; +import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.MalformedURLException; @@ -25,6 +26,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import org.apache.commons.io.IOUtils; import org.folio.HttpStatus; import org.folio.rest.jaxrs.model.MarcJson; import org.folio.rest.support.*; @@ -35,6 +37,7 @@ import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; +import org.junit.Ignore; import org.junit.Test; public class InstanceStorageTest extends TestBase { @@ -510,6 +513,15 @@ public void canProvideLargePageOffsetAndLimit() marcJson.setFields(fields); } + private MarcJson toMarcJson(String resourcePath) throws IOException { + String mrcjson = IOUtils.toString(this.getClass().getResourceAsStream(resourcePath), "UTF-8"); + JsonObject json = new JsonObject(mrcjson); + MarcJson newMarcJson = new MarcJson(); + newMarcJson.setLeader(json.getString("leader")); + newMarcJson.setFields(json.getJsonArray("fields").getList()); + return newMarcJson; + } + private Response put(UUID id, MarcJson marcJson, HttpStatus expectedStatus) throws Exception { CompletableFuture putCompleted = new CompletableFuture<>(); client.put(instancesStorageUrl("/" + id + "/source-record/marc-json"), marcJson, @@ -532,12 +544,19 @@ private String getSourceRecordFormat(UUID instanceId) throws Exception { return getResponse.getJson().getString("sourceRecordFormat"); } - private void getSourceNotFound(UUID id) throws Exception { + private Response getMarcJson(UUID id) throws Exception { + CompletableFuture getCompleted = new CompletableFuture<>(); + client.get(instancesStorageUrl("/" + id + "/source-record/marc-json"), + StorageTestSuite.TENANT_ID, ResponseHandler.json(getCompleted)); + return getCompleted.get(5, TimeUnit.SECONDS); + } + + private void getMarcJsonNotFound(UUID id) throws Exception { CompletableFuture getCompleted = new CompletableFuture<>(); client.get(instancesStorageUrl("/" + id + "/source-record/marc-json"), StorageTestSuite.TENANT_ID, ResponseHandler.text(getCompleted)); - Response getResponse = getCompleted.get(5, TimeUnit.SECONDS); - assertThat(getResponse.getStatusCode(), is(HttpStatus.HTTP_NOT_FOUND.toInt())); + Response response = getCompleted.get(5, TimeUnit.SECONDS); + assertThat(response.getStatusCode(), is(HttpStatus.HTTP_NOT_FOUND.toInt())); } @Test @@ -548,11 +567,7 @@ public void canCreateInstanceSourceRecord() throws Exception { put(id, marcJson); - // get MARC source record - CompletableFuture getCompleted = new CompletableFuture<>(); - client.get(instancesStorageUrl("/" + id + "/source-record/marc-json"), - StorageTestSuite.TENANT_ID, ResponseHandler.json(getCompleted)); - Response getResponse = getCompleted.get(5, TimeUnit.SECONDS); + Response getResponse = getMarcJson(id); assertThat(getResponse.getStatusCode(), is(200)); assertThat(getResponse.getJson().getString("leader"), is("xxxxxnam a22yyyyy c 4500")); JsonArray fields = getResponse.getJson().getJsonArray("fields"); @@ -562,9 +577,52 @@ public void canCreateInstanceSourceRecord() throws Exception { assertThat(getResponse.getJson().size(), is(2)); // leader and fields } + @Test // https://issues.folio.org/browse/MODINVSTOR-142?focusedCommentId=33665#comment-33665 + public void canCreateInstanceSourceRecord101073931X() throws Exception { + UUID id = UUID.randomUUID(); + JsonObject instance = smallAngryPlanet(id); + createInstance(instance); + + put(id, toMarcJson("/101073931X.mrcjson")); + + Response getResponse = getMarcJson(id); + assertThat(getResponse.getStatusCode(), is(200)); + JsonArray fields = getResponse.getJson().getJsonArray("fields"); + assertThat(fields.getJsonObject(0).getString("001"), is("101073931X")); + } + + @Test // https://issues.folio.org/browse/MODINVSTOR-143?focusedCommentId=33618#comment-33618 + public void canCreateInstanceSourceRecord1011273942() throws Exception { + UUID id = UUID.randomUUID(); + JsonObject instance = smallAngryPlanet(id); + createInstance(instance); + + put(id, toMarcJson("/1011273942.mrcjson")); + + Response getResponse = getMarcJson(id); + assertThat(getResponse.getStatusCode(), is(200)); + JsonArray fields = getResponse.getJson().getJsonArray("fields"); + assertThat(fields.getJsonObject(0).getString("001"), is("1011273942")); + } + + @Test + public void canUpdateInstanceSourceRecord() throws Exception { + UUID id = UUID.randomUUID(); + JsonObject instance = smallAngryPlanet(id); + createInstance(instance); + + put(id, marcJson); // create + put(id, toMarcJson("/101073931X.mrcjson")); // update + + Response getResponse = getMarcJson(id); + assertThat(getResponse.getStatusCode(), is(200)); + JsonArray fields = getResponse.getJson().getJsonArray("fields"); + assertThat(fields.getJsonObject(0).getString("001"), is("101073931X")); + } + @Test public void cannotGetNonExistingSourceRecord() throws Exception { - getSourceNotFound(UUID.randomUUID()); + getMarcJsonNotFound(UUID.randomUUID()); } @Test @@ -585,7 +643,7 @@ public void canDeleteInstanceSourceRecord() throws Exception { assertThat(deleteResponse.getStatusCode(), is(HttpStatus.HTTP_NO_CONTENT.toInt())); assertThat(getSourceRecordFormat(id), is(nullValue())); - getSourceNotFound(id); + getMarcJsonNotFound(id); } @Test @@ -603,7 +661,7 @@ public void canDeleteSourceRecordWhenDeletingInstance() throws Exception { Response deleteResponse = deleteCompleted.get(5, TimeUnit.SECONDS); assertThat(deleteResponse.getStatusCode(), is(HttpStatus.HTTP_NO_CONTENT.toInt())); - getSourceNotFound(id); + getMarcJsonNotFound(id); } @Test diff --git a/src/test/resources/101073931X.mrcjson b/src/test/resources/101073931X.mrcjson new file mode 100644 index 000000000..a72761440 --- /dev/null +++ b/src/test/resources/101073931X.mrcjson @@ -0,0 +1,134 @@ +{"leader":"01741nam a2200373 cb4500", +"fields":[{"001":"101073931X"}, +{"003":"DE-601"}, +{"005":"20180416162657.0"}, +{"008":"180111s2018\\\\\\\\sz\\\\\\\\\\\\\\\\\\\\\\\\000\\0\\eng\\d"}, +{"020":{"ind1":"\\", +"ind2":"\\", +"subfields":[{"a":"3319643991"}, +{"9":"3-319-64399-1"}]}}, +{"020":{"ind1":"\\", +"ind2":"\\", +"subfields":[{"a":"9783319643991"}, +{"9":"978-3-319-64399-1"}]}}, +{"020":{"ind1":"\\", +"ind2":"\\", +"subfields":[{"a":"9783319644004 (electronic)"}, +{"9":"978-3-319-64400-4"}]}}, +{"035":{"ind1":"\\", +"ind2":"\\", +"subfields":[{"a":"(OCoLC)ocn992783736"}]}}, +{"035":{"ind1":"\\", +"ind2":"\\", +"subfields":[{"a":"(OCoLC)992783736"}]}}, +{"035":{"ind1":"\\", +"ind2":"\\", +"subfields":[{"a":"(DE-599)GBV101073931X"}]}}, +{"040":{"ind1":"\\", +"ind2":"\\", +"subfields":[{"b":"ger"}, +{"c":"GBVCP"}, +{"e":"rda"}]}}, +{"041":{"ind1":"0", +"ind2":"\\", +"subfields":[{"a":"eng"}]}}, +{"245":{"ind1":"0", +"ind2":"0", +"subfields":[{"a":"Futures, biometrics and neuroscience research"}, +{"c":"Luiz Moutinho, Mladen Sokele, editors"}]}}, +{"264":{"ind1":"3", +"ind2":"1", +"subfields":[{"a":"Cham"}, +{"b":"Palgrave Macmillan"}, +{"c":"[2018]"}]}}, +{"300":{"ind1":"\\", +"ind2":"\\", +"subfields":[{"a":"xxix, 224 Seiten"}, +{"b":"Illustrationen"}]}}, +{"336":{"ind1":"\\", +"ind2":"\\", +"subfields":[{"a":"Text"}, +{"b":"txt"}, +{"2":"rdacontent"}]}}, +{"337":{"ind1":"\\", +"ind2":"\\", +"subfields":[{"a":"ohne Hilfsmittel zu benutzen"}, +{"b":"n"}, +{"2":"rdamedia"}]}}, +{"338":{"ind1":"\\", +"ind2":"\\", +"subfields":[{"a":"Band"}, +{"b":"nc"}, +{"2":"rdacarrier"}]}}, +{"490":{"ind1":"0", +"ind2":"\\", +"subfields":[{"a":"Innovative research methodologies in management"}, +{"v":" \/ Luiz Moutinho, Mladen Sokele ; Volume 2"}]}}, +{"500":{"ind1":"\\", +"ind2":"\\", +"subfields":[{"a":"Enthält 9 Beiträge"}]}}, +{"650":{"ind1":"\\", +"ind2":"7", +"subfields":[{"8":"1.1\\x"}, +{"a":"Betriebswirtschaftslehre"}, +{"0":"(DE-601)091351391"}, +{"0":"(DE-STW)12041-5"}, +{"2":"stw"}]}}, +{"650":{"ind1":"\\", +"ind2":"7", +"subfields":[{"8":"1.2\\x"}, +{"a":"Management"}, +{"0":"(DE-601)091376173"}, +{"0":"(DE-STW)12085-6"}, +{"2":"stw"}]}}, +{"650":{"ind1":"\\", +"ind2":"7", +"subfields":[{"8":"1.3\\x"}, +{"a":"Wissenschaftliche Methode"}, +{"0":"(DE-601)091401445"}, +{"0":"(DE-STW)16727-0"}, +{"2":"stw"}]}}, +{"700":{"ind1":"1", +"ind2":"\\", +"subfields":[{"a":"Moutinho, Luiz"}, +{"e":"HerausgeberIn"}, +{"4":"edt"}, +{"0":"(DE-601)509450954"}, +{"0":"(DE-588)131450204"}]}}, +{"700":{"ind1":"1", +"ind2":"\\", +"subfields":[{"a":"Sokele, Mladen"}, +{"e":"HerausgeberIn"}, +{"4":"edt"}]}}, +{"830":{"ind1":"\\", +"ind2":"0", +"subfields":[{"a":"Innovative research methodologies in management"}, +{"b":" \/ Luiz Moutinho, Mladen Sokele"}, +{"v":"Volume 2"}, +{"9":"2.2018"}, +{"w":"(DE-601)1011380293"}]}}, +{"856":{"ind1":"4", +"ind2":"2", +"subfields":[{"y":"Inhaltsverzeichnis"}, +{"u":"http:\/\/www.gbv.de\/dms\/zbw\/101073931X.pdf"}, +{"m":"V:DE-601;B:DE-206"}, +{"q":"application\/pdf"}, +{"3":"Inhaltsverzeichnis"}]}}, +{"900":{"ind1":"\\", +"ind2":"\\", +"subfields":[{"a":"GBV"}, +{"b":"ZBW Kiel <206>"}, +{"d":"!H:! A18-1775"}, +{"x":"L"}, +{"z":"LC"}, +{"s":"206\/1"}]}}, +{"954":{"ind1":"\\", +"ind2":"\\", +"subfields":[{"0":"ZBW Kiel <206>"}, +{"a":"26"}, +{"b":"1740761685"}, +{"c":"01"}, +{"f":"H:"}, +{"d":"A18-1775"}, +{"e":"u"}, +{"x":"206\/1"}]}}]} \ No newline at end of file diff --git a/src/test/resources/1011273942.mrcjson b/src/test/resources/1011273942.mrcjson new file mode 100644 index 000000000..2887b9300 --- /dev/null +++ b/src/test/resources/1011273942.mrcjson @@ -0,0 +1,111 @@ +{"leader":"01922nma a2200325 c 4500", +"fields":[{"001":"1011273942"}, +{"003":"DE-601"}, +{"005":"20180122150003.0"}, +{"007":"cu\\uuu---uuuuu"}, +{"008":"180122s2017\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\000\\0\\eng\\d"}, +{"020":{"ind1":"\\", +"ind2":"\\", +"subfields":[{"a":"9781450351430"}, +{"9":"978-1-4503-5143-0"}]}}, +{"035":{"ind1":"\\", +"ind2":"\\", +"subfields":[{"a":"(DE-599)GBV1011273942"}]}}, +{"040":{"ind1":"\\", +"ind2":"\\", +"subfields":[{"b":"ger"}, +{"c":"GBVCP"}, +{"e":"rda"}]}}, +{"041":{"ind1":"0", +"ind2":"\\", +"subfields":[{"a":"eng"}]}}, +{"110":{"ind1":"2", +"ind2":"\\", +"subfields":[{"a":"ACM Workshop on Millimeter Wave Networks and Sensing Systems"}, +{"n":"1."}, +{"d":"2017"}, +{"c":"Snowbird, Utah"}, +{"e":"VerfasserIn"}, +{"4":"aut"}]}}, +{"245":{"ind1":"0", +"ind2":"0", +"subfields":[{"a":"MobiCom'17"}, +{"n":"5"}, +{"p":"mmNets'17, October 16, 2017, Snowbird, UT, USA \/ general chairs: Haitham Hassanieh (University of Illinois at Urbana Champaign, USA), Xinyu Zhang (University of California San Diego, USA)"}]}}, +{"246":{"ind1":"3", +"ind2":"0", +"subfields":[{"a":"1st First ACM Workshop Millimeter Wave Networks Sensing Systems"}]}}, +{"336":{"ind1":"\\", +"ind2":"\\", +"subfields":[{"a":"Text"}, +{"b":"txt"}, +{"2":"rdacontent"}]}}, +{"337":{"ind1":"\\", +"ind2":"\\", +"subfields":[{"a":"Computermedien"}, +{"b":"c"}, +{"2":"rdamedia"}]}}, +{"338":{"ind1":"\\", +"ind2":"\\", +"subfields":[{"a":"Computerdisk"}, +{"b":"cd"}, +{"2":"rdacarrier"}]}}, +{"700":{"ind1":"1", +"ind2":"\\", +"subfields":[{"a":"Hassanieh, Haitham"}, +{"e":"VeranstalterIn"}, +{"4":"orm"}]}}, +{"700":{"ind1":"1", +"ind2":"\\", +"subfields":[{"a":"Zhang, Xinyu"}, +{"e":"VeranstalterIn"}, +{"4":"orm"}]}}, +{"710":{"ind1":"2", +"ind2":"\\", +"subfields":[{"a":"MobiCom"}, +{"n":"23."}, +{"d":"2017"}, +{"c":"Snowbird, Utah"}, +{"e":"VeranstalterIn"}, +{"4":"orm"}]}}, +{"710":{"ind1":"2", +"ind2":"\\", +"subfields":[{"a":"Association for Computing Machinery"}, +{"b":"Special Interest Group on Mobility of Systems Users, Data, and Computing"}, +{"e":"SponsorIn"}, +{"4":"spn"}, +{"0":"(DE-601)499677137"}, +{"0":"(DE-588)10113390-X"}]}}, +{"711":{"ind1":"2", +"ind2":"\\", +"subfields":[{"a":"ACM Workshop on Millimeter Wave Networks and Sensing Systems"}, +{"n":"1"}, +{"d":"2017.10.16"}, +{"c":"Snowbird, Utah"}]}}, +{"711":{"ind1":"2", +"ind2":"\\", +"subfields":[{"a":"mmNets"}, +{"n":"1"}, +{"d":"2017.10.16"}, +{"c":"Snowbird, Utah"}]}}, +{"711":{"ind1":"2", +"ind2":"\\", +"subfields":[{"a":"Annual International Conference on Mobile Computing and Networking (ACM MobiCom)"}, +{"n":"23"}, +{"d":"2017.10.16-20"}, +{"c":"Snowbird, Utah"}]}}, +{"773":{"ind1":"0", +"ind2":"\\", +"subfields":[{"w":"(DE-601)1011270897"}, +{"t":"MobiCom'17, MobiCom'17, proceedings and co-located workshops of the 23rd Annual International Conference on Mobile Computing and Networking : October 16-20, 2017, Snowbird, UT, USA, MobiCom, Snowbird, Utah. - New York, NY : ACM, Association for Computing Machinery"}]}}, +{"900":{"ind1":"\\", +"ind2":"\\", +"subfields":[{"a":"GBV"}, +{"b":"TIB\/UB Hannover <89>"}]}}, +{"954":{"ind1":"\\", +"ind2":"\\", +"subfields":[{"0":"TIB\/UB Hannover <89>"}, +{"a":"70"}, +{"b":"1743063695"}, +{"c":"01"}, +{"x":"0089"}]}}]} \ No newline at end of file