From 8f75807584d896f6844bb6182d3836f39a4c7d10 Mon Sep 17 00:00:00 2001 From: obozhko-folio <83696213+obozhko-folio@users.noreply.github.com> Date: Thu, 26 Sep 2024 15:13:49 +0300 Subject: [PATCH] MODBULKOPS-334 - Preventing record update with values from different tenants (#264) * MODBULKOPS-334 Added validation --- .../entity/BulkOperationRuleDetails.java | 2 + .../RuleValidationTenantsException.java | 7 + .../processor/AbstractDataProcessor.java | 33 ++- .../processor/FolioInstanceDataProcessor.java | 7 +- .../processor/HoldingsDataProcessor.java | 22 +- .../bulkops/processor/ItemDataProcessor.java | 22 +- .../bulkops/processor/UserDataProcessor.java | 7 +- .../folio/bulkops/processor/Validator.java | 5 +- .../bulkops/service/ConsortiaService.java | 4 +- .../folio/bulkops/service/RuleService.java | 4 +- .../org/folio/bulkops/util/Constants.java | 1 + .../db/changelog/changelog-master.xml | 1 + .../20-09-2024_add_tenants_to_rules.sql | 2 + .../20-09-2024_add_tenants_to_rules.xml | 13 + .../resources/swagger.api/schemas/action.json | 6 + .../schemas/bulk_operation_rule.json | 6 + src/test/java/org/folio/bulkops/BaseTest.java | 8 + .../FolioInstanceDataProcessorTest.java | 4 +- .../processor/HoldingsDataProcessorTest.java | 179 ++++++++++---- .../processor/ItemDataProcessorTest.java | 228 ++++++++++++------ 20 files changed, 419 insertions(+), 142 deletions(-) create mode 100644 src/main/java/org/folio/bulkops/exception/RuleValidationTenantsException.java create mode 100644 src/main/resources/db/changelog/changes/20-09-2024_add_tenants_to_rules.sql create mode 100644 src/main/resources/db/changelog/changes/20-09-2024_add_tenants_to_rules.xml diff --git a/src/main/java/org/folio/bulkops/domain/entity/BulkOperationRuleDetails.java b/src/main/java/org/folio/bulkops/domain/entity/BulkOperationRuleDetails.java index 913a2eab..c3374016 100644 --- a/src/main/java/org/folio/bulkops/domain/entity/BulkOperationRuleDetails.java +++ b/src/main/java/org/folio/bulkops/domain/entity/BulkOperationRuleDetails.java @@ -45,4 +45,6 @@ public class BulkOperationRuleDetails { @Type(JsonBinaryType.class) @Column(columnDefinition = "jsonb") private List<Parameter> parameters; + + private List<String> tenants; } diff --git a/src/main/java/org/folio/bulkops/exception/RuleValidationTenantsException.java b/src/main/java/org/folio/bulkops/exception/RuleValidationTenantsException.java new file mode 100644 index 00000000..0dab065f --- /dev/null +++ b/src/main/java/org/folio/bulkops/exception/RuleValidationTenantsException.java @@ -0,0 +1,7 @@ +package org.folio.bulkops.exception; + +public class RuleValidationTenantsException extends Exception { + public RuleValidationTenantsException(String message) { + super(message); + } +} diff --git a/src/main/java/org/folio/bulkops/processor/AbstractDataProcessor.java b/src/main/java/org/folio/bulkops/processor/AbstractDataProcessor.java index 8166668f..902222f2 100644 --- a/src/main/java/org/folio/bulkops/processor/AbstractDataProcessor.java +++ b/src/main/java/org/folio/bulkops/processor/AbstractDataProcessor.java @@ -8,17 +8,30 @@ import org.folio.bulkops.domain.dto.BulkOperationRuleCollection; import org.folio.bulkops.domain.dto.UpdateOptionType; import org.folio.bulkops.exception.RuleValidationException; +import org.folio.bulkops.exception.RuleValidationTenantsException; +import org.folio.bulkops.service.ConsortiaService; import org.folio.bulkops.service.ErrorService; +import org.folio.spring.FolioExecutionContext; +import org.folio.spring.FolioModuleMetadata; +import org.folio.spring.scope.FolioExecutionContextSetter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import lombok.extern.log4j.Log4j2; +import static org.folio.bulkops.util.FolioExecutionContextUtil.prepareContextForTenant; + @Log4j2 @Component public abstract class AbstractDataProcessor<T extends BulkOperationsEntity> implements DataProcessor<T> { @Autowired private ErrorService errorService; + @Autowired + private FolioModuleMetadata folioModuleMetadata; + @Autowired + private ConsortiaService consortiaService; + @Autowired + protected FolioExecutionContext folioExecutionContext; @Override public UpdatedEntityHolder process(String identifier, T entity, BulkOperationRuleCollection rules) { @@ -30,11 +43,17 @@ public UpdatedEntityHolder process(String identifier, T entity, BulkOperationRul var option = details.getOption(); for (Action action : details.getActions()) { try { - updater(option, action).apply(preview); - validator(entity).validate(option, action); - updater(option, action).apply(updated); + updater(option, action, entity, rule).apply(preview); + validator(entity).validate(option, action, rule); + updater(option, action, entity, rule).apply(updated); } catch (RuleValidationException e) { errorService.saveError(rule.getBulkOperationId(), identifier, e.getMessage()); + } catch (RuleValidationTenantsException e) { + try (var ignored = new FolioExecutionContextSetter(prepareContextForTenant(consortiaService.getCentralTenantId(folioExecutionContext.getTenantId()), folioModuleMetadata, folioExecutionContext))) { + log.info("current tenant: {}", folioExecutionContext.getTenantId()); + errorService.saveError(rule.getBulkOperationId(), identifier, e.getMessage()); + } + log.error(e.getMessage()); } catch (Exception e) { log.error(String.format("%s id=%s, error: %s", updated.getRecordBulkOperationEntity().getClass().getSimpleName(), "id", e.getMessage())); errorService.saveError(rule.getBulkOperationId(), identifier, e.getMessage()); @@ -50,18 +69,20 @@ public UpdatedEntityHolder process(String identifier, T entity, BulkOperationRul * Returns validator * * @param entity entity of type {@link T} to validate - * @return true if {@link UpdateOptionType} and {@link Action} can be applied to entity + * @return true if {@link UpdateOptionType} and {@link Action}, and {@link BulkOperationRule} can be applied to entity */ - public abstract Validator<UpdateOptionType, Action> validator(T entity); + public abstract Validator<UpdateOptionType, Action, BulkOperationRule> validator(T entity); /** * Returns {@link Consumer<T>} for applying changes for entity of type {@link T} * * @param option {@link UpdateOptionType} for update * @param action {@link Action} for update + * @param action {@link T} for update + * @param action {@link BulkOperationRule} for update * @return updater */ - public abstract Updater<T> updater(UpdateOptionType option, Action action); + public abstract Updater<T> updater(UpdateOptionType option, Action action, T entity, BulkOperationRule rule) throws RuleValidationTenantsException; /** * Clones object of type {@link T} diff --git a/src/main/java/org/folio/bulkops/processor/FolioInstanceDataProcessor.java b/src/main/java/org/folio/bulkops/processor/FolioInstanceDataProcessor.java index 83b78dc2..c1a4aa50 100644 --- a/src/main/java/org/folio/bulkops/processor/FolioInstanceDataProcessor.java +++ b/src/main/java/org/folio/bulkops/processor/FolioInstanceDataProcessor.java @@ -16,6 +16,7 @@ import org.folio.bulkops.domain.bean.ExtendedInstance; import org.folio.bulkops.domain.dto.Action; import org.folio.bulkops.domain.dto.UpdateOptionType; +import org.folio.bulkops.domain.dto.BulkOperationRule; import org.folio.bulkops.exception.BulkOperationException; import org.folio.bulkops.exception.RuleValidationException; import org.springframework.stereotype.Component; @@ -32,8 +33,8 @@ public class FolioInstanceDataProcessor extends AbstractDataProcessor<ExtendedIn private final InstanceNotesUpdaterFactory instanceNotesUpdaterFactory; @Override - public Validator<UpdateOptionType, Action> validator(ExtendedInstance extendedInstance) { - return (option, action) -> { + public Validator<UpdateOptionType, Action, BulkOperationRule> validator(ExtendedInstance extendedInstance) { + return (option, action, rule) -> { if (CLEAR_FIELD.equals(action.getType()) && Set.of(STAFF_SUPPRESS, SUPPRESS_FROM_DISCOVERY).contains(option)) { throw new RuleValidationException("Suppress flag cannot be cleared."); } else if (INSTANCE_NOTE.equals(option) && !"FOLIO".equals(extendedInstance.getEntity().getSource())) { @@ -45,7 +46,7 @@ public Validator<UpdateOptionType, Action> validator(ExtendedInstance extendedIn } @Override - public Updater<ExtendedInstance> updater(UpdateOptionType option, Action action) { + public Updater<ExtendedInstance> updater(UpdateOptionType option, Action action, ExtendedInstance entity, BulkOperationRule rule) { if (STAFF_SUPPRESS.equals(option)) { if (SET_TO_TRUE.equals(action.getType())) { return extendedInstance -> extendedInstance.getEntity().setStaffSuppress(true); diff --git a/src/main/java/org/folio/bulkops/processor/HoldingsDataProcessor.java b/src/main/java/org/folio/bulkops/processor/HoldingsDataProcessor.java index f7b48c65..cbf35e59 100644 --- a/src/main/java/org/folio/bulkops/processor/HoldingsDataProcessor.java +++ b/src/main/java/org/folio/bulkops/processor/HoldingsDataProcessor.java @@ -1,6 +1,7 @@ package org.folio.bulkops.processor; import static java.lang.String.format; +import static java.util.Objects.nonNull; import static org.apache.commons.lang3.ObjectUtils.isEmpty; import static org.folio.bulkops.domain.dto.UpdateActionType.CLEAR_FIELD; import static org.folio.bulkops.domain.dto.UpdateActionType.REPLACE_WITH; @@ -16,6 +17,7 @@ import static org.folio.bulkops.domain.dto.UpdateOptionType.PERMANENT_LOCATION; import static org.folio.bulkops.domain.dto.UpdateOptionType.SUPPRESS_FROM_DISCOVERY; import static org.folio.bulkops.domain.dto.UpdateOptionType.TEMPORARY_LOCATION; +import static org.folio.bulkops.util.Constants.RECORD_CANNOT_BE_UPDATED_ERROR_TEMPLATE; import java.util.ArrayList; import java.util.Objects; @@ -25,15 +27,16 @@ import org.apache.commons.lang3.StringUtils; import org.folio.bulkops.domain.bean.ExtendedHoldingsRecord; import org.folio.bulkops.domain.dto.Action; +import org.folio.bulkops.domain.dto.BulkOperationRule; import org.folio.bulkops.domain.dto.UpdateActionType; import org.folio.bulkops.domain.dto.UpdateOptionType; import org.folio.bulkops.exception.BulkOperationException; import org.folio.bulkops.exception.NotFoundException; import org.folio.bulkops.exception.RuleValidationException; +import org.folio.bulkops.exception.RuleValidationTenantsException; import org.folio.bulkops.service.HoldingsReferenceService; import org.folio.bulkops.service.ItemReferenceService; import org.folio.bulkops.service.ElectronicAccessReferenceService; -import org.folio.spring.FolioExecutionContext; import org.springframework.stereotype.Component; import lombok.AllArgsConstructor; @@ -50,14 +53,13 @@ public class HoldingsDataProcessor extends AbstractDataProcessor<ExtendedHolding private final HoldingsNotesUpdater holdingsNotesUpdater; private final ElectronicAccessUpdaterFactory electronicAccessUpdaterFactory; private final ElectronicAccessReferenceService electronicAccessReferenceService; - private final FolioExecutionContext folioExecutionContext; private static final Pattern UUID_REGEX = Pattern.compile("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"); @Override - public Validator<UpdateOptionType, Action> validator(ExtendedHoldingsRecord extendedHoldingsRecord) { - return (option, action) -> { + public Validator<UpdateOptionType, Action, BulkOperationRule> validator(ExtendedHoldingsRecord extendedHoldingsRecord) { + return (option, action, rule) -> { try { if ("MARC".equals(holdingsReferenceService.getSourceById(extendedHoldingsRecord.getEntity().getSourceId(), folioExecutionContext.getTenantId()).getName())) { throw new RuleValidationException("Holdings records that have source \"MARC\" cannot be changed"); @@ -74,7 +76,10 @@ public Validator<UpdateOptionType, Action> validator(ExtendedHoldingsRecord exte }; } - public Updater<ExtendedHoldingsRecord> updater(UpdateOptionType option, Action action) { + public Updater<ExtendedHoldingsRecord> updater(UpdateOptionType option, Action action, ExtendedHoldingsRecord entity, BulkOperationRule rule) throws RuleValidationTenantsException { + if (ruleTenantsAreNotValid(rule, action, entity)) { + throw new RuleValidationTenantsException(String.format(RECORD_CANNOT_BE_UPDATED_ERROR_TEMPLATE, entity.getIdentifier(org.folio.bulkops.domain.dto.IdentifierType.ID), entity.getTenant(), option.getValue())); + } if (isElectronicAccessUpdate(option)) { return (Updater<ExtendedHoldingsRecord>) electronicAccessUpdaterFactory.updater(option, action); } else if (REPLACE_WITH == action.getType()) { @@ -181,4 +186,11 @@ public boolean compare(ExtendedHoldingsRecord first, ExtendedHoldingsRecord seco public Class<ExtendedHoldingsRecord> getProcessedType() { return ExtendedHoldingsRecord.class; } + + private boolean ruleTenantsAreNotValid(BulkOperationRule rule, Action action, ExtendedHoldingsRecord extendedHolding) { + var ruleTenants = rule.getRuleDetails().getTenants(); + var actionTenants = action.getTenants(); + return nonNull(ruleTenants) && !ruleTenants.isEmpty() && !ruleTenants.contains(extendedHolding.getTenant()) || + nonNull(actionTenants) && !actionTenants.isEmpty() && !actionTenants.contains(extendedHolding.getTenant()); + } } diff --git a/src/main/java/org/folio/bulkops/processor/ItemDataProcessor.java b/src/main/java/org/folio/bulkops/processor/ItemDataProcessor.java index fcfe4482..993fa624 100644 --- a/src/main/java/org/folio/bulkops/processor/ItemDataProcessor.java +++ b/src/main/java/org/folio/bulkops/processor/ItemDataProcessor.java @@ -2,6 +2,7 @@ import static java.lang.String.format; import static java.util.Objects.isNull; +import static java.util.Objects.nonNull; import static org.apache.commons.lang3.StringUtils.isEmpty; import static org.folio.bulkops.domain.dto.UpdateActionType.CLEAR_FIELD; import static org.folio.bulkops.domain.dto.UpdateActionType.REPLACE_WITH; @@ -10,6 +11,7 @@ import static org.folio.bulkops.domain.dto.UpdateOptionType.PERMANENT_LOAN_TYPE; 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.util.Constants.RECORD_CANNOT_BE_UPDATED_ERROR_TEMPLATE; import java.util.ArrayList; import java.util.Date; @@ -22,11 +24,12 @@ import org.folio.bulkops.domain.bean.ItemLocation; import org.folio.bulkops.domain.dto.Action; import org.folio.bulkops.domain.dto.UpdateOptionType; +import org.folio.bulkops.domain.dto.BulkOperationRule; import org.folio.bulkops.exception.BulkOperationException; import org.folio.bulkops.exception.RuleValidationException; +import org.folio.bulkops.exception.RuleValidationTenantsException; import org.folio.bulkops.service.HoldingsReferenceService; import org.folio.bulkops.service.ItemReferenceService; -import org.folio.spring.FolioExecutionContext; import org.springframework.stereotype.Component; import lombok.AllArgsConstructor; @@ -39,11 +42,10 @@ public class ItemDataProcessor extends AbstractDataProcessor<ExtendedItem> { private final HoldingsReferenceService holdingsReferenceService; private final ItemReferenceService itemReferenceService; private final ItemsNotesUpdater itemsNotesUpdater; - private final FolioExecutionContext folioExecutionContext; @Override - public Validator<UpdateOptionType, Action> validator(ExtendedItem extendedItem) { - return (option, action) -> { + public Validator<UpdateOptionType, Action, BulkOperationRule> validator(ExtendedItem extendedItem) { + return (option, action, rule) -> { if (CLEAR_FIELD == action.getType() && STATUS == option) { throw new RuleValidationException("Status field can not be cleared"); } else if (CLEAR_FIELD == action.getType() && PERMANENT_LOAN_TYPE == option) { @@ -66,7 +68,10 @@ public Validator<UpdateOptionType, Action> validator(ExtendedItem extendedItem) } @Override - public Updater<ExtendedItem> updater(UpdateOptionType option, Action action) { + public Updater<ExtendedItem> updater(UpdateOptionType option, Action action, ExtendedItem entity, BulkOperationRule rule) throws RuleValidationTenantsException { + if (ruleTenantsAreNotValid(rule, action, entity)) { + throw new RuleValidationTenantsException(String.format(RECORD_CANNOT_BE_UPDATED_ERROR_TEMPLATE, entity.getIdentifier(org.folio.bulkops.domain.dto.IdentifierType.ID), entity.getTenant(), option.getValue())); + } if (REPLACE_WITH == action.getType()) { return switch (option) { case PERMANENT_LOAN_TYPE -> @@ -156,5 +161,12 @@ private ItemLocation getEffectiveLocation(Item item) { return isNull(item.getTemporaryLocation()) ? item.getPermanentLocation() : item.getTemporaryLocation(); } } + + private boolean ruleTenantsAreNotValid(BulkOperationRule rule, Action action, ExtendedItem extendedItem) { + var ruleTenants = rule.getRuleDetails().getTenants(); + var actionTenants = action.getTenants(); + return nonNull(ruleTenants) && !ruleTenants.isEmpty() && !ruleTenants.contains(extendedItem.getTenant()) || + nonNull(actionTenants) && !actionTenants.isEmpty() && !actionTenants.contains(extendedItem.getTenant()); + } } diff --git a/src/main/java/org/folio/bulkops/processor/UserDataProcessor.java b/src/main/java/org/folio/bulkops/processor/UserDataProcessor.java index 8018566f..0cf48f49 100644 --- a/src/main/java/org/folio/bulkops/processor/UserDataProcessor.java +++ b/src/main/java/org/folio/bulkops/processor/UserDataProcessor.java @@ -16,6 +16,7 @@ import org.folio.bulkops.domain.bean.User; import org.folio.bulkops.domain.dto.Action; import org.folio.bulkops.domain.dto.UpdateOptionType; +import org.folio.bulkops.domain.dto.BulkOperationRule; import org.folio.bulkops.exception.BulkOperationException; import org.folio.bulkops.exception.RuleValidationException; import org.folio.bulkops.service.UserReferenceService; @@ -33,8 +34,8 @@ public class UserDataProcessor extends AbstractDataProcessor<User> { private final UserReferenceService userReferenceService; @Override - public Validator<UpdateOptionType, Action> validator(User entity) { - return (option, action) -> { + public Validator<UpdateOptionType, Action, BulkOperationRule> validator(User entity) { + return (option, action, rule) -> { if (EXPIRATION_DATE == option) { if (action.getType() != REPLACE_WITH) { throw new RuleValidationException( @@ -63,7 +64,7 @@ public Validator<UpdateOptionType, Action> validator(User entity) { } @Override - public Updater<User> updater(UpdateOptionType option, Action action) { + public Updater<User> updater(UpdateOptionType option, Action action, User entity, BulkOperationRule rule) { return switch (option) { case PATRON_GROUP -> user -> user.setPatronGroup(action.getUpdated()); case EXPIRATION_DATE -> user -> { diff --git a/src/main/java/org/folio/bulkops/processor/Validator.java b/src/main/java/org/folio/bulkops/processor/Validator.java index 29974660..4ab709f8 100644 --- a/src/main/java/org/folio/bulkops/processor/Validator.java +++ b/src/main/java/org/folio/bulkops/processor/Validator.java @@ -1,8 +1,9 @@ package org.folio.bulkops.processor; import org.folio.bulkops.exception.RuleValidationException; +import org.folio.bulkops.exception.RuleValidationTenantsException; @FunctionalInterface -public interface Validator<T, U> { - void validate(T t, U u) throws RuleValidationException; +public interface Validator<T, U, V> { + void validate(T t, U u, V v) throws RuleValidationException, RuleValidationTenantsException; } diff --git a/src/main/java/org/folio/bulkops/service/ConsortiaService.java b/src/main/java/org/folio/bulkops/service/ConsortiaService.java index ee2d15cd..eb948154 100644 --- a/src/main/java/org/folio/bulkops/service/ConsortiaService.java +++ b/src/main/java/org/folio/bulkops/service/ConsortiaService.java @@ -29,10 +29,10 @@ public String getCentralTenantId(String currentTenantId) { var userTenantCollection = consortiaClient.getUserTenantCollection(); var userTenants = userTenantCollection.getUserTenants(); if (!userTenants.isEmpty()) { - log.debug("userTenants: {}", userTenants); + log.info("userTenants: {}", userTenants); return userTenants.get(0).getCentralTenantId(); } - log.debug("No central tenant found for {}", currentTenantId); + log.info("No central tenant found for {}", currentTenantId); return StringUtils.EMPTY; } diff --git a/src/main/java/org/folio/bulkops/service/RuleService.java b/src/main/java/org/folio/bulkops/service/RuleService.java index e573141b..694b7413 100644 --- a/src/main/java/org/folio/bulkops/service/RuleService.java +++ b/src/main/java/org/folio/bulkops/service/RuleService.java @@ -47,6 +47,7 @@ public BulkOperationRuleCollection saveRules(BulkOperationRuleCollection ruleCol .initialValue(action.getInitial()) .updatedValue(action.getUpdated()) .parameters(action.getParameters()) + .tenants(action.getTenants()) .build())); }); return ruleCollection; @@ -72,7 +73,8 @@ private BulkOperationRule mapBulkOperationRuleToDto(org.folio.bulkops.domain.ent .type(details.getUpdateAction()) .initial(details.getInitialValue()) .updated(details.getUpdatedValue()) - .parameters(details.getParameters())) + .parameters(details.getParameters()) + .tenants(details.getTenants())) .toList())); } diff --git a/src/main/java/org/folio/bulkops/util/Constants.java b/src/main/java/org/folio/bulkops/util/Constants.java index 5bb97161..c0d4d3bc 100644 --- a/src/main/java/org/folio/bulkops/util/Constants.java +++ b/src/main/java/org/folio/bulkops/util/Constants.java @@ -62,6 +62,7 @@ public class Constants { public static final String MSG_ERROR_OPTIMISTIC_LOCKING_DEFAULT = "The record cannot be saved because it is not the most recent version."; public static final String CSV_MSG_ERROR_TEMPLATE_OPTIMISTIC_LOCKING = "The record cannot be saved because it is not the most recent version. Stored version is %s, bulk edit version is %s."; + public static final String RECORD_CANNOT_BE_UPDATED_ERROR_TEMPLATE = "%s cannot be updated because the record is associated with %s and %s is not associated with this tenant."; public static final String ITEM_TYPE = "ITEM"; public static final String HOLDING_TYPE = "HOLDINGS_RECORD"; public static final Set<String> SPLIT_NOTE_ENTITIES = Set.of(ITEM_TYPE, HOLDING_TYPE); diff --git a/src/main/resources/db/changelog/changelog-master.xml b/src/main/resources/db/changelog/changelog-master.xml index ff8db8c2..17e2b8e8 100644 --- a/src/main/resources/db/changelog/changelog-master.xml +++ b/src/main/resources/db/changelog/changelog-master.xml @@ -30,4 +30,5 @@ <include file="changes/14-06-2024_add_marc_links_to_bulk_operation_table.xml" relativeToChangelogFile="true"/> <include file="changes/18-06-2024_updates_for_editing_marc.xml" relativeToChangelogFile="true"/> <include file="changes/29-08-2024_add_used_tenants.xml" relativeToChangelogFile="true"/> + <include file="changes/20-09-2024_add_tenants_to_rules.xml" relativeToChangelogFile="true"/> </databaseChangeLog> diff --git a/src/main/resources/db/changelog/changes/20-09-2024_add_tenants_to_rules.sql b/src/main/resources/db/changelog/changes/20-09-2024_add_tenants_to_rules.sql new file mode 100644 index 00000000..ed35cc4d --- /dev/null +++ b/src/main/resources/db/changelog/changes/20-09-2024_add_tenants_to_rules.sql @@ -0,0 +1,2 @@ +ALTER TABLE bulk_operation_rule_details +ADD COLUMN IF NOT EXISTS tenants TEXT[]; diff --git a/src/main/resources/db/changelog/changes/20-09-2024_add_tenants_to_rules.xml b/src/main/resources/db/changelog/changes/20-09-2024_add_tenants_to_rules.xml new file mode 100644 index 00000000..94a91cbe --- /dev/null +++ b/src/main/resources/db/changelog/changes/20-09-2024_add_tenants_to_rules.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<databaseChangeLog + xmlns="http://www.liquibase.org/xml/ns/dbchangelog" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog + http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.8.xsd"> + + + <changeSet id="20-09-2024_add_tenants_to_rules.sql" author="firebird"> + <sqlFile path="20-09-2024_add_tenants_to_rules.sql" relativeToChangelogFile="true" /> + </changeSet> + +</databaseChangeLog> diff --git a/src/main/resources/swagger.api/schemas/action.json b/src/main/resources/swagger.api/schemas/action.json index a6922eaf..8db7332f 100644 --- a/src/main/resources/swagger.api/schemas/action.json +++ b/src/main/resources/swagger.api/schemas/action.json @@ -21,6 +21,12 @@ "items": { "$ref": "action_parameter.json#/Parameter" } + }, + "tenants": { + "type": "array", + "items": { + "type": "string" + } } }, "required": [ diff --git a/src/main/resources/swagger.api/schemas/bulk_operation_rule.json b/src/main/resources/swagger.api/schemas/bulk_operation_rule.json index 6aace308..5832c466 100644 --- a/src/main/resources/swagger.api/schemas/bulk_operation_rule.json +++ b/src/main/resources/swagger.api/schemas/bulk_operation_rule.json @@ -22,6 +22,12 @@ "description": "Option to change", "$ref": "update_option_type.json#/UpdateOptionType" }, + "tenants": { + "type": "array", + "items": { + "type": "string" + } + }, "actions": { "type": "array", "items": { diff --git a/src/test/java/org/folio/bulkops/BaseTest.java b/src/test/java/org/folio/bulkops/BaseTest.java index 63e75728..40b4a780 100644 --- a/src/test/java/org/folio/bulkops/BaseTest.java +++ b/src/test/java/org/folio/bulkops/BaseTest.java @@ -298,6 +298,14 @@ public static BulkOperationRule rule(UpdateOptionType option, UpdateActionType a return rule(option, action, null, updated); } + public static BulkOperationRule rule(UpdateOptionType option, UpdateActionType action, String updated, + List<String> actionsTenants, List<String> ruleTenants) { + var rule = rule(option, action, null, updated); + rule.getRuleDetails().setTenants(ruleTenants); + rule.getRuleDetails().getActions().get(0).setTenants(actionsTenants); + return rule; + } + public BulkOperation buildBulkOperation(String fileName, org.folio.bulkops.domain.dto.EntityType entityType, org.folio.bulkops.domain.dto.BulkOperationStep step) { return switch (step) { case UPLOAD -> BulkOperation.builder() diff --git a/src/test/java/org/folio/bulkops/processor/FolioInstanceDataProcessorTest.java b/src/test/java/org/folio/bulkops/processor/FolioInstanceDataProcessorTest.java index e8644b41..a1944495 100644 --- a/src/test/java/org/folio/bulkops/processor/FolioInstanceDataProcessorTest.java +++ b/src/test/java/org/folio/bulkops/processor/FolioInstanceDataProcessorTest.java @@ -385,7 +385,7 @@ void shouldNotUpdateNotesIfSourceIsNotFolio(UpdateActionType actionType) { var extendedInstance = ExtendedInstance.builder().entity(instance).tenantId("tenantId").build(); var validator = ((FolioInstanceDataProcessor) processor).validator(extendedInstance); - assertThrows(RuleValidationException.class, () -> validator.validate(INSTANCE_NOTE, new Action().type(actionType))); + assertThrows(RuleValidationException.class, () -> validator.validate(INSTANCE_NOTE, new Action().type(actionType), new BulkOperationRule())); } @Test @@ -398,6 +398,6 @@ void shouldNotChangeTypeForAdministrativeNotesIfSourceIsNotFolio() { var extendedInstance = ExtendedInstance.builder().entity(instance).tenantId("tenantId").build(); var validator = ((FolioInstanceDataProcessor) processor).validator(extendedInstance); - assertThrows(RuleValidationException.class, () -> validator.validate(ADMINISTRATIVE_NOTE, new Action().type(CHANGE_TYPE))); + assertThrows(RuleValidationException.class, () -> validator.validate(ADMINISTRATIVE_NOTE, new Action().type(CHANGE_TYPE), new BulkOperationRule())); } } diff --git a/src/test/java/org/folio/bulkops/processor/HoldingsDataProcessorTest.java b/src/test/java/org/folio/bulkops/processor/HoldingsDataProcessorTest.java index 921e8a5b..97eb38ce 100644 --- a/src/test/java/org/folio/bulkops/processor/HoldingsDataProcessorTest.java +++ b/src/test/java/org/folio/bulkops/processor/HoldingsDataProcessorTest.java @@ -28,6 +28,10 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; import java.util.ArrayList; @@ -43,21 +47,28 @@ import org.folio.bulkops.domain.bean.HoldingsRecordsSource; import org.folio.bulkops.domain.bean.ItemLocation; import org.folio.bulkops.domain.dto.Action; +import org.folio.bulkops.domain.dto.BulkOperationRuleRuleDetails; +import org.folio.bulkops.domain.dto.BulkOperationRule; import org.folio.bulkops.domain.dto.Parameter; import org.folio.bulkops.domain.dto.UpdateOptionType; import org.folio.bulkops.exception.NotFoundException; import org.folio.bulkops.repository.BulkOperationExecutionContentRepository; +import org.folio.bulkops.service.ConsortiaService; import org.folio.bulkops.service.ElectronicAccessService; import org.folio.bulkops.service.ErrorService; +import org.folio.bulkops.util.FolioExecutionContextUtil; +import org.folio.spring.FolioExecutionContext; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.mock.mockito.MockBean; import feign.FeignException; +import org.springframework.boot.test.mock.mockito.SpyBean; class HoldingsDataProcessorTest extends BaseTest { @@ -70,6 +81,10 @@ class HoldingsDataProcessorTest extends BaseTest { ErrorService errorService; @MockBean ElectronicAccessService electronicAccessService; + @MockBean + private ConsortiaService consortiaService; + @SpyBean + private FolioExecutionContext folioExecutionContext; private DataProcessor<ExtendedHoldingsRecord> processor; @@ -276,15 +291,15 @@ void testUpdaterForSuppressFromDiscoveryOption() { .withDiscoverySuppress(false); var extendedHoldingsRecord = ExtendedHoldingsRecord.builder().entity(holdingsRecord).tenantId("tenant").build(); - var processor = new HoldingsDataProcessor(null, null, null, null, null, folioExecutionContext); + var processor = new HoldingsDataProcessor(null, null, null, null, null); - processor.updater(SUPPRESS_FROM_DISCOVERY, new Action().type(SET_TO_TRUE)).apply(extendedHoldingsRecord); + processor.updater(SUPPRESS_FROM_DISCOVERY, new Action().type(SET_TO_TRUE), extendedHoldingsRecord, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedHoldingsRecord); assertTrue(holdingsRecord.getDiscoverySuppress()); - processor.updater(SUPPRESS_FROM_DISCOVERY, new Action().type(SET_TO_FALSE)).apply(extendedHoldingsRecord); + processor.updater(SUPPRESS_FROM_DISCOVERY, new Action().type(SET_TO_FALSE), extendedHoldingsRecord, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedHoldingsRecord); assertFalse(holdingsRecord.getDiscoverySuppress()); - processor.updater(SUPPRESS_FROM_DISCOVERY, new Action().type(SET_TO_TRUE_INCLUDING_ITEMS)).apply(extendedHoldingsRecord); + processor.updater(SUPPRESS_FROM_DISCOVERY, new Action().type(SET_TO_TRUE_INCLUDING_ITEMS), extendedHoldingsRecord, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedHoldingsRecord); assertTrue(holdingsRecord.getDiscoverySuppress()); - processor.updater(SUPPRESS_FROM_DISCOVERY, new Action().type(SET_TO_FALSE_INCLUDING_ITEMS)).apply(extendedHoldingsRecord); + processor.updater(SUPPRESS_FROM_DISCOVERY, new Action().type(SET_TO_FALSE_INCLUDING_ITEMS), extendedHoldingsRecord, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedHoldingsRecord); assertFalse(holdingsRecord.getDiscoverySuppress()); } @@ -299,9 +314,9 @@ void testUpdateMarkAsStaffOnlyForHoldingsNotes() { var parameter = new Parameter(); parameter.setKey(HOLDINGS_NOTE_TYPE_ID_KEY); parameter.setValue("typeId"); - var processor = new HoldingsDataProcessor(null, null, new HoldingsNotesUpdater(new AdministrativeNotesUpdater()), null, null, folioExecutionContext); + var processor = new HoldingsDataProcessor(null, null, new HoldingsNotesUpdater(new AdministrativeNotesUpdater()), null, null); - processor.updater(HOLDINGS_NOTE, new Action().type(MARK_AS_STAFF_ONLY).parameters(List.of(parameter))).apply(extendedHoldingsRecord); + processor.updater(HOLDINGS_NOTE, new Action().type(MARK_AS_STAFF_ONLY).parameters(List.of(parameter)), extendedHoldingsRecord, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedHoldingsRecord); assertTrue(holding.getNotes().get(0).getStaffOnly()); } @@ -317,9 +332,9 @@ void testUpdateRemoveMarkAsStaffOnlyForHoldingsNotes() { var parameter = new Parameter(); parameter.setKey(HOLDINGS_NOTE_TYPE_ID_KEY); parameter.setValue("typeId"); - var processor = new HoldingsDataProcessor(null, null, new HoldingsNotesUpdater(new AdministrativeNotesUpdater()), null, null, folioExecutionContext); + var processor = new HoldingsDataProcessor(null, null, new HoldingsNotesUpdater(new AdministrativeNotesUpdater()), null, null); - processor.updater(HOLDINGS_NOTE, new Action().type(REMOVE_MARK_AS_STAFF_ONLY).parameters(List.of(parameter))).apply(extendedHoldingsRecord); + processor.updater(HOLDINGS_NOTE, new Action().type(REMOVE_MARK_AS_STAFF_ONLY).parameters(List.of(parameter)), extendedHoldingsRecord, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedHoldingsRecord); assertFalse(holding.getNotes().get(0).getStaffOnly()); } @@ -331,9 +346,9 @@ void testRemoveAdministrativeNotes() { var holding = new HoldingsRecord().withAdministrativeNotes(List.of(administrativeNote)); var extendedHoldingsRecord = ExtendedHoldingsRecord.builder().entity(holding).tenantId("tenant").build(); - var processor = new HoldingsDataProcessor(null, null, new HoldingsNotesUpdater(new AdministrativeNotesUpdater()), null, null, folioExecutionContext); + var processor = new HoldingsDataProcessor(null, null, new HoldingsNotesUpdater(new AdministrativeNotesUpdater()), null, null); - processor.updater(ADMINISTRATIVE_NOTE, new Action().type(REMOVE_ALL)).apply(extendedHoldingsRecord); + processor.updater(ADMINISTRATIVE_NOTE, new Action().type(REMOVE_ALL), extendedHoldingsRecord, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedHoldingsRecord); assertTrue(holding.getAdministrativeNotes().isEmpty()); } @@ -347,9 +362,9 @@ void testRemoveHoldingsNotes() { var parameter = new Parameter(); parameter.setKey(HOLDINGS_NOTE_TYPE_ID_KEY); parameter.setValue("typeId1"); - var processor = new HoldingsDataProcessor(null, null, new HoldingsNotesUpdater(new AdministrativeNotesUpdater()), null, null, folioExecutionContext); + var processor = new HoldingsDataProcessor(null, null, new HoldingsNotesUpdater(new AdministrativeNotesUpdater()), null, null); - processor.updater(HOLDINGS_NOTE, new Action().type(REMOVE_ALL).parameters(List.of(parameter))).apply(extendedHoldingsRecord); + processor.updater(HOLDINGS_NOTE, new Action().type(REMOVE_ALL).parameters(List.of(parameter)), extendedHoldingsRecord, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedHoldingsRecord); assertEquals(1, holding.getNotes().size()); assertEquals("typeId2", holding.getNotes().get(0).getHoldingsNoteTypeId()); } @@ -361,13 +376,13 @@ void testAddAdministrativeNotes() { var administrativeNote2 = "administrative note 2"; var holding = new HoldingsRecord(); var extendedHoldingsRecord = ExtendedHoldingsRecord.builder().entity(holding).tenantId("tenant").build(); - var processor = new HoldingsDataProcessor(null, null, new HoldingsNotesUpdater(new AdministrativeNotesUpdater()), null, null, folioExecutionContext); + var processor = new HoldingsDataProcessor(null, null, new HoldingsNotesUpdater(new AdministrativeNotesUpdater()), null, null); - processor.updater(ADMINISTRATIVE_NOTE, new Action().type(ADD_TO_EXISTING).updated(administrativeNote1)).apply(extendedHoldingsRecord); + processor.updater(ADMINISTRATIVE_NOTE, new Action().type(ADD_TO_EXISTING).updated(administrativeNote1), extendedHoldingsRecord, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedHoldingsRecord); assertEquals(1, holding.getAdministrativeNotes().size()); assertEquals(administrativeNote1, holding.getAdministrativeNotes().get(0)); - processor.updater(ADMINISTRATIVE_NOTE, new Action().type(ADD_TO_EXISTING).updated(administrativeNote2)).apply(extendedHoldingsRecord); + processor.updater(ADMINISTRATIVE_NOTE, new Action().type(ADD_TO_EXISTING).updated(administrativeNote2), extendedHoldingsRecord, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedHoldingsRecord); assertEquals(2, holding.getAdministrativeNotes().size()); } @@ -382,9 +397,9 @@ void testAddHoldingsNotes() { parameter.setKey(HOLDINGS_NOTE_TYPE_ID_KEY); parameter.setValue("typeId1"); var extendedHoldingsRecord = ExtendedHoldingsRecord.builder().entity(holding).tenantId("tenant").build(); - var processor = new HoldingsDataProcessor(null, null, new HoldingsNotesUpdater(new AdministrativeNotesUpdater()), null, null, folioExecutionContext); + var processor = new HoldingsDataProcessor(null, null, new HoldingsNotesUpdater(new AdministrativeNotesUpdater()), null, null); - processor.updater(HOLDINGS_NOTE, new Action().type(ADD_TO_EXISTING).parameters(List.of(parameter)).updated(note1)).apply(extendedHoldingsRecord); + processor.updater(HOLDINGS_NOTE, new Action().type(ADD_TO_EXISTING).parameters(List.of(parameter)).updated(note1), extendedHoldingsRecord, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedHoldingsRecord); assertEquals(1, holding.getNotes().size()); assertEquals("typeId1", holding.getNotes().get(0).getHoldingsNoteTypeId()); @@ -392,7 +407,7 @@ void testAddHoldingsNotes() { assertEquals(false, holding.getNotes().get(0).getStaffOnly()); parameter.setValue("typeId2"); - processor.updater(HOLDINGS_NOTE, new Action().type(ADD_TO_EXISTING).parameters(List.of(parameter)).updated(note2)).apply(extendedHoldingsRecord); + processor.updater(HOLDINGS_NOTE, new Action().type(ADD_TO_EXISTING).parameters(List.of(parameter)).updated(note2), extendedHoldingsRecord, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedHoldingsRecord); assertEquals(2, holding.getNotes().size()); assertEquals("typeId2", holding.getNotes().get(1).getHoldingsNoteTypeId()); @@ -401,7 +416,7 @@ void testAddHoldingsNotes() { parameter.setValue("typeId3"); List<Parameter> params = List.of(new Parameter().key(STAFF_ONLY_NOTE_PARAMETER_KEY).value("true"), parameter); - processor.updater(HOLDINGS_NOTE, new Action().type(ADD_TO_EXISTING).parameters(params).updated(note3)).apply(extendedHoldingsRecord); + processor.updater(HOLDINGS_NOTE, new Action().type(ADD_TO_EXISTING).parameters(params).updated(note3), extendedHoldingsRecord, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedHoldingsRecord); assertEquals(3, holding.getNotes().size()); assertEquals("typeId3", holding.getNotes().get(2).getHoldingsNoteTypeId()); assertEquals(note3, holding.getNotes().get(2).getNote()); @@ -415,12 +430,12 @@ void testFindAndRemoveForAdministrativeNotes() { var administrativeNote2 = "administrative note 2"; var holding = new HoldingsRecord().withAdministrativeNotes(new ArrayList<>(List.of(administrativeNote1, administrativeNote2))); var extendedHoldingsRecord = ExtendedHoldingsRecord.builder().entity(holding).tenantId("tenant").build(); - var processor = new HoldingsDataProcessor(null, null, new HoldingsNotesUpdater(new AdministrativeNotesUpdater()), null, null, folioExecutionContext); + var processor = new HoldingsDataProcessor(null, null, new HoldingsNotesUpdater(new AdministrativeNotesUpdater()), null, null); - processor.updater(ADMINISTRATIVE_NOTE, new Action().type(FIND_AND_REMOVE_THESE).initial("administrative note")).apply(extendedHoldingsRecord); + processor.updater(ADMINISTRATIVE_NOTE, new Action().type(FIND_AND_REMOVE_THESE).initial("administrative note"), extendedHoldingsRecord, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedHoldingsRecord); assertEquals(2, holding.getAdministrativeNotes().size()); - processor.updater(ADMINISTRATIVE_NOTE, new Action().type(FIND_AND_REMOVE_THESE).initial(administrativeNote2)).apply(extendedHoldingsRecord); + processor.updater(ADMINISTRATIVE_NOTE, new Action().type(FIND_AND_REMOVE_THESE).initial(administrativeNote2), extendedHoldingsRecord, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedHoldingsRecord); assertEquals(1, holding.getAdministrativeNotes().size()); assertEquals(administrativeNote1, holding.getAdministrativeNotes().get(0)); } @@ -436,16 +451,16 @@ void testFindAndRemoveHoldingsNotes() { parameter.setValue("typeId1"); var holding = new HoldingsRecord().withNotes(List.of(note1, note2, note3)); var extendedHoldingsRecord = ExtendedHoldingsRecord.builder().entity(holding).tenantId("tenant").build(); - var processor = new HoldingsDataProcessor(null, null, new HoldingsNotesUpdater(new AdministrativeNotesUpdater()), null, null, folioExecutionContext); + var processor = new HoldingsDataProcessor(null, null, new HoldingsNotesUpdater(new AdministrativeNotesUpdater()), null, null); processor.updater(HOLDINGS_NOTE, new Action().type(FIND_AND_REMOVE_THESE).initial("note") - .parameters(List.of(parameter))).apply(extendedHoldingsRecord); + .parameters(List.of(parameter)), extendedHoldingsRecord, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedHoldingsRecord); assertEquals(3, holding.getNotes().size()); processor.updater(HOLDINGS_NOTE, new Action().type(FIND_AND_REMOVE_THESE).initial("note1") - .parameters(List.of(parameter))).apply(extendedHoldingsRecord); + .parameters(List.of(parameter)), extendedHoldingsRecord, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedHoldingsRecord); assertEquals(2, holding.getNotes().size()); processor.updater(HOLDINGS_NOTE, new Action().type(FIND_AND_REMOVE_THESE).initial("note2") - .parameters(List.of(parameter))).apply(extendedHoldingsRecord); + .parameters(List.of(parameter)), extendedHoldingsRecord, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedHoldingsRecord); assertEquals(1, holding.getNotes().size()); } @@ -457,10 +472,10 @@ void testFindAndReplaceForAdministrativeNotes() { var administrativeNote3 = "administrative note 3"; var holding = new HoldingsRecord().withAdministrativeNotes(new ArrayList<>(List.of(administrativeNote1, administrativeNote2))); var extendedHoldingsRecord = ExtendedHoldingsRecord.builder().entity(holding).tenantId("tenant").build(); - var processor = new HoldingsDataProcessor(null, null, new HoldingsNotesUpdater(new AdministrativeNotesUpdater()), null, null, folioExecutionContext); + var processor = new HoldingsDataProcessor(null, null, new HoldingsNotesUpdater(new AdministrativeNotesUpdater()), null, null); processor.updater(ADMINISTRATIVE_NOTE, new Action().type(FIND_AND_REPLACE) - .initial(administrativeNote1).updated(administrativeNote3)).apply(extendedHoldingsRecord); + .initial(administrativeNote1).updated(administrativeNote3), extendedHoldingsRecord, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedHoldingsRecord); assertEquals(2, holding.getAdministrativeNotes().size()); assertEquals(administrativeNote3, holding.getAdministrativeNotes().get(0)); assertEquals(administrativeNote2, holding.getAdministrativeNotes().get(1)); @@ -476,10 +491,10 @@ void testFindAndReplaceForHoldingNotes() { parameter.setValue("typeId1"); var holding = new HoldingsRecord().withNotes(List.of(note1, note2)); var extendedHoldingsRecord = ExtendedHoldingsRecord.builder().entity(holding).tenantId("tenant").build(); - var processor = new HoldingsDataProcessor(null, null, new HoldingsNotesUpdater(new AdministrativeNotesUpdater()), null, null, folioExecutionContext); + var processor = new HoldingsDataProcessor(null, null, new HoldingsNotesUpdater(new AdministrativeNotesUpdater()), null, null); processor.updater(HOLDINGS_NOTE, new Action().type(FIND_AND_REPLACE).parameters(List.of(parameter)) - .initial("note1").updated("note3")).apply(extendedHoldingsRecord); + .initial("note1").updated("note3"), extendedHoldingsRecord, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedHoldingsRecord); assertEquals("note3", holding.getNotes().get(0).getNote()); assertEquals("note1", holding.getNotes().get(1).getNote()); @@ -491,10 +506,10 @@ void testChangeTypeForAdministrativeNotes() { var administrativeNote = "note"; var holding = new HoldingsRecord().withAdministrativeNotes(new ArrayList<>(List.of(administrativeNote))); var extendedHoldingsRecord = ExtendedHoldingsRecord.builder().entity(holding).tenantId("tenant").build(); - var processor = new HoldingsDataProcessor(null, null, new HoldingsNotesUpdater(new AdministrativeNotesUpdater()), null, null, folioExecutionContext); + var processor = new HoldingsDataProcessor(null, null, new HoldingsNotesUpdater(new AdministrativeNotesUpdater()), null, null); processor.updater(ADMINISTRATIVE_NOTE, new Action().type(CHANGE_TYPE) - .updated("typeId")).apply(extendedHoldingsRecord); + .updated("typeId"), extendedHoldingsRecord, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedHoldingsRecord); assertEquals(0, holding.getAdministrativeNotes().size()); assertEquals("note", holding.getNotes().get(0).getNote()); assertEquals("typeId", holding.getNotes().get(0).getHoldingsNoteTypeId()); @@ -510,9 +525,9 @@ void testChangeNoteTypeForHoldingsNotes() { parameter.setValue("typeId1"); var holding = new HoldingsRecord().withNotes(List.of(note1, note2)); var extendedHoldingsRecord = ExtendedHoldingsRecord.builder().entity(holding).tenantId("tenant").build(); - var processor = new HoldingsDataProcessor(null, null, new HoldingsNotesUpdater(new AdministrativeNotesUpdater()), null, null, folioExecutionContext); + var processor = new HoldingsDataProcessor(null, null, new HoldingsNotesUpdater(new AdministrativeNotesUpdater()), null, null); - processor.updater(HOLDINGS_NOTE, new Action().type(CHANGE_TYPE).updated(ADMINISTRATIVE_NOTE.getValue()).parameters(List.of(parameter))).apply(extendedHoldingsRecord); + processor.updater(HOLDINGS_NOTE, new Action().type(CHANGE_TYPE).updated(ADMINISTRATIVE_NOTE.getValue()).parameters(List.of(parameter)), extendedHoldingsRecord, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedHoldingsRecord); assertEquals(1, holding.getAdministrativeNotes().size()); assertEquals("note1", holding.getAdministrativeNotes().get(0)); @@ -522,7 +537,7 @@ void testChangeNoteTypeForHoldingsNotes() { holding.setAdministrativeNotes(null); holding.setNotes(List.of(note1, note2)); - processor.updater(HOLDINGS_NOTE, new Action().type(CHANGE_TYPE).updated("typeId3").parameters(List.of(parameter))).apply(extendedHoldingsRecord); + processor.updater(HOLDINGS_NOTE, new Action().type(CHANGE_TYPE).updated("typeId3").parameters(List.of(parameter)), extendedHoldingsRecord, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedHoldingsRecord); assertEquals(2, holding.getNotes().size()); assertEquals("note1", holding.getNotes().get(0).getNote()); assertEquals("typeId3", holding.getNotes().get(0).getHoldingsNoteTypeId()); @@ -533,7 +548,7 @@ void testChangeNoteTypeForHoldingsNotes() { @Test @SneakyThrows void testClone() { - var processor = new HoldingsDataProcessor(null, null, new HoldingsNotesUpdater(new AdministrativeNotesUpdater()), null, null, folioExecutionContext); + var processor = new HoldingsDataProcessor(null, null, new HoldingsNotesUpdater(new AdministrativeNotesUpdater()), null, null); var administrativeNotes = new ArrayList<String>(); administrativeNotes.add("note1"); var holding1 = new HoldingsRecord().withId("id") @@ -574,11 +589,11 @@ void testClone() { void shouldClearElectronicAccessFields(UpdateOptionType option) { var holdingsRecord = buildHoldingsWithElectronicAccess(); - var processor = new HoldingsDataProcessor(null, null, null, new ElectronicAccessUpdaterFactory(), null, folioExecutionContext); + var processor = new HoldingsDataProcessor(null, null, null, new ElectronicAccessUpdaterFactory(), null); var action = new Action().type(CLEAR_FIELD); var extendedHoldingsRecord = ExtendedHoldingsRecord.builder().entity(holdingsRecord).tenantId("tenant").build(); - processor.updater(option, action).apply(extendedHoldingsRecord); + processor.updater(option, action, extendedHoldingsRecord, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedHoldingsRecord); var electronicAccess = holdingsRecord.getElectronicAccess().get(0); switch (option) { @@ -602,10 +617,10 @@ void shouldClearElectronicAccessFields(UpdateOptionType option) { void shouldFindAndClearExactlyMatchedElectronicAccessFields(UpdateOptionType option, String value) { var holdingsRecord = buildHoldingsWithElectronicAccess(); var extendedHoldingsRecord = ExtendedHoldingsRecord.builder().entity(holdingsRecord).tenantId("tenant").build(); - var processor = new HoldingsDataProcessor(null, null, null, new ElectronicAccessUpdaterFactory(), null, folioExecutionContext); + var processor = new HoldingsDataProcessor(null, null, null, new ElectronicAccessUpdaterFactory(), null); var action = new Action().type(FIND_AND_REMOVE_THESE).initial(value); - processor.updater(option, action).apply(extendedHoldingsRecord); + processor.updater(option, action, extendedHoldingsRecord, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedHoldingsRecord); var modified = holdingsRecord.getElectronicAccess().get(0); var unmodified = holdingsRecord.getElectronicAccess().get(1); @@ -647,10 +662,10 @@ void shouldFindAndClearExactlyMatchedElectronicAccessFields(UpdateOptionType opt void shouldReplaceElectronicAccessFields(UpdateOptionType option, String newValue) { var holdingsRecord = buildHoldingsWithElectronicAccess(); var extendedHoldingsRecord = ExtendedHoldingsRecord.builder().entity(holdingsRecord).tenantId("tenant").build(); - var processor = new HoldingsDataProcessor(null, null, null, new ElectronicAccessUpdaterFactory(), null, folioExecutionContext); + var processor = new HoldingsDataProcessor(null, null, null, new ElectronicAccessUpdaterFactory(), null); var action = new Action().type(REPLACE_WITH).updated(newValue); - processor.updater(option, action).apply(extendedHoldingsRecord); + processor.updater(option, action, extendedHoldingsRecord, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedHoldingsRecord); var electronicAccess = holdingsRecord.getElectronicAccess().get(0); switch (option) { @@ -674,10 +689,10 @@ void shouldReplaceElectronicAccessFields(UpdateOptionType option, String newValu void shouldFindAndReplaceExactlyMatchedElectronicAccessFields(UpdateOptionType option, String initial, String updated) { var holdingsRecord = buildHoldingsWithElectronicAccess(); var extendedHoldingsRecord = ExtendedHoldingsRecord.builder().entity(holdingsRecord).tenantId("tenant").build(); - var processor = new HoldingsDataProcessor(null, null, null, new ElectronicAccessUpdaterFactory(), null, folioExecutionContext); + var processor = new HoldingsDataProcessor(null, null, null, new ElectronicAccessUpdaterFactory(), null); var action = new Action().type(FIND_AND_REPLACE).initial(initial).updated(updated); - processor.updater(option, action).apply(extendedHoldingsRecord); + processor.updater(option, action, extendedHoldingsRecord, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedHoldingsRecord); var modified = holdingsRecord.getElectronicAccess().get(0); var unmodified = holdingsRecord.getElectronicAccess().get(1); @@ -707,6 +722,80 @@ void shouldFindAndReplaceExactlyMatchedElectronicAccessFields(UpdateOptionType o } } + @Test + void testShouldNotUpdateHoldingWithPermanentLocation_whenLocationFromOtherTenantThanActionTenants() { + when(folioExecutionContext.getTenantId()).thenReturn("memberB"); + when(consortiaService.getCentralTenantId("memberB")).thenReturn("central"); + + try (var ignored = Mockito.mockStatic(FolioExecutionContextUtil.class)) { + when(FolioExecutionContextUtil.prepareContextForTenant(any(), any(), any())).thenReturn(folioExecutionContext); + + var permLocationFromMemberB = UUID.randomUUID().toString(); + var actionTenants = List.of("memberB"); + var holdId = UUID.randomUUID().toString(); + var initPermLocation = UUID.randomUUID().toString(); + var extendedHolding = ExtendedHoldingsRecord.builder().entity(new HoldingsRecord().withId(holdId).withPermanentLocationId(initPermLocation)).tenantId("memberA").build(); + + var rules = rules(rule(PERMANENT_LOCATION, REPLACE_WITH, permLocationFromMemberB, actionTenants, List.of())); + var operationId = rules.getBulkOperationRules().get(0).getBulkOperationId(); + + var result = processor.process(IDENTIFIER, extendedHolding, rules); + + assertNotNull(result); + assertEquals(initPermLocation, result.getUpdated().getEntity().getPermanentLocationId()); + + verify(errorService, times(1)).saveError(operationId, IDENTIFIER, String.format("%s cannot be updated because the record is associated with %s and %s is not associated with this tenant.", + holdId, "memberA", "PERMANENT_LOCATION").trim()); + } + } + + @Test + void testShouldNotUpdateHoldingWithPermanentLocation_whenLocationFromOtherTenantThanRuleTenants() { + when(folioExecutionContext.getTenantId()).thenReturn("memberB"); + when(consortiaService.getCentralTenantId("memberB")).thenReturn("central"); + + try (var ignored = Mockito.mockStatic(FolioExecutionContextUtil.class)) { + when(FolioExecutionContextUtil.prepareContextForTenant(any(), any(), any())).thenReturn(folioExecutionContext); + + var adminNoteFromMemberB = UUID.randomUUID().toString(); + var ruleTenants = List.of("memberB"); + var holdId = UUID.randomUUID().toString(); + var initPermLocation = UUID.randomUUID().toString(); + var extendedHolding = ExtendedHoldingsRecord.builder().entity(new HoldingsRecord().withId(holdId).withPermanentLocationId(initPermLocation)).tenantId("memberA").build(); + + var rules = rules(rule(PERMANENT_LOCATION, REPLACE_WITH, adminNoteFromMemberB, List.of(), ruleTenants)); + var operationId = rules.getBulkOperationRules().get(0).getBulkOperationId(); + + var result = processor.process(IDENTIFIER, extendedHolding, rules); + + assertNotNull(result); + assertEquals(initPermLocation, result.getUpdated().getEntity().getPermanentLocationId()); + + verify(errorService, times(1)).saveError(operationId, IDENTIFIER, String.format("%s cannot be updated because the record is associated with %s and %s is not associated with this tenant.", + holdId, "memberA", "PERMANENT_LOCATION").trim()); + } + } + + @Test + void testShouldUpdateHoldingWithLoanType_whenLoanTypeFromTenantAmongRuleTenants() { + + var locationIdFromMemberB = UUID.randomUUID().toString(); + + var ruleTenants = List.of("memberB", "memberA"); + var holdId = UUID.randomUUID().toString(); + var initPermLocation = UUID.randomUUID().toString(); + var extendedHold = ExtendedHoldingsRecord.builder().entity(new HoldingsRecord().withId(holdId).withPermanentLocationId(initPermLocation)).tenantId("memberA").build(); + + var rules = rules(rule(PERMANENT_LOCATION, REPLACE_WITH, locationIdFromMemberB, List.of(), ruleTenants)); + + var result = processor.process(IDENTIFIER, extendedHold, rules); + + assertNotNull(result); + assertEquals(locationIdFromMemberB, result.getUpdated().getEntity().getPermanentLocationId()); + + verifyNoInteractions(errorService); + } + private HoldingsRecord buildHoldingsWithElectronicAccess() { return HoldingsRecord.builder() .electronicAccess(List.of( diff --git a/src/test/java/org/folio/bulkops/processor/ItemDataProcessorTest.java b/src/test/java/org/folio/bulkops/processor/ItemDataProcessorTest.java index 0e0c1911..7887af8f 100644 --- a/src/test/java/org/folio/bulkops/processor/ItemDataProcessorTest.java +++ b/src/test/java/org/folio/bulkops/processor/ItemDataProcessorTest.java @@ -34,6 +34,10 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; import java.util.ArrayList; @@ -55,20 +59,27 @@ import org.folio.bulkops.domain.dto.Parameter; import org.folio.bulkops.domain.dto.UpdateActionType; import org.folio.bulkops.domain.dto.UpdateOptionType; +import org.folio.bulkops.domain.dto.BulkOperationRuleRuleDetails; +import org.folio.bulkops.domain.dto.BulkOperationRule; import org.folio.bulkops.repository.BulkOperationExecutionContentRepository; +import org.folio.bulkops.service.ConsortiaService; import org.folio.bulkops.service.ErrorService; import org.folio.bulkops.service.HoldingsReferenceService; import org.folio.bulkops.service.ItemReferenceService; +import org.folio.bulkops.util.FolioExecutionContextUtil; +import org.folio.spring.FolioExecutionContext; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.mock.mockito.MockBean; import lombok.SneakyThrows; +import org.springframework.boot.test.mock.mockito.SpyBean; class ItemDataProcessorTest extends BaseTest { @@ -80,6 +91,10 @@ class ItemDataProcessorTest extends BaseTest { private HoldingsReferenceService holdingsReferenceService; @MockBean private ItemReferenceService itemReferenceService; + @SpyBean + private FolioExecutionContext folioExecutionContext; + @MockBean + private ConsortiaService consortiaService; private DataProcessor<ExtendedItem> processor; @@ -325,9 +340,9 @@ void testUpdateMarkAsStaffOnlyForItemNotes() { var parameter = new Parameter(); parameter.setKey(ITEM_NOTE_TYPE_ID_KEY); parameter.setValue("typeId"); - var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater()), folioExecutionContext); + var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater())); - processor.updater(ITEM_NOTE, new Action().type(MARK_AS_STAFF_ONLY).parameters(List.of(parameter))).apply(extendedItem); + processor.updater(ITEM_NOTE, new Action().type(MARK_AS_STAFF_ONLY).parameters(List.of(parameter)), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); assertTrue(item.getNotes().get(0).getStaffOnly()); } @@ -341,9 +356,9 @@ void testUpdateRemoveMarkAsStaffOnlyForItemNotes() { var extendedItem = ExtendedItem.builder().entity(item).tenantId("tenant").build(); parameter.setKey(ITEM_NOTE_TYPE_ID_KEY); parameter.setValue("typeId"); - var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater()), folioExecutionContext); + var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater())); - processor.updater(ITEM_NOTE, new Action().type(REMOVE_MARK_AS_STAFF_ONLY).parameters(List.of(parameter))).apply(extendedItem); + processor.updater(ITEM_NOTE, new Action().type(REMOVE_MARK_AS_STAFF_ONLY).parameters(List.of(parameter)), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); assertFalse(item.getNotes().get(0).getStaffOnly()); } @@ -357,15 +372,15 @@ void testUpdateMarkAsStaffOnlyForCirculationNotes() { var parameter = new Parameter(); parameter.setKey(ITEM_NOTE_TYPE_ID_KEY); parameter.setValue("typeId"); - var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater()), folioExecutionContext); + var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater())); - processor.updater(CHECK_IN_NOTE, new Action().type(MARK_AS_STAFF_ONLY).parameters(List.of(parameter))).apply(extendedItem); + processor.updater(CHECK_IN_NOTE, new Action().type(MARK_AS_STAFF_ONLY).parameters(List.of(parameter)), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); assertTrue(item.getCirculationNotes().get(0).getStaffOnly()); circulationNote.setStaffOnly(false); circulationNote.setNoteType(CirculationNote.NoteTypeEnum.OUT); - processor.updater(CHECK_OUT_NOTE, new Action().type(MARK_AS_STAFF_ONLY).parameters(List.of(parameter))).apply(extendedItem); + processor.updater(CHECK_OUT_NOTE, new Action().type(MARK_AS_STAFF_ONLY).parameters(List.of(parameter)), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); assertTrue(item.getCirculationNotes().get(0).getStaffOnly()); } @@ -378,15 +393,15 @@ void testUpdateRemoveMarkAsStaffOnlyForCirculationNotes() { var parameter = new Parameter(); parameter.setKey(ITEM_NOTE_TYPE_ID_KEY); parameter.setValue("typeId"); - var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater()), folioExecutionContext); + var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater())); - processor.updater(CHECK_IN_NOTE, new Action().type(REMOVE_MARK_AS_STAFF_ONLY).parameters(List.of(parameter))).apply(extendedItem); + processor.updater(CHECK_IN_NOTE, new Action().type(REMOVE_MARK_AS_STAFF_ONLY).parameters(List.of(parameter)), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); assertFalse(item.getCirculationNotes().get(0).getStaffOnly()); circulationNote.setStaffOnly(true); circulationNote.setNoteType(CirculationNote.NoteTypeEnum.OUT); - processor.updater(CHECK_OUT_NOTE, new Action().type(REMOVE_MARK_AS_STAFF_ONLY).parameters(List.of(parameter))).apply(extendedItem); + processor.updater(CHECK_OUT_NOTE, new Action().type(REMOVE_MARK_AS_STAFF_ONLY).parameters(List.of(parameter)), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); assertFalse(item.getCirculationNotes().get(0).getStaffOnly()); } @@ -396,9 +411,9 @@ void testRemoveAdministrativeNotes() { var administrativeNote = "administrative note"; var item = new Item().withAdministrativeNotes(List.of(administrativeNote)); var extendedItem = ExtendedItem.builder().entity(item).tenantId("tenant").build(); - var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater()), folioExecutionContext); + var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater())); - processor.updater(ADMINISTRATIVE_NOTE, new Action().type(REMOVE_ALL)).apply(extendedItem); + processor.updater(ADMINISTRATIVE_NOTE, new Action().type(REMOVE_ALL), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); assertTrue(item.getAdministrativeNotes().isEmpty()); } @@ -409,14 +424,14 @@ void testRemoveCirculationNotes() { var checkOutNote = new CirculationNote().withNoteType(CirculationNote.NoteTypeEnum.OUT); var item = new Item().withCirculationNotes(List.of(checkInNote, checkOutNote)); var extendedItem = ExtendedItem.builder().entity(item).tenantId("tenant").build(); - var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater()), folioExecutionContext); + var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater())); - processor.updater(CHECK_IN_NOTE, new Action().type(REMOVE_ALL)).apply(extendedItem); + processor.updater(CHECK_IN_NOTE, new Action().type(REMOVE_ALL), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); assertEquals(1, item.getCirculationNotes().size()); assertEquals(CirculationNote.NoteTypeEnum.OUT, item.getCirculationNotes().get(0).getNoteType()); item.setCirculationNotes(List.of(checkInNote, checkOutNote)); - processor.updater(CHECK_OUT_NOTE, new Action().type(REMOVE_ALL)).apply(extendedItem); + processor.updater(CHECK_OUT_NOTE, new Action().type(REMOVE_ALL), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); assertEquals(1, item.getCirculationNotes().size()); assertEquals(CirculationNote.NoteTypeEnum.IN, item.getCirculationNotes().get(0).getNoteType()); } @@ -427,10 +442,10 @@ void testRemoveCheckInNoteAndAddCheckOutNoteOfTheSameNoteType() { var checkInNote = new CirculationNote().withNoteType(CirculationNote.NoteTypeEnum.IN); var item = new Item().withCirculationNotes(List.of(checkInNote)); var extendedItem = ExtendedItem.builder().entity(item).tenantId("tenant").build(); - var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater()), folioExecutionContext); + var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater())); - processor.updater(CHECK_IN_NOTE, new Action().type(REMOVE_ALL)).apply(extendedItem); - processor.updater(CHECK_OUT_NOTE, new Action().type(ADD_TO_EXISTING)).apply(extendedItem); + processor.updater(CHECK_IN_NOTE, new Action().type(REMOVE_ALL), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); + processor.updater(CHECK_OUT_NOTE, new Action().type(ADD_TO_EXISTING), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); assertEquals(1, item.getCirculationNotes().size()); assertEquals(CirculationNote.NoteTypeEnum.OUT, item.getCirculationNotes().get(0).getNoteType()); } @@ -444,10 +459,10 @@ void testRemoveItemNoteAndAddItemNoteOfTheSameNoteType() { var parameter = new Parameter(); parameter.setKey(ITEM_NOTE_TYPE_ID_KEY); parameter.setValue("typeId1"); - var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater()), folioExecutionContext); + var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater())); - processor.updater(ITEM_NOTE, new Action().type(FIND_AND_REMOVE_THESE).parameters(List.of(parameter)).initial("Action note")).apply(extendedItem); - processor.updater(ITEM_NOTE, new Action().type(ADD_TO_EXISTING).parameters(List.of(parameter))).apply(extendedItem); + processor.updater(ITEM_NOTE, new Action().type(FIND_AND_REMOVE_THESE).parameters(List.of(parameter)).initial("Action note"), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); + processor.updater(ITEM_NOTE, new Action().type(ADD_TO_EXISTING).parameters(List.of(parameter)), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); assertEquals(1, item.getNotes().size()); assertEquals("typeId1", item.getNotes().get(0).getItemNoteTypeId()); } @@ -462,9 +477,9 @@ void testRemoveItemNotes() { var parameter = new Parameter(); parameter.setKey(ITEM_NOTE_TYPE_ID_KEY); parameter.setValue("typeId1"); - var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater()), folioExecutionContext); + var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater())); - processor.updater(ITEM_NOTE, new Action().type(REMOVE_ALL).parameters(List.of(parameter))).apply(extendedItem); + processor.updater(ITEM_NOTE, new Action().type(REMOVE_ALL).parameters(List.of(parameter)), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); assertEquals(1, item.getNotes().size()); assertEquals("typeId2", item.getNotes().get(0).getItemNoteTypeId()); } @@ -476,13 +491,13 @@ void testAddAdministrativeNotes() { var administrativeNote2 = "administrative note 2"; var item = new Item(); var extendedItem = ExtendedItem.builder().entity(item).tenantId("tenant").build(); - var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater()), folioExecutionContext); + var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater())); - processor.updater(ADMINISTRATIVE_NOTE, new Action().type(ADD_TO_EXISTING).updated(administrativeNote1)).apply(extendedItem); + processor.updater(ADMINISTRATIVE_NOTE, new Action().type(ADD_TO_EXISTING).updated(administrativeNote1), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); assertEquals(1, item.getAdministrativeNotes().size()); assertEquals(administrativeNote1, item.getAdministrativeNotes().get(0)); - processor.updater(ADMINISTRATIVE_NOTE, new Action().type(ADD_TO_EXISTING).updated(administrativeNote2)).apply(extendedItem); + processor.updater(ADMINISTRATIVE_NOTE, new Action().type(ADD_TO_EXISTING).updated(administrativeNote2), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); assertEquals(2, item.getAdministrativeNotes().size()); } @@ -493,22 +508,22 @@ void testAddCirculationNotes() { var checkOutNote = "checkOutNote"; var item = new Item(); var extendedItem = ExtendedItem.builder().entity(item).tenantId("tenant").build(); - var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater()), folioExecutionContext); + var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater())); - processor.updater(CHECK_IN_NOTE, new Action().type(ADD_TO_EXISTING).updated(checkInNote)).apply(extendedItem); + processor.updater(CHECK_IN_NOTE, new Action().type(ADD_TO_EXISTING).updated(checkInNote), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); assertEquals(1, item.getCirculationNotes().size()); assertEquals(checkInNote, item.getCirculationNotes().get(0).getNote()); assertEquals(false, item.getCirculationNotes().get(0).getStaffOnly()); assertEquals(CirculationNote.NoteTypeEnum.IN, item.getCirculationNotes().get(0).getNoteType()); - processor.updater(CHECK_OUT_NOTE, new Action().type(ADD_TO_EXISTING).updated(checkOutNote)).apply(extendedItem); + processor.updater(CHECK_OUT_NOTE, new Action().type(ADD_TO_EXISTING).updated(checkOutNote), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); assertEquals(2, item.getCirculationNotes().size()); assertEquals(checkOutNote, item.getCirculationNotes().get(1).getNote()); assertEquals(false, item.getCirculationNotes().get(1).getStaffOnly()); assertEquals(CirculationNote.NoteTypeEnum.OUT, item.getCirculationNotes().get(1).getNoteType()); List<Parameter> params = Collections.singletonList(new Parameter().key(STAFF_ONLY_NOTE_PARAMETER_KEY).value("true")); - processor.updater(CHECK_OUT_NOTE, new Action().type(ADD_TO_EXISTING).parameters(params).updated(checkOutNote)).apply(extendedItem); + processor.updater(CHECK_OUT_NOTE, new Action().type(ADD_TO_EXISTING).parameters(params).updated(checkOutNote), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); assertEquals(3, item.getCirculationNotes().size()); assertEquals(checkOutNote, item.getCirculationNotes().get(2).getNote()); assertEquals(true, item.getCirculationNotes().get(2).getStaffOnly()); @@ -526,16 +541,16 @@ void testAddItemNotes() { parameter.setKey(ITEM_NOTE_TYPE_ID_KEY); parameter.setValue("typeId1"); - var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater()), folioExecutionContext); + var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater())); - processor.updater(ITEM_NOTE, new Action().type(ADD_TO_EXISTING).parameters(List.of(parameter)).updated(itemNote1)).apply(extendedItem); + processor.updater(ITEM_NOTE, new Action().type(ADD_TO_EXISTING).parameters(List.of(parameter)).updated(itemNote1), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); assertEquals(1, item.getNotes().size()); assertEquals("typeId1", item.getNotes().get(0).getItemNoteTypeId()); assertEquals(itemNote1, item.getNotes().get(0).getNote()); parameter.setValue("typeId2"); - processor.updater(ITEM_NOTE, new Action().type(ADD_TO_EXISTING).parameters(List.of(parameter)).updated(itemNote2)).apply(extendedItem); + processor.updater(ITEM_NOTE, new Action().type(ADD_TO_EXISTING).parameters(List.of(parameter)).updated(itemNote2), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); assertEquals(2, item.getNotes().size()); assertEquals("typeId2", item.getNotes().get(1).getItemNoteTypeId()); @@ -549,12 +564,12 @@ void testFindAndRemoveForAdministrativeNotes() { var administrativeNote2 = "administrative note 2"; var item = new Item().withAdministrativeNotes(new ArrayList<>(List.of(administrativeNote1, administrativeNote2))); var extendedItem = ExtendedItem.builder().entity(item).tenantId("tenant").build(); - var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater()), folioExecutionContext); + var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater())); - processor.updater(ADMINISTRATIVE_NOTE, new Action().type(FIND_AND_REMOVE_THESE).initial("administrative note")).apply(extendedItem); + processor.updater(ADMINISTRATIVE_NOTE, new Action().type(FIND_AND_REMOVE_THESE).initial("administrative note"), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); assertEquals(2, item.getAdministrativeNotes().size()); - processor.updater(ADMINISTRATIVE_NOTE, new Action().type(FIND_AND_REMOVE_THESE).initial(administrativeNote2)).apply(extendedItem); + processor.updater(ADMINISTRATIVE_NOTE, new Action().type(FIND_AND_REMOVE_THESE).initial(administrativeNote2), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); assertEquals(1, item.getAdministrativeNotes().size()); assertEquals(administrativeNote1, item.getAdministrativeNotes().get(0)); } @@ -568,17 +583,17 @@ void testFindAndRemoveForCirculationNotes() { .withNoteType(CirculationNote.NoteTypeEnum.OUT).withNote("circ note"); var item = new Item().withCirculationNotes(List.of(checkInNote, checkOutNote)); var extendedItem = ExtendedItem.builder().entity(item).tenantId("tenant").build(); - var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater()), folioExecutionContext); + var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater())); - processor.updater(CHECK_OUT_NOTE, new Action().type(FIND_AND_REMOVE_THESE).initial("note")).apply(extendedItem); + processor.updater(CHECK_OUT_NOTE, new Action().type(FIND_AND_REMOVE_THESE).initial("note"), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); assertEquals(2, item.getCirculationNotes().size()); - processor.updater(CHECK_OUT_NOTE, new Action().type(FIND_AND_REMOVE_THESE).initial("circ note")).apply(extendedItem); + processor.updater(CHECK_OUT_NOTE, new Action().type(FIND_AND_REMOVE_THESE).initial("circ note"), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); assertEquals(1, item.getCirculationNotes().size()); assertEquals("circ note", item.getCirculationNotes().get(0).getNote()); assertEquals(CirculationNote.NoteTypeEnum.IN, item.getCirculationNotes().get(0).getNoteType()); item.setCirculationNotes(List.of(checkInNote, checkOutNote)); - processor.updater(CHECK_IN_NOTE, new Action().type(FIND_AND_REMOVE_THESE).initial("circ note")).apply(extendedItem); + processor.updater(CHECK_IN_NOTE, new Action().type(FIND_AND_REMOVE_THESE).initial("circ note"), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); assertEquals(1, item.getCirculationNotes().size()); assertEquals("circ note", item.getCirculationNotes().get(0).getNote()); assertEquals(CirculationNote.NoteTypeEnum.OUT, item.getCirculationNotes().get(0).getNoteType()); @@ -595,16 +610,16 @@ void testFindAndRemoveItemNotes() { parameter.setValue("typeId1"); var item = new Item().withNotes(List.of(itemNote1, itemNote2, itemNote3)); var extendedItem = ExtendedItem.builder().entity(item).tenantId("tenant").build(); - var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater()), folioExecutionContext); + var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater())); processor.updater(ITEM_NOTE, new Action().type(FIND_AND_REMOVE_THESE).initial("itemNote") - .parameters(List.of(parameter))).apply(extendedItem); + .parameters(List.of(parameter)), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); assertEquals(3, item.getNotes().size()); processor.updater(ITEM_NOTE, new Action().type(FIND_AND_REMOVE_THESE).initial("itemNote1") - .parameters(List.of(parameter))).apply(extendedItem); + .parameters(List.of(parameter)), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); assertEquals(2, item.getNotes().size()); processor.updater(ITEM_NOTE, new Action().type(FIND_AND_REMOVE_THESE).initial("itemNote2") - .parameters(List.of(parameter))).apply(extendedItem); + .parameters(List.of(parameter)), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); assertEquals(1, item.getNotes().size()); } @@ -616,10 +631,10 @@ void testFindAndReplaceForAdministrativeNotes() { var administrativeNote3 = "administrative note 3"; var item = new Item().withAdministrativeNotes(new ArrayList<>(List.of(administrativeNote1, administrativeNote2))); var extendedItem = ExtendedItem.builder().entity(item).tenantId("tenant").build(); - var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater()), folioExecutionContext); + var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater())); processor.updater(ADMINISTRATIVE_NOTE, new Action().type(FIND_AND_REPLACE) - .initial(administrativeNote1).updated(administrativeNote3)).apply(extendedItem); + .initial(administrativeNote1).updated(administrativeNote3), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); assertEquals(2, item.getAdministrativeNotes().size()); assertEquals(administrativeNote3, item.getAdministrativeNotes().get(0)); assertEquals(administrativeNote2, item.getAdministrativeNotes().get(1)); @@ -634,10 +649,10 @@ void testFindAndReplaceForCirculationNotes() { .withNoteType(CirculationNote.NoteTypeEnum.OUT).withNote("note"); var item = new Item().withCirculationNotes(List.of(checkInNote, checkOutNote)); var extendedItem = ExtendedItem.builder().entity(item).tenantId("tenant").build(); - var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater()), folioExecutionContext); + var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater())); processor.updater(CHECK_IN_NOTE, new Action().type(FIND_AND_REPLACE) - .initial("note").updated("note 2")).apply(extendedItem); + .initial("note").updated("note 2"), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); assertEquals(2, item.getCirculationNotes().size()); assertEquals("note 2", item.getCirculationNotes().get(0).getNote()); assertEquals(CirculationNote.NoteTypeEnum.IN, item.getCirculationNotes().get(0).getNoteType()); @@ -647,7 +662,7 @@ void testFindAndReplaceForCirculationNotes() { checkInNote.setNote("note"); processor.updater(CHECK_OUT_NOTE, new Action().type(FIND_AND_REPLACE) - .initial("note").updated("note 2")).apply(extendedItem); + .initial("note").updated("note 2"), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); assertEquals(2, item.getCirculationNotes().size()); assertEquals("note", item.getCirculationNotes().get(0).getNote()); assertEquals(CirculationNote.NoteTypeEnum.IN, item.getCirculationNotes().get(0).getNoteType()); @@ -665,10 +680,10 @@ void testFindAndReplaceForItemNotes() { parameter.setValue("typeId1"); var item = new Item().withNotes(List.of(itemNote1, itemNote2)); var extendedItem = ExtendedItem.builder().entity(item).tenantId("tenant").build(); - var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater()), folioExecutionContext); + var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater())); processor.updater(ITEM_NOTE, new Action().type(FIND_AND_REPLACE).parameters(List.of(parameter)) - .initial("itemNote1").updated("itemNote3")).apply(extendedItem); + .initial("itemNote1").updated("itemNote3"), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); assertEquals("itemNote3", item.getNotes().get(0).getNote()); assertEquals("itemNote1", item.getNotes().get(1).getNote()); @@ -680,10 +695,10 @@ void testChangeTypeForAdministrativeNotes() { var administrativeNote = "note"; var item = new Item().withAdministrativeNotes(new ArrayList<>(List.of(administrativeNote))); var extendedItem = ExtendedItem.builder().entity(item).tenantId("tenant").build(); - var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater()), folioExecutionContext); + var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater())); processor.updater(ADMINISTRATIVE_NOTE, new Action().type(CHANGE_TYPE) - .updated(CHECK_IN_NOTE_TYPE)).apply(extendedItem); + .updated(CHECK_IN_NOTE_TYPE), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); assertEquals(0, item.getAdministrativeNotes().size()); assertEquals(1, item.getCirculationNotes().size()); assertEquals("note", item.getCirculationNotes().get(0).getNote()); @@ -693,7 +708,7 @@ void testChangeTypeForAdministrativeNotes() { item.setAdministrativeNotes(List.of(administrativeNote)); processor.updater(ADMINISTRATIVE_NOTE, new Action().type(CHANGE_TYPE) - .updated(CHECK_OUT_NOTE_TYPE)).apply(extendedItem); + .updated(CHECK_OUT_NOTE_TYPE), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); assertEquals(0, item.getAdministrativeNotes().size()); assertEquals(1, item.getCirculationNotes().size()); assertEquals("note", item.getCirculationNotes().get(0).getNote()); @@ -703,7 +718,7 @@ void testChangeTypeForAdministrativeNotes() { item.setAdministrativeNotes(List.of(administrativeNote)); processor.updater(ADMINISTRATIVE_NOTE, new Action().type(CHANGE_TYPE) - .updated("typeId")).apply(extendedItem); + .updated("typeId"), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); assertEquals(0, item.getAdministrativeNotes().size()); assertEquals(1, item.getNotes().size()); @@ -720,10 +735,10 @@ void testChangeNoteTypeForCirculationNotes() { .withNoteType(CirculationNote.NoteTypeEnum.OUT).withNote("note 2").withStaffOnly(true); var item = new Item().withCirculationNotes(List.of(checkInNote, checkOutNote)); var extendedItem = ExtendedItem.builder().entity(item).tenantId("tenant").build(); - var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater()), folioExecutionContext); + var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater())); processor.updater(CHECK_IN_NOTE, new Action().type(CHANGE_TYPE) - .updated(CHECK_OUT_NOTE_TYPE)).apply(extendedItem); + .updated(CHECK_OUT_NOTE_TYPE), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); assertEquals(2, item.getCirculationNotes().size()); assertEquals("note", item.getCirculationNotes().get(0).getNote()); assertEquals(CirculationNote.NoteTypeEnum.OUT, item.getCirculationNotes().get(0).getNoteType()); @@ -732,7 +747,7 @@ void testChangeNoteTypeForCirculationNotes() { checkInNote.setNoteType(CirculationNote.NoteTypeEnum.IN); processor.updater(CHECK_IN_NOTE, new Action().type(CHANGE_TYPE) - .updated(ADMINISTRATIVE_NOTE_TYPE)).apply(extendedItem); + .updated(ADMINISTRATIVE_NOTE_TYPE), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); assertEquals(1, item.getCirculationNotes().size()); assertEquals(CirculationNote.NoteTypeEnum.OUT, item.getCirculationNotes().get(0).getNoteType()); assertEquals(1, item.getAdministrativeNotes().size()); @@ -742,7 +757,7 @@ void testChangeNoteTypeForCirculationNotes() { item.setCirculationNotes(List.of(checkInNote, checkOutNote)); processor.updater(CHECK_IN_NOTE, new Action().type(CHANGE_TYPE) - .updated("typeId")).apply(extendedItem); + .updated("typeId"), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); assertEquals(1, item.getCirculationNotes().size()); assertEquals(CirculationNote.NoteTypeEnum.OUT, item.getCirculationNotes().get(0).getNoteType()); assertEquals(1, item.getNotes().size()); @@ -761,9 +776,9 @@ void testChangeNoteTypeForItemNotes() { parameter.setValue("typeId1"); var item = new Item().withNotes(List.of(itemNote1, itemNote2)); var extendedItem = ExtendedItem.builder().entity(item).tenantId("tenant").build(); - var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater()), folioExecutionContext); + var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater())); - processor.updater(ITEM_NOTE, new Action().type(CHANGE_TYPE).updated(ADMINISTRATIVE_NOTE_TYPE).parameters(List.of(parameter))).apply(extendedItem); + processor.updater(ITEM_NOTE, new Action().type(CHANGE_TYPE).updated(ADMINISTRATIVE_NOTE_TYPE).parameters(List.of(parameter)), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); assertEquals(1, item.getAdministrativeNotes().size()); assertEquals("itemNote1", item.getAdministrativeNotes().get(0)); @@ -773,7 +788,7 @@ void testChangeNoteTypeForItemNotes() { item.setAdministrativeNotes(null); item.setNotes(List.of(itemNote1, itemNote2)); - processor.updater(ITEM_NOTE, new Action().type(CHANGE_TYPE).updated(CHECK_IN_NOTE_TYPE).parameters(List.of(parameter))).apply(extendedItem); + processor.updater(ITEM_NOTE, new Action().type(CHANGE_TYPE).updated(CHECK_IN_NOTE_TYPE).parameters(List.of(parameter)), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); assertEquals(1, item.getCirculationNotes().size()); assertEquals("itemNote1", item.getCirculationNotes().get(0).getNote()); assertEquals(CirculationNote.NoteTypeEnum.IN, item.getCirculationNotes().get(0).getNoteType()); @@ -784,7 +799,7 @@ void testChangeNoteTypeForItemNotes() { item.setCirculationNotes(null); item.setNotes(List.of(itemNote1, itemNote2)); - processor.updater(ITEM_NOTE, new Action().type(CHANGE_TYPE).updated(CHECK_OUT_NOTE_TYPE).parameters(List.of(parameter))).apply(extendedItem); + processor.updater(ITEM_NOTE, new Action().type(CHANGE_TYPE).updated(CHECK_OUT_NOTE_TYPE).parameters(List.of(parameter)), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); assertEquals(1, item.getCirculationNotes().size()); assertEquals("itemNote1", item.getCirculationNotes().get(0).getNote()); assertEquals(CirculationNote.NoteTypeEnum.OUT, item.getCirculationNotes().get(0).getNoteType()); @@ -795,7 +810,7 @@ void testChangeNoteTypeForItemNotes() { item.setCirculationNotes(null); item.setNotes(List.of(itemNote1, itemNote2)); - processor.updater(ITEM_NOTE, new Action().type(CHANGE_TYPE).updated("typeId3").parameters(List.of(parameter))).apply(extendedItem); + processor.updater(ITEM_NOTE, new Action().type(CHANGE_TYPE).updated("typeId3").parameters(List.of(parameter)), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); assertEquals(2, item.getNotes().size()); assertEquals("itemNote1", item.getNotes().get(0).getNote()); assertEquals("typeId3", item.getNotes().get(0).getItemNoteTypeId()); @@ -812,9 +827,9 @@ void testDuplicateForCirculationNotes() { .withNoteType(CirculationNote.NoteTypeEnum.OUT).withNote("note 2").withStaffOnly(true); var item = new Item().withCirculationNotes(new ArrayList<>(List.of(checkInNote, checkOutNote))); var extendedItem = ExtendedItem.builder().entity(item).tenantId("tenant").build(); - var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater()), folioExecutionContext); + var processor = new ItemDataProcessor(null, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater())); - processor.updater(CHECK_IN_NOTE, new Action().type(DUPLICATE).updated(CHECK_OUT_NOTE_TYPE)).apply(extendedItem); + processor.updater(CHECK_IN_NOTE, new Action().type(DUPLICATE).updated(CHECK_OUT_NOTE_TYPE), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); assertEquals(3, item.getCirculationNotes().size()); assertEquals(2, item.getCirculationNotes().stream().filter(circNote -> circNote.getNoteType() == CirculationNote.NoteTypeEnum.OUT).count()); var duplicated = item.getCirculationNotes().stream().filter(circNote -> @@ -822,7 +837,7 @@ void testDuplicateForCirculationNotes() { assertTrue(duplicated.isPresent()); assertTrue(duplicated.get().getStaffOnly()); - processor.updater(CHECK_OUT_NOTE, new Action().type(DUPLICATE).updated(CHECK_IN_NOTE_TYPE)).apply(extendedItem); + processor.updater(CHECK_OUT_NOTE, new Action().type(DUPLICATE).updated(CHECK_IN_NOTE_TYPE), extendedItem, new BulkOperationRule().ruleDetails(new BulkOperationRuleRuleDetails())).apply(extendedItem); assertEquals(5, item.getCirculationNotes().size()); assertEquals(3, item.getCirculationNotes().stream().filter(circNote -> circNote.getNoteType() == CirculationNote.NoteTypeEnum.IN).count()); @@ -838,7 +853,7 @@ void testDuplicateForCirculationNotes() { @Test void testClone() { - var processor = new ItemDataProcessor(holdingsReferenceService, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater()), folioExecutionContext); + var processor = new ItemDataProcessor(holdingsReferenceService, null, new ItemsNotesUpdater(new AdministrativeNotesUpdater())); var administrativeNotes = new ArrayList<String>(); administrativeNotes.add("note1"); var item1 = new Item().withId("id") @@ -883,4 +898,81 @@ void testClone() { assertFalse(processor.compare(extendedItem1, extendedItem2)); } + + @Test + void testShouldNotUpdateItemWithLoanType_whenLoanTypeFromOtherTenantThanActionTenants() { + when(folioExecutionContext.getTenantId()).thenReturn("memberB"); + when(consortiaService.getCentralTenantId("memberB")).thenReturn("central"); + + try (var ignored = Mockito.mockStatic(FolioExecutionContextUtil.class)) { + when(FolioExecutionContextUtil.prepareContextForTenant(any(), any(), any())).thenReturn(folioExecutionContext); + + var loanTypeFromMemberB = UUID.randomUUID().toString(); + var actionTenants = List.of("memberB"); + var itemId = UUID.randomUUID().toString(); + var initPermanentLoanTypeId = UUID.randomUUID().toString(); + var extendedItem = ExtendedItem.builder().entity(new Item().withId(itemId).withPermanentLoanType(new LoanType().withId(initPermanentLoanTypeId))).tenantId("memberA").build(); + + var rules = rules(rule(PERMANENT_LOAN_TYPE, REPLACE_WITH, loanTypeFromMemberB, actionTenants, List.of())); + var operationId = rules.getBulkOperationRules().get(0).getBulkOperationId(); + + var result = processor.process(IDENTIFIER, extendedItem, rules); + + assertNotNull(result); + assertEquals(initPermanentLoanTypeId, result.getUpdated().getEntity().getPermanentLoanType().getId()); + + verify(errorService, times(1)).saveError(operationId, IDENTIFIER, String.format("%s cannot be updated because the record is associated with %s and %s is not associated with this tenant.", + itemId, "memberA", "PERMANENT_LOAN_TYPE").trim()); + } + } + + @Test + void testShouldNotUpdateItemWithLoanType_whenLoanTypeFromOtherTenantThanRuleTenants() { + when(folioExecutionContext.getTenantId()).thenReturn("memberB"); + when(consortiaService.getCentralTenantId("memberB")).thenReturn("central"); + + try (var ignored = Mockito.mockStatic(FolioExecutionContextUtil.class)) { + when(FolioExecutionContextUtil.prepareContextForTenant(any(), any(), any())).thenReturn(folioExecutionContext); + + var adminNoteFromMemberB = UUID.randomUUID().toString(); + var ruleTenants = List.of("memberB"); + var itemId = UUID.randomUUID().toString(); + var initPermanentLoanTypeId = UUID.randomUUID().toString(); + var extendedItem = ExtendedItem.builder().entity(new Item().withId(itemId).withPermanentLoanType(new LoanType().withId(initPermanentLoanTypeId))).tenantId("memberA").build(); + + var rules = rules(rule(PERMANENT_LOAN_TYPE, REPLACE_WITH, adminNoteFromMemberB, List.of(), ruleTenants)); + var operationId = rules.getBulkOperationRules().get(0).getBulkOperationId(); + + var result = processor.process(IDENTIFIER, extendedItem, rules); + + assertNotNull(result); + assertEquals(initPermanentLoanTypeId, result.getUpdated().getEntity().getPermanentLoanType().getId()); + + verify(errorService, times(1)).saveError(operationId, IDENTIFIER, String.format("%s cannot be updated because the record is associated with %s and %s is not associated with this tenant.", + itemId, "memberA", "PERMANENT_LOAN_TYPE").trim()); + } + } + + @Test + void testShouldUpdateItemWithLoanType_whenLoanTypeFromTenantAmongRuleTenants() { + + var permanentLoanTypeFromMemberB = UUID.randomUUID().toString(); + + when(loanTypeClient.getLoanTypeById(permanentLoanTypeFromMemberB)).thenReturn(new LoanType().withId(permanentLoanTypeFromMemberB)); + when(itemReferenceService.getLoanTypeById(permanentLoanTypeFromMemberB)).thenReturn(new LoanType().withId(permanentLoanTypeFromMemberB)); + + var ruleTenants = List.of("memberB", "memberA"); + var itemId = UUID.randomUUID().toString(); + var initPermanentLoanTypeId = UUID.randomUUID().toString(); + var extendedItem = ExtendedItem.builder().entity(new Item().withId(itemId).withPermanentLoanType(new LoanType().withId(initPermanentLoanTypeId))).tenantId("memberA").build(); + + var rules = rules(rule(PERMANENT_LOAN_TYPE, REPLACE_WITH, permanentLoanTypeFromMemberB, List.of(), ruleTenants)); + + var result = processor.process(IDENTIFIER, extendedItem, rules); + + assertNotNull(result); + assertEquals(permanentLoanTypeFromMemberB, result.getUpdated().getEntity().getPermanentLoanType().getId()); + + verifyNoInteractions(errorService); + } }