From e679d79ee7bc60c464903de3cb5ac7730aaa0766 Mon Sep 17 00:00:00 2001 From: Siarhei Charniak Date: Wed, 27 Dec 2023 15:14:20 +0300 Subject: [PATCH] MODBULKOPS-179 - Rendering Instance record data in bulk edit forms and files --- .../bulkops/client/InstanceFormatsClient.java | 20 ++ .../client/InstanceStatusesClient.java | 20 ++ .../bulkops/client/InstanceTypesClient.java | 20 ++ .../bulkops/client/ModesOfIssuanceClient.java | 20 ++ .../client/NatureOfContentTermsClient.java | 20 ++ .../folio/bulkops/domain/bean/Instance.java | 197 ++++++++++++++++++ .../bulkops/domain/bean/InstanceFormat.java | 22 ++ .../bulkops/domain/bean/InstanceFormats.java | 27 +++ .../bulkops/domain/bean/InstanceStatus.java | 22 ++ .../bulkops/domain/bean/InstanceStatuses.java | 27 +++ .../bulkops/domain/bean/InstanceType.java | 22 ++ .../bulkops/domain/bean/InstanceTypes.java | 27 +++ .../bulkops/domain/bean/ModeOfIssuance.java | 22 ++ .../bulkops/domain/bean/ModesOfIssuance.java | 27 +++ .../domain/bean/NatureOfContentTerm.java | 22 ++ .../domain/bean/NatureOfContentTerms.java | 27 +++ .../org/folio/bulkops/domain/bean/Series.java | 22 ++ .../InstanceFormatListConverter.java | 38 ++++ .../converter/InstanceStatusConverter.java | 16 ++ .../converter/InstanceTypeConverter.java | 16 ++ .../converter/ModeOfIssuanceConverter.java | 16 ++ .../NatureOfContentTermListConverter.java | 38 ++++ .../domain/converter/SeriesListConverter.java | 36 ++++ .../converter/StringListPipedConverter.java | 30 +++ .../service/InstanceReferenceHelper.java | 64 ++++++ .../service/InstanceReferenceService.java | 119 +++++++++++ .../java/org/folio/bulkops/util/Utils.java | 2 + .../db/changelog/changelog-master.xml | 1 + .../27-12-2023_add_types_for_instance.sql | 3 + .../27-12-2023_add_types_for_instance.xml | 13 ++ .../swagger.api/schemas/entity_type.json | 3 +- .../swagger.api/schemas/identifier_type.json | 4 +- src/test/java/org/folio/bulkops/BaseTest.java | 15 ++ .../service/BulkOperationServiceTest.java | 5 +- .../bulkops/service/OpenCSVConverterTest.java | 61 +++++- .../resources/files/bad_data_instance.json | 82 ++++++++ src/test/resources/files/instance.json | 102 +++++++++ .../resources/files/instances_preview.csv | 6 + 38 files changed, 1227 insertions(+), 7 deletions(-) create mode 100644 src/main/java/org/folio/bulkops/client/InstanceFormatsClient.java create mode 100644 src/main/java/org/folio/bulkops/client/InstanceStatusesClient.java create mode 100644 src/main/java/org/folio/bulkops/client/InstanceTypesClient.java create mode 100644 src/main/java/org/folio/bulkops/client/ModesOfIssuanceClient.java create mode 100644 src/main/java/org/folio/bulkops/client/NatureOfContentTermsClient.java create mode 100644 src/main/java/org/folio/bulkops/domain/bean/Instance.java create mode 100644 src/main/java/org/folio/bulkops/domain/bean/InstanceFormat.java create mode 100644 src/main/java/org/folio/bulkops/domain/bean/InstanceFormats.java create mode 100644 src/main/java/org/folio/bulkops/domain/bean/InstanceStatus.java create mode 100644 src/main/java/org/folio/bulkops/domain/bean/InstanceStatuses.java create mode 100644 src/main/java/org/folio/bulkops/domain/bean/InstanceType.java create mode 100644 src/main/java/org/folio/bulkops/domain/bean/InstanceTypes.java create mode 100644 src/main/java/org/folio/bulkops/domain/bean/ModeOfIssuance.java create mode 100644 src/main/java/org/folio/bulkops/domain/bean/ModesOfIssuance.java create mode 100644 src/main/java/org/folio/bulkops/domain/bean/NatureOfContentTerm.java create mode 100644 src/main/java/org/folio/bulkops/domain/bean/NatureOfContentTerms.java create mode 100644 src/main/java/org/folio/bulkops/domain/bean/Series.java create mode 100644 src/main/java/org/folio/bulkops/domain/converter/InstanceFormatListConverter.java create mode 100644 src/main/java/org/folio/bulkops/domain/converter/InstanceStatusConverter.java create mode 100644 src/main/java/org/folio/bulkops/domain/converter/InstanceTypeConverter.java create mode 100644 src/main/java/org/folio/bulkops/domain/converter/ModeOfIssuanceConverter.java create mode 100644 src/main/java/org/folio/bulkops/domain/converter/NatureOfContentTermListConverter.java create mode 100644 src/main/java/org/folio/bulkops/domain/converter/SeriesListConverter.java create mode 100644 src/main/java/org/folio/bulkops/domain/converter/StringListPipedConverter.java create mode 100644 src/main/java/org/folio/bulkops/service/InstanceReferenceHelper.java create mode 100644 src/main/java/org/folio/bulkops/service/InstanceReferenceService.java create mode 100644 src/main/resources/db/changelog/changes/27-12-2023_add_types_for_instance.sql create mode 100644 src/main/resources/db/changelog/changes/27-12-2023_add_types_for_instance.xml create mode 100644 src/test/resources/files/bad_data_instance.json create mode 100644 src/test/resources/files/instance.json create mode 100644 src/test/resources/files/instances_preview.csv diff --git a/src/main/java/org/folio/bulkops/client/InstanceFormatsClient.java b/src/main/java/org/folio/bulkops/client/InstanceFormatsClient.java new file mode 100644 index 00000000..2eba6c31 --- /dev/null +++ b/src/main/java/org/folio/bulkops/client/InstanceFormatsClient.java @@ -0,0 +1,20 @@ +package org.folio.bulkops.client; + +import org.folio.bulkops.configs.FeignClientConfiguration; +import org.folio.bulkops.domain.bean.InstanceFormat; +import org.folio.bulkops.domain.bean.InstanceFormats; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestParam; + +@FeignClient(name = "instance-formats", configuration = FeignClientConfiguration.class) +public interface InstanceFormatsClient { + @GetMapping(value = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE) + InstanceFormat getById(@PathVariable String id); + + @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE) + InstanceFormats getByQuery(@RequestParam String query, @RequestParam long limit); + +} diff --git a/src/main/java/org/folio/bulkops/client/InstanceStatusesClient.java b/src/main/java/org/folio/bulkops/client/InstanceStatusesClient.java new file mode 100644 index 00000000..17ecc771 --- /dev/null +++ b/src/main/java/org/folio/bulkops/client/InstanceStatusesClient.java @@ -0,0 +1,20 @@ +package org.folio.bulkops.client; + +import org.folio.bulkops.configs.FeignClientConfiguration; +import org.folio.bulkops.domain.bean.InstanceStatus; +import org.folio.bulkops.domain.bean.InstanceStatuses; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestParam; + +@FeignClient(name = "instance-statuses", configuration = FeignClientConfiguration.class) +public interface InstanceStatusesClient { + @GetMapping(value = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE) + InstanceStatus getById(@PathVariable String id); + + @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE) + InstanceStatuses getByQuery(@RequestParam String query, @RequestParam long limit); + +} diff --git a/src/main/java/org/folio/bulkops/client/InstanceTypesClient.java b/src/main/java/org/folio/bulkops/client/InstanceTypesClient.java new file mode 100644 index 00000000..440ffc62 --- /dev/null +++ b/src/main/java/org/folio/bulkops/client/InstanceTypesClient.java @@ -0,0 +1,20 @@ +package org.folio.bulkops.client; + +import org.folio.bulkops.configs.FeignClientConfiguration; +import org.folio.bulkops.domain.bean.InstanceType; +import org.folio.bulkops.domain.bean.InstanceTypes; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestParam; + +@FeignClient(name = "instance-types", configuration = FeignClientConfiguration.class) +public interface InstanceTypesClient { + @GetMapping(value = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE) + InstanceType getById(@PathVariable String id); + + @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE) + InstanceTypes getByQuery(@RequestParam String query, @RequestParam long limit); + +} diff --git a/src/main/java/org/folio/bulkops/client/ModesOfIssuanceClient.java b/src/main/java/org/folio/bulkops/client/ModesOfIssuanceClient.java new file mode 100644 index 00000000..66cbb36e --- /dev/null +++ b/src/main/java/org/folio/bulkops/client/ModesOfIssuanceClient.java @@ -0,0 +1,20 @@ +package org.folio.bulkops.client; + +import org.folio.bulkops.configs.FeignClientConfiguration; +import org.folio.bulkops.domain.bean.ModeOfIssuance; +import org.folio.bulkops.domain.bean.ModesOfIssuance; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestParam; + +@FeignClient(name = "modes-of-issuance", configuration = FeignClientConfiguration.class) +public interface ModesOfIssuanceClient { + @GetMapping(value = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE) + ModeOfIssuance getById(@PathVariable String id); + + @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE) + ModesOfIssuance getByQuery(@RequestParam String query, @RequestParam long limit); + +} diff --git a/src/main/java/org/folio/bulkops/client/NatureOfContentTermsClient.java b/src/main/java/org/folio/bulkops/client/NatureOfContentTermsClient.java new file mode 100644 index 00000000..81901d7d --- /dev/null +++ b/src/main/java/org/folio/bulkops/client/NatureOfContentTermsClient.java @@ -0,0 +1,20 @@ +package org.folio.bulkops.client; + +import org.folio.bulkops.configs.FeignClientConfiguration; +import org.folio.bulkops.domain.bean.NatureOfContentTerm; +import org.folio.bulkops.domain.bean.NatureOfContentTerms; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestParam; + +@FeignClient(name = "nature-of-content-terms", configuration = FeignClientConfiguration.class) +public interface NatureOfContentTermsClient { + @GetMapping(value = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE) + NatureOfContentTerm getById(@PathVariable String id); + + @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE) + NatureOfContentTerms getByQuery(@RequestParam String query, @RequestParam long limit); + +} diff --git a/src/main/java/org/folio/bulkops/domain/bean/Instance.java b/src/main/java/org/folio/bulkops/domain/bean/Instance.java new file mode 100644 index 00000000..792a2ab2 --- /dev/null +++ b/src/main/java/org/folio/bulkops/domain/bean/Instance.java @@ -0,0 +1,197 @@ +package org.folio.bulkops.domain.bean; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.opencsv.bean.CsvCustomBindByName; +import com.opencsv.bean.CsvCustomBindByPosition; +import jakarta.validation.Valid; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.With; +import org.folio.bulkops.domain.converter.BooleanConverter; +import org.folio.bulkops.domain.converter.ContributorListConverter; +import org.folio.bulkops.domain.converter.DateWithoutTimeConverter; +import org.folio.bulkops.domain.converter.InstanceFormatListConverter; +import org.folio.bulkops.domain.converter.InstanceStatusConverter; +import org.folio.bulkops.domain.converter.InstanceTypeConverter; +import org.folio.bulkops.domain.converter.ModeOfIssuanceConverter; +import org.folio.bulkops.domain.converter.NatureOfContentTermListConverter; +import org.folio.bulkops.domain.converter.SeriesListConverter; +import org.folio.bulkops.domain.converter.StringConverter; +import org.folio.bulkops.domain.converter.StringListPipedConverter; +import org.folio.bulkops.domain.dto.DataType; +import org.folio.bulkops.domain.dto.IdentifierType; + +import java.util.Date; +import java.util.List; + +@Data +@With +@Builder(toBuilder = true) +@NoArgsConstructor +@AllArgsConstructor +@JsonTypeName("instance") +@EqualsAndHashCode(exclude = "version") +public class Instance implements BulkOperationsEntity { + @JsonProperty("id") + @CsvCustomBindByName(column = "Instance UUID", converter = StringConverter.class) + @CsvCustomBindByPosition(position = 0, converter = StringConverter.class) + @UnifiedTableCell(visible = false) + private String id; + + @JsonProperty("_version") + private Integer version; + + @JsonProperty("discoverySuppress") + @CsvCustomBindByName(column = "Suppress from discovery", converter = BooleanConverter.class) + @CsvCustomBindByPosition(position = 1, converter = BooleanConverter.class) + @UnifiedTableCell + private Boolean discoverySuppress; + + @JsonProperty("staffSuppress") + @CsvCustomBindByName(column = "Staff suppress", converter = BooleanConverter.class) + @CsvCustomBindByPosition(position = 2, converter = BooleanConverter.class) + @UnifiedTableCell(visible = false) + private Boolean staffSuppress; + + @JsonProperty("previouslyHeld") + @CsvCustomBindByName(column = "Previously held", converter = BooleanConverter.class) + @CsvCustomBindByPosition(position = 3, converter = BooleanConverter.class) + @UnifiedTableCell(visible = false) + private Boolean previouslyHeld; + + @JsonProperty("hrid") + @CsvCustomBindByName(column = "Instance HRID", converter = StringConverter.class) + @CsvCustomBindByPosition(position = 4, converter = StringConverter.class) + @UnifiedTableCell + private String hrid; + + @JsonProperty("source") + @CsvCustomBindByName(column = "Source", converter = StringConverter.class) + @CsvCustomBindByPosition(position = 5, converter = StringConverter.class) + @UnifiedTableCell(visible = false) + private String source; + + @JsonProperty("catalogedDate") + @CsvCustomBindByName(column = "Cataloged date", converter = DateWithoutTimeConverter.class) + @CsvCustomBindByPosition(position = 6, converter = DateWithoutTimeConverter.class) + @UnifiedTableCell(dataType = DataType.DATE_TIME, visible = false) + private Date catalogedDate; + + @JsonProperty("statusId") + @CsvCustomBindByName(column = "Instance status term", converter = InstanceStatusConverter.class) + @CsvCustomBindByPosition(position = 7, converter = InstanceStatusConverter.class) + @UnifiedTableCell + private String statusId; + + @JsonProperty("modeOfIssuanceId") + @CsvCustomBindByName(column = "Mode of issuance", converter = ModeOfIssuanceConverter.class) + @CsvCustomBindByPosition(position = 8, converter = ModeOfIssuanceConverter.class) + @UnifiedTableCell(visible = false) + private String modeOfIssuanceId; + + @JsonProperty("administrativeNotes") + @CsvCustomBindByName(column = "Administrative note", converter = StringListPipedConverter.class) + @CsvCustomBindByPosition(position = 9, converter = StringListPipedConverter.class) + @UnifiedTableCell(visible = false) + private List administrativeNotes; + + @JsonProperty("title") + @CsvCustomBindByName(column = "Resource title", converter = StringConverter.class) + @CsvCustomBindByPosition(position = 10, converter = StringConverter.class) + @UnifiedTableCell + private String title; + + @JsonProperty("indexTitle") + @CsvCustomBindByName(column = "Index title", converter = StringConverter.class) + @CsvCustomBindByPosition(position = 11, converter = StringConverter.class) + @UnifiedTableCell(visible = false) + private String indexTitle; + + @JsonProperty("series") + @CsvCustomBindByName(column = "Series statements", converter = SeriesListConverter.class) + @CsvCustomBindByPosition(position = 12, converter = SeriesListConverter.class) + @UnifiedTableCell(visible = false) + private List series; + + @JsonProperty("contributors") + @CsvCustomBindByName(column = "Contributors", converter = ContributorListConverter.class) + @CsvCustomBindByPosition(position = 13, converter = ContributorListConverter.class) + @UnifiedTableCell + private List contributors; + + @JsonProperty("editions") + @CsvCustomBindByName(column = "Edition", converter = StringListPipedConverter.class) + @CsvCustomBindByPosition(position = 14, converter = StringListPipedConverter.class) + @UnifiedTableCell(visible = false) + private List editions; + + @JsonProperty("physicalDescriptions") + @CsvCustomBindByName(column = "Physical description", converter = StringListPipedConverter.class) + @CsvCustomBindByPosition(position = 15, converter = StringListPipedConverter.class) + @UnifiedTableCell(visible = false) + private List physicalDescriptions; + + @JsonProperty("instanceTypeId") + @CsvCustomBindByName(column = "Resource type", converter = InstanceTypeConverter.class) + @CsvCustomBindByPosition(position = 16, converter = InstanceTypeConverter.class) + @UnifiedTableCell + private String instanceTypeId; + + @JsonProperty("natureOfContentTermIds") + @CsvCustomBindByName(column = "Nature of content", converter = NatureOfContentTermListConverter.class) + @CsvCustomBindByPosition(position = 17, converter = NatureOfContentTermListConverter.class) + @UnifiedTableCell(visible = false) + private List natureOfContentTermIds; + + @JsonProperty("instanceFormatIds") + @CsvCustomBindByName(column = "Formats", converter = InstanceFormatListConverter.class) + @CsvCustomBindByPosition(position = 18, converter = InstanceFormatListConverter.class) + @UnifiedTableCell(visible = false) + private List instanceFormatIds; + + @JsonProperty("languages") + @CsvCustomBindByName(column = "Languages", converter = StringListPipedConverter.class) + @CsvCustomBindByPosition(position = 19, converter = StringListPipedConverter.class) + @UnifiedTableCell(visible = false) + private List languages; + + @JsonProperty("publicationFrequency") + @Valid + @CsvCustomBindByName(column = "Publication frequency", converter = StringListPipedConverter.class) + @CsvCustomBindByPosition(position = 20, converter = StringListPipedConverter.class) + @UnifiedTableCell(visible = false) + private List publicationFrequency; + + @JsonProperty("publicationRange") + @CsvCustomBindByName(column = "Publication range", converter = StringListPipedConverter.class) + @CsvCustomBindByPosition(position = 21, converter = StringListPipedConverter.class) + @UnifiedTableCell(visible = false) + private List publicationRange; + + @JsonIgnore + @CsvCustomBindByName(column = "ISBN", converter = StringConverter.class) + @CsvCustomBindByPosition(position = 22, converter = StringConverter.class) + @UnifiedTableCell + private String isbn; + + @JsonIgnore + @CsvCustomBindByName(column = "ISSN", converter = StringConverter.class) + @CsvCustomBindByPosition(position = 23, converter = StringConverter.class) + @UnifiedTableCell + private String issn; + + @Override + public String getIdentifier(IdentifierType identifierType) { + return switch (identifierType) { + case HRID -> hrid; + case ISBN -> isbn; + case ISSN -> issn; + default -> id; + }; + } +} diff --git a/src/main/java/org/folio/bulkops/domain/bean/InstanceFormat.java b/src/main/java/org/folio/bulkops/domain/bean/InstanceFormat.java new file mode 100644 index 00000000..402c6d76 --- /dev/null +++ b/src/main/java/org/folio/bulkops/domain/bean/InstanceFormat.java @@ -0,0 +1,22 @@ +package org.folio.bulkops.domain.bean; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.With; + +@Data +@With +@Builder(toBuilder = true) +@NoArgsConstructor +@AllArgsConstructor +public class InstanceFormat { + @JsonProperty("id") + private String id; + + @JsonProperty("name") + private String name; +} + diff --git a/src/main/java/org/folio/bulkops/domain/bean/InstanceFormats.java b/src/main/java/org/folio/bulkops/domain/bean/InstanceFormats.java new file mode 100644 index 00000000..25047094 --- /dev/null +++ b/src/main/java/org/folio/bulkops/domain/bean/InstanceFormats.java @@ -0,0 +1,27 @@ +package org.folio.bulkops.domain.bean; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.With; + +import java.util.ArrayList; +import java.util.List; + +@Data +@With +@Builder(toBuilder = true) +@NoArgsConstructor +@AllArgsConstructor +public class InstanceFormats { + @JsonProperty("instanceFormats") + @Valid + private List formats = new ArrayList<>(); + + @JsonProperty("totalRecords") + private Integer totalRecords; +} + diff --git a/src/main/java/org/folio/bulkops/domain/bean/InstanceStatus.java b/src/main/java/org/folio/bulkops/domain/bean/InstanceStatus.java new file mode 100644 index 00000000..9f086f61 --- /dev/null +++ b/src/main/java/org/folio/bulkops/domain/bean/InstanceStatus.java @@ -0,0 +1,22 @@ +package org.folio.bulkops.domain.bean; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.With; + +@Data +@With +@Builder(toBuilder = true) +@NoArgsConstructor +@AllArgsConstructor +public class InstanceStatus { + @JsonProperty("id") + private String id; + + @JsonProperty("name") + private String name; +} + diff --git a/src/main/java/org/folio/bulkops/domain/bean/InstanceStatuses.java b/src/main/java/org/folio/bulkops/domain/bean/InstanceStatuses.java new file mode 100644 index 00000000..452ed941 --- /dev/null +++ b/src/main/java/org/folio/bulkops/domain/bean/InstanceStatuses.java @@ -0,0 +1,27 @@ +package org.folio.bulkops.domain.bean; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.With; + +import java.util.ArrayList; +import java.util.List; + +@Data +@With +@Builder(toBuilder = true) +@NoArgsConstructor +@AllArgsConstructor +public class InstanceStatuses { + @JsonProperty("instanceStatuses") + @Valid + private List statuses = new ArrayList<>(); + + @JsonProperty("totalRecords") + private Integer totalRecords; +} + diff --git a/src/main/java/org/folio/bulkops/domain/bean/InstanceType.java b/src/main/java/org/folio/bulkops/domain/bean/InstanceType.java new file mode 100644 index 00000000..0239c2be --- /dev/null +++ b/src/main/java/org/folio/bulkops/domain/bean/InstanceType.java @@ -0,0 +1,22 @@ +package org.folio.bulkops.domain.bean; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.With; + +@Data +@With +@Builder(toBuilder = true) +@NoArgsConstructor +@AllArgsConstructor +public class InstanceType { + @JsonProperty("id") + private String id; + + @JsonProperty("name") + private String name; +} + diff --git a/src/main/java/org/folio/bulkops/domain/bean/InstanceTypes.java b/src/main/java/org/folio/bulkops/domain/bean/InstanceTypes.java new file mode 100644 index 00000000..7025c2fa --- /dev/null +++ b/src/main/java/org/folio/bulkops/domain/bean/InstanceTypes.java @@ -0,0 +1,27 @@ +package org.folio.bulkops.domain.bean; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.With; + +import java.util.ArrayList; +import java.util.List; + +@Data +@With +@Builder(toBuilder = true) +@NoArgsConstructor +@AllArgsConstructor +public class InstanceTypes { + @JsonProperty("instanceTypes") + @Valid + private List types = new ArrayList<>(); + + @JsonProperty("totalRecords") + private Integer totalRecords; +} + diff --git a/src/main/java/org/folio/bulkops/domain/bean/ModeOfIssuance.java b/src/main/java/org/folio/bulkops/domain/bean/ModeOfIssuance.java new file mode 100644 index 00000000..d2d76917 --- /dev/null +++ b/src/main/java/org/folio/bulkops/domain/bean/ModeOfIssuance.java @@ -0,0 +1,22 @@ +package org.folio.bulkops.domain.bean; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.With; + +@Data +@With +@Builder(toBuilder = true) +@NoArgsConstructor +@AllArgsConstructor +public class ModeOfIssuance { + @JsonProperty("id") + private String id; + + @JsonProperty("name") + private String name; +} + diff --git a/src/main/java/org/folio/bulkops/domain/bean/ModesOfIssuance.java b/src/main/java/org/folio/bulkops/domain/bean/ModesOfIssuance.java new file mode 100644 index 00000000..60ea8d1d --- /dev/null +++ b/src/main/java/org/folio/bulkops/domain/bean/ModesOfIssuance.java @@ -0,0 +1,27 @@ +package org.folio.bulkops.domain.bean; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.With; + +import java.util.ArrayList; +import java.util.List; + +@Data +@With +@Builder(toBuilder = true) +@NoArgsConstructor +@AllArgsConstructor +public class ModesOfIssuance { + @JsonProperty("issuanceModes") + @Valid + private List modes = new ArrayList<>(); + + @JsonProperty("totalRecords") + private Integer totalRecords; +} + diff --git a/src/main/java/org/folio/bulkops/domain/bean/NatureOfContentTerm.java b/src/main/java/org/folio/bulkops/domain/bean/NatureOfContentTerm.java new file mode 100644 index 00000000..1288b223 --- /dev/null +++ b/src/main/java/org/folio/bulkops/domain/bean/NatureOfContentTerm.java @@ -0,0 +1,22 @@ +package org.folio.bulkops.domain.bean; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.With; + +@Data +@With +@Builder(toBuilder = true) +@NoArgsConstructor +@AllArgsConstructor +public class NatureOfContentTerm { + @JsonProperty("id") + private String id; + + @JsonProperty("name") + private String name; +} + diff --git a/src/main/java/org/folio/bulkops/domain/bean/NatureOfContentTerms.java b/src/main/java/org/folio/bulkops/domain/bean/NatureOfContentTerms.java new file mode 100644 index 00000000..3daa2676 --- /dev/null +++ b/src/main/java/org/folio/bulkops/domain/bean/NatureOfContentTerms.java @@ -0,0 +1,27 @@ +package org.folio.bulkops.domain.bean; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.With; + +import java.util.ArrayList; +import java.util.List; + +@Data +@With +@Builder(toBuilder = true) +@NoArgsConstructor +@AllArgsConstructor +public class NatureOfContentTerms { + @JsonProperty("natureOfContentTerms") + @Valid + private List terms = new ArrayList<>(); + + @JsonProperty("totalRecords") + private Integer totalRecords; +} + diff --git a/src/main/java/org/folio/bulkops/domain/bean/Series.java b/src/main/java/org/folio/bulkops/domain/bean/Series.java new file mode 100644 index 00000000..32ad5694 --- /dev/null +++ b/src/main/java/org/folio/bulkops/domain/bean/Series.java @@ -0,0 +1,22 @@ +package org.folio.bulkops.domain.bean; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.With; + +@Data +@With +@Builder(toBuilder = true) +@NoArgsConstructor +@AllArgsConstructor +public class Series { + @JsonProperty("value") + private String value; + + @JsonProperty("authorityId") + private String authorityId; +} + diff --git a/src/main/java/org/folio/bulkops/domain/converter/InstanceFormatListConverter.java b/src/main/java/org/folio/bulkops/domain/converter/InstanceFormatListConverter.java new file mode 100644 index 00000000..a9b3363f --- /dev/null +++ b/src/main/java/org/folio/bulkops/domain/converter/InstanceFormatListConverter.java @@ -0,0 +1,38 @@ +package org.folio.bulkops.domain.converter; + +import static org.folio.bulkops.util.Constants.ITEM_DELIMITER_PATTERN; +import static org.folio.bulkops.util.Constants.ITEM_DELIMITER_SPACED; + +import org.apache.commons.lang3.StringUtils; +import org.folio.bulkops.domain.format.SpecialCharacterEscaper; +import org.folio.bulkops.service.InstanceReferenceHelper; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +public class InstanceFormatListConverter extends BaseConverter> { + + + @Override + public List convertToObject(String value) { + return StringUtils.isEmpty(value) ? + Collections.emptyList() : + Arrays.stream(value.split(ITEM_DELIMITER_PATTERN)) + .map(String::trim) + .map(SpecialCharacterEscaper::restore) + .map(InstanceReferenceHelper.service()::getInstanceFormatIdByName) + .toList(); + } + + @Override + public String convertToString(List object) { + return object.stream() + .filter(Objects::nonNull) + .map(InstanceReferenceHelper.service()::getInstanceFormatNameById) + .map(SpecialCharacterEscaper::escape) + .collect(Collectors.joining(ITEM_DELIMITER_SPACED)); + } +} diff --git a/src/main/java/org/folio/bulkops/domain/converter/InstanceStatusConverter.java b/src/main/java/org/folio/bulkops/domain/converter/InstanceStatusConverter.java new file mode 100644 index 00000000..aa027748 --- /dev/null +++ b/src/main/java/org/folio/bulkops/domain/converter/InstanceStatusConverter.java @@ -0,0 +1,16 @@ +package org.folio.bulkops.domain.converter; + +import org.folio.bulkops.service.InstanceReferenceHelper; + +public class InstanceStatusConverter extends BaseConverter { + + @Override + public String convertToObject(String value) { + return InstanceReferenceHelper.service().getInstanceStatusIdByName(value); + } + + @Override + public String convertToString(String object) { + return InstanceReferenceHelper.service().getInstanceStatusNameById(object); + } +} diff --git a/src/main/java/org/folio/bulkops/domain/converter/InstanceTypeConverter.java b/src/main/java/org/folio/bulkops/domain/converter/InstanceTypeConverter.java new file mode 100644 index 00000000..8f36c22b --- /dev/null +++ b/src/main/java/org/folio/bulkops/domain/converter/InstanceTypeConverter.java @@ -0,0 +1,16 @@ +package org.folio.bulkops.domain.converter; + +import org.folio.bulkops.service.InstanceReferenceHelper; + +public class InstanceTypeConverter extends BaseConverter { + + @Override + public String convertToObject(String value) { + return InstanceReferenceHelper.service().getInstanceTypeIdByName(value); + } + + @Override + public String convertToString(String object) { + return InstanceReferenceHelper.service().getInstanceTypeNameById(object); + } +} diff --git a/src/main/java/org/folio/bulkops/domain/converter/ModeOfIssuanceConverter.java b/src/main/java/org/folio/bulkops/domain/converter/ModeOfIssuanceConverter.java new file mode 100644 index 00000000..915974dc --- /dev/null +++ b/src/main/java/org/folio/bulkops/domain/converter/ModeOfIssuanceConverter.java @@ -0,0 +1,16 @@ +package org.folio.bulkops.domain.converter; + +import org.folio.bulkops.service.InstanceReferenceHelper; + +public class ModeOfIssuanceConverter extends BaseConverter { + + @Override + public String convertToObject(String value) { + return InstanceReferenceHelper.service().getModeOfIssuanceIdByName(value); + } + + @Override + public String convertToString(String object) { + return InstanceReferenceHelper.service().getModeOfIssuanceNameById(object); + } +} diff --git a/src/main/java/org/folio/bulkops/domain/converter/NatureOfContentTermListConverter.java b/src/main/java/org/folio/bulkops/domain/converter/NatureOfContentTermListConverter.java new file mode 100644 index 00000000..e33963d3 --- /dev/null +++ b/src/main/java/org/folio/bulkops/domain/converter/NatureOfContentTermListConverter.java @@ -0,0 +1,38 @@ +package org.folio.bulkops.domain.converter; + +import static org.folio.bulkops.util.Constants.ITEM_DELIMITER_PATTERN; +import static org.folio.bulkops.util.Constants.ITEM_DELIMITER_SPACED; + +import org.apache.commons.lang3.StringUtils; +import org.folio.bulkops.domain.format.SpecialCharacterEscaper; +import org.folio.bulkops.service.InstanceReferenceHelper; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +public class NatureOfContentTermListConverter extends BaseConverter> { + + + @Override + public List convertToObject(String value) { + return StringUtils.isEmpty(value) ? + Collections.emptyList() : + Arrays.stream(value.split(ITEM_DELIMITER_PATTERN)) + .map(String::trim) + .map(SpecialCharacterEscaper::restore) + .map(InstanceReferenceHelper.service()::getNatureOfContentTermIdByName) + .toList(); + } + + @Override + public String convertToString(List object) { + return object.stream() + .filter(Objects::nonNull) + .map(InstanceReferenceHelper.service()::getNatureOfContentTermNameById) + .map(SpecialCharacterEscaper::escape) + .collect(Collectors.joining(ITEM_DELIMITER_SPACED)); + } +} diff --git a/src/main/java/org/folio/bulkops/domain/converter/SeriesListConverter.java b/src/main/java/org/folio/bulkops/domain/converter/SeriesListConverter.java new file mode 100644 index 00000000..67532cda --- /dev/null +++ b/src/main/java/org/folio/bulkops/domain/converter/SeriesListConverter.java @@ -0,0 +1,36 @@ +package org.folio.bulkops.domain.converter; + +import static org.folio.bulkops.domain.format.SpecialCharacterEscaper.restore; +import static org.folio.bulkops.util.Constants.ARRAY_DELIMITER; + +import org.apache.commons.lang3.StringUtils; +import org.folio.bulkops.domain.bean.Series; +import org.folio.bulkops.domain.format.SpecialCharacterEscaper; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +public class SeriesListConverter extends BaseConverter> { + + + @Override + public List convertToObject(String value) { + return StringUtils.isEmpty(value) ? + Collections.emptyList() : + Arrays.stream(value.split(ARRAY_DELIMITER)) + .map(val -> Series.builder().value(restore(val)).build()) + .toList(); + } + + @Override + public String convertToString(List object) { + return object.stream() + .filter(Objects::nonNull) + .map(Series::getValue) + .map(SpecialCharacterEscaper::escape) + .collect(Collectors.joining(ARRAY_DELIMITER)); + } +} diff --git a/src/main/java/org/folio/bulkops/domain/converter/StringListPipedConverter.java b/src/main/java/org/folio/bulkops/domain/converter/StringListPipedConverter.java new file mode 100644 index 00000000..725a1697 --- /dev/null +++ b/src/main/java/org/folio/bulkops/domain/converter/StringListPipedConverter.java @@ -0,0 +1,30 @@ +package org.folio.bulkops.domain.converter; + +import static org.folio.bulkops.util.Constants.ITEM_DELIMITER_PATTERN; +import static org.folio.bulkops.util.Constants.ITEM_DELIMITER_SPACED; + +import org.folio.bulkops.domain.format.SpecialCharacterEscaper; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +public class StringListPipedConverter extends BaseConverter> { + + + @Override + public List convertToObject(String value) { + return SpecialCharacterEscaper.restore(Arrays.stream(value.split(ITEM_DELIMITER_PATTERN)) + .map(String::trim) + .toList()); + } + + @Override + public String convertToString(List object) { + return object.stream() + .filter(Objects::nonNull) + .map(SpecialCharacterEscaper::escape) + .collect(Collectors.joining(ITEM_DELIMITER_SPACED)); + } +} diff --git a/src/main/java/org/folio/bulkops/service/InstanceReferenceHelper.java b/src/main/java/org/folio/bulkops/service/InstanceReferenceHelper.java new file mode 100644 index 00000000..e9c4713c --- /dev/null +++ b/src/main/java/org/folio/bulkops/service/InstanceReferenceHelper.java @@ -0,0 +1,64 @@ +package org.folio.bulkops.service; + +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.stereotype.Service; + +@Service +@Log4j2 +@RequiredArgsConstructor +public class InstanceReferenceHelper implements InitializingBean { + private final InstanceReferenceService instanceReferenceService; + + public String getInstanceStatusNameById(String id) { + return instanceReferenceService.getInstanceStatusNameById(id); + } + + public String getInstanceStatusIdByName(String name) { + return instanceReferenceService.getInstanceStatusIdByName(name); + } + + public String getModeOfIssuanceNameById(String id) { + return instanceReferenceService.getModeOfIssuanceNameById(id); + } + + public String getModeOfIssuanceIdByName(String name) { + return instanceReferenceService.getModeOfIssuanceIdByName(name); + } + + public String getInstanceTypeNameById(String id) { + return instanceReferenceService.getInstanceTypeNameById(id); + } + + public String getInstanceTypeIdByName(String name) { + return instanceReferenceService.getInstanceTypeIdByName(name); + } + + public String getNatureOfContentTermNameById(String id) { + return instanceReferenceService.getNatureOfContentTermNameById(id); + } + + public String getNatureOfContentTermIdByName(String name) { + return instanceReferenceService.getNatureOfContentTermIdByName(name); + } + + public String getInstanceFormatNameById(String id) { + return instanceReferenceService.getInstanceFormatNameById(id); + } + + public String getInstanceFormatIdByName(String name) { + return instanceReferenceService.getInstanceFormatIdByName(name); + } + + private static InstanceReferenceHelper service; + + @Override + public void afterPropertiesSet() { + service = this; + } + + public static InstanceReferenceHelper service() { + return service; + } +} diff --git a/src/main/java/org/folio/bulkops/service/InstanceReferenceService.java b/src/main/java/org/folio/bulkops/service/InstanceReferenceService.java new file mode 100644 index 00000000..4bfb6fd2 --- /dev/null +++ b/src/main/java/org/folio/bulkops/service/InstanceReferenceService.java @@ -0,0 +1,119 @@ +package org.folio.bulkops.service; + +import static java.lang.String.format; +import static org.apache.commons.lang3.ObjectUtils.isEmpty; +import static org.apache.commons.lang3.StringUtils.EMPTY; +import static org.folio.bulkops.util.Constants.QUERY_PATTERN_NAME; +import static org.folio.bulkops.util.Utils.encode; + +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.folio.bulkops.client.InstanceFormatsClient; +import org.folio.bulkops.client.InstanceStatusesClient; +import org.folio.bulkops.client.InstanceTypesClient; +import org.folio.bulkops.client.ModesOfIssuanceClient; +import org.folio.bulkops.client.NatureOfContentTermsClient; +import org.folio.bulkops.exception.NotFoundException; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; + +@Service +@Log4j2 +@RequiredArgsConstructor +public class InstanceReferenceService { + private final InstanceStatusesClient instanceStatusesClient; + private final ModesOfIssuanceClient modesOfIssuanceClient; + private final InstanceTypesClient instanceTypesClient; + private final NatureOfContentTermsClient natureOfContentTermsClient; + private final InstanceFormatsClient instanceFormatsClient; + + @Cacheable(cacheNames = "instanceStatusNames") + public String getInstanceStatusNameById(String id) { + try { + return isEmpty(id) ? EMPTY : instanceStatusesClient.getById(id).getName(); + } catch (Exception e) { + throw new NotFoundException(format("Instance status was not found by id=%s", id)); + } + } + + @Cacheable(cacheNames = "instanceStatusIds") + public String getInstanceStatusIdByName(String name) { + var response = instanceStatusesClient.getByQuery(String.format(QUERY_PATTERN_NAME, encode(name)), 1); + if (response.getStatuses().isEmpty()) { + throw new NotFoundException(format("Instance status was not found by name=%s", name)); + } + return response.getStatuses().get(0).getId(); + } + + @Cacheable(cacheNames = "modesOfIssuanceNames") + public String getModeOfIssuanceNameById(String id) { + try { + return isEmpty(id) ? EMPTY : modesOfIssuanceClient.getById(id).getName(); + } catch (Exception e) { + throw new NotFoundException(format("Mode of issuance was not found by id=%s", id)); + } + } + + @Cacheable(cacheNames = "modesOfIssuanceIds") + public String getModeOfIssuanceIdByName(String name) { + var response = modesOfIssuanceClient.getByQuery(String.format(QUERY_PATTERN_NAME, encode(name)), 1); + if (response.getModes().isEmpty()) { + throw new NotFoundException(format("Mode of issuance was not found by name=%s", name)); + } + return response.getModes().get(0).getId(); + } + + @Cacheable(cacheNames = "instanceTypeNames") + public String getInstanceTypeNameById(String id) { + try { + return isEmpty(id) ? EMPTY : instanceTypesClient.getById(id).getName(); + } catch (Exception e) { + throw new NotFoundException(format("Instance type was not found by id=%s", id)); + } + } + + @Cacheable(cacheNames = "instanceTypeIds") + public String getInstanceTypeIdByName(String name) { + var response = instanceTypesClient.getByQuery(String.format(QUERY_PATTERN_NAME, encode(name)), 1); + if (response.getTypes().isEmpty()) { + throw new NotFoundException(format("Instance type was not found by name=%s", name)); + } + return response.getTypes().get(0).getId(); + } + + @Cacheable(cacheNames = "natureOfContentTermNames") + public String getNatureOfContentTermNameById(String id) { + try { + return isEmpty(id) ? EMPTY : natureOfContentTermsClient.getById(id).getName(); + } catch (Exception e) { + throw new NotFoundException(format("Nature of content term was not found by id=%s", id)); + } + } + + @Cacheable(cacheNames = "natureOfContentTermIds") + public String getNatureOfContentTermIdByName(String name) { + var response = natureOfContentTermsClient.getByQuery(String.format(QUERY_PATTERN_NAME, encode(name)), 1); + if (response.getTerms().isEmpty()) { + throw new NotFoundException(format("Nature of content term was not found by name=%s", name)); + } + return response.getTerms().get(0).getId(); + } + + @Cacheable(cacheNames = "instanceFormatNames") + public String getInstanceFormatNameById(String id) { + try { + return isEmpty(id) ? EMPTY : instanceFormatsClient.getById(id).getName(); + } catch (Exception e) { + throw new NotFoundException(format("Nature of content term was not found by id=%s", id)); + } + } + + @Cacheable(cacheNames = "instanceFormatIds") + public String getInstanceFormatIdByName(String name) { + var response = instanceFormatsClient.getByQuery(String.format(QUERY_PATTERN_NAME, encode(name)), 1); + if (response.getFormats().isEmpty()) { + throw new NotFoundException(format("Nature of content term was not found by name=%s", name)); + } + return response.getFormats().get(0).getId(); + } +} diff --git a/src/main/java/org/folio/bulkops/util/Utils.java b/src/main/java/org/folio/bulkops/util/Utils.java index 3bbbe523..140a9903 100644 --- a/src/main/java/org/folio/bulkops/util/Utils.java +++ b/src/main/java/org/folio/bulkops/util/Utils.java @@ -4,6 +4,7 @@ import org.apache.commons.lang3.StringUtils; import org.folio.bulkops.domain.bean.BulkOperationsEntity; import org.folio.bulkops.domain.bean.HoldingsRecord; +import org.folio.bulkops.domain.bean.Instance; import org.folio.bulkops.domain.bean.Item; import org.folio.bulkops.domain.bean.User; import org.folio.bulkops.domain.dto.EntityType; @@ -40,6 +41,7 @@ public static String encode(CharSequence s) { public static Class resolveEntityClass(EntityType clazz) { return switch (clazz) { case USER -> User.class; + case INSTANCE -> Instance.class; case ITEM -> Item.class; case HOLDINGS_RECORD -> HoldingsRecord.class; }; diff --git a/src/main/resources/db/changelog/changelog-master.xml b/src/main/resources/db/changelog/changelog-master.xml index 2c584a12..118e7961 100644 --- a/src/main/resources/db/changelog/changelog-master.xml +++ b/src/main/resources/db/changelog/changelog-master.xml @@ -20,4 +20,5 @@ + diff --git a/src/main/resources/db/changelog/changes/27-12-2023_add_types_for_instance.sql b/src/main/resources/db/changelog/changes/27-12-2023_add_types_for_instance.sql new file mode 100644 index 00000000..8f4c8210 --- /dev/null +++ b/src/main/resources/db/changelog/changes/27-12-2023_add_types_for_instance.sql @@ -0,0 +1,3 @@ +ALTER TYPE EntityType ADD VALUE IF NOT EXISTS 'INSTANCE'; +ALTER TYPE IdentifierType ADD VALUE IF NOT EXISTS 'ISBN'; +ALTER TYPE IdentifierType ADD VALUE IF NOT EXISTS 'ISSN'; diff --git a/src/main/resources/db/changelog/changes/27-12-2023_add_types_for_instance.xml b/src/main/resources/db/changelog/changes/27-12-2023_add_types_for_instance.xml new file mode 100644 index 00000000..8388ed49 --- /dev/null +++ b/src/main/resources/db/changelog/changes/27-12-2023_add_types_for_instance.xml @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/src/main/resources/swagger.api/schemas/entity_type.json b/src/main/resources/swagger.api/schemas/entity_type.json index 7f9aedb5..56c83a3a 100644 --- a/src/main/resources/swagger.api/schemas/entity_type.json +++ b/src/main/resources/swagger.api/schemas/entity_type.json @@ -6,7 +6,8 @@ "enum": [ "USER", "ITEM", - "HOLDINGS_RECORD" + "HOLDINGS_RECORD", + "INSTANCE" ] } } diff --git a/src/main/resources/swagger.api/schemas/identifier_type.json b/src/main/resources/swagger.api/schemas/identifier_type.json index e51c7d2e..33d147a2 100644 --- a/src/main/resources/swagger.api/schemas/identifier_type.json +++ b/src/main/resources/swagger.api/schemas/identifier_type.json @@ -13,7 +13,9 @@ "USER_NAME", "EXTERNAL_SYSTEM_ID", "INSTANCE_HRID", - "ITEM_BARCODE" + "ITEM_BARCODE", + "ISBN", + "ISSN" ] } } diff --git a/src/test/java/org/folio/bulkops/BaseTest.java b/src/test/java/org/folio/bulkops/BaseTest.java index 32b34e87..3112920b 100644 --- a/src/test/java/org/folio/bulkops/BaseTest.java +++ b/src/test/java/org/folio/bulkops/BaseTest.java @@ -37,11 +37,16 @@ import org.folio.bulkops.client.HoldingsSourceClient; import org.folio.bulkops.client.HoldingsTypeClient; import org.folio.bulkops.client.IllPolicyClient; +import org.folio.bulkops.client.InstanceFormatsClient; +import org.folio.bulkops.client.InstanceStatusesClient; +import org.folio.bulkops.client.InstanceTypesClient; import org.folio.bulkops.client.ItemClient; import org.folio.bulkops.client.ItemNoteTypeClient; import org.folio.bulkops.client.LoanTypeClient; import org.folio.bulkops.client.LocationClient; import org.folio.bulkops.client.MaterialTypeClient; +import org.folio.bulkops.client.ModesOfIssuanceClient; +import org.folio.bulkops.client.NatureOfContentTermsClient; import org.folio.bulkops.client.OkapiClient; import org.folio.bulkops.client.RemoteFileSystemClient; import org.folio.bulkops.client.ServicePointClient; @@ -177,6 +182,16 @@ public abstract class BaseTest { public MaterialTypeClient materialTypeClient; @MockBean public ElectronicAccessRelationshipClient relationshipClient; + @MockBean + public InstanceStatusesClient instanceStatusesClient; + @MockBean + public ModesOfIssuanceClient modesOfIssuanceClient; + @MockBean + public InstanceTypesClient instanceTypesClient; + @MockBean + public NatureOfContentTermsClient natureOfContentTermsClient; + @MockBean + public InstanceFormatsClient instanceFormatsClient; @Autowired protected MockMvc mockMvc; diff --git a/src/test/java/org/folio/bulkops/service/BulkOperationServiceTest.java b/src/test/java/org/folio/bulkops/service/BulkOperationServiceTest.java index 384d8772..43ef4800 100644 --- a/src/test/java/org/folio/bulkops/service/BulkOperationServiceTest.java +++ b/src/test/java/org/folio/bulkops/service/BulkOperationServiceTest.java @@ -874,7 +874,10 @@ void shouldProcessIfNoLinkToModifiedFile() { "items_preview.csv,ITEM,COMMIT", "holdings_preview.csv,HOLDINGS_RECORD,UPLOAD", "holdings_preview.csv,HOLDINGS_RECORD,EDIT", - "holdings_preview.csv,HOLDINGS_RECORD,COMMIT"}, delimiter = ',') + "holdings_preview.csv,HOLDINGS_RECORD,COMMIT", + "instances_preview.csv,INSTANCE,UPLOAD", + "instances_preview.csv,INSTANCE,EDIT", + "instances_preview.csv,INSTANCE,COMMIT"}, delimiter = ',') @SneakyThrows void shouldReturnPreviewIfAvailable(String fileName, EntityType entityType, BulkOperationStep step) { var path = "src/test/resources/files/" + fileName; diff --git a/src/test/java/org/folio/bulkops/service/OpenCSVConverterTest.java b/src/test/java/org/folio/bulkops/service/OpenCSVConverterTest.java index b458f306..8b8886f0 100644 --- a/src/test/java/org/folio/bulkops/service/OpenCSVConverterTest.java +++ b/src/test/java/org/folio/bulkops/service/OpenCSVConverterTest.java @@ -30,6 +30,13 @@ import org.folio.bulkops.domain.bean.HoldingsRecordsSourceCollection; import org.folio.bulkops.domain.bean.HoldingsType; import org.folio.bulkops.domain.bean.HoldingsTypeCollection; +import org.folio.bulkops.domain.bean.Instance; +import org.folio.bulkops.domain.bean.InstanceFormat; +import org.folio.bulkops.domain.bean.InstanceFormats; +import org.folio.bulkops.domain.bean.InstanceStatus; +import org.folio.bulkops.domain.bean.InstanceStatuses; +import org.folio.bulkops.domain.bean.InstanceType; +import org.folio.bulkops.domain.bean.InstanceTypes; import org.folio.bulkops.domain.bean.Item; import org.folio.bulkops.domain.bean.ItemLocation; import org.folio.bulkops.domain.bean.ItemLocationCollection; @@ -37,6 +44,10 @@ import org.folio.bulkops.domain.bean.LoanTypeCollection; import org.folio.bulkops.domain.bean.MaterialType; import org.folio.bulkops.domain.bean.MaterialTypeCollection; +import org.folio.bulkops.domain.bean.ModeOfIssuance; +import org.folio.bulkops.domain.bean.ModesOfIssuance; +import org.folio.bulkops.domain.bean.NatureOfContentTerm; +import org.folio.bulkops.domain.bean.NatureOfContentTerms; import org.folio.bulkops.domain.bean.NoteType; import org.folio.bulkops.domain.bean.NoteTypeCollection; import org.folio.bulkops.domain.bean.SelectField; @@ -93,7 +104,8 @@ public Stream provideArguments(ExtensionContext context) { return Stream.of( Arguments.of(User.class), Arguments.of(Item.class), - Arguments.of(HoldingsRecord.class) + Arguments.of(HoldingsRecord.class), + Arguments.of(Instance.class) ); } } @@ -255,8 +267,10 @@ private static BulkOperationsEntity getEmptyBean(Class cla bean = new User(); } else if (clazz.equals(Item.class)) { bean = new Item().withVersion(1); - } else { + } else if (clazz.equals(HoldingsRecord.class)) { bean = new HoldingsRecord().withVersion(2); + } else { + bean = new Instance(); } return bean; } @@ -266,8 +280,10 @@ private static String getPathToSample(Class clazz) { return "src/test/resources/files/complete_user.json"; } else if (clazz.equals(Item.class)) { return "src/test/resources/files/complete_item.json"; - } else { + } else if (clazz.equals(HoldingsRecord.class)) { return "src/test/resources/files/complete_holding_record.json"; + } else { + return "src/test/resources/files/instance.json"; } } @@ -276,8 +292,10 @@ private static String getPathToBadData(Class clazz) { return "src/test/resources/files/bad_data_user.json"; } else if (clazz.equals(Item.class)) { return "src/test/resources/files/bad_data_item.json"; - } else { + } else if (clazz.equals(HoldingsRecord.class)) { return "src/test/resources/files/bad_data_holding_record.json"; + } else { + return "src/test/resources/files/bad_data_instance.json"; } } @@ -399,5 +417,40 @@ private void initMocks() { when(holdingsSourceClient.getByQuery("name==\"Holdings@record@source\"")).thenReturn(new HoldingsRecordsSourceCollection().withHoldingsRecordsSources(List.of(new HoldingsRecordsSource().withId("f32d531e-df79-46b3-8932-cdd35f7a2264").withName("Holdings@record@source")))); when(holdingsSourceClient.getById("a06889ff-d178-4dc8-815a-06f7f97bf975")).thenThrow(new NotFoundException("Not found")); + // Instance + var instanceStatus = InstanceStatus.builder().id("2a340d34-6b70-443a-bb1b-1b8d1c65d862").name("Other").build(); + when(instanceStatusesClient.getById("2a340d34-6b70-443a-bb1b-1b8d1c65d862")).thenReturn(instanceStatus); + when(instanceStatusesClient.getByQuery("name==\"Other\"", 1)).thenReturn(InstanceStatuses.builder() + .statuses(Collections.singletonList(instanceStatus)) + .totalRecords(1) + .build()); + + var modeOfIssuance = ModeOfIssuance.builder().id("068b5344-e2a6-40df-9186-1829e13cd344").name("serial").build(); + when(modesOfIssuanceClient.getById("068b5344-e2a6-40df-9186-1829e13cd344")).thenReturn(modeOfIssuance); + when(modesOfIssuanceClient.getByQuery("name==\"serial\"", 1)).thenReturn(ModesOfIssuance.builder() + .modes(Collections.singletonList(modeOfIssuance)) + .totalRecords(1) + .build()); + + var instanceType = InstanceType.builder().id("6312d172-f0cf-40f6-b27d-9fa8feaf332f").name("text").build(); + when(instanceTypesClient.getById("6312d172-f0cf-40f6-b27d-9fa8feaf332f")).thenReturn(instanceType); + when(instanceTypesClient.getByQuery("name==\"text\"", 1)).thenReturn(InstanceTypes.builder() + .types(Collections.singletonList(instanceType)) + .totalRecords(1) + .build()); + + var natureOfContentTerm = NatureOfContentTerm.builder().id("44cd89f3-2e76-469f-a955-cc57cb9e0395").name("textbook").build(); + when(natureOfContentTermsClient.getById("44cd89f3-2e76-469f-a955-cc57cb9e0395")).thenReturn(natureOfContentTerm); + when(natureOfContentTermsClient.getByQuery("name==\"textbook\"", 1)).thenReturn(NatureOfContentTerms.builder() + .terms(Collections.singletonList(natureOfContentTerm)) + .totalRecords(1) + .build()); + + var instanceFormat = InstanceFormat.builder().id("fe1b9adb-e0cf-4e05-905f-ce9986279404").name("computer -- other").build(); + when(instanceFormatsClient.getById("fe1b9adb-e0cf-4e05-905f-ce9986279404")).thenReturn(instanceFormat); + when(instanceFormatsClient.getByQuery("name==\"computer -- other\"", 1)).thenReturn(InstanceFormats.builder() + .formats(Collections.singletonList(instanceFormat)) + .totalRecords(1) + .build()); } } diff --git a/src/test/resources/files/bad_data_instance.json b/src/test/resources/files/bad_data_instance.json new file mode 100644 index 00000000..974a6669 --- /dev/null +++ b/src/test/resources/files/bad_data_instance.json @@ -0,0 +1,82 @@ +{ + "id": "69640328-788e-43fc-9c3c-af39e243f3b7", + "_version": "81", + "hrid": "inst000000000001", + "source": "FOLIO", + "title": "ABA Journal", + "administrativeNotes": [ + "Sample note" + ], + "indexTitle": "Index title", + "parentInstances": [], + "childInstances": [], + "isBoundWith": false, + "alternativeTitles": [], + "editions": [ + "2021", + "2022" + ], + "series": [ + + ], + "identifiers": [ + { + "identifierTypeId": "913300b2-03ed-469a-8179-c1092c991227", + "value": "0747-0088" + }, + { + "identifierTypeId": "c858e4f2-2b6b-4385-842b-60732ee14abb", + "value": "84641839" + } + ], + "contributors": [ + + ], + "subjects": [], + "classifications": [], + "publication": [ + + ], + "publicationFrequency": [ + + ], + "publicationRange": [ + + ], + "electronicAccess": [], + "instanceTypeId": "6312d172-f0cf-40f6-b27d-9fa8feaf332f", + "instanceFormatIds": [ + + ], + "physicalDescriptions": [ + + ], + "languages": [ + + ], + "notes": [], + "modeOfIssuanceId": "068b5344-e2a6-40df-9186-1829e13cd345", + "catalogedDate": "2023-12-27", + "previouslyHeld": false, + "discoverySuppress": false, + "statisticalCodeIds": [], + "statusId": "2a340d34-6b70-443a-bb1b-1b8d1c65d862", + "statusUpdatedDate": "2023-12-27T10:57:11.903Z", + "metadata": { + "createdDate": "2023-11-10T11:54:16.279+00:00", + "createdByUserId": "cffb2565-07fc-470b-86c6-17d8ce14432e", + "updatedDate": "2023-12-27T11:01:48.248+00:00", + "updatedByUserId": "ca6022de-2644-46fe-b6f2-78df15483721" + }, + "tags": { + "tagList": [] + }, + "natureOfContentTermIds": [ + + ], + "publicationPeriod": { + + }, + "precedingTitles": [], + "succeedingTitles": [] +} diff --git a/src/test/resources/files/instance.json b/src/test/resources/files/instance.json new file mode 100644 index 00000000..9b92807f --- /dev/null +++ b/src/test/resources/files/instance.json @@ -0,0 +1,102 @@ +{ + "id": "69640328-788e-43fc-9c3c-af39e243f3b7", + "_version": "81", + "hrid": "inst000000000001", + "source": "FOLIO", + "title": "ABA Journal", + "administrativeNotes": [ + "Sample note" + ], + "indexTitle": "Index title", + "parentInstances": [], + "childInstances": [], + "isBoundWith": false, + "alternativeTitles": [], + "editions": [ + "2021", + "2022" + ], + "series": [ + { + "authorityId": null, + "value": "series" + } + ], + "identifiers": [ + { + "identifierTypeId": "913300b2-03ed-469a-8179-c1092c991227", + "value": "0747-0088" + }, + { + "identifierTypeId": "c858e4f2-2b6b-4385-842b-60732ee14abb", + "value": "84641839" + } + ], + "contributors": [ + { + "authorityId": null, + "contributorNameTypeId": "2b94c631-fca9-4892-a730-03ee529ffe2a", + "name": "Sample contributor", + "contributorTypeId": null, + "contributorTypeText": "", + "primary": false + } + ], + "subjects": [], + "classifications": [], + "publication": [ + { + "publisher": "American Bar Association", + "place": "Chicago, Ill.", + "dateOfPublication": "1915-1983", + "role": null + } + ], + "publicationFrequency": [ + "freq1", + "freq2" + ], + "publicationRange": [ + "range1", + "range2" + ], + "electronicAccess": [], + "instanceTypeId": "6312d172-f0cf-40f6-b27d-9fa8feaf332f", + "instanceFormatIds": [ + "fe1b9adb-e0cf-4e05-905f-ce9986279404" + ], + "physicalDescriptions": [ + "Physical description1", + "Physical description2" + ], + "languages": [ + "eng", + "fre" + ], + "notes": [], + "modeOfIssuanceId": "068b5344-e2a6-40df-9186-1829e13cd344", + "catalogedDate": "2023-12-27", + "previouslyHeld": false, + "discoverySuppress": false, + "statisticalCodeIds": [], + "statusId": "2a340d34-6b70-443a-bb1b-1b8d1c65d862", + "statusUpdatedDate": "2023-12-27T10:57:11.903Z", + "metadata": { + "createdDate": "2023-11-10T11:54:16.279+00:00", + "createdByUserId": "cffb2565-07fc-470b-86c6-17d8ce14432e", + "updatedDate": "2023-12-27T11:01:48.248+00:00", + "updatedByUserId": "ca6022de-2644-46fe-b6f2-78df15483721" + }, + "tags": { + "tagList": [] + }, + "natureOfContentTermIds": [ + "44cd89f3-2e76-469f-a955-cc57cb9e0395" + ], + "publicationPeriod": { + "start": 1915, + "end": 1983 + }, + "precedingTitles": [], + "succeedingTitles": [] +} diff --git a/src/test/resources/files/instances_preview.csv b/src/test/resources/files/instances_preview.csv new file mode 100644 index 00000000..4f70a8ec --- /dev/null +++ b/src/test/resources/files/instances_preview.csv @@ -0,0 +1,6 @@ +Instance UUID,Suppress from discovery,Staff suppress,Previously held,Instance HRID,Source,Cataloged date,Instance status term,Mode of issuance,Administrative note,Resource title,Index title,Series statements,Contributors,Edition,Physical description,Resource type,Nature of content,Formats,Languages,Publication frequency,Publication range,ISBN,ISSN +69640328-788e-43fc-9c3c-af39e243f3b7,false,,false,inst000000000001,FOLIO,2023-12-27,Other,serial,Sample note,ABA Journal,Index title,series,Sample contributor,2021 | 2022,Physical description1 | Physical description2,text,textbook,computer -- other,eng | fre,freq1 | freq2,range1 | range2,, +ed32b4a6-3895-42a0-b696-7b8ed667313f,false,,false,inst000000000001,FOLIO,2023-12-27,Other,serial,Sample note,ABA Journal,Index title,series,Sample contributor,2021 | 2022,Physical description1 | Physical description2,text,textbook,computer -- other,eng | fre,freq1 | freq2,range1 | range2,, +998cc146-a582-4d36-a148-ae3c653ce431,false,,false,inst000000000001,FOLIO,2023-12-27,Other,serial,Sample note,ABA Journal,Index title,series,Sample contributor,2021 | 2022,Physical description1 | Physical description2,text,textbook,computer -- other,eng | fre,freq1 | freq2,range1 | range2,, +34c1cba5-7308-445a-976a-1096edfd8c2c,false,,false,inst000000000001,FOLIO,2023-12-27,Other,serial,Sample note,ABA Journal,Index title,series,Sample contributor,2021 | 2022,Physical description1 | Physical description2,text,textbook,computer -- other,eng | fre,freq1 | freq2,range1 | range2,, +445043d9-3ac3-4689-90e5-23be69fd6a3d,false,,false,inst000000000001,FOLIO,2023-12-27,Other,serial,Sample note,ABA Journal,Index title,series,Sample contributor,2021 | 2022,Physical description1 | Physical description2,text,textbook,computer -- other,eng | fre,freq1 | freq2,range1 | range2,,