Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
## Unreleased v3.6.0

### Stories
[MODEXPW-630](https://folio-org.atlassian.net/browse/MODEXPW-630) eholdings export: Add additional export fields

## 2026-06-04 v3.5.1

[Full Changelog](https://github.com/folio-org/mod-data-export-worker/compare/v3.5.0...v3.5.1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@
import org.folio.dew.domain.dto.eholdings.Identifier.SubtypeEnum;
import org.folio.dew.domain.dto.eholdings.Identifier.TypeEnum;
import org.folio.dew.domain.dto.eholdings.Note;
import org.folio.dew.domain.dto.eholdings.PackageAltName;
import org.folio.dew.domain.dto.eholdings.PackageAttributes;
import org.folio.dew.domain.dto.eholdings.PackageVisibility;
import org.folio.dew.domain.dto.eholdings.Proxy;
import org.folio.dew.domain.dto.eholdings.Subject;
import org.folio.dew.domain.dto.eholdings.Tags;
Expand Down Expand Up @@ -109,15 +111,23 @@

exportFormat.setPackageId(ePackage.getData().getId());
exportFormat.setPackageName(packageAtr.getName());
exportFormat.setPackageDisplayName(packageAtr.getCustomDisplayName());
exportFormat.setManagedAlternativeNames(mapAltNames(packageAtr.getManagedAltNames()));
exportFormat.setCustomAlternativeNames(mapAltNames(packageAtr.getCustomAltNames()));
exportFormat.setManagedDescription(packageAtr.getManagedDescription());
exportFormat.setCustomDescription(packageAtr.getCustomDescription());
exportFormat.setPackageType(packageAtr.getPackageType());
exportFormat.setPackageContentType(packageAtr.getContentType().getValue());
exportFormat.setPackageCustomCoverage(mapCoverage(packageAtr.getCustomCoverage()));
exportFormat.setPackageLevelToken(mapToken(packageAtr.getPackageToken()));
exportFormat.setPackageProxy(mapProxy(packageAtr.getProxy()));
exportFormat.setPackageTags(mapTags(packageAtr.getTags()));
exportFormat.setPackageHoldingsStatus(mapHoldingsStatus(packageAtr.getIsSelected()));
exportFormat.setPackageShowToPatrons(mapShowToPatrons(packageAtr.getVisibilityData()));
exportFormat.setPackageAutomaticallySelect(convertBoolToStr(packageAtr.getAllowKbToAddTitles()));
exportFormat.setPackageAccess(mapPackageAccess(packageAtr.getIsFreeAccess()));
exportFormat.setHideInPublicationFinder(mapVisibilityCategory(packageAtr.getVisibility(), "PF"));
exportFormat.setHideInFullTextFinder(mapVisibilityCategory(packageAtr.getVisibility(), "FTF"));
exportFormat.setExcludeFromMARCExport(mapVisibilityCategory(packageAtr.getVisibility(), "MARC"));
exportFormat.setPackageAccessStatusType(mapAccessType(ePackage.getIncluded()));
exportFormat.setPackageNotes(convertNotes(eHoldingsPackageDTO.getNotes()));
exportFormat.setPackageAgreements(convertAgreements(eHoldingsPackageDTO.getAgreements()));
Expand Down Expand Up @@ -148,6 +158,7 @@
exportFormat.setCustomEmbargo(mapEmbargo(resourceAtr.getCustomEmbargoPeriod()));
exportFormat.setTitleShowToPatrons(mapShowToPatrons(resourceAtr.getVisibilityData()));
exportFormat.setTitleProxy(mapProxy(resourceAtr.getProxy()));
exportFormat.setTitleProxiedUrl(mapProxiedUrl(resourceAtr.getProxy()));
exportFormat.setUrl(resourceAtr.getUrl());
exportFormat.setSubjects(mapSubjects(resourceAtr.getSubjects()));
exportFormat.setCustomValue1(resourceAtr.getUserDefinedField1());
Expand Down Expand Up @@ -230,7 +241,7 @@
}

private String mapProxy(Proxy proxy) {
if (proxy.getId().equals("<n>")) {
if (isNull(proxy) || isNull(proxy.getId()) || proxy.getId().equals("<n>")) {
return "None";
}
var inherited = Boolean.TRUE.equals(proxy.getInherited()) ? "(inherited)" : "";
Expand Down Expand Up @@ -281,6 +292,47 @@
.collect(Collectors.joining(PIPE_DELIMITER));
}

private String mapAltNames(List<PackageAltName> altNames) {
if (isNull(altNames) || altNames.isEmpty()) {
return EMPTY;
}
return altNames.stream()
.map(PackageAltName::getAltName)
.collect(Collectors.joining("; "));
}

private String mapVisibilityCategory(List<PackageVisibility> visibility, String category) {
if (isNull(visibility) || visibility.isEmpty()) {
return EMPTY;
}
return visibility.stream()
.filter(v -> v.getCategory() != null && category.equals(v.getCategory().getValue()))
.findFirst()
.map(v -> {
var result = Boolean.TRUE.equals(v.getHidden()) ? "Yes" : "No";
var reason = v.getReason();
if (reason != null && !reason.isBlank()) {
result += String.format(" (%s)", reason);
}
return result;
})
.orElse(EMPTY);
}

private String mapPackageAccess(Boolean isFreeAccess) {
if (isNull(isFreeAccess)) {
return EMPTY;
}
return isFreeAccess ? "Public" : "Controlled";

Check warning on line 326 in src/main/java/org/folio/dew/batch/eholdings/EHoldingsToExportFormatMapper.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use a primitive boolean expression here.

See more on https://sonarcloud.io/project/issues?id=org.folio%3Amod-data-export-worker&issues=AZ9Gr--Ygg1BN7bXTFT4&open=AZ9Gr--Ygg1BN7bXTFT4&pullRequest=744
}

private String mapProxiedUrl(Proxy proxy) {
if (isNull(proxy) || isNull(proxy.getProxiedUrl())) {
return EMPTY;
}
return proxy.getProxiedUrl();
}

private String mapShowToPatrons(VisibilityData visibility) {
var result = Boolean.TRUE.equals(visibility.getIsHidden()) ? "No" : "Yes";
var reason = visibility.getReason();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,22 @@ public class EHoldingsPackageExportFormat {
//Package fields
private String packageId;
private String packageName;
private String packageDisplayName;
private String managedAlternativeNames;
private String customAlternativeNames;
private String managedDescription;
private String customDescription;
private String packageType;
private String packageHoldingsStatus;
private String packageContentType;
private String packageCustomCoverage;
private String packageShowToPatrons;
private String packageAutomaticallySelect;
private String packageLevelToken;
private String packageProxy;
private String packageAccess;
private String hideInPublicationFinder;
private String hideInFullTextFinder;
private String excludeFromMARCExport;
private String packageAccessStatusType;
private String packageTags;
private String packageAgreements;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public class EHoldingsResourceExportFormat {
private String customEmbargo;
private String titleShowToPatrons;
private String titleProxy;
private String titleProxiedUrl;
private String url;
private String titleAccessStatusType;
private String customValue1;
Expand Down
4 changes: 2 additions & 2 deletions src/test/java/org/folio/dew/EHoldingsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -333,12 +333,12 @@ private JobExecution runJobWithFolioContext(JobOperatorTestUtils launcher,
private void populateOtherJobsDataInDatabase(){
var packageFromAnotherJob = new EHoldingsPackage();
packageFromAnotherJob.setId("1-22");
packageFromAnotherJob.setJobExecutionId(2L);
packageFromAnotherJob.setJobExecutionId(Long.MAX_VALUE);
packageRepository.save(packageFromAnotherJob);

var resourceFromAnotherJob = new EHoldingsResource();
resourceFromAnotherJob.setId("1-22-3334");
resourceFromAnotherJob.setJobExecutionId(2L);
resourceFromAnotherJob.setJobExecutionId(Long.MAX_VALUE);
resourceFromAnotherJob.setName("ABC of Diabetes (ABC Series)");
resourceRepository.save(resourceFromAnotherJob);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
package org.folio.dew.batch.eholdings;

import static org.assertj.core.api.Assertions.assertThat;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;
import lombok.SneakyThrows;
import org.folio.de.entity.EHoldingsPackage;
import org.folio.dew.domain.dto.eholdings.ContentTypeEnum;
import org.folio.dew.domain.dto.eholdings.Coverage;
import org.folio.dew.domain.dto.eholdings.EHoldingsResourceDTO;
import org.folio.dew.domain.dto.eholdings.EPackage;
import org.folio.dew.domain.dto.eholdings.EmbargoPeriod;
import org.folio.dew.domain.dto.eholdings.PackageAltName;
import org.folio.dew.domain.dto.eholdings.PackageAttributes;
import org.folio.dew.domain.dto.eholdings.PackageData;
import org.folio.dew.domain.dto.eholdings.PackageVisibility;
import org.folio.dew.domain.dto.eholdings.PackageVisibility.CategoryEnum;
import org.folio.dew.domain.dto.eholdings.Proxy;
import org.folio.dew.domain.dto.eholdings.PublicationType;
import org.folio.dew.domain.dto.eholdings.ResourcesAttributes;
import org.folio.dew.domain.dto.eholdings.ResourcesData;
import org.folio.dew.domain.dto.eholdings.VisibilityData;
import org.jspecify.annotations.NonNull;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.NullAndEmptySource;
import org.junit.jupiter.params.provider.NullSource;

class EHoldingsToExportFormatMapperTest {

private final EHoldingsToExportFormatMapper mapper = new EHoldingsToExportFormatMapper();
private static final ObjectMapper objectMapper = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

@Test
void shouldMapPackageAccessToPublicWhenFreeAccess() {
var result = mapper.convertToExportFormat(buildPackage(true, null, null, null));
assertThat(result.getPackageAccess()).isEqualTo("Public");
}

@Test
void shouldMapPackageAccessToControlledWhenNotFreeAccess() {
var result = mapper.convertToExportFormat(buildPackage(false, null, null, null));
assertThat(result.getPackageAccess()).isEqualTo("Controlled");
}

@Test
void shouldMapAltNamesJoinedWithSemicolon() {
var alt1 = new PackageAltName();
alt1.setAltName("Alt 1");
var alt2 = new PackageAltName();
alt2.setAltName("Alt 2");
var alt3 = new PackageAltName();
alt3.setAltName("Alt 3");
var alt4 = new PackageAltName();
alt4.setAltName("Alt 4");

var result = mapper.convertToExportFormat(buildPackage(null, List.of(alt1, alt2), List.of(alt3, alt4), null));

assertThat(result.getCustomAlternativeNames()).isEqualTo("Alt 1; Alt 2");
assertThat(result.getManagedAlternativeNames()).isEqualTo("Alt 3; Alt 4");
}

@Test
void shouldMapVisibilityByCategoryWithReasonAppended() {
var visibility = List.of(
buildVisibility(CategoryEnum.PF, true, "Set by System"),
buildVisibility(CategoryEnum.FTF, false, null),
buildVisibility(CategoryEnum.MARC, true, "because")
);

var result = mapper.convertToExportFormat(buildPackage(null, null, null, visibility));

assertThat(result.getHideInPublicationFinder()).isEqualTo("Yes (Set by System)");
assertThat(result.getHideInFullTextFinder()).isEqualTo("No");
assertThat(result.getExcludeFromMARCExport()).isEqualTo("Yes (because)");
}

@ParameterizedTest
@NullAndEmptySource
@SuppressWarnings("unchecked")
void shouldReturnEmptyForNullOrEmptyInput(List<?> list) {
var result = mapper.convertToExportFormat(
buildPackage(null, (List<PackageAltName>) list, (List<PackageAltName>) list, (List<PackageVisibility>) list));

assertThat(result.getManagedAlternativeNames()).isEmpty();
assertThat(result.getCustomAlternativeNames()).isEmpty();
assertThat(result.getPackageAccess()).isEmpty();
assertThat(result.getHideInPublicationFinder()).isEmpty();
assertThat(result.getHideInFullTextFinder()).isEmpty();
assertThat(result.getExcludeFromMARCExport()).isEmpty();
}

@Test
void shouldMapProxiedUrl() {
var proxy = new Proxy();
proxy.setId("ezproxy");
proxy.setProxiedUrl("https://proxy.example.com");

var result = mapper.convertToExportFormat(buildMinimalResource(proxy));

assertThat(result.getTitleProxiedUrl()).isEqualTo("https://proxy.example.com");
}

@ParameterizedTest
@NullSource
@MethodSource("nullProxiedUrlProxy")
void shouldReturnEmptyProxiedUrlForNullOrMissingUrl(Proxy proxy) {
assertThat(mapper.convertToExportFormat(buildMinimalResource(proxy)).getTitleProxiedUrl()).isEmpty();
}

private static Stream<Proxy> nullProxiedUrlProxy() {
return Stream.of(new Proxy()); // proxy present but proxiedUrl is null
}

private EHoldingsResourceDTO buildMinimalResource(Proxy proxy) {
var embargo = new EmbargoPeriod();
embargo.setEmbargoValue(0);

var visibility = new VisibilityData();
visibility.setIsHidden(false);

var attrs = getResourcesAttributes(proxy, embargo, visibility);

var data = new ResourcesData();
data.setAttributes(attrs);
data.setIncluded(Collections.emptyList());

return EHoldingsResourceDTO.builder()
.resourcesData(data)
.notes(Collections.emptyList())
.agreements(Collections.emptyList())
.build();
}

private static @NonNull ResourcesAttributes getResourcesAttributes(Proxy proxy, EmbargoPeriod embargo,
VisibilityData visibility) {
var attrs = new ResourcesAttributes();
attrs.setTitleId(1);
attrs.setIsSelected(false);
attrs.setIsTitleCustom(false);
attrs.setPublicationType(PublicationType.BOOK);
attrs.setAlternateTitles(Collections.emptyList());
attrs.setContributors(Collections.emptyList());
attrs.setIdentifiers(Collections.emptyList());
attrs.setManagedCoverages(Collections.emptyList());
attrs.setCustomCoverages(Collections.emptyList());
attrs.setManagedEmbargoPeriod(embargo);
attrs.setCustomEmbargoPeriod(embargo);
attrs.setVisibilityData(visibility);
attrs.setSubjects(Collections.emptyList());
attrs.setProxy(proxy);
return attrs;
}

private PackageVisibility buildVisibility(CategoryEnum category, boolean hidden, String reason) {
var vis = new PackageVisibility();
vis.setCategory(category);
vis.setHidden(hidden);
vis.setReason(reason);
return vis;
}

@SneakyThrows
private EHoldingsPackage buildPackage(Boolean isFreeAccess,
List<PackageAltName> customAltNames,
List<PackageAltName> managedAltNames,
List<PackageVisibility> visibility) {
var attrs = getPackageAttributes(isFreeAccess, customAltNames, managedAltNames, visibility);

var packageData = new PackageData();
packageData.setId("1-22");
packageData.setType("packages");
packageData.setAttributes(attrs);

var ePackage = new EPackage();
ePackage.setData(packageData);
ePackage.setIncluded(Collections.emptyList());

var entity = new EHoldingsPackage();
entity.setId("1-22");
entity.setJobExecutionId(1L);
entity.setEPackage(objectMapper.writeValueAsString(ePackage));
entity.setEProvider("null");
entity.setAgreements("[]");
entity.setNotes("[]");
return entity;
}

private static @NonNull PackageAttributes getPackageAttributes(Boolean isFreeAccess, List<PackageAltName> customAltNames,
List<PackageAltName> managedAltNames, List<PackageVisibility> visibility) {
var coverage = new Coverage();
coverage.setBeginCoverage("");
coverage.setEndCoverage("");

var proxy = new Proxy();
proxy.setId("<n>");

var attrs = new PackageAttributes();
attrs.setContentType(ContentTypeEnum.UNKNOWN);
attrs.setCustomCoverage(coverage);
attrs.setProxy(proxy);
attrs.setIsSelected(false);
attrs.setIsFreeAccess(isFreeAccess);
attrs.setManagedAltNames(managedAltNames);
attrs.setCustomAltNames(customAltNames);
attrs.setVisibility(visibility);
return attrs;
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"id": "1-22-333",
"jobExecutionId": 2,
"ePackage" : "{\"data\":{\"id\":\"1-22\",\"type\":\"packages\",\"attributes\":{\"contentType\":\"Unknown\",\"customCoverage\":{\"beginCoverage\":\"\",\"endCoverage\":\"\"},\"isCustom\":true,\"isSelected\":true,\"name\":\"custom-package, 13\",\"packageId\":\"22\",\"packageType\":\"Custom\",\"providerId\":\"1\",\"providerName\":\"API DEV CORPORATE CUSTOMER\",\"selectedCount\":2,\"titleCount\":2,\"visibilityData\":{\"isHidden\":false,\"reason\":\"\"},\"allowKbToAddTitles\":false,\"packageToken\":{\"factName\":\"[[proxyid]]\",\"prompt\":\"ISBN\",\"helpText\":\"<div></div>\",\"value\":\"token 1-22\"},\"proxy\":{\"id\":\"ezproxy\",\"inherited\":true},\"tags\":{\"tagList\":[]}}},\"included\":[{\"id\":\"31ba067b-280c-49ec-a76d-ca7deacd6ff6\",\"type\":\"accessTypes\",\"attributes\":\"{name=Test, credentialsId=80898dee-449f-44dd-9c8e-37d5eb469b1d}\",\"creator\":\"{}\",\"usageNumber\":\"0\",\"metadata\":\"{createdDate=2022-05-10T05:45:33.560+00:00, createdByUserId=548a5d79-f628-5a0d-a304-11d0e41ce7b7}\"}]}",
"eProvider" : "{\"data\":{\"id\":\"1\",\"type\":\"providers\",\"attributes\":{\"name\":\"API DEV CORPORATE CUSTOMER\",\"packagesTotal\":1,\"packagesSelected\":1,\"providerToken\":{\"factName\":\"[[vleid]]\",\"prompt\":\"id=\",\"helpText\":\"<p></p>\",\"value\":\"ProviderToken 1\"},\"supportsCustomPackages\":false,\"proxy\":{\"id\":\"ezproxy\",\"inherited\":true},\"tags\":{\"tagList\":[]}}},\"included\":[]}",
"ePackage" : "{\"data\":{\"id\":\"1-22\",\"type\":\"packages\",\"attributes\":{\"contentType\":\"Unknown\",\"customCoverage\":{\"beginCoverage\":\"\",\"endCoverage\":\"\"},\"isCustom\":true,\"isSelected\":true,\"name\":\"custom-package, 13\",\"customDisplayName\":null,\"managedAltNames\":null,\"customAltNames\":null,\"managedDescription\":null,\"customDescription\":null,\"isFreeAccess\":null,\"packageId\":\"22\",\"packageType\":\"Custom\",\"providerId\":\"1\",\"providerName\":\"API DEV CORPORATE CUSTOMER\",\"selectedCount\":2,\"titleCount\":2,\"visibilityData\":{\"isHidden\":false,\"reason\":\"\"},\"visibility\":null,\"allowKbToAddTitles\":false,\"packageToken\":{\"factName\":\"[[proxyid]]\",\"prompt\":\"ISBN\",\"helpText\":\"<div></div>\",\"value\":\"token 1-22\"},\"proxy\":{\"id\":\"ezproxy\",\"inherited\":true,\"proxiedUrl\":null},\"tags\":{\"tagList\":[]}}},\"included\":[{\"id\":\"31ba067b-280c-49ec-a76d-ca7deacd6ff6\",\"type\":\"accessTypes\",\"attributes\":\"{name=Test, credentialsId=80898dee-449f-44dd-9c8e-37d5eb469b1d}\",\"creator\":\"{}\",\"usageNumber\":\"0\",\"metadata\":\"{createdDate=2022-05-10T05:45:33.560+00:00, createdByUserId=548a5d79-f628-5a0d-a304-11d0e41ce7b7}\"}]}",
"eProvider" : "{\"data\":{\"id\":\"1\",\"type\":\"providers\",\"attributes\":{\"name\":\"API DEV CORPORATE CUSTOMER\",\"packagesTotal\":1,\"packagesSelected\":1,\"providerToken\":{\"factName\":\"[[vleid]]\",\"prompt\":\"id=\",\"helpText\":\"<p></p>\",\"value\":\"ProviderToken 1\"},\"supportsCustomPackages\":false,\"proxy\":{\"id\":\"ezproxy\",\"inherited\":true,\"proxiedUrl\":null},\"tags\":{\"tagList\":[]}}},\"included\":[]}",
"agreements": "[{\"status\":\"Active\",\"name\":\"Test\",\"startDate\":\"2022-06-01\"}]",
"notes": "[{\"id\":\"9e235fb9-e4f8-4995-89a5-3b2fa06dfbb9\",\"typeId\":\"5bea5cf6-d15f-4bc2-b9e1-f1631a14ab6a\",\"type\":\"Technical note\",\"title\":\"OCLC Record Status\",\"domain\":\"eholdings\",\"content\":\"<p>Not updated</p>\",\"popUpOnCheckOut\":false,\"popUpOnUser\":false,\"metadata\":{\"createdDate\":1617235321051,\"createdByUserId\":\"cc47a7f8-b665-4311-8401-d5bb055dc388\",\"createdByUsername\":null,\"updatedDate\":1617235321051,\"updatedByUserId\":\"cc47a7f8-b665-4311-8401-d5bb055dc388\",\"updatedByUsername\":null}},{\"id\":\"0bab2772-e2c1-4a33-803c-407fb154d775\",\"typeId\":\"25e17bd3-4c7f-42e5-9e04-65f5ea76c151\",\"type\":\"Customer Support \",\"title\":\"UBM\",\"domain\":\"eholdings\",\"content\":\"<p>UBM</p>\",\"popUpOnCheckOut\":false,\"popUpOnUser\":false,\"metadata\":{\"createdDate\":1585180987582,\"createdByUserId\":\"9eb67301-6f6e-468f-9b1a-6134dc39a684\",\"createdByUsername\":null,\"updatedDate\":1585180987582,\"updatedByUserId\":\"9eb67301-6f6e-468f-9b1a-6134dc39a684\",\"updatedByUsername\":null}}]"
}
Loading
Loading