diff --git a/NEWS.md b/NEWS.md index 27a7f4fb7..bf365f805 100644 --- a/NEWS.md +++ b/NEWS.md @@ -20,6 +20,7 @@ * Disallow updating holdings ownership with related boundwith [MODINV-1051](https://folio-org.atlassian.net/browse/MODINV-1051) * Disallow updating ownership of boundwith item [MODINV-1052](https://folio-org.atlassian.net/browse/MODINV-1052) * InstanceIngress update events consumption [MODINV-1008](https://folio-org.atlassian.net/browse/MODINV-1008) +* Apply new date type fields to instance schema [MODINV-1067](https://folio-org.atlassian.net/browse/MODINV-1067) ## 20.2.0 2023-03-20 * Inventory cannot process Holdings with virtual fields ([MODINV-941](https://issues.folio.org/browse/MODINV-941)) diff --git a/descriptors/ModuleDescriptor-template.json b/descriptors/ModuleDescriptor-template.json index 5bc3ee80b..cea866adc 100644 --- a/descriptors/ModuleDescriptor-template.json +++ b/descriptors/ModuleDescriptor-template.json @@ -4,7 +4,7 @@ "provides": [ { "id": "inventory", - "version": "13.1", + "version": "13.2", "handlers": [ { "methods": ["GET"], @@ -602,7 +602,7 @@ }, { "id": "instance-storage", - "version": "10.0" + "version": "10.2" }, { "id": "instance-storage-batch", diff --git a/ramls/instance.json b/ramls/instance.json index d27ab0f00..ee7703fee 100644 --- a/ramls/instance.json +++ b/ramls/instance.json @@ -335,6 +335,27 @@ ] } }, + "dates": { + "type": "object", + "description": "Instance Dates", + "properties": { + "dateTypeId": { + "type": "string", + "description": "Date type ID", + "$ref": "uuid.json" + }, + "date1": { + "type": "string", + "description": "Date 1", + "maxLength": 4 + }, + "date2": { + "type": "string", + "description": "Date 2", + "maxLength": 4 + } + } + }, "instanceTypeId": { "type": "string", "description": "The unique term for the resource type whether it's from the RDA content term list of locally defined" diff --git a/src/main/java/org/folio/inventory/config/InventoryConfigurationImpl.java b/src/main/java/org/folio/inventory/config/InventoryConfigurationImpl.java index 6fbd93eb0..b750fb5ff 100644 --- a/src/main/java/org/folio/inventory/config/InventoryConfigurationImpl.java +++ b/src/main/java/org/folio/inventory/config/InventoryConfigurationImpl.java @@ -32,7 +32,8 @@ public class InventoryConfigurationImpl implements InventoryConfiguration { Instance.INSTANCE_TYPE_ID_KEY, Instance.MODE_OF_ISSUANCE_ID_KEY, Instance.PRECEDING_TITLES_KEY, - Instance.SUCCEEDING_TITLES_KEY + Instance.SUCCEEDING_TITLES_KEY, + Instance.DATES_KEY ); private static final Set HOLDINGS_BLOCKED_FIELDS = Sets.newHashSet( diff --git a/src/main/java/org/folio/inventory/domain/instances/Dates.java b/src/main/java/org/folio/inventory/domain/instances/Dates.java new file mode 100644 index 000000000..58ee18458 --- /dev/null +++ b/src/main/java/org/folio/inventory/domain/instances/Dates.java @@ -0,0 +1,42 @@ +package org.folio.inventory.domain.instances; + +import io.vertx.core.json.JsonObject; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import static org.apache.commons.lang3.ObjectUtils.anyNotNull; +import static org.folio.inventory.support.JsonHelper.includeIfPresent; + +@Getter +@AllArgsConstructor +public class Dates { + // JSON property names + public static final String DATE_TYPE_ID_KEY = "dateTypeId"; + public static final String DATE1_KEY = "date1"; + public static final String DATE2_KEY = "date2"; + + public final String dateTypeId; + public final String date1; + public final String date2; + + public static JsonObject datesToJson(Dates dates) { + if (dates == null || (dates.getDate1() == null && dates.getDate2() == null && dates.getDateTypeId() == null)) { + return null; + } + var json = new JsonObject(); + includeIfPresent(json, DATE_TYPE_ID_KEY, dates.getDateTypeId()); + includeIfPresent(json, DATE1_KEY, dates.getDate1()); + includeIfPresent(json, DATE2_KEY, dates.getDate2()); + return json; + } + + public static Dates datesFromJson(JsonObject datesJson) { + if (datesJson == null) { + return null; + } + var dateTypeId = datesJson.getString(DATE_TYPE_ID_KEY); + var date1 = datesJson.getString(DATE1_KEY); + var date2 = datesJson.getString(DATE2_KEY); + return anyNotNull(dateTypeId, date1, date2) ? new Dates(dateTypeId, date1, date2) : null; + } +} diff --git a/src/main/java/org/folio/inventory/domain/instances/Instance.java b/src/main/java/org/folio/inventory/domain/instances/Instance.java index bd92b3508..3efeba9df 100644 --- a/src/main/java/org/folio/inventory/domain/instances/Instance.java +++ b/src/main/java/org/folio/inventory/domain/instances/Instance.java @@ -1,5 +1,7 @@ package org.folio.inventory.domain.instances; +import static org.folio.inventory.domain.instances.Dates.datesFromJson; +import static org.folio.inventory.domain.instances.Dates.datesToJson; import static org.folio.inventory.domain.instances.PublicationPeriod.publicationPeriodFromJson; import static org.folio.inventory.domain.instances.PublicationPeriod.publicationPeriodToJson; import static org.folio.inventory.support.JsonArrayHelper.toListOfStrings; @@ -73,6 +75,7 @@ public class Instance { public static final String TAG_LIST_KEY = "tagList"; public static final String NATURE_OF_CONTENT_TERM_IDS_KEY = "natureOfContentTermIds"; public static final String PUBLICATION_PERIOD_KEY = "publicationPeriod"; + public static final String DATES_KEY = "dates"; private final String id; @JsonProperty("_version") @@ -117,6 +120,7 @@ public class Instance { private List tags; private List natureOfContentTermIds = new ArrayList<>(); private PublicationPeriod publicationPeriod; + private Dates dates; protected static final String INVENTORY_PATH = "/inventory"; protected static final String INSTANCES_PATH = INVENTORY_PATH + "/instances"; @@ -188,7 +192,8 @@ public static Instance fromJson(JsonObject instanceJson) { .setStatusUpdatedDate(instanceJson.getString(STATUS_UPDATED_DATE_KEY)) .setTags(getTags(instanceJson)) .setNatureOfContentTermIds(toListOfStrings(instanceJson.getJsonArray(NATURE_OF_CONTENT_TERM_IDS_KEY))) - .setPublicationPeriod(publicationPeriodFromJson(instanceJson.getJsonObject(PUBLICATION_PERIOD_KEY))); + .setPublicationPeriod(publicationPeriodFromJson(instanceJson.getJsonObject(PUBLICATION_PERIOD_KEY))) + .setDates(datesFromJson(instanceJson.getJsonObject(DATES_KEY))); } /** @@ -237,6 +242,7 @@ public JsonObject getJsonForStorage() { json.put(TAGS_KEY, new JsonObject().put(TAG_LIST_KEY, new JsonArray(getTags() == null ? Collections.emptyList() : getTags()))); json.put(NATURE_OF_CONTENT_TERM_IDS_KEY, natureOfContentTermIds); putIfNotNull(json, PUBLICATION_PERIOD_KEY, publicationPeriodToJson(publicationPeriod)); + putIfNotNull(json, DATES_KEY, datesToJson(dates)); return json; } @@ -290,6 +296,7 @@ public JsonObject getJsonForResponse(WebContext context) { putIfNotNull(json, TAGS_KEY, new JsonObject().put(TAG_LIST_KEY, new JsonArray(getTags()))); putIfNotNull(json, NATURE_OF_CONTENT_TERM_IDS_KEY, getNatureOfContentTermIds()); putIfNotNull(json, PUBLICATION_PERIOD_KEY, publicationPeriodToJson(publicationPeriod)); + putIfNotNull(json, DATES_KEY, datesToJson(dates)); if (precedingTitles != null) { JsonArray precedingTitlesJsonArray = new JsonArray(); @@ -461,6 +468,11 @@ public Instance setElectronicAccess (JsonArray array) { return this; } + public Instance setDates(Dates dates) { + this.dates = dates; + return this; + } + public Instance setInstanceFormatIds(List instanceFormatIds) { this.instanceFormatIds = instanceFormatIds; return this; @@ -747,7 +759,8 @@ public Instance copyWithNewId(String newId) { .setMetadata(metadata) .setTags(tags) .setNatureOfContentTermIds(natureOfContentTermIds) - .setPublicationPeriod(publicationPeriod); + .setPublicationPeriod(publicationPeriod) + .setDates(dates); } public Instance copyInstance() { @@ -781,7 +794,8 @@ public Instance copyInstance() { .setMetadata(metadata) .setTags(tags) .setNatureOfContentTermIds(natureOfContentTermIds) - .setPublicationPeriod(publicationPeriod); + .setPublicationPeriod(publicationPeriod) + .setDates(dates); } public Instance addIdentifier(Identifier identifier) { diff --git a/src/test/java/api/InstancesApiExamples.java b/src/test/java/api/InstancesApiExamples.java index c33a7bb76..a9c25c8de 100644 --- a/src/test/java/api/InstancesApiExamples.java +++ b/src/test/java/api/InstancesApiExamples.java @@ -12,6 +12,11 @@ import static io.vertx.core.http.HttpMethod.PUT; import static java.util.Arrays.asList; import static java.util.concurrent.TimeUnit.SECONDS; +import static org.folio.inventory.domain.instances.Dates.DATE_TYPE_ID_KEY; +import static org.folio.inventory.domain.instances.Dates.DATE1_KEY; +import static org.folio.inventory.domain.instances.Dates.DATE2_KEY; +import static org.folio.inventory.domain.instances.Dates.datesToJson; +import static org.folio.inventory.domain.instances.Instance.DATES_KEY; import static org.folio.inventory.domain.instances.Instance.PRECEDING_TITLES_KEY; import static org.folio.inventory.domain.instances.Instance.PUBLICATION_PERIOD_KEY; import static org.folio.inventory.domain.instances.Instance.TAGS_KEY; @@ -40,6 +45,7 @@ import org.folio.HttpStatus; import org.folio.inventory.config.InventoryConfiguration; import org.folio.inventory.config.InventoryConfigurationImpl; +import org.folio.inventory.domain.instances.Dates; import org.folio.inventory.domain.instances.PublicationPeriod; import org.folio.inventory.domain.instances.titles.PrecedingSucceedingTitle; import org.folio.inventory.support.JsonArrayHelper; @@ -63,6 +69,9 @@ public class InstancesApiExamples extends ApiTests { private static final InventoryConfiguration config = new InventoryConfigurationImpl(); private final String tagNameOne = "important"; private final String tagNameTwo = "very important"; + private final String dateTypeId = "0750f52b-3bfc-458d-9307-e9afc8bcdffa"; + private final String date1 = "2014"; + private final String date2 = "2016"; @After public void disableFailureEmulation() throws Exception { @@ -93,6 +102,7 @@ public void canCreateInstanceWithoutAnIDAndHRID() .put("instanceTypeId", ApiTestSuite.getTextInstanceType()) .put(TAGS_KEY, new JsonObject().put(TAG_LIST_KEY, new JsonArray().add(tagNameOne))) .put(PUBLICATION_PERIOD_KEY, publicationPeriodToJson(new PublicationPeriod(1000, 2000))) + .put(DATES_KEY, datesToJson(new Dates(dateTypeId, date1, date2))) .put("natureOfContentTermIds", new JsonArray(asList( ApiTestSuite.getAudiobookNatureOfContentTermId(), @@ -161,6 +171,11 @@ public void canCreateInstanceWithoutAnIDAndHRID() var publicationPeriod = createdInstance.getJsonObject(PUBLICATION_PERIOD_KEY); assertThat(publicationPeriod.getInteger("start"), is(1000)); assertThat(publicationPeriod.getInteger("end"), is(2000)); + + var dates = createdInstance.getJsonObject(DATES_KEY); + assertThat(dates.getString(DATE_TYPE_ID_KEY), is(dateTypeId)); + assertThat(dates.getString(DATE1_KEY), is(date1)); + assertThat(dates.getString(DATE2_KEY), is(date2)); } @Test @@ -392,6 +407,7 @@ public void canUpdateAnExistingInstance() smallAngryPlanet.put("natureOfContentTermIds", new JsonArray().add(ApiTestSuite.getBibliographyNatureOfContentTermId())); smallAngryPlanet.put(PUBLICATION_PERIOD_KEY, publicationPeriodToJson(new PublicationPeriod(1000, 2000))); + smallAngryPlanet.put(DATES_KEY, datesToJson(new Dates(null, date1, date2))); JsonObject newInstance = createInstance(smallAngryPlanet); @@ -399,6 +415,7 @@ public void canUpdateAnExistingInstance() .put("title", "The Long Way to a Small, Angry Planet") .put(TAGS_KEY, new JsonObject().put(TAG_LIST_KEY, new JsonArray().add(tagNameTwo))) .put(PUBLICATION_PERIOD_KEY, publicationPeriodToJson(new PublicationPeriod(2000, 2012))) + .put(DATES_KEY, datesToJson(new Dates(dateTypeId, date1, date2))) .put("natureOfContentTermIds", new JsonArray().add(ApiTestSuite.getAudiobookNatureOfContentTermId())); @@ -434,6 +451,11 @@ public void canUpdateAnExistingInstance() var publicationPeriod = updatedInstance.getJsonObject(PUBLICATION_PERIOD_KEY); assertThat(publicationPeriod.getInteger("start"), is(2000)); assertThat(publicationPeriod.getInteger("end"), is(2012)); + + var dates = updatedInstance.getJsonObject(DATES_KEY); + assertThat(dates.getString(DATE_TYPE_ID_KEY), is(dateTypeId)); + assertThat(dates.getString(DATE1_KEY), is(date1)); + assertThat(dates.getString(DATE2_KEY), is(date2)); } @Test @@ -612,7 +634,7 @@ public void canNotUpdateAnExistingMARCInstanceIfBlockedFieldsAreChangedToNulls() assertThat(errors.size(), is(1)); assertThat(errors.getJsonObject(0).getString("message"), is( "Instance is controlled by MARC record, these fields are blocked and can not be updated: " + - "physicalDescriptions,notes,languages,precedingTitles,identifiers,instanceTypeId,modeOfIssuanceId,subjects," + + "physicalDescriptions,notes,languages,precedingTitles,identifiers,instanceTypeId,modeOfIssuanceId,subjects,dates," + "source,title,indexTitle,publicationFrequency,electronicAccess,publicationRange," + "classifications,succeedingTitles,editions,hrid,series,instanceFormatIds,publication,contributors," + "alternativeTitles")); diff --git a/src/test/java/org/folio/inventory/domain/instances/DatesTest.java b/src/test/java/org/folio/inventory/domain/instances/DatesTest.java new file mode 100644 index 000000000..770ea1b40 --- /dev/null +++ b/src/test/java/org/folio/inventory/domain/instances/DatesTest.java @@ -0,0 +1,74 @@ +package org.folio.inventory.domain.instances; + +import io.vertx.core.json.JsonObject; +import junitparams.JUnitParamsRunner; +import junitparams.Parameters; +import junitparams.converters.Nullable; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.folio.inventory.domain.instances.Dates.datesFromJson; +import static org.folio.inventory.domain.instances.Dates.datesToJson; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; + +@RunWith(JUnitParamsRunner.class) +public class DatesTest { + + @Parameters({ + "1, 1990, 2002", + "1, 1990, null", + "1, null, 2022", + "null, 1990, 2002", + "null, 1990, null", + }) + @Test + public void shouldCreateDatesFromJson(@Nullable String dateTypeId, @Nullable String date1, @Nullable String date2) { + var dates = datesFromJson(datesJson(dateTypeId, date1, date2)); + + assertThat(dates.getDateTypeId(), is(dateTypeId)); + assertThat(dates.getDate1(), is(date1)); + assertThat(dates.getDate2(), is(date2)); + } + + @Test + public void shouldNotCreateDatesFromJsonWhenJsonIsNull() { + assertThat(datesFromJson(null), nullValue()); + } + + @Test + public void shouldNotCreateDatesFromJsonWhenAllFieldsAreNull() { + assertThat(datesFromJson(datesJson(null, null, null)), nullValue()); + } + + @Parameters({ + "1, 1990, 2002", + "1, 1990, null", + "1, null, 2022", + "null, 1990, 2002", + "null, 1990, null", + }) + @Test + public void shouldConvertDatesToJson(@Nullable String dateTypeId, @Nullable String date1, @Nullable String date2) { + var json = datesToJson(new Dates(dateTypeId, date1, date2)); + + assertThat(json.getString("dateTypeId"), is(dateTypeId)); + assertThat(json.getString("date1"), is(date1)); + assertThat(json.getString("date2"), is(date2)); + } + + @Test + public void shouldNotConvertDatesToJsonWhenItIsNull() { + assertThat(datesToJson(null), nullValue()); + } + + @Test + public void shouldNotConvertDatesToJsonWhenAllFieldsAreNull() { + assertThat(datesToJson(new Dates(null, null, null)), nullValue()); + } + + private JsonObject datesJson(String dateTypeId, String date1, String date2) { + return new JsonObject().put("dateTypeId", dateTypeId).put("date1", date1).put("date2", date2); + } +}