Skip to content

Commit

Permalink
Merge branch 'master' into MODBULKOPS-149
Browse files Browse the repository at this point in the history
  • Loading branch information
alekGbuz authored Nov 17, 2023
2 parents 9c4e3a9 + 80c70fc commit 4c59c13
Show file tree
Hide file tree
Showing 11 changed files with 432 additions and 30 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.folio.bulkops.domain.bean;

import java.util.List;

public interface ElectronicAccessEntity {
List<ElectronicAccess> getElectronicAccess();
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
@AllArgsConstructor
@JsonTypeName("holdingsRecord")
@EqualsAndHashCode(exclude = {"metadata", "instanceId", "permanentLocation", "effectiveLocationId", "illPolicy", "instanceHrid", "itemBarcode"})
public class HoldingsRecord implements BulkOperationsEntity {
public class HoldingsRecord implements BulkOperationsEntity, ElectronicAccessEntity {

@JsonProperty("id")
@CsvCustomBindByName(column = "Holdings record id", converter = StringConverter.class)
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/folio/bulkops/domain/bean/Item.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
@AllArgsConstructor
@JsonTypeName("item")
@EqualsAndHashCode(exclude = {"metadata", "effectiveCallNumberComponents", "effectiveLocation", "boundWithTitles"})
public class Item implements BulkOperationsEntity {
public class Item implements BulkOperationsEntity, ElectronicAccessEntity {
@JsonProperty("id")
@CsvCustomBindByName(column = "Item id", converter = StringConverter.class)
@CsvCustomBindByPosition(position = 0, converter = StringConverter.class)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package org.folio.bulkops.processor;

import static java.lang.String.format;
import static java.util.Objects.isNull;
import static java.util.Optional.ofNullable;
import static org.apache.commons.lang3.StringUtils.EMPTY;
import static org.apache.commons.lang3.StringUtils.equalsIgnoreCase;

import org.folio.bulkops.domain.bean.ElectronicAccessEntity;
import org.folio.bulkops.domain.dto.Action;
import org.folio.bulkops.domain.dto.UpdateOptionType;
import org.folio.bulkops.exception.BulkOperationException;
import org.springframework.stereotype.Component;

import java.util.Objects;

@Component
public class ElectronicAccessUpdaterFactory {
public Updater<? extends ElectronicAccessEntity> updater(UpdateOptionType option, Action action) {
return switch (option) {
case ELECTRONIC_ACCESS_URL_RELATIONSHIP -> updateUrlRelationship(option, action);
case ELECTRONIC_ACCESS_URI -> updateUri(option, action);
case ELECTRONIC_ACCESS_LINK_TEXT -> updateLinkText(option, action);
case ELECTRONIC_ACCESS_MATERIALS_SPECIFIED -> updateMaterialsSpecified(option, action);
case ELECTRONIC_ACCESS_URL_PUBLIC_NOTE -> updatePublicNote(option, action);
default -> notSupported(option, action);
};
}

private Updater<? extends ElectronicAccessEntity> updateUrlRelationship(UpdateOptionType option, Action action) {
return switch (action.getType()) {
case CLEAR_FIELD -> electronicAccessEntity -> ofNullable(electronicAccessEntity.getElectronicAccess())
.ifPresent(list -> list.forEach(electronicAccess -> electronicAccess.setRelationshipId(null)));
case FIND_AND_REMOVE_THESE -> electronicAccessEntity -> ofNullable(electronicAccessEntity.getElectronicAccess())
.ifPresent(list -> list.stream()
.filter(electronicAccess -> equalsIgnoreCase(electronicAccess.getRelationshipId(), action.getInitial()))
.forEach(electronicAccess -> electronicAccess.setRelationshipId(null)));
case FIND_AND_REPLACE -> electronicAccessEntity -> ofNullable(electronicAccessEntity.getElectronicAccess())
.ifPresent(list -> list.stream()
.filter(electronicAccess -> equalsIgnoreCase(electronicAccess.getRelationshipId(), action.getInitial()))
.forEach(electronicAccess -> electronicAccess.setRelationshipId(action.getUpdated())));
case REPLACE_WITH -> electronicAccessEntity -> ofNullable(electronicAccessEntity.getElectronicAccess())
.ifPresent(list -> list.forEach(electronicAccess -> electronicAccess.setRelationshipId(action.getUpdated())));
default -> notSupported(option, action);
};
}

private Updater<? extends ElectronicAccessEntity> updateUri(UpdateOptionType option, Action action) {
return switch (action.getType()) {
case CLEAR_FIELD -> electronicAccessEntity -> ofNullable(electronicAccessEntity.getElectronicAccess())
.ifPresent(list -> list.forEach(electronicAccess -> electronicAccess.setUri(EMPTY)));
case FIND_AND_REMOVE_THESE -> electronicAccessEntity -> ofNullable(electronicAccessEntity.getElectronicAccess())
.ifPresent(list -> list.stream()
.filter(electronicAccess -> Objects.equals(electronicAccess.getUri(), action.getInitial()))
.forEach(electronicAccess -> electronicAccess.setUri(EMPTY)));
case FIND_AND_REPLACE -> electronicAccessEntity -> ofNullable(electronicAccessEntity.getElectronicAccess())
.ifPresent(list -> list.stream()
.filter(electronicAccess -> Objects.equals(electronicAccess.getUri(), action.getInitial()))
.forEach(electronicAccess -> electronicAccess.setUri(isNull(action.getUpdated()) ? EMPTY : action.getUpdated())));
case REPLACE_WITH -> electronicAccessEntity -> ofNullable(electronicAccessEntity.getElectronicAccess())
.ifPresent(list -> list.forEach(electronicAccess -> electronicAccess.setUri(isNull(action.getUpdated()) ? EMPTY : action.getUpdated())));
default -> notSupported(option, action);
};
}

private Updater<? extends ElectronicAccessEntity> updateLinkText(UpdateOptionType option, Action action) {
return switch (action.getType()) {
case CLEAR_FIELD -> electronicAccessEntity -> ofNullable(electronicAccessEntity.getElectronicAccess())
.ifPresent(list -> list.forEach(electronicAccess -> electronicAccess.setLinkText(null)));
case FIND_AND_REMOVE_THESE -> electronicAccessEntity -> ofNullable(electronicAccessEntity.getElectronicAccess())
.ifPresent(list -> list.stream()
.filter(electronicAccess -> Objects.equals(electronicAccess.getLinkText(), action.getInitial()))
.forEach(electronicAccess -> electronicAccess.setLinkText(null)));
case FIND_AND_REPLACE -> electronicAccessEntity -> ofNullable(electronicAccessEntity.getElectronicAccess())
.ifPresent(list -> list.stream()
.filter(electronicAccess -> Objects.equals(electronicAccess.getLinkText(), action.getInitial()))
.forEach(electronicAccess -> electronicAccess.setLinkText(action.getUpdated())));
case REPLACE_WITH -> electronicAccessEntity -> ofNullable(electronicAccessEntity.getElectronicAccess())
.ifPresent(list -> list.forEach(electronicAccess -> electronicAccess.setLinkText(action.getUpdated())));
default -> notSupported(option, action);
};
}

private Updater<? extends ElectronicAccessEntity> updateMaterialsSpecified(UpdateOptionType option, Action action) {
return switch (action.getType()) {
case CLEAR_FIELD -> electronicAccessEntity -> ofNullable(electronicAccessEntity.getElectronicAccess())
.ifPresent(list -> list.forEach(electronicAccess -> electronicAccess.setMaterialsSpecification(null)));
case FIND_AND_REMOVE_THESE -> electronicAccessEntity -> ofNullable(electronicAccessEntity.getElectronicAccess())
.ifPresent(list -> list.stream()
.filter(electronicAccess -> Objects.equals(electronicAccess.getMaterialsSpecification(), action.getInitial()))
.forEach(electronicAccess -> electronicAccess.setMaterialsSpecification(null)));
case FIND_AND_REPLACE -> electronicAccessEntity -> ofNullable(electronicAccessEntity.getElectronicAccess())
.ifPresent(list -> list.stream()
.filter(electronicAccess -> Objects.equals(electronicAccess.getMaterialsSpecification(), action.getInitial()))
.forEach(electronicAccess -> electronicAccess.setMaterialsSpecification(action.getUpdated())));
case REPLACE_WITH -> electronicAccessEntity -> ofNullable(electronicAccessEntity.getElectronicAccess())
.ifPresent(list -> list.forEach(electronicAccess -> electronicAccess.setMaterialsSpecification(action.getUpdated())));
default -> notSupported(option, action);
};
}

private Updater<? extends ElectronicAccessEntity> updatePublicNote(UpdateOptionType option, Action action) {
return switch (action.getType()) {
case CLEAR_FIELD -> electronicAccessEntity -> ofNullable(electronicAccessEntity.getElectronicAccess())
.ifPresent(list -> list.forEach(electronicAccess -> electronicAccess.setPublicNote(null)));
case FIND_AND_REMOVE_THESE -> electronicAccessEntity -> ofNullable(electronicAccessEntity.getElectronicAccess())
.ifPresent(list -> list.stream()
.filter(electronicAccess -> Objects.equals(electronicAccess.getPublicNote(), action.getInitial()))
.forEach(electronicAccess -> electronicAccess.setPublicNote(null)));
case FIND_AND_REPLACE -> electronicAccessEntity -> ofNullable(electronicAccessEntity.getElectronicAccess())
.ifPresent(list -> list.stream()
.filter(electronicAccess -> Objects.equals(electronicAccess.getPublicNote(), action.getInitial()))
.forEach(electronicAccess -> electronicAccess.setPublicNote(action.getUpdated())));
case REPLACE_WITH -> electronicAccessEntity -> ofNullable(electronicAccessEntity.getElectronicAccess())
.ifPresent(list -> list.forEach(electronicAccess -> electronicAccess.setPublicNote(action.getUpdated())));
default -> notSupported(option, action);
};
}

private Updater<? extends ElectronicAccessEntity> notSupported(UpdateOptionType option, Action action) {
return electronicAccessEntity -> {
throw new BulkOperationException(format("Combination %s and %s isn't supported yet", option, action.getType()));
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,18 @@
import static org.folio.bulkops.domain.dto.UpdateActionType.SET_TO_FALSE_INCLUDING_ITEMS;
import static org.folio.bulkops.domain.dto.UpdateActionType.SET_TO_TRUE;
import static org.folio.bulkops.domain.dto.UpdateActionType.SET_TO_TRUE_INCLUDING_ITEMS;
import static org.folio.bulkops.domain.dto.UpdateOptionType.ELECTRONIC_ACCESS_LINK_TEXT;
import static org.folio.bulkops.domain.dto.UpdateOptionType.ELECTRONIC_ACCESS_MATERIALS_SPECIFIED;
import static org.folio.bulkops.domain.dto.UpdateOptionType.ELECTRONIC_ACCESS_URI;
import static org.folio.bulkops.domain.dto.UpdateOptionType.ELECTRONIC_ACCESS_URL_PUBLIC_NOTE;
import static org.folio.bulkops.domain.dto.UpdateOptionType.ELECTRONIC_ACCESS_URL_RELATIONSHIP;
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 java.util.ArrayList;
import java.util.Objects;
import java.util.Set;
import java.util.regex.Pattern;

import org.folio.bulkops.domain.bean.HoldingsRecord;
Expand All @@ -22,6 +29,7 @@
import org.folio.bulkops.exception.BulkOperationException;
import org.folio.bulkops.exception.NotFoundException;
import org.folio.bulkops.exception.RuleValidationException;
import org.folio.bulkops.service.ElectronicAccessService;
import org.folio.bulkops.service.HoldingsReferenceService;
import org.folio.bulkops.service.ItemReferenceService;
import org.springframework.stereotype.Component;
Expand All @@ -38,6 +46,8 @@ public class HoldingsDataProcessor extends AbstractDataProcessor<HoldingsRecord>
private final ItemReferenceService itemReferenceService;
private final HoldingsReferenceService holdingsReferenceService;
private final HoldingsNotesUpdater holdingsNotesUpdater;
private final ElectronicAccessUpdaterFactory electronicAccessUpdaterFactory;
private final ElectronicAccessService electronicAccessService;

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}$");
Expand All @@ -53,18 +63,7 @@ public Validator<UpdateOptionType, Action> validator(HoldingsRecord entity) {
log.error("Holdings source was not found by id={}", entity.getSourceId());
}
if (REPLACE_WITH == action.getType()) {
var locationId = action.getUpdated();
if (isEmpty(locationId)) {
throw new RuleValidationException("Location id cannot be empty");
}
if (!UUID_REGEX.matcher(locationId).matches()) {
throw new RuleValidationException("Location id has invalid format: %s" + locationId);
}
try {
itemReferenceService.getLocationById(locationId);
} catch (Exception e) {
throw new RuleValidationException(format("Location %s doesn't exist", locationId));
}
validateReplacement(option, action);
}
if (PERMANENT_LOCATION == option && CLEAR_FIELD == action.getType()) {
throw new RuleValidationException("Permanent location cannot be cleared");
Expand All @@ -73,7 +72,9 @@ public Validator<UpdateOptionType, Action> validator(HoldingsRecord entity) {
}

public Updater<HoldingsRecord> updater(UpdateOptionType option, Action action) {
if (REPLACE_WITH == action.getType()) {
if (isElectronicAccessUpdate(option)) {
return (Updater<HoldingsRecord>) electronicAccessUpdaterFactory.updater(option, action);
} else if (REPLACE_WITH == action.getType()) {
return holding -> {
var locationId = action.getUpdated();
if (PERMANENT_LOCATION == option) {
Expand Down Expand Up @@ -102,6 +103,46 @@ public Updater<HoldingsRecord> updater(UpdateOptionType option, Action action) {
};
}

private void validateReplacement(UpdateOptionType option, Action action) throws RuleValidationException {
if (isIdValue(option)) {
var newId = action.getUpdated();
if (isEmpty(newId)) {
throw new RuleValidationException("Id value cannot be empty");
}
if (!UUID_REGEX.matcher(action.getUpdated()).matches()) {
throw new RuleValidationException("UUID has invalid format: %s" + newId);
}

if (Set.of(PERMANENT_LOCATION, TEMPORARY_LOCATION).contains(option)) {
try {
itemReferenceService.getLocationById(newId);
} catch (Exception e) {
throw new RuleValidationException(format("Location %s doesn't exist", newId));
}
} else if (ELECTRONIC_ACCESS_URL_RELATIONSHIP.equals(option)) {
try {
electronicAccessService.getRelationshipNameAndIdById(newId);
} catch (Exception e) {
throw new RuleValidationException(format("URL relationship %s doesn't exist", newId));
}
}
}
}

private boolean isIdValue(UpdateOptionType option) {
return Set.of(PERMANENT_LOCATION,
TEMPORARY_LOCATION,
ELECTRONIC_ACCESS_URL_RELATIONSHIP).contains(option);
}

private boolean isElectronicAccessUpdate(UpdateOptionType option) {
return Set.of(ELECTRONIC_ACCESS_URL_RELATIONSHIP,
ELECTRONIC_ACCESS_URI,
ELECTRONIC_ACCESS_LINK_TEXT,
ELECTRONIC_ACCESS_MATERIALS_SPECIFIED,
ELECTRONIC_ACCESS_URL_PUBLIC_NOTE).contains(option);
}

private boolean isSetDiscoverySuppressTrue(UpdateActionType actionType, UpdateOptionType optionType) {
return (actionType == SET_TO_TRUE || actionType == SET_TO_TRUE_INCLUDING_ITEMS) && optionType == SUPPRESS_FROM_DISCOVERY;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.folio.bulkops.service;

import static java.lang.String.format;
import static java.util.Objects.isNull;
import static org.apache.commons.lang3.ObjectUtils.isEmpty;
import static org.apache.commons.lang3.StringUtils.EMPTY;
Expand All @@ -10,6 +11,7 @@

import org.folio.bulkops.client.ElectronicAccessRelationshipClient;
import org.folio.bulkops.domain.bean.ElectronicAccess;
import org.folio.bulkops.domain.bean.ElectronicAccessRelationship;
import org.folio.bulkops.exception.EntityFormatException;
import org.folio.bulkops.exception.NotFoundException;
import org.springframework.cache.annotation.Cacheable;
Expand Down Expand Up @@ -50,6 +52,15 @@ public String getRelationshipNameAndIdById(String id) {
}
}

@Cacheable(cacheNames = "electronicAccessRelationships")
public ElectronicAccessRelationship getRelationshipById(String id) {
try {
return relationshipClient.getById(id);
} catch (Exception e) {
throw new NotFoundException(format("URL relationship was not found by id=%s", id));
}
}

public ElectronicAccess restoreElectronicAccessItem(String s) {
if (isNotEmpty(s)) {
var tokens = s.split(ARRAY_DELIMITER, -1);
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/db/changelog/changelog-master.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@
<include file="changes/06-07-2023_add_parameters_to_bulk_operation_rules_details.xml" relativeToChangelogFile="true"/>
<include file="changes/06_07_2023_update_bulk_operation_add_expired.xml" relativeToChangelogFile="true"/>
<include file="changes/25-10-2023_add_notes_options_types_for_holdings.xml" relativeToChangelogFile="true"/>
<include file="changes/08-11-2023_add_electronic_access_options_types.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
ALTER TYPE UpdateOptionType ADD VALUE IF NOT EXISTS 'ELECTRONIC_ACCESS_URL_RELATIONSHIP';
ALTER TYPE UpdateOptionType ADD VALUE IF NOT EXISTS 'ELECTRONIC_ACCESS_URI';
ALTER TYPE UpdateOptionType ADD VALUE IF NOT EXISTS 'ELECTRONIC_ACCESS_LINK_TEXT';
ALTER TYPE UpdateOptionType ADD VALUE IF NOT EXISTS 'ELECTRONIC_ACCESS_MATERIALS_SPECIFIED';
ALTER TYPE UpdateOptionType ADD VALUE IF NOT EXISTS 'ELECTRONIC_ACCESS_URL_PUBLIC_NOTE';
Original file line number Diff line number Diff line change
@@ -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="08-11-2023_add_electronic_access_options_types" author="firebird">
<sqlFile path="08-11-2023_add_electronic_access_options_types.sql" relativeToChangelogFile="true" />
</changeSet>

</databaseChangeLog>
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@
"ADMINISTRATIVE_NOTE",
"CHECK_IN_NOTE",
"CHECK_OUT_NOTE",
"HOLDINGS_NOTE"
"HOLDINGS_NOTE",
"ELECTRONIC_ACCESS_URL_RELATIONSHIP",
"ELECTRONIC_ACCESS_URI",
"ELECTRONIC_ACCESS_LINK_TEXT",
"ELECTRONIC_ACCESS_MATERIALS_SPECIFIED",
"ELECTRONIC_ACCESS_URL_PUBLIC_NOTE"
]
}
}
Loading

0 comments on commit 4c59c13

Please sign in to comment.