diff --git a/src/main/java/org/folio/bulkops/domain/bean/Instance.java b/src/main/java/org/folio/bulkops/domain/bean/Instance.java index a55ad0b51..abae25ed7 100644 --- a/src/main/java/org/folio/bulkops/domain/bean/Instance.java +++ b/src/main/java/org/folio/bulkops/domain/bean/Instance.java @@ -13,7 +13,6 @@ 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.InstanceNoteListConverter; import org.folio.bulkops.domain.converter.InstanceStatusConverter; @@ -21,12 +20,11 @@ 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.InstanceStatisticalCodeListConverter; 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 @@ -41,6 +39,7 @@ public class Instance implements BulkOperationsEntity { public static final String INSTANCE_HRID = "Instance HRID"; public static final String INSTANCE_SOURCE = "Source"; public static final String INSTANCE_MODE_OF_ISSUANCE = "Mode of issuance"; + public static final String INSTANCE_STATISTICAL_CODES = "Statistical code"; public static final String INSTANCE_RESOURCE_TITLE = "Resource title"; public static final String INSTANCE_INDEX_TITLE = "Index title"; public static final String INSTANCE_SERIES_STATEMENTS = "Series statements"; @@ -117,88 +116,94 @@ public class Instance implements BulkOperationsEntity { @UnifiedTableCell(visible = false) private String modeOfIssuanceId; + @JsonProperty("statisticalCodeIds") + @CsvCustomBindByName(column = INSTANCE_STATISTICAL_CODES, converter = InstanceStatisticalCodeListConverter.class) + @CsvCustomBindByPosition(position = 9, converter = InstanceStatisticalCodeListConverter.class) + @UnifiedTableCell(visible = false) + private List statisticalCodeIds; + @JsonProperty("administrativeNotes") @CsvCustomBindByName(column = INSTANCE_ADMINISTRATIVE_NOTE, converter = StringListPipedConverter.class) - @CsvCustomBindByPosition(position = 9, converter = StringListPipedConverter.class) + @CsvCustomBindByPosition(position = 10, converter = StringListPipedConverter.class) @UnifiedTableCell(visible = false) private List administrativeNotes; @JsonProperty("title") @CsvCustomBindByName(column = INSTANCE_RESOURCE_TITLE, converter = StringConverter.class) - @CsvCustomBindByPosition(position = 10, converter = StringConverter.class) + @CsvCustomBindByPosition(position = 11, converter = StringConverter.class) @UnifiedTableCell private String title; @JsonProperty("indexTitle") @CsvCustomBindByName(column = INSTANCE_INDEX_TITLE, converter = StringConverter.class) - @CsvCustomBindByPosition(position = 11, converter = StringConverter.class) + @CsvCustomBindByPosition(position = 12, converter = StringConverter.class) @UnifiedTableCell(visible = false) private String indexTitle; @JsonProperty("series") @CsvCustomBindByName(column = INSTANCE_SERIES_STATEMENTS, converter = SeriesListConverter.class) - @CsvCustomBindByPosition(position = 12, converter = SeriesListConverter.class) + @CsvCustomBindByPosition(position = 13, converter = SeriesListConverter.class) @UnifiedTableCell(visible = false) private List series; @JsonProperty("contributors") @CsvCustomBindByName(column = INSTANCE_CONTRIBUTORS, converter = ContributorListConverter.class) - @CsvCustomBindByPosition(position = 13, converter = ContributorListConverter.class) + @CsvCustomBindByPosition(position = 14, converter = ContributorListConverter.class) @UnifiedTableCell private List contributors; @JsonProperty("editions") @CsvCustomBindByName(column = INSTANCE_EDITION, converter = StringListPipedConverter.class) - @CsvCustomBindByPosition(position = 14, converter = StringListPipedConverter.class) + @CsvCustomBindByPosition(position = 15, converter = StringListPipedConverter.class) @UnifiedTableCell(visible = false) private List editions; @JsonProperty("physicalDescriptions") @CsvCustomBindByName(column = INSTANCE_PHYSICAL_DESCRIPTION, converter = StringListPipedConverter.class) - @CsvCustomBindByPosition(position = 15, converter = StringListPipedConverter.class) + @CsvCustomBindByPosition(position = 16, converter = StringListPipedConverter.class) @UnifiedTableCell(visible = false) private List physicalDescriptions; @JsonProperty("instanceTypeId") @CsvCustomBindByName(column = INSTANCE_RESOURCE_TYPE, converter = InstanceTypeConverter.class) - @CsvCustomBindByPosition(position = 16, converter = InstanceTypeConverter.class) + @CsvCustomBindByPosition(position = 17, converter = InstanceTypeConverter.class) @UnifiedTableCell private String instanceTypeId; @JsonProperty("natureOfContentTermIds") @CsvCustomBindByName(column = INSTANCE_NATURE_OF_CONTENT, converter = NatureOfContentTermListConverter.class) - @CsvCustomBindByPosition(position = 17, converter = NatureOfContentTermListConverter.class) + @CsvCustomBindByPosition(position = 18, converter = NatureOfContentTermListConverter.class) @UnifiedTableCell(visible = false) private List natureOfContentTermIds; @JsonProperty("instanceFormatIds") @CsvCustomBindByName(column = INSTANCE_FORMATS, converter = InstanceFormatListConverter.class) - @CsvCustomBindByPosition(position = 18, converter = InstanceFormatListConverter.class) + @CsvCustomBindByPosition(position = 19, converter = InstanceFormatListConverter.class) @UnifiedTableCell(visible = false) private List instanceFormatIds; @JsonProperty("languages") @CsvCustomBindByName(column = INSTANCE_LANGUAGES, converter = StringListPipedConverter.class) - @CsvCustomBindByPosition(position = 19, converter = StringListPipedConverter.class) + @CsvCustomBindByPosition(position = 20, converter = StringListPipedConverter.class) @UnifiedTableCell(visible = false) private List languages; @JsonProperty("publicationFrequency") @Valid @CsvCustomBindByName(column = INSTANCE_PUBLICATION_FREQUENCY, converter = StringListPipedConverter.class) - @CsvCustomBindByPosition(position = 20, converter = StringListPipedConverter.class) + @CsvCustomBindByPosition(position = 21, converter = StringListPipedConverter.class) @UnifiedTableCell(visible = false) private List publicationFrequency; @JsonProperty("publicationRange") @CsvCustomBindByName(column = INSTANCE_PUBLICATION_RANGE, converter = StringListPipedConverter.class) - @CsvCustomBindByPosition(position = 21, converter = StringListPipedConverter.class) + @CsvCustomBindByPosition(position = 22, converter = StringListPipedConverter.class) @UnifiedTableCell(visible = false) private List publicationRange; @JsonProperty("notes") @CsvCustomBindByName(column = "Notes", converter = InstanceNoteListConverter.class) - @CsvCustomBindByPosition(position = 22, converter = InstanceNoteListConverter.class) + @CsvCustomBindByPosition(position = 23, converter = InstanceNoteListConverter.class) @UnifiedTableCell(visible = false) private List instanceNotes; @@ -216,8 +221,6 @@ public class Instance implements BulkOperationsEntity { private List publications; @JsonProperty("electronicAccess") private List electronicAccesses; - @JsonProperty("statisticalCodeIds") - private List statisticalCodeIds; @JsonProperty("sourceRecordFormat") private String sourceRecordFormat; @JsonProperty("statusUpdatedDate") diff --git a/src/main/java/org/folio/bulkops/domain/converter/InstanceStatisticalCodeListConverter.java b/src/main/java/org/folio/bulkops/domain/converter/InstanceStatisticalCodeListConverter.java new file mode 100644 index 000000000..ba14a5d30 --- /dev/null +++ b/src/main/java/org/folio/bulkops/domain/converter/InstanceStatisticalCodeListConverter.java @@ -0,0 +1,31 @@ +package org.folio.bulkops.domain.converter; + +import org.folio.bulkops.domain.format.SpecialCharacterEscaper; +import org.folio.bulkops.service.InstanceReferenceHelper; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +import static org.folio.bulkops.util.Constants.ARRAY_DELIMITER; + +public class InstanceStatisticalCodeListConverter extends BaseConverter> { + @Override + public List convertToObject(String value) { + return Arrays.stream(value.split(ARRAY_DELIMITER)) + .map(SpecialCharacterEscaper::restore) + .map(name -> InstanceReferenceHelper.service().getStatisticalCodeByName(name).getId()) + .filter(Objects::nonNull) + .toList(); + } + + @Override + public String convertToString(List object) { + return object.stream() + .filter(Objects::nonNull) + .map(id -> InstanceReferenceHelper.service().getStatisticalCodeById(id).getName()) + .map(SpecialCharacterEscaper::escape) + .collect(Collectors.joining(ARRAY_DELIMITER)); + } +} diff --git a/src/main/java/org/folio/bulkops/processor/FolioAbstractDataProcessor.java b/src/main/java/org/folio/bulkops/processor/FolioAbstractDataProcessor.java index a5ce90abd..47ef7599f 100644 --- a/src/main/java/org/folio/bulkops/processor/FolioAbstractDataProcessor.java +++ b/src/main/java/org/folio/bulkops/processor/FolioAbstractDataProcessor.java @@ -20,6 +20,8 @@ import lombok.extern.log4j.Log4j2; +import static org.folio.bulkops.domain.dto.UpdateActionType.REMOVE_ALL; +import static org.folio.bulkops.domain.dto.UpdateOptionType.STATISTICAL_CODE; import static org.folio.bulkops.util.FolioExecutionContextUtil.prepareContextForTenant; @Log4j2 @@ -54,6 +56,14 @@ public UpdatedEntityHolder process(String identifier, T entity, BulkOperationRul var holder = UpdatedEntityHolder.builder().build(); var updated = clone(entity); var preview = clone(entity); + try { + validator().validate(rules); + } catch (RuleValidationException e) { + log.warn(String.format("Rule validation exception: %s", e.getMessage())); + errorService.saveError(rules.getBulkOperationRules().get(0).getBulkOperationId(), identifier, e.getMessage()); + } catch (Exception e) { + log.error(e.getMessage()); + } for (BulkOperationRule rule : rules.getBulkOperationRules()) { var details = rule.getRuleDetails(); var option = details.getOption(); @@ -93,6 +103,14 @@ public UpdatedEntityHolder process(String identifier, T entity, BulkOperationRul */ public abstract Validator validator(T entity); + public StatisticalCodeValidator validator() { + return rules -> { + if (getNumberOfRulesWithStatisticalCode(rules) > 1 && existsRuleWithStatisticalCodeAndRemoveAll(rules)) { + throw new RuleValidationException("Combination REMOVE_ALL with other actions is not supported for Statistical code"); + } + }; + } + /** * Returns {@link Consumer} for applying changes for entity of type {@link T} * @@ -136,4 +154,13 @@ public String getRecordPropertyName(UpdateOptionType optionType) { private boolean isTenantApplicableForProcessingAsMember(T entity) { return entity.getRecordBulkOperationEntity().getClass() != User.class && consortiaService.isTenantMember(entity.getTenant()); } + + private long getNumberOfRulesWithStatisticalCode(BulkOperationRuleCollection rules) { + return rules.getBulkOperationRules().stream().filter(rule -> rule.getRuleDetails().getOption() == STATISTICAL_CODE).count(); + } + + private boolean existsRuleWithStatisticalCodeAndRemoveAll(BulkOperationRuleCollection rules) { + return rules.getBulkOperationRules().stream().anyMatch(rule -> rule.getRuleDetails().getOption() == STATISTICAL_CODE && + rule.getRuleDetails().getActions().stream().anyMatch(act -> act.getType() == REMOVE_ALL)); + } } diff --git a/src/main/java/org/folio/bulkops/processor/StatisticalCodeValidator.java b/src/main/java/org/folio/bulkops/processor/StatisticalCodeValidator.java new file mode 100644 index 000000000..1d6faf4fb --- /dev/null +++ b/src/main/java/org/folio/bulkops/processor/StatisticalCodeValidator.java @@ -0,0 +1,8 @@ +package org.folio.bulkops.processor; + +import org.folio.bulkops.exception.RuleValidationException; + +@FunctionalInterface +public interface StatisticalCodeValidator { + void validate(T t) throws RuleValidationException; +} diff --git a/src/main/java/org/folio/bulkops/processor/folio/FolioInstanceDataProcessor.java b/src/main/java/org/folio/bulkops/processor/folio/FolioInstanceDataProcessor.java index 4397823b7..b01c56ea0 100644 --- a/src/main/java/org/folio/bulkops/processor/folio/FolioInstanceDataProcessor.java +++ b/src/main/java/org/folio/bulkops/processor/folio/FolioInstanceDataProcessor.java @@ -63,7 +63,7 @@ public Updater updater(UpdateOptionType option, Action action, return extendedInstance -> extendedInstance.getEntity().setDiscoverySuppress(false); } } - return instanceNotesUpdaterFactory.getUpdater(option, action).orElseGet(() -> instance -> { + return instanceNotesUpdaterFactory.getUpdater(option, action, forPreview).orElseGet(() -> instance -> { throw new BulkOperationException(format("Combination %s and %s isn't supported yet", option, action.getType())); }); } @@ -81,6 +81,9 @@ public ExtendedInstance clone(ExtendedInstance extendedInstance) { .map(instanceNote -> instanceNote.toBuilder().build()) .toList())); } + if (instance.getStatisticalCodeIds() != null) { + clone.setStatisticalCodeIds(new ArrayList<>(clone.getStatisticalCodeIds())); + } return ExtendedInstance.builder().tenantId(extendedInstance.getTenantId()).entity(clone).build(); } diff --git a/src/main/java/org/folio/bulkops/processor/folio/InstanceNotesUpdaterFactory.java b/src/main/java/org/folio/bulkops/processor/folio/InstanceNotesUpdaterFactory.java index b3f115ea6..fefa19c8f 100644 --- a/src/main/java/org/folio/bulkops/processor/folio/InstanceNotesUpdaterFactory.java +++ b/src/main/java/org/folio/bulkops/processor/folio/InstanceNotesUpdaterFactory.java @@ -6,6 +6,7 @@ import static org.folio.bulkops.domain.dto.UpdateActionType.MARK_AS_STAFF_ONLY; import static org.folio.bulkops.domain.dto.UpdateOptionType.ADMINISTRATIVE_NOTE; import static org.folio.bulkops.domain.dto.UpdateOptionType.INSTANCE_NOTE; +import static org.folio.bulkops.domain.dto.UpdateOptionType.STATISTICAL_CODE; import static org.folio.bulkops.util.Constants.STAFF_ONLY_NOTE_PARAMETER_KEY; import lombok.AllArgsConstructor; @@ -30,15 +31,17 @@ public class InstanceNotesUpdaterFactory { public static final String INSTANCE_NOTE_TYPE_ID_KEY = "INSTANCE_NOTE_TYPE_ID_KEY"; private final AdministrativeNotesUpdater administrativeNotesUpdater; + private final StatisticalCodesUpdater statisticalCodesUpdater; - public Optional> getUpdater(UpdateOptionType option, Action action) { + public Optional> getUpdater(UpdateOptionType option, Action action, boolean forPreview) { return switch (action.getType()) { case MARK_AS_STAFF_ONLY, REMOVE_MARK_AS_STAFF_ONLY -> Optional.of(extendedInstance -> setStaffOnly(extendedInstance.getEntity(), action, option)); case REMOVE_ALL -> Optional.of(extendedInstance -> removeAll(extendedInstance.getEntity(), action, option)); - case ADD_TO_EXISTING -> Optional.of(extendedInstance -> addToExisting(extendedInstance.getEntity(), action, option)); + case ADD_TO_EXISTING -> Optional.of(extendedInstance -> addToExisting(extendedInstance.getEntity(), action, option, forPreview)); case FIND_AND_REMOVE_THESE -> Optional.of(extendedInstance -> findAndRemove(extendedInstance.getEntity(), action, option)); case FIND_AND_REPLACE -> Optional.of(extendedInstance -> findAndReplace(extendedInstance.getEntity(), action, option)); case CHANGE_TYPE -> Optional.of(extendedInstance -> changeNoteType(extendedInstance.getEntity(), action, option)); + case REMOVE_SOME -> Optional.of(extendedInstance -> removeSome(extendedInstance.getEntity(), action, option)); default -> Optional.empty(); }; } @@ -57,14 +60,24 @@ private void removeAll(Instance instance, Action action, UpdateOptionType option instance.setAdministrativeNotes(Collections.emptyList()); } else if (INSTANCE_NOTE.equals(option)) { instance.setInstanceNotes(removeNotesByTypeId(instance.getInstanceNotes(), action.getParameters())); + } else if (STATISTICAL_CODE.equals(option)) { + instance.setStatisticalCodeIds(Collections.emptyList()); } } - private void addToExisting(Instance instance, Action action, UpdateOptionType option) { + private void removeSome(Instance instance, Action action, UpdateOptionType option) { + if (STATISTICAL_CODE.equals(option)) { + instance.setStatisticalCodeIds(statisticalCodesUpdater.removeSomeStatisticalCodeIds(action.getUpdated(), instance.getStatisticalCodeIds())); + } + } + + private void addToExisting(Instance instance, Action action, UpdateOptionType option, boolean forPreview) { if (ADMINISTRATIVE_NOTE.equals(option)) { instance.setAdministrativeNotes(administrativeNotesUpdater.addToAdministrativeNotes(action.getUpdated(), instance.getAdministrativeNotes())); } else if (INSTANCE_NOTE.equals(option)) { instance.setInstanceNotes(addToNotesByTypeId(instance.getInstanceNotes(), action.getParameters(), action.getUpdated())); + } else if (STATISTICAL_CODE.equals(option)) { + instance.setStatisticalCodeIds(statisticalCodesUpdater.addToStatisticalCodeIds(action.getUpdated(), instance.getStatisticalCodeIds(), forPreview)); } } diff --git a/src/main/java/org/folio/bulkops/processor/folio/StatisticalCodesUpdater.java b/src/main/java/org/folio/bulkops/processor/folio/StatisticalCodesUpdater.java new file mode 100644 index 000000000..d36486425 --- /dev/null +++ b/src/main/java/org/folio/bulkops/processor/folio/StatisticalCodesUpdater.java @@ -0,0 +1,37 @@ +package org.folio.bulkops.processor.folio; + +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static java.util.Objects.isNull; + +@Component +public class StatisticalCodesUpdater { + + public List addToStatisticalCodeIds(String statisticalCodeIdsFromAction, List existingStatisticalCodeIds, boolean forPreview) { + var newStatisticalCodeIds = new ArrayList<>(Arrays.asList(statisticalCodeIdsFromAction.split(","))); + if (isNull(existingStatisticalCodeIds)) { + return forPreview ? newStatisticalCodeIds : newStatisticalCodeIds.stream().distinct().toList(); + } + if (forPreview) { + existingStatisticalCodeIds.addAll(newStatisticalCodeIds); + } else { + newStatisticalCodeIds.stream().distinct().filter(newCode -> !existingStatisticalCodeIds.contains(newCode)) + .forEach(existingStatisticalCodeIds::add); + } + return existingStatisticalCodeIds; + } + + public List removeSomeStatisticalCodeIds(String statisticalCodeIdsFromAction, List existingStatisticalCodeIds) { + var statisticalCodeIdsToRemove = new ArrayList<>(Arrays.asList(statisticalCodeIdsFromAction.split(","))); + if (isNull(existingStatisticalCodeIds)) { + return Collections.emptyList(); + } + existingStatisticalCodeIds.removeAll(statisticalCodeIdsToRemove); + return existingStatisticalCodeIds; + } +} diff --git a/src/main/java/org/folio/bulkops/service/InstanceReferenceHelper.java b/src/main/java/org/folio/bulkops/service/InstanceReferenceHelper.java index 7ce1f8a6f..fee0f50b8 100644 --- a/src/main/java/org/folio/bulkops/service/InstanceReferenceHelper.java +++ b/src/main/java/org/folio/bulkops/service/InstanceReferenceHelper.java @@ -2,6 +2,8 @@ import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; +import org.folio.bulkops.domain.bean.StatisticalCode; +import org.folio.spring.FolioExecutionContext; import org.springframework.beans.factory.InitializingBean; import org.springframework.stereotype.Service; @@ -10,6 +12,7 @@ @RequiredArgsConstructor public class InstanceReferenceHelper implements InitializingBean { private final InstanceReferenceService instanceReferenceService; + private final FolioExecutionContext folioExecutionContext; public String getInstanceStatusNameById(String id) { return instanceReferenceService.getInstanceStatusNameById(id); @@ -27,6 +30,14 @@ public String getModeOfIssuanceIdByName(String name) { return instanceReferenceService.getModeOfIssuanceIdByName(name); } + public StatisticalCode getStatisticalCodeByName(String name) { + return instanceReferenceService.getStatisticalCodeByName(name, folioExecutionContext.getTenantId()); + } + + public StatisticalCode getStatisticalCodeById(String id) { + return instanceReferenceService.getStatisticalCodeById(id, folioExecutionContext.getTenantId()); + } + public String getInstanceTypeNameById(String id) { return instanceReferenceService.getInstanceTypeNameById(id); } diff --git a/src/main/java/org/folio/bulkops/service/InstanceReferenceService.java b/src/main/java/org/folio/bulkops/service/InstanceReferenceService.java index a9019bcfa..8af3fc078 100644 --- a/src/main/java/org/folio/bulkops/service/InstanceReferenceService.java +++ b/src/main/java/org/folio/bulkops/service/InstanceReferenceService.java @@ -17,8 +17,10 @@ import org.folio.bulkops.client.InstanceTypesClient; import org.folio.bulkops.client.ModesOfIssuanceClient; import org.folio.bulkops.client.NatureOfContentTermsClient; +import org.folio.bulkops.client.StatisticalCodeClient; import org.folio.bulkops.domain.bean.InstanceFormats; import org.folio.bulkops.domain.bean.InstanceTypes; +import org.folio.bulkops.domain.bean.StatisticalCode; import org.folio.bulkops.exception.NotFoundException; import org.folio.bulkops.domain.dto.ContributorTypeCollection; import org.folio.bulkops.domain.dto.InstanceNoteType; @@ -34,6 +36,7 @@ public class InstanceReferenceService { private final InstanceStatusesClient instanceStatusesClient; private final ModesOfIssuanceClient modesOfIssuanceClient; + private final StatisticalCodeClient statisticalCodeClient; private final InstanceTypesClient instanceTypesClient; private final NatureOfContentTermsClient natureOfContentTermsClient; private final InstanceFormatsClient instanceFormatsClient; @@ -76,6 +79,24 @@ public String getModeOfIssuanceIdByName(String name) { return response.getModes().get(0).getId(); } + @Cacheable(cacheNames = "instanceStatisticalCodes") + public StatisticalCode getStatisticalCodeByName(String name, String tenantId) { + var response = statisticalCodeClient.getByQuery(String.format(QUERY_PATTERN_NAME, encode(name))); + if (response.getStatisticalCodes().isEmpty()) { + throw new NotFoundException(format("Statistical code was not found by name=%s", name)); + } + return response.getStatisticalCodes().get(0); + } + + @Cacheable(cacheNames = "instanceStatisticalCodeNames") + public StatisticalCode getStatisticalCodeById(String id, String tenantId) { + try { + return statisticalCodeClient.getById(id); + } catch (Exception e) { + throw new NotFoundException(format("Statistical code was not found by id=%s", id)); + } + } + @Cacheable(cacheNames = "instanceTypeNames") public String getInstanceTypeNameById(String id) { try { diff --git a/src/main/java/org/folio/bulkops/util/Constants.java b/src/main/java/org/folio/bulkops/util/Constants.java index f35824401..f740e2614 100644 --- a/src/main/java/org/folio/bulkops/util/Constants.java +++ b/src/main/java/org/folio/bulkops/util/Constants.java @@ -28,7 +28,7 @@ public class Constants { public static final int ITEM_NOTE_POSITION = 31; public static final int HOLDINGS_NOTE_POSITION = 24; - public static final int INSTANCE_NOTE_POSITION = 22; + public static final int INSTANCE_NOTE_POSITION = 23; public static final String ITEM_DELIMITER_PATTERN = "\\|"; public static final String KEY_VALUE_DELIMITER = ":"; diff --git a/src/main/java/org/folio/bulkops/util/UpdateOptionTypeToFieldResolver.java b/src/main/java/org/folio/bulkops/util/UpdateOptionTypeToFieldResolver.java index 1adb36d3a..82e1a205e 100644 --- a/src/main/java/org/folio/bulkops/util/UpdateOptionTypeToFieldResolver.java +++ b/src/main/java/org/folio/bulkops/util/UpdateOptionTypeToFieldResolver.java @@ -19,6 +19,7 @@ import static org.folio.bulkops.domain.dto.UpdateOptionType.PERMANENT_LOAN_TYPE; import static org.folio.bulkops.domain.dto.UpdateOptionType.PERMANENT_LOCATION; import static org.folio.bulkops.domain.dto.UpdateOptionType.STAFF_SUPPRESS; +import static org.folio.bulkops.domain.dto.UpdateOptionType.STATISTICAL_CODE; import static org.folio.bulkops.domain.dto.UpdateOptionType.STATUS; import static org.folio.bulkops.domain.dto.UpdateOptionType.SUPPRESS_FROM_DISCOVERY; import static org.folio.bulkops.domain.dto.UpdateOptionType.TEMPORARY_LOAN_TYPE; @@ -83,6 +84,8 @@ public static String getFieldByUpdateOptionType(UpdateOptionType type, EntityTyp return "Electronic access"; } else if (INSTANCE_NOTE == type) { return "Instance note"; + } else if (STATISTICAL_CODE == type) { + return "Statistical code"; } else { throw new UnsupportedOperationException("There is no matching for Operation Type: " + type); } diff --git a/src/main/resources/db/changelog/changelog-master.xml b/src/main/resources/db/changelog/changelog-master.xml index 193dd096e..3a2c6e497 100644 --- a/src/main/resources/db/changelog/changelog-master.xml +++ b/src/main/resources/db/changelog/changelog-master.xml @@ -36,4 +36,6 @@ + + diff --git a/src/main/resources/db/changelog/changes/10-12-2024_add_statistical_code_type.sql b/src/main/resources/db/changelog/changes/10-12-2024_add_statistical_code_type.sql new file mode 100644 index 000000000..47fc26741 --- /dev/null +++ b/src/main/resources/db/changelog/changes/10-12-2024_add_statistical_code_type.sql @@ -0,0 +1 @@ +ALTER TYPE UpdateOptionType ADD VALUE IF NOT EXISTS 'STATISTICAL_CODE'; diff --git a/src/main/resources/db/changelog/changes/10-12-2024_add_statistical_code_type.xml b/src/main/resources/db/changelog/changes/10-12-2024_add_statistical_code_type.xml new file mode 100644 index 000000000..fc015c1d2 --- /dev/null +++ b/src/main/resources/db/changelog/changes/10-12-2024_add_statistical_code_type.xml @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/src/main/resources/db/changelog/changes/11-12-2024_add_remove_some_to_action_type.sql b/src/main/resources/db/changelog/changes/11-12-2024_add_remove_some_to_action_type.sql new file mode 100644 index 000000000..ef8cecb54 --- /dev/null +++ b/src/main/resources/db/changelog/changes/11-12-2024_add_remove_some_to_action_type.sql @@ -0,0 +1 @@ +ALTER TYPE UpdateActionType ADD VALUE IF NOT EXISTS 'REMOVE_SOME'; diff --git a/src/main/resources/db/changelog/changes/11-12-2024_add_remove_some_to_action_type.xml b/src/main/resources/db/changelog/changes/11-12-2024_add_remove_some_to_action_type.xml new file mode 100644 index 000000000..557d794d8 --- /dev/null +++ b/src/main/resources/db/changelog/changes/11-12-2024_add_remove_some_to_action_type.xml @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/src/main/resources/swagger.api/schemas/update_action_type.json b/src/main/resources/swagger.api/schemas/update_action_type.json index 8b52eab41..c748a0187 100644 --- a/src/main/resources/swagger.api/schemas/update_action_type.json +++ b/src/main/resources/swagger.api/schemas/update_action_type.json @@ -23,7 +23,8 @@ "ADDITIONAL_SUBFIELD", "REMOVE_FIELD", "REMOVE_SUBFIELD", - "" + "", + "REMOVE_SOME" ] } } diff --git a/src/main/resources/swagger.api/schemas/update_option_type.json b/src/main/resources/swagger.api/schemas/update_option_type.json index 802e7f75d..17ec66d03 100644 --- a/src/main/resources/swagger.api/schemas/update_option_type.json +++ b/src/main/resources/swagger.api/schemas/update_option_type.json @@ -24,7 +24,8 @@ "ELECTRONIC_ACCESS_LINK_TEXT", "ELECTRONIC_ACCESS_MATERIALS_SPECIFIED", "ELECTRONIC_ACCESS_URL_PUBLIC_NOTE", - "STAFF_SUPPRESS" + "STAFF_SUPPRESS", + "STATISTICAL_CODE" ] } } diff --git a/src/test/java/org/folio/bulkops/domain/converter/InstanceStatisticalCodeListConverterTest.java b/src/test/java/org/folio/bulkops/domain/converter/InstanceStatisticalCodeListConverterTest.java new file mode 100644 index 000000000..ac17f4e44 --- /dev/null +++ b/src/test/java/org/folio/bulkops/domain/converter/InstanceStatisticalCodeListConverterTest.java @@ -0,0 +1,44 @@ +package org.folio.bulkops.domain.converter; + +import org.folio.bulkops.BaseTest; +import org.folio.bulkops.domain.bean.StatisticalCode; +import org.folio.bulkops.domain.bean.StatisticalCodeCollection; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.when; + +class InstanceStatisticalCodeListConverterTest extends BaseTest { + + private final String id1 = UUID.randomUUID().toString(); + private final String id2 = UUID.randomUUID().toString(); + + @Test + void convertToObjectWhenTwoStatisticalCodesTest() { + var converter = new InstanceStatisticalCodeListConverter(); + + when(statisticalCodeClient.getByQuery("name==\"some code\"")) + .thenReturn(StatisticalCodeCollection.builder().statisticalCodes( + List.of(StatisticalCode.builder().name("some code").id(id1).build())).build()); + + when(statisticalCodeClient.getByQuery("name==\"some code 2\"")) + .thenReturn(StatisticalCodeCollection.builder().statisticalCodes( + List.of(StatisticalCode.builder().name("some code 2").id(id2).build())).build()); + + var actual = converter.convertToObject("some code;some code 2"); + assertEquals("[" + id1 + ", " + id2 + "]", actual.toString()); + } + + @Test + void convertToStringWhenOneStatisticalCodesTest() { + var converter = new InstanceStatisticalCodeListConverter(); + + when(statisticalCodeClient.getById(id1)).thenReturn(StatisticalCode.builder().name("some code").id(id1).build()); + + var actual = converter.convertToString(List.of(id1)); + assertEquals("some code", actual); + } +} diff --git a/src/test/java/org/folio/bulkops/processor/FolioInstanceDataProcessorTest.java b/src/test/java/org/folio/bulkops/processor/FolioInstanceDataProcessorTest.java index 2ff14c789..e750182e9 100644 --- a/src/test/java/org/folio/bulkops/processor/FolioInstanceDataProcessorTest.java +++ b/src/test/java/org/folio/bulkops/processor/FolioInstanceDataProcessorTest.java @@ -9,12 +9,14 @@ import static org.folio.bulkops.domain.dto.UpdateActionType.FIND_AND_REPLACE; import static org.folio.bulkops.domain.dto.UpdateActionType.REMOVE_ALL; import static org.folio.bulkops.domain.dto.UpdateActionType.REMOVE_MARK_AS_STAFF_ONLY; +import static org.folio.bulkops.domain.dto.UpdateActionType.REMOVE_SOME; import static org.folio.bulkops.domain.dto.UpdateActionType.SET_TO_FALSE; import static org.folio.bulkops.domain.dto.UpdateActionType.SET_TO_TRUE; import static org.folio.bulkops.domain.dto.UpdateOptionType.ADMINISTRATIVE_NOTE; import static org.folio.bulkops.domain.dto.UpdateOptionType.INSTANCE_NOTE; import static org.folio.bulkops.domain.dto.UpdateOptionType.STAFF_SUPPRESS; import static org.folio.bulkops.processor.folio.InstanceNotesUpdaterFactory.INSTANCE_NOTE_TYPE_ID_KEY; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -30,12 +32,14 @@ import org.folio.bulkops.domain.dto.Action; import org.folio.bulkops.domain.bean.Instance; import org.folio.bulkops.domain.bean.InstanceNote; +import org.folio.bulkops.domain.dto.BulkOperationRuleCollection; import org.folio.bulkops.domain.dto.Parameter; import org.folio.bulkops.exception.RuleValidationException; import org.folio.bulkops.processor.folio.AdministrativeNotesUpdater; import org.folio.bulkops.processor.folio.DataProcessorFactory; import org.folio.bulkops.processor.folio.FolioInstanceDataProcessor; import org.folio.bulkops.processor.folio.InstanceNotesUpdaterFactory; +import org.folio.bulkops.processor.folio.StatisticalCodesUpdater; import org.folio.bulkops.service.ConsortiaService; import org.folio.bulkops.service.ErrorService; import org.folio.bulkops.domain.dto.BulkOperationRule; @@ -101,7 +105,8 @@ void shouldNotUpdateInstanceWhenActionIsInvalid() { @Test void testClone() { - var processor = new FolioInstanceDataProcessor(new InstanceNotesUpdaterFactory(new AdministrativeNotesUpdater())); + var instanceDataProcessor = new FolioInstanceDataProcessor(new InstanceNotesUpdaterFactory(new AdministrativeNotesUpdater(), + new StatisticalCodesUpdater())); var instance = Instance.builder() .id(UUID.randomUUID().toString()) .title("Title") @@ -109,11 +114,11 @@ void testClone() { .administrativeNotes(List.of("Note1", "Note2")) .build(); var extendedInstance = ExtendedInstance.builder().entity(instance).tenantId("tenantId").build(); - var cloned = processor.clone(extendedInstance); - assertTrue(processor.compare(extendedInstance, cloned)); + var cloned = instanceDataProcessor.clone(extendedInstance); + assertTrue(instanceDataProcessor.compare(extendedInstance, cloned)); cloned.getEntity().setAdministrativeNotes(Collections.singletonList("Note3")); - assertFalse(processor.compare(extendedInstance, cloned)); + assertFalse(instanceDataProcessor.compare(extendedInstance, cloned)); } @ParameterizedTest @@ -410,4 +415,189 @@ void shouldNotChangeTypeForAdministrativeNotesIfSourceIsNotFolio() { assertThrows(RuleValidationException.class, () -> validator.validate(ADMINISTRATIVE_NOTE, new Action().type(CHANGE_TYPE), new BulkOperationRule())); } + + @Test + void shouldThrowExceptionIfStatisticalCodeRemoveAllCombinedWithOtherActions() { + var validator = ((FolioInstanceDataProcessor) processor).validator(); + var rules = new BulkOperationRuleCollection(); + rules.addBulkOperationRulesItem(new BulkOperationRule().ruleDetails( + new BulkOperationRuleRuleDetails().actions(List.of(new Action().type(ADD_TO_EXISTING))).option(UpdateOptionType.STATISTICAL_CODE))); + rules.addBulkOperationRulesItem(new BulkOperationRule().ruleDetails( + new BulkOperationRuleRuleDetails().actions(List.of(new Action().type(REMOVE_ALL))).option(UpdateOptionType.STATISTICAL_CODE))); + + assertThrows(RuleValidationException.class, () -> validator.validate(rules)); + } + + @Test + void shouldAddToExistingMoreThanOneStatisticalCodes() { + var statCode1 = UUID.randomUUID().toString(); + var statCode2 = UUID.randomUUID().toString(); + var instance = Instance.builder() + .id(UUID.randomUUID().toString()) + .source("FOLIO") + .title("Sample title") + .build(); + + var rules = rules(new BulkOperationRule() + .ruleDetails(new BulkOperationRuleRuleDetails() + .option(UpdateOptionType.STATISTICAL_CODE) + .actions(Collections.singletonList(new Action() + .type(ADD_TO_EXISTING).updated(statCode1 + "," + statCode2))))); + var extendedInstance = ExtendedInstance.builder().entity(instance).tenantId("tenantId").build(); + + var result = processor.process(IDENTIFIER, extendedInstance, rules); + + assertEquals("[" + statCode1 + ", " + statCode2 + "]", result.getUpdated().getEntity().getStatisticalCodeIds().toString()); + assertEquals("[" + statCode1 + ", " + statCode2 + "]", result.getPreview().getEntity().getStatisticalCodeIds().toString()); + } + + @Test + void shouldAddToExistingIfAlreadyPresentStatisticalCodes() { + var statCode1 = UUID.randomUUID().toString(); + var statCode2 = UUID.randomUUID().toString(); + var instance = Instance.builder() + .id(UUID.randomUUID().toString()) + .source("FOLIO") + .title("Sample title") + .statisticalCodeIds(List.of(statCode1)) + .build(); + + var rules = rules(new BulkOperationRule() + .ruleDetails(new BulkOperationRuleRuleDetails() + .option(UpdateOptionType.STATISTICAL_CODE) + .actions(Collections.singletonList(new Action() + .type(ADD_TO_EXISTING).updated(statCode1 + "," + statCode2))))); + var extendedInstance = ExtendedInstance.builder().entity(instance).tenantId("tenantId").build(); + + var result = processor.process(IDENTIFIER, extendedInstance, rules); + + assertEquals("[" + statCode1 + ", " + statCode1 + ", " + statCode2 + "]", + result.getPreview().getEntity().getStatisticalCodeIds().toString()); + assertEquals("[" + statCode1 + ", " + statCode2 + "]", + result.getUpdated().getEntity().getStatisticalCodeIds().toString()); + } + + @Test + void shouldAddDuplicatesToExistingIfNotPresentStatisticalCodes() { + var statCode1 = UUID.randomUUID().toString(); + var statCode2 = UUID.randomUUID().toString(); + var instance = Instance.builder() + .id(UUID.randomUUID().toString()) + .source("FOLIO") + .title("Sample title") + .build(); + + var rules = rules(new BulkOperationRule() + .ruleDetails(new BulkOperationRuleRuleDetails() + .option(UpdateOptionType.STATISTICAL_CODE) + .actions(Collections.singletonList(new Action() + .type(ADD_TO_EXISTING).updated(statCode1 + "," + statCode2 + "," + statCode1))))); + var extendedInstance = ExtendedInstance.builder().entity(instance).tenantId("tenantId").build(); + + var result = processor.process(IDENTIFIER, extendedInstance, rules); + + assertEquals("[" + statCode1 + ", " + statCode2 + ", " + statCode1 + "]", + result.getPreview().getEntity().getStatisticalCodeIds().toString()); + assertEquals("[" + statCode1 + ", " + statCode2 + "]", + result.getUpdated().getEntity().getStatisticalCodeIds().toString()); + } + + @Test + void shouldAddDuplicatesToExistingIfPresentStatisticalCodes() { + var statCode1 = UUID.randomUUID().toString(); + var statCode2 = UUID.randomUUID().toString(); + var instance = Instance.builder() + .id(UUID.randomUUID().toString()) + .source("FOLIO") + .statisticalCodeIds(List.of(statCode2)) + .title("Sample title") + .build(); + + var rules = rules(new BulkOperationRule() + .ruleDetails(new BulkOperationRuleRuleDetails() + .option(UpdateOptionType.STATISTICAL_CODE) + .actions(Collections.singletonList(new Action() + .type(ADD_TO_EXISTING).updated(statCode1 + "," + statCode2 + "," + statCode1))))); + var extendedInstance = ExtendedInstance.builder().entity(instance).tenantId("tenantId").build(); + + var result = processor.process(IDENTIFIER, extendedInstance, rules); + + assertEquals("[" + statCode2 + ", " + statCode1 + ", " + statCode2 + ", " + statCode1 + "]", + result.getPreview().getEntity().getStatisticalCodeIds().toString()); + assertEquals("[" + statCode2 + ", " + statCode1 + "]", + result.getUpdated().getEntity().getStatisticalCodeIds().toString()); + } + + @Test + void shouldRemoveMoreThanOneStatisticalCodes() { + var statCode1 = UUID.randomUUID().toString(); + var statCode2 = UUID.randomUUID().toString(); + var statCode3 = UUID.randomUUID().toString(); + var instance = Instance.builder() + .id(UUID.randomUUID().toString()) + .source("FOLIO") + .title("Sample title") + .statisticalCodeIds(List.of(statCode1, statCode2, statCode3)) + .build(); + + var rules = rules(new BulkOperationRule() + .ruleDetails(new BulkOperationRuleRuleDetails() + .option(UpdateOptionType.STATISTICAL_CODE) + .actions(Collections.singletonList(new Action() + .type(REMOVE_SOME).updated(statCode1 + "," + statCode2))))); + var extendedInstance = ExtendedInstance.builder().entity(instance).tenantId("tenantId").build(); + + var result = processor.process(IDENTIFIER, extendedInstance, rules); + + assertThat(result.getUpdated().getEntity().getStatisticalCodeIds()).hasSize(1); + assertThat(result.getPreview().getEntity().getStatisticalCodeIds()).hasSize(1); + assertEquals(statCode3, result.getUpdated().getEntity().getStatisticalCodeIds().get(0)); + } + + @Test + void shouldRemoveNothingIfNoStatisticalCodes() { + var statCode1 = UUID.randomUUID().toString(); + var statCode2 = UUID.randomUUID().toString(); + var instance = Instance.builder() + .id(UUID.randomUUID().toString()) + .source("FOLIO") + .title("Sample title") + .build(); + + var rules = rules(new BulkOperationRule() + .ruleDetails(new BulkOperationRuleRuleDetails() + .option(UpdateOptionType.STATISTICAL_CODE) + .actions(Collections.singletonList(new Action() + .type(REMOVE_SOME).updated(statCode1 + "," + statCode2))))); + var extendedInstance = ExtendedInstance.builder().entity(instance).tenantId("tenantId").build(); + + var result = processor.process(IDENTIFIER, extendedInstance, rules); + + assertThat(result.getUpdated().getEntity().getStatisticalCodeIds()).isEmpty(); + assertThat(result.getPreview().getEntity().getStatisticalCodeIds()).isEmpty(); + } + + @Test + void shouldRemoveAllStatisticalCodes() { + var statCode1 = UUID.randomUUID().toString(); + var statCode2 = UUID.randomUUID().toString(); + var instance = Instance.builder() + .id(UUID.randomUUID().toString()) + .source("FOLIO") + .statisticalCodeIds(List.of(statCode1, statCode2)) + .title("Sample title") + .build(); + + var rules = rules(new BulkOperationRule() + .ruleDetails(new BulkOperationRuleRuleDetails() + .option(UpdateOptionType.STATISTICAL_CODE) + .actions(Collections.singletonList(new Action() + .type(REMOVE_ALL))))); + var extendedInstance = ExtendedInstance.builder().entity(instance).tenantId("tenantId").build(); + + var result = processor.process(IDENTIFIER, extendedInstance, rules); + + assertThat(result.getUpdated().getEntity().getStatisticalCodeIds()).isEmpty(); + assertThat(result.getPreview().getEntity().getStatisticalCodeIds()).isEmpty(); + } } diff --git a/src/test/java/org/folio/bulkops/service/InstanceReferenceServiceTest.java b/src/test/java/org/folio/bulkops/service/InstanceReferenceServiceTest.java index c19146752..6f95f94f8 100644 --- a/src/test/java/org/folio/bulkops/service/InstanceReferenceServiceTest.java +++ b/src/test/java/org/folio/bulkops/service/InstanceReferenceServiceTest.java @@ -13,6 +13,7 @@ import org.folio.bulkops.client.InstanceTypesClient; import org.folio.bulkops.client.ModesOfIssuanceClient; import org.folio.bulkops.client.NatureOfContentTermsClient; +import org.folio.bulkops.client.StatisticalCodeClient; import org.folio.bulkops.domain.bean.InstanceFormats; import org.folio.bulkops.domain.bean.InstanceStatuses; import org.folio.bulkops.domain.bean.InstanceTypes; @@ -43,6 +44,8 @@ class InstanceReferenceServiceTest { private InstanceFormatsClient instanceFormatsClient; @Mock private ContributorTypesClient contributorTypesClient; + @Mock + private StatisticalCodeClient statisticalCodeClient; @InjectMocks private InstanceReferenceService instanceReferenceService; @@ -123,4 +126,13 @@ void shouldReturnContributorTypesByCodeIfCodeContainsSlashes() { var res = instanceReferenceService.getContributorTypesByCode("http://code/code"); assertThat(res.getTotalRecords()).isEqualTo(1); } + + @Test + void shouldThrowNotFoundExceptionIfStatisticalCodeNotFound() { + var id = UUID.randomUUID().toString(); + doThrow(new NotFoundException("not found")).when(statisticalCodeClient).getById(id); + doThrow(new NotFoundException("not found")).when(statisticalCodeClient).getByQuery("name==\"some name\""); + assertThrows(NotFoundException.class, () -> instanceReferenceService.getStatisticalCodeById(id, "tenant")); + assertThrows(NotFoundException.class, () -> instanceReferenceService.getStatisticalCodeByName("some name", "tenant")); + } } diff --git a/src/test/java/org/folio/bulkops/service/PreviewServiceTest.java b/src/test/java/org/folio/bulkops/service/PreviewServiceTest.java index 5eefd2188..71da79125 100644 --- a/src/test/java/org/folio/bulkops/service/PreviewServiceTest.java +++ b/src/test/java/org/folio/bulkops/service/PreviewServiceTest.java @@ -470,10 +470,10 @@ void shouldGetCompositePreviewOnEditStep() { var res = previewService.getPreview(bulkOperation, EDIT, 0, 10); - assertThat(res.getHeader().get(22).getValue(), equalTo("General note")); - assertThat(res.getHeader().get(22).getForceVisible(), equalTo(Boolean.FALSE)); - assertThat(res.getHeader().get(23).getValue(), equalTo("Summary")); - assertThat(res.getHeader().get(23).getForceVisible(), equalTo(Boolean.TRUE)); + assertThat(res.getHeader().get(23).getValue(), equalTo("General note")); + assertThat(res.getHeader().get(23).getForceVisible(), equalTo(Boolean.FALSE)); + assertThat(res.getHeader().get(24).getValue(), equalTo("Summary")); + assertThat(res.getHeader().get(24).getForceVisible(), equalTo(Boolean.TRUE)); assertThat(res.getRows().get(1).getRow().get(0), equalTo("ed32b4a6-3895-42a0-b696-7b8ed667313f")); assertThat(res.getRows().get(1).getRow().get(4), equalTo("inst000000000001")); @@ -481,40 +481,40 @@ void shouldGetCompositePreviewOnEditStep() { assertThat(res.getRows().get(1).getRow().get(6), equalTo("2023-12-27")); assertThat(res.getRows().get(1).getRow().get(7), equalTo("Other")); assertThat(res.getRows().get(1).getRow().get(8), equalTo("serial")); - assertThat(res.getRows().get(1).getRow().get(9), equalTo("Sample note")); - assertThat(res.getRows().get(1).getRow().get(10), equalTo("ABA Journal")); - assertThat(res.getRows().get(1).getRow().get(11), equalTo("Index title")); - assertThat(res.getRows().get(1).getRow().get(12), equalTo("series")); - assertThat(res.getRows().get(1).getRow().get(13), equalTo("Sample contributor")); - assertThat(res.getRows().get(1).getRow().get(14), equalTo("2021 | 2022")); - assertThat(res.getRows().get(1).getRow().get(15), equalTo("Physical description1 | Physical description2")); - assertThat(res.getRows().get(1).getRow().get(16), equalTo("text")); - assertThat(res.getRows().get(1).getRow().get(18), equalTo("computer -- other")); - assertThat(res.getRows().get(1).getRow().get(19), equalTo("eng | fre")); - assertThat(res.getRows().get(1).getRow().get(20), equalTo("freq1 | freq2")); - assertThat(res.getRows().get(1).getRow().get(21), equalTo("range1 | range2")); - assertThat(res.getRows().get(1).getRow().get(22), equalTo("General note text")); - assertThat(res.getRows().get(1).getRow().get(23), equalTo("Summary note text")); + assertThat(res.getRows().get(1).getRow().get(10), equalTo("Sample note")); + assertThat(res.getRows().get(1).getRow().get(11), equalTo("ABA Journal")); + assertThat(res.getRows().get(1).getRow().get(12), equalTo("Index title")); + assertThat(res.getRows().get(1).getRow().get(13), equalTo("series")); + assertThat(res.getRows().get(1).getRow().get(14), equalTo("Sample contributor")); + assertThat(res.getRows().get(1).getRow().get(15), equalTo("2021 | 2022")); + assertThat(res.getRows().get(1).getRow().get(16), equalTo("Physical description1 | Physical description2")); + assertThat(res.getRows().get(1).getRow().get(17), equalTo("text")); + assertThat(res.getRows().get(1).getRow().get(19), equalTo("computer -- other")); + assertThat(res.getRows().get(1).getRow().get(20), equalTo("eng | fre")); + assertThat(res.getRows().get(1).getRow().get(21), equalTo("freq1 | freq2")); + assertThat(res.getRows().get(1).getRow().get(22), equalTo("range1 | range2")); + assertThat(res.getRows().get(1).getRow().get(23), equalTo("General note text")); + assertThat(res.getRows().get(1).getRow().get(24), equalTo("Summary note text")); assertThat(res.getRows().get(2).getRow().get(0), equalTo("e3784e11-1431-4658-b147-cad88ada1920")); assertThat(res.getRows().get(2).getRow().get(2), equalTo("true")); assertThat(res.getRows().get(2).getRow().get(4), equalTo("in00000000002")); assertThat(res.getRows().get(2).getRow().get(5), equalTo("MARC")); assertThat(res.getRows().get(2).getRow().get(8), equalTo("single unit")); - assertThat(res.getRows().get(2).getRow().get(9), equalTo("Sample note")); - assertThat(res.getRows().get(2).getRow().get(10), equalTo("summerland / Michael Chabon.")); - assertThat(res.getRows().get(2).getRow().get(11), equalTo("Mmerland /")); - assertThat(res.getRows().get(2).getRow().get(12), equalTo("series800 | series810 | series811 | series830")); - assertThat(res.getRows().get(2).getRow().get(13), equalTo("Sample contributor")); - assertThat(res.getRows().get(2).getRow().get(14), equalTo("1st ed.")); - assertThat(res.getRows().get(2).getRow().get(15), equalTo("500 p. ; 22 cm.")); - assertThat(res.getRows().get(2).getRow().get(16), equalTo("text")); - assertThat(res.getRows().get(2).getRow().get(18), equalTo("computer -- other")); - assertThat(res.getRows().get(2).getRow().get(19), equalTo("eng | fre")); - assertThat(res.getRows().get(2).getRow().get(20), equalTo("monthly. Jun 10, 2024 | yearly. 2024")); - assertThat(res.getRows().get(2).getRow().get(21), equalTo("2002-2024")); - assertThat(res.getRows().get(2).getRow().get(22), equalTo("language note (staff only)")); - assertThat(res.getRows().get(2).getRow().get(23), equalTo("Ethan Feld, the worst baseball player in the history of the game, finds himself recruited by a 100-year-old scout to help a band of fairies triumph over an ancient enemy. 2nd")); + assertThat(res.getRows().get(2).getRow().get(10), equalTo("Sample note")); + assertThat(res.getRows().get(2).getRow().get(11), equalTo("summerland / Michael Chabon.")); + assertThat(res.getRows().get(2).getRow().get(12), equalTo("Mmerland /")); + assertThat(res.getRows().get(2).getRow().get(13), equalTo("series800 | series810 | series811 | series830")); + assertThat(res.getRows().get(2).getRow().get(14), equalTo("Sample contributor")); + assertThat(res.getRows().get(2).getRow().get(15), equalTo("1st ed.")); + assertThat(res.getRows().get(2).getRow().get(16), equalTo("500 p. ; 22 cm.")); + assertThat(res.getRows().get(2).getRow().get(17), equalTo("text")); + assertThat(res.getRows().get(2).getRow().get(19), equalTo("computer -- other")); + assertThat(res.getRows().get(2).getRow().get(20), equalTo("eng | fre")); + assertThat(res.getRows().get(2).getRow().get(21), equalTo("monthly. Jun 10, 2024 | yearly. 2024")); + assertThat(res.getRows().get(2).getRow().get(22), equalTo("2002-2024")); + assertThat(res.getRows().get(2).getRow().get(23), equalTo("language note (staff only)")); + assertThat(res.getRows().get(2).getRow().get(24), equalTo("Ethan Feld, the worst baseball player in the history of the game, finds himself recruited by a 100-year-old scout to help a band of fairies triumph over an ancient enemy. 2nd")); } @Test @@ -565,30 +565,30 @@ void shouldEnrichMarcPreviewWithAdministrativeDataOnCommitStep() { var res = previewService.getPreview(bulkOperation, COMMIT, 0, 10); - assertThat(res.getHeader().get(22).getValue(), equalTo("General note")); - assertThat(res.getHeader().get(22).getForceVisible(), equalTo(Boolean.FALSE)); - assertThat(res.getHeader().get(23).getValue(), equalTo("Summary")); - assertThat(res.getHeader().get(23).getForceVisible(), equalTo(Boolean.TRUE)); + assertThat(res.getHeader().get(23).getValue(), equalTo("General note")); + assertThat(res.getHeader().get(23).getForceVisible(), equalTo(Boolean.FALSE)); + assertThat(res.getHeader().get(24).getValue(), equalTo("Summary")); + assertThat(res.getHeader().get(24).getForceVisible(), equalTo(Boolean.TRUE)); assertThat(res.getRows().get(0).getRow().get(0), equalTo("e3784e11-1431-4658-b147-cad88ada1920")); assertThat(res.getRows().get(0).getRow().get(2), equalTo("true")); assertThat(res.getRows().get(0).getRow().get(4), equalTo("in00000000002")); assertThat(res.getRows().get(0).getRow().get(5), equalTo("MARC")); assertThat(res.getRows().get(0).getRow().get(8), equalTo("single unit")); - assertThat(res.getRows().get(0).getRow().get(9), equalTo("Sample note")); - assertThat(res.getRows().get(0).getRow().get(10), equalTo("summerland / Michael Chabon.")); - assertThat(res.getRows().get(0).getRow().get(11), equalTo("Mmerland /")); - assertThat(res.getRows().get(0).getRow().get(12), equalTo("series800 | series810 | series811 | series830")); - assertThat(res.getRows().get(0).getRow().get(13), equalTo("Sample contributor")); - assertThat(res.getRows().get(0).getRow().get(14), equalTo("1st ed.")); - assertThat(res.getRows().get(0).getRow().get(15), equalTo("500 p. ; 22 cm.")); - assertThat(res.getRows().get(0).getRow().get(16), equalTo("text")); - assertThat(res.getRows().get(0).getRow().get(18), equalTo("computer -- other")); - assertThat(res.getRows().get(0).getRow().get(19), equalTo("eng | fre")); - assertThat(res.getRows().get(0).getRow().get(20), equalTo("monthly. Jun 10, 2024 | yearly. 2024")); - assertThat(res.getRows().get(0).getRow().get(21), equalTo("2002-2024")); - assertThat(res.getRows().get(0).getRow().get(22), equalTo("language note (staff only)")); - assertThat(res.getRows().get(0).getRow().get(23), equalTo("Ethan Feld, the worst baseball player in the history of the game, finds himself recruited by a 100-year-old scout to help a band of fairies triumph over an ancient enemy. 2nd")); + assertThat(res.getRows().get(0).getRow().get(10), equalTo("Sample note")); + assertThat(res.getRows().get(0).getRow().get(11), equalTo("summerland / Michael Chabon.")); + assertThat(res.getRows().get(0).getRow().get(12), equalTo("Mmerland /")); + assertThat(res.getRows().get(0).getRow().get(13), equalTo("series800 | series810 | series811 | series830")); + assertThat(res.getRows().get(0).getRow().get(14), equalTo("Sample contributor")); + assertThat(res.getRows().get(0).getRow().get(15), equalTo("1st ed.")); + assertThat(res.getRows().get(0).getRow().get(16), equalTo("500 p. ; 22 cm.")); + assertThat(res.getRows().get(0).getRow().get(17), equalTo("text")); + assertThat(res.getRows().get(0).getRow().get(19), equalTo("computer -- other")); + assertThat(res.getRows().get(0).getRow().get(20), equalTo("eng | fre")); + assertThat(res.getRows().get(0).getRow().get(21), equalTo("monthly. Jun 10, 2024 | yearly. 2024")); + assertThat(res.getRows().get(0).getRow().get(22), equalTo("2002-2024")); + assertThat(res.getRows().get(0).getRow().get(23), equalTo("language note (staff only)")); + assertThat(res.getRows().get(0).getRow().get(24), equalTo("Ethan Feld, the worst baseball player in the history of the game, finds himself recruited by a 100-year-old scout to help a band of fairies triumph over an ancient enemy. 2nd")); } @SneakyThrows @@ -620,11 +620,12 @@ void shouldCorrectlyProcessSlashCommaSequence() { var table = previewService.getPreview(bulkOperation, UPLOAD, offset, limit); - assertThat(table.getHeader().size(), equalTo(25)); - assertThat(table.getRows().get(0).getRow().size(), equalTo(25)); - assertThat(table.getRows().get(0).getRow().get(22), equalTo("Accumulation and Frequency of Use note text")); - assertThat(table.getRows().get(0).getRow().get(23), equalTo("Bibliography note text")); - assertThat(table.getRows().get(0).getRow().get(24), equalTo("General note text")); + assertThat(table.getHeader().size(), equalTo(26)); + assertThat(table.getRows().get(0).getRow().size(), equalTo(26)); + assertThat(table.getRows().get(0).getRow().get(23), equalTo("Accumulation and Frequency of Use note text")); + assertThat(table.getRows().get(0).getRow().get(24), equalTo("Bibliography note text")); + assertThat(table.getRows().get(0).getRow().get(25), equalTo("General note text")); + assertThat(table.getRows().get(0).getRow().get(9), equalTo("some code")); } private String getPathToContentUpdateRequest(org.folio.bulkops.domain.dto.EntityType entityType) { diff --git a/src/test/java/org/folio/bulkops/util/UpdateOptionTypeToFieldResolverTest.java b/src/test/java/org/folio/bulkops/util/UpdateOptionTypeToFieldResolverTest.java index 6d1dbec14..5a14dac18 100644 --- a/src/test/java/org/folio/bulkops/util/UpdateOptionTypeToFieldResolverTest.java +++ b/src/test/java/org/folio/bulkops/util/UpdateOptionTypeToFieldResolverTest.java @@ -28,6 +28,7 @@ import static org.folio.bulkops.domain.dto.UpdateOptionType.PERMANENT_LOAN_TYPE; import static org.folio.bulkops.domain.dto.UpdateOptionType.PERMANENT_LOCATION; import static org.folio.bulkops.domain.dto.UpdateOptionType.STAFF_SUPPRESS; +import static org.folio.bulkops.domain.dto.UpdateOptionType.STATISTICAL_CODE; import static org.folio.bulkops.domain.dto.UpdateOptionType.STATUS; import static org.folio.bulkops.domain.dto.UpdateOptionType.SUPPRESS_FROM_DISCOVERY; import static org.folio.bulkops.domain.dto.UpdateOptionType.TEMPORARY_LOAN_TYPE; @@ -69,7 +70,8 @@ private static Stream fieldToOptionToEntity() { Arguments.of("Electronic access", ELECTRONIC_ACCESS_LINK_TEXT, INSTANCE), Arguments.of("Electronic access", ELECTRONIC_ACCESS_MATERIALS_SPECIFIED, INSTANCE), Arguments.of("Electronic access", ELECTRONIC_ACCESS_URL_PUBLIC_NOTE, INSTANCE), - Arguments.of("Instance note", INSTANCE_NOTE, INSTANCE) + Arguments.of("Instance note", INSTANCE_NOTE, INSTANCE), + Arguments.of("Statistical code", STATISTICAL_CODE, INSTANCE) ); } diff --git a/src/test/resources/files/instance_preview_slash_before_comma.csv b/src/test/resources/files/instance_preview_slash_before_comma.csv index 2f36364df..21d64c971 100644 --- a/src/test/resources/files/instance_preview_slash_before_comma.csv +++ b/src/test/resources/files/instance_preview_slash_before_comma.csv @@ -1,2 +1,2 @@ -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,Notes -7dccf894-b8ee-4284-9c68-79348b5bd4f4,false,,false,in00000000080,MARC,,,single unit,,"The ""Benevolent"" state : the growth of welfare in Canada / Allan Moscovitch & Jim Albert, editors.",""Benevolent"" state : the growth of welfare in Canada /,,"Moscovitch, Allan, 1946-; Albert, Jim; University. School of Social Work test",,273 p. : ill. ; 23 cm.,unspecified,,,eng,,,"General note;General note text;false | Bibliography note;Bibliography note text;false | Accumulation and Frequency of Use note;Accumulation and Frequency of Use note text;false" +Instance UUID,Suppress from discovery,Staff suppress,Previously held,Instance HRID,Source,Cataloged date,Instance status term,Mode of issuance,Statistical code,Administrative note,Resource title,Index title,Series statements,Contributors,Edition,Physical description,Resource type,Nature of content,Formats,Languages,Publication frequency,Publication range,Notes +7dccf894-b8ee-4284-9c68-79348b5bd4f4,false,,false,in00000000080,MARC,,,single unit,some code,,"The ""Benevolent"" state : the growth of welfare in Canada / Allan Moscovitch & Jim Albert, editors.",""Benevolent"" state : the growth of welfare in Canada /,,"Moscovitch, Allan, 1946-; Albert, Jim; University. School of Social Work test",,273 p. : ill. ; 23 cm.,unspecified,,,eng,,,"General note;General note text;false | Bibliography note;Bibliography note text;false | Accumulation and Frequency of Use note;Accumulation and Frequency of Use note text;false" diff --git a/src/test/resources/files/instances_preview.csv b/src/test/resources/files/instances_preview.csv index 2c56717bb..19589c064 100644 --- a/src/test/resources/files/instances_preview.csv +++ b/src/test/resources/files/instances_preview.csv @@ -1,6 +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,Notes -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,"General note;General note text;false | Summary;Summary note text;false" -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,"General note;General note text;false | Summary;Summary note text;false" -998cc146-a582-4d36-a148-ae3c653ce431,false,true,false,in00000000002,MARC,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,"General note;General note text;false | Summary;Summary note text;false" -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,"General note;General note text;false | Summary;Summary note text;false" -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,"General note;General note text;false | Summary;Summary note text;false" +Instance UUID,Suppress from discovery,Staff suppress,Previously held,Instance HRID,Source,Cataloged date,Instance status term,Mode of issuance,Statistical code,Administrative note,Resource title,Index title,Series statements,Contributors,Edition,Physical description,Resource type,Nature of content,Formats,Languages,Publication frequency,Publication range,Notes +69640328-788e-43fc-9c3c-af39e243f3b7,false,,false,inst000000000001,FOLIO,2023-12-27,Other,serial,some code,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,"General note;General note text;false | Summary;Summary note text;false" +ed32b4a6-3895-42a0-b696-7b8ed667313f,false,,false,inst000000000001,FOLIO,2023-12-27,Other,serial,some code,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,"General note;General note text;false | Summary;Summary note text;false" +998cc146-a582-4d36-a148-ae3c653ce431,false,true,false,in00000000002,MARC,2023-12-27,Other,serial,some code,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,"General note;General note text;false | Summary;Summary note text;false" +34c1cba5-7308-445a-976a-1096edfd8c2c,false,,false,inst000000000001,FOLIO,2023-12-27,Other,serial,some code,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,"General note;General note text;false | Summary;Summary note text;false" +445043d9-3ac3-4689-90e5-23be69fd6a3d,false,,false,inst000000000001,FOLIO,2023-12-27,Other,serial,some code,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,"General note;General note text;false | Summary;Summary note text;false"