From fea26716df6bdb07599027a1d36d30d2cb225b6c Mon Sep 17 00:00:00 2001 From: SvitlanaKovalova1 Date: Fri, 13 Feb 2026 20:10:58 +0200 Subject: [PATCH 1/8] feat[auth-export-report]: remove the Linked bib fields column --- NEWS.md | 1 + descriptors/ModuleDescriptor-template.json | 2 +- folio-export-common | 2 +- .../dew/ModDataExportWorkerApplication.java | 5 +- .../AuthorityControlToExportFormatMapper.java | 1 - .../readers/AuthUpdateHeadingsItemReader.java | 147 +++++++++++++++++- .../readers/AuthorityControlItemReader.java | 17 +- .../AuthUpdateHeadingExportFormat.java | 1 - .../folio/dew/service/FolioTenantService.java | 12 +- .../dew/AuthorityControlConsortiumTest.java | 7 +- .../java/org/folio/dew/BaseBatchTest.java | 61 ++++++++ .../resources/mappings/entitesLinksStats.json | 4 +- .../mappings/entitesLinksStatsConsortium.json | 24 ++- .../authority_control/auth_heading_update.csv | 12 +- .../auth_heading_update_consortium.csv | 12 +- .../auth_heading_update_empty.csv | 2 +- 16 files changed, 276 insertions(+), 34 deletions(-) diff --git a/NEWS.md b/NEWS.md index 32bde4bee..f955e1e0e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,7 @@ ## Unreleased v3.5.0 ### Stories * [MODEXPW-576](https://folio-org.atlassian.net/browse/MODEXPW-576) Enhance "MARC authority headings update" Report with Record Type Column Based on Consortium Environment. +* [MODEXPW-619](https://folio-org.atlassian.net/browse/MODEXPW-619) Remove the Linked bib fields column for the Authority headings change report ### Bug fixes * [MODEXPW-617](https://folio-org.atlassian.net/browse/MODEXPW-617) Update Apache Kafka topic template to prevent ambiguous env name matches. diff --git a/descriptors/ModuleDescriptor-template.json b/descriptors/ModuleDescriptor-template.json index e07efb192..84974a679 100644 --- a/descriptors/ModuleDescriptor-template.json +++ b/descriptors/ModuleDescriptor-template.json @@ -40,7 +40,7 @@ }, { "id": "instance-authority-links-statistics", - "version": "2.1" + "version": "3.0" }, { "id": "holdings-note-types", diff --git a/folio-export-common b/folio-export-common index 87127ad84..7856bc84e 160000 --- a/folio-export-common +++ b/folio-export-common @@ -1 +1 @@ -Subproject commit 87127ad84065ee9b1ea4ca7107b0d046b3336a1a +Subproject commit 7856bc84e68dc324376330a46c6fb9a8bd920369 diff --git a/src/main/java/org/folio/dew/ModDataExportWorkerApplication.java b/src/main/java/org/folio/dew/ModDataExportWorkerApplication.java index d4542ac3f..b6a73009e 100644 --- a/src/main/java/org/folio/dew/ModDataExportWorkerApplication.java +++ b/src/main/java/org/folio/dew/ModDataExportWorkerApplication.java @@ -6,7 +6,10 @@ import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.cloud.openfeign.EnableFeignClients; -@SpringBootApplication +@SpringBootApplication(scanBasePackages = { + "org.folio.dew", + "org.folio.spring.scope" +}) @EnableFeignClients(basePackages = "org.folio.dew.client") @EnableBatchProcessing(isolationLevelForCreate = "ISOLATION_READ_COMMITTED") @EntityScan("org.folio.de.entity") diff --git a/src/main/java/org/folio/dew/batch/authoritycontrol/AuthorityControlToExportFormatMapper.java b/src/main/java/org/folio/dew/batch/authoritycontrol/AuthorityControlToExportFormatMapper.java index 7065ba356..93e5703e2 100644 --- a/src/main/java/org/folio/dew/batch/authoritycontrol/AuthorityControlToExportFormatMapper.java +++ b/src/main/java/org/folio/dew/batch/authoritycontrol/AuthorityControlToExportFormatMapper.java @@ -38,7 +38,6 @@ public AuthUpdateHeadingExportFormat convertToAuthUpdateHeadingsExportFormat(Aut exportFormat.setOriginalHeadingType(dto.getHeadingTypeOld()); exportFormat.setIdentifier(dto.getNaturalIdNew()); exportFormat.setAuthoritySourceFileName(dto.getSourceFileNew()); - exportFormat.setNumberOfBibliographicRecordsLinked(dto.getLbTotal().toString()); if (folioTenantService.isConsortiumTenant()) { exportFormat.setSource(Boolean.TRUE.equals(dto.getShared()) ? "shared" : "local"); } diff --git a/src/main/java/org/folio/dew/batch/authoritycontrol/readers/AuthUpdateHeadingsItemReader.java b/src/main/java/org/folio/dew/batch/authoritycontrol/readers/AuthUpdateHeadingsItemReader.java index 1da46c5ce..f7524fec4 100644 --- a/src/main/java/org/folio/dew/batch/authoritycontrol/readers/AuthUpdateHeadingsItemReader.java +++ b/src/main/java/org/folio/dew/batch/authoritycontrol/readers/AuthUpdateHeadingsItemReader.java @@ -2,26 +2,171 @@ import static org.folio.dew.domain.dto.authority.control.AuthorityDataStatDto.ActionEnum.UPDATE_HEADING; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import lombok.extern.log4j.Log4j2; import org.folio.dew.client.EntitiesLinksStatsClient; import org.folio.dew.config.properties.AuthorityControlJobProperties; import org.folio.dew.domain.dto.authority.control.AuthorityControlExportConfig; import org.folio.dew.domain.dto.authority.control.AuthorityDataStatDto; import org.folio.dew.domain.dto.authority.control.AuthorityDataStatDtoCollection; +import org.folio.dew.service.FolioTenantService; +import org.folio.spring.FolioExecutionContext; +import org.folio.spring.scope.FolioExecutionContextService; import org.springframework.batch.core.configuration.annotation.StepScope; import org.springframework.stereotype.Component; +@Log4j2 @StepScope @Component public class AuthUpdateHeadingsItemReader extends AuthorityControlItemReader { + private final FolioExecutionContext context; + private final FolioExecutionContextService executionService; + private final String consortiumTenant; + + private List overflowStats; + public AuthUpdateHeadingsItemReader(EntitiesLinksStatsClient entitiesLinksStatsClient, AuthorityControlExportConfig exportConfig, - AuthorityControlJobProperties jobProperties) { + AuthorityControlJobProperties jobProperties, + FolioTenantService folioTenantService, + FolioExecutionContext context, + FolioExecutionContextService executionService) { super(entitiesLinksStatsClient, exportConfig, jobProperties); + this.context = context; + this.executionService = executionService; + this.consortiumTenant = folioTenantService.getConsortiumTenant(); } @Override protected AuthorityDataStatDtoCollection getCollection(int limit) { + log.debug("Fetching authority stats for tenant [{}] ", context.getTenantId()); + if (isConsortiumMemberTenant()) { + return getConsortiumAuthorityStats(limit); + } + if (toDate() == null) { + return null; + } return entitiesLinksStatsClient.getAuthorityStats(limit, UPDATE_HEADING, fromDate(), toDate()); } + + @Override + protected AuthorityDataStatDto doRead() { + if (currentChunk == null || currentChunkOffset >= currentChunk.size()) { + if (toDate == null && toConsortiumDate == null && currentChunk != null) { + var collection = getCollection(limit); + if (collection == null || collection.getStats() == null || collection.getStats().isEmpty()) { + return null; + } + currentChunk = collection.getStats(); + currentChunkOffset = 0; + } else { + var collection = getCollection(limit); + currentChunk = collection.getStats(); + toDate = collection.getNext(); + toConsortiumDate = collection.getConsortiumNext(); + currentChunkOffset = 0; + } + } + + if (currentChunk.isEmpty()) { + return null; + } + return currentChunk.get(currentChunkOffset++); + } + + private AuthorityDataStatDtoCollection getConsortiumAuthorityStats(int limit) { + var memberTenantStats = fetchMemberTenantStats(limit); + var centralTenantStats = fetchCentralTenantStats(limit); + + if (memberTenantStats == null && centralTenantStats == null) { + return getDataFromOverflowStats(limit); + } + var mergedStats = mergeStats(memberTenantStats, centralTenantStats); + if (mergedStats != null && mergedStats.size() > limit) { + return createStatsPage(limit, mergedStats, memberTenantStats, centralTenantStats); + } + return getMergedAuthorityStats(mergedStats, memberTenantStats, centralTenantStats); + } + + private boolean isConsortiumMemberTenant() { + return consortiumTenant != null && !consortiumTenant.equals(context.getTenantId()); + } + + private AuthorityDataStatDtoCollection fetchMemberTenantStats(int limit) { + if (toDate() != null) { + return entitiesLinksStatsClient.getAuthorityStats(limit, UPDATE_HEADING, fromDate(), toDate()); + } + return null; + } + + private AuthorityDataStatDtoCollection fetchCentralTenantStats(int limit) { + if (toConsortiumDate() != null) { + var centralTenantStats = executionService.execute(consortiumTenant, context, () -> + entitiesLinksStatsClient.getAuthorityStats(limit, UPDATE_HEADING, fromDate(), toConsortiumDate())); + if (centralTenantStats != null && !centralTenantStats.getStats().isEmpty()) { + centralTenantStats.getStats().forEach(stat -> stat.setShared(true)); + } + return centralTenantStats; + } + return null; + } + + private AuthorityDataStatDtoCollection getDataFromOverflowStats(int limit) { + if (overflowStats == null) { + return getMergedAuthorityStats(null, null, null); + } + if (overflowStats.size() > limit) { + var mergedStats = overflowStats.stream().limit(limit).toList(); + overflowStats = mergedStats.subList(limit, mergedStats.size() - 1); + return getMergedAuthorityStats(mergedStats, null, null); + } + var result = getMergedAuthorityStats(overflowStats, null, null); + overflowStats = null; + return result; + } + + private List mergeStats(AuthorityDataStatDtoCollection memberTenantStats, + AuthorityDataStatDtoCollection centralTenantStats) { + + List mergedStats = new ArrayList<>(); + + if (memberTenantStats != null) { + mergedStats.addAll(memberTenantStats.getStats()); + } + if (centralTenantStats != null) { + mergedStats.addAll(centralTenantStats.getStats()); + } + if (overflowStats != null && !overflowStats.isEmpty()) { + mergedStats.addAll(overflowStats); + overflowStats = null; + } + return sortStatsByStartedAtDesc(mergedStats); + } + + private AuthorityDataStatDtoCollection createStatsPage(int limit, List mergedStats, + AuthorityDataStatDtoCollection memberTenantStats, + AuthorityDataStatDtoCollection centralTenantStats) { + var resultStats = mergedStats.stream().limit(limit).toList(); + overflowStats = mergedStats.subList(limit, mergedStats.size()); + return getMergedAuthorityStats(resultStats, memberTenantStats, centralTenantStats); + } + + private AuthorityDataStatDtoCollection getMergedAuthorityStats(List mergedStats, + AuthorityDataStatDtoCollection memberTenantStats, + AuthorityDataStatDtoCollection centralTenantStats) { + return new AuthorityDataStatDtoCollection() + .stats(mergedStats) + .next(memberTenantStats != null ? memberTenantStats.getNext() : null) + .consortiumNext(centralTenantStats != null ? centralTenantStats.getNext() : null); + } + + private List sortStatsByStartedAtDesc(List stats) { + return stats.stream() + .sorted(Comparator.comparing(tenantStats -> tenantStats.getMetadata().getStartedAt())) + .toList() + .reversed(); + } } diff --git a/src/main/java/org/folio/dew/batch/authoritycontrol/readers/AuthorityControlItemReader.java b/src/main/java/org/folio/dew/batch/authoritycontrol/readers/AuthorityControlItemReader.java index f2d41b17d..f0ba41d8d 100644 --- a/src/main/java/org/folio/dew/batch/authoritycontrol/readers/AuthorityControlItemReader.java +++ b/src/main/java/org/folio/dew/batch/authoritycontrol/readers/AuthorityControlItemReader.java @@ -16,11 +16,12 @@ public abstract class AuthorityControlItemReader extends AbstractItemCountingItemStreamItemReader { protected final EntitiesLinksStatsClient entitiesLinksStatsClient; - private final int limit; - private OffsetDateTime fromDate; - private OffsetDateTime toDate; - private int currentChunkOffset; - private List currentChunk; + protected final int limit; + protected OffsetDateTime fromDate; + protected OffsetDateTime toDate; + protected int currentChunkOffset; + protected List currentChunk; + protected OffsetDateTime toConsortiumDate; protected AuthorityControlItemReader(EntitiesLinksStatsClient entitiesLinksStatsClient, AuthorityControlExportConfig exportConfig, @@ -30,6 +31,7 @@ protected AuthorityControlItemReader(EntitiesLinksStatsClient entitiesLinksStats } if (exportConfig.getToDate() != null) { this.toDate = OffsetDateTime.of(exportConfig.getToDate(), LocalTime.MAX, ZoneOffset.UTC); + this.toConsortiumDate = this.toDate; } this.entitiesLinksStatsClient = entitiesLinksStatsClient; this.limit = jobProperties.getEntitiesLinksChunkSize(); @@ -54,7 +56,6 @@ protected T doRead() { if (currentChunk.isEmpty()) { return null; } - return currentChunk.get(currentChunkOffset++); } @@ -68,6 +69,10 @@ protected String toDate() { return Objects.isNull(toDate) ? null : toDate.toString(); } + protected String toConsortiumDate() { + return Objects.isNull(toConsortiumDate) ? null : toConsortiumDate.toString(); + } + @Override protected void doOpen() { // Nothing to do diff --git a/src/main/java/org/folio/dew/domain/dto/authoritycontrol/exportformat/AuthUpdateHeadingExportFormat.java b/src/main/java/org/folio/dew/domain/dto/authoritycontrol/exportformat/AuthUpdateHeadingExportFormat.java index f46af0cb4..90cfe2a90 100644 --- a/src/main/java/org/folio/dew/domain/dto/authoritycontrol/exportformat/AuthUpdateHeadingExportFormat.java +++ b/src/main/java/org/folio/dew/domain/dto/authoritycontrol/exportformat/AuthUpdateHeadingExportFormat.java @@ -20,7 +20,6 @@ public class AuthUpdateHeadingExportFormat implements AuthorityControlExportForm @ExportHeader(NEW_1XX) private String newHeadingType; private String authoritySourceFileName; - private String numberOfBibliographicRecordsLinked; @ExportHeader(AUTHORITY_RECORD_TYPE) private String source; private String updater; diff --git a/src/main/java/org/folio/dew/service/FolioTenantService.java b/src/main/java/org/folio/dew/service/FolioTenantService.java index da5c806ec..87f3f88c7 100644 --- a/src/main/java/org/folio/dew/service/FolioTenantService.java +++ b/src/main/java/org/folio/dew/service/FolioTenantService.java @@ -63,7 +63,17 @@ protected boolean tenantExists() { * Check if current context tenant is a part of consortium * */ public boolean isConsortiumTenant() { - var centralTenant = userTenantsService.getCentralTenant(context.getTenantId()); + var centralTenant = userTenantsService.getCentralTenant(context.getTenantId()); return centralTenant.isPresent() && StringUtils.isNotEmpty(centralTenant.get()); } + + /** + * Get consortium tenant if current context tenant is a part of consortium + * + * @return consortium tenant or null if current context tenant is not a part of consortium + */ + public String getConsortiumTenant() { + var centralTenant = userTenantsService.getCentralTenant(context.getTenantId()); + return centralTenant.orElse(null); + } } diff --git a/src/test/java/org/folio/dew/AuthorityControlConsortiumTest.java b/src/test/java/org/folio/dew/AuthorityControlConsortiumTest.java index 302529b6c..22988eecb 100644 --- a/src/test/java/org/folio/dew/AuthorityControlConsortiumTest.java +++ b/src/test/java/org/folio/dew/AuthorityControlConsortiumTest.java @@ -15,6 +15,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import java.io.File; import java.time.LocalDate; +import java.util.List; import java.util.UUID; import lombok.SneakyThrows; import org.folio.de.entity.JobCommand; @@ -43,7 +44,7 @@ class AuthorityControlConsortiumTest extends BaseBatchTest { private static final String EXPECTED_AUTH_HEADING_UPDATE_OUTPUT = "src/test/resources/output/authority_control/auth_heading_update_consortium.csv"; private static final String EXPECTED_S3_FILE_PATH = - "mod-data-export-worker/authority_control_export/consortium/"; + "mod-data-export-worker/authority_control_export/college/"; @Autowired private Job getAuthHeadingJob; @Autowired @@ -55,7 +56,7 @@ class AuthorityControlConsortiumTest extends BaseBatchTest { @BeforeAll static void beforeAll() { - setUpTenant(CONSORTIUM_TENANT); + setUpConsortiumTenant(CONSORTIUM_TENANT, List.of(CONSORTIUM_MEMBER_TENANT), CONSORTIUM_MEMBER_TENANT); } @Test @@ -73,7 +74,7 @@ void authHeadingJobTest() throws Exception { wireMockServer.verify(getRequestedFor(urlEqualTo( "/links/stats/authority?limit=2&action=UPDATE_HEADING&fromDate=2024-01-01T00%3A00Z&toDate=2024-12-01T23%3A59%3A59.999999999Z"))); wireMockServer.verify(getRequestedFor(urlEqualTo( - "/links/stats/authority?limit=2&action=UPDATE_HEADING&fromDate=2024-01-01T00%3A00Z&toDate=2024-08-01T12%3A00Z"))); + "/links/stats/authority?limit=2&action=UPDATE_HEADING&fromDate=2024-01-01T00%3A00Z&toDate=2024-08-01T11%3A00Z"))); verifyJobEvent(); } diff --git a/src/test/java/org/folio/dew/BaseBatchTest.java b/src/test/java/org/folio/dew/BaseBatchTest.java index ebbe319b2..f04cf029f 100644 --- a/src/test/java/org/folio/dew/BaseBatchTest.java +++ b/src/test/java/org/folio/dew/BaseBatchTest.java @@ -1,5 +1,10 @@ package org.folio.dew; +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; +import static org.apache.http.HttpStatus.SC_OK; +import static org.folio.spring.integration.XOkapiHeaders.TENANT; +import static org.folio.spring.integration.XOkapiHeaders.URL; import static org.springframework.http.MediaType.APPLICATION_JSON; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -10,6 +15,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; @@ -27,6 +33,7 @@ import lombok.SneakyThrows; import org.folio.dew.batch.ExportJobManager; import org.folio.dew.batch.ExportJobManagerSync; +import org.folio.dew.client.UserTenantsClient; import org.folio.dew.repository.RemoteFilesStorage; import org.folio.dew.service.JobCommandsReceiverService; import org.folio.spring.DefaultFolioExecutionContext; @@ -54,6 +61,7 @@ import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.io.FileSystemResource; import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; import org.springframework.kafka.test.context.EmbeddedKafka; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.DynamicPropertyRegistry; @@ -81,6 +89,7 @@ public abstract class BaseBatchTest { protected static final String TOKEN = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJkaWt1X2FkbWluIiwidXNlcl9pZCI6IjFkM2I1OGNiLTA3YjUtNWZjZC04YTJhLTNjZTA2YTBlYjkwZiIsImlhdCI6MTYxNjQyMDM5MywidGVuYW50IjoiZGlrdSJ9.2nvEYQBbJP1PewEgxixBWLHSX_eELiBEBpjufWiJZRs"; protected static final String NON_CONSORTIUM_TENANT = "diku"; protected static final String CONSORTIUM_TENANT = "consortium"; + protected static final String CONSORTIUM_MEMBER_TENANT = "college"; public static final int WIRE_MOCK_PORT = TestSocketUtils.findAvailableTcpPort(); private static String tenant = NON_CONSORTIUM_TENANT; @@ -172,6 +181,15 @@ protected static void setUpTenant(String tenant) { .configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true) .registerModule(new JavaTimeModule()); + @SneakyThrows + protected static void setUpConsortiumTenant(String centralTenantId, + List memberTenantIds, + String currentTenant) { + BaseBatchTest.tenant = currentTenant; + setUpNewTenant(centralTenantId); + setUpConsortiumMemberTenants(centralTenantId, memberTenantIds); + } + @SneakyThrows public static String asJsonString(Object value) { return OBJECT_MAPPER.writeValueAsString(value); @@ -242,4 +260,47 @@ static void tearDown() { wireMockServer.stop(); } + @SneakyThrows + private static void setUpConsortiumMemberTenants(String centralTenantId, + List memberTenantIds) { + memberTenantIds.forEach(BaseBatchTest::setUpNewTenant); + var consortiumId = UUID.randomUUID().toString(); + var userTenants = new UserTenantsClient.UserTenants( + List.of(new UserTenantsClient.UserTenant(centralTenantId, consortiumId))); + mockGet("/user-tenants", OBJECT_MAPPER.writeValueAsString(userTenants), SC_OK, wireMockServer); + var consortiumTenantList = memberTenantIds.stream() + .map(s -> new ConsortiumTenant(s, false)) + .collect(Collectors.toList()); + consortiumTenantList.add(new ConsortiumTenant(centralTenantId, true)); + var consortiumTenants = new ConsortiumTenants(consortiumTenantList); + mockGet("/consortia/" + consortiumId + "/tenants", OBJECT_MAPPER.writeValueAsString(consortiumTenants), SC_OK, + wireMockServer); + } + + @SneakyThrows + private static void setUpNewTenant(String tenant) { + var httpHeaders = new HttpHeaders(); + httpHeaders.setContentType(APPLICATION_JSON); + httpHeaders.add(TENANT, tenant); + httpHeaders.add(XOkapiHeaders.USER_ID, UUID.randomUUID().toString()); + httpHeaders.add(URL, wireMockServer.baseUrl()); + + mockMvc.perform(post("/_/tenant").content(asJsonString(new TenantAttributes().moduleTo("mod-data-export-worker"))) + .headers(httpHeaders) + .contentType(APPLICATION_JSON)).andExpect(status().isNoContent()); + mockGet("/user-tenants", "{\"userTenants\": [],\"totalRecords\": 0}", SC_OK, wireMockServer); + } + + private static void mockGet(String url, String body, int status, WireMockServer mockServer) { + mockServer.stubFor(WireMock.get(urlPathEqualTo(url)) + .willReturn(aResponse().withBody(body) + .withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .withStatus(status))); + } + + record ConsortiumTenant(String id, boolean isCentral) { + } + + record ConsortiumTenants(List tenants) { + } } diff --git a/src/test/resources/mappings/entitesLinksStats.json b/src/test/resources/mappings/entitesLinksStats.json index 975a0d763..d523d268f 100644 --- a/src/test/resources/mappings/entitesLinksStats.json +++ b/src/test/resources/mappings/entitesLinksStats.json @@ -12,7 +12,7 @@ }, "response": { "status": 200, - "body": "{\"stats\": [{\"id\": \"497f6eca-6276-4993-bfeb-53cbbbba6f08\",\"authorityId\": \"f4d72275-fbca-44d0-9214-72647a812332\",\"action\": \"UPDATE_HEADING\",\"naturalIdOld\": \"n1234567\",\"naturalIdNew\": \"n1234567\",\"headingOld\": \"Charles, Prince of Wales\",\"headingNew\": \"Charles III, King\",\"headingTypeOld\": \"150\",\"headingTypeNew\": \"110\",\"sourceFileOld\": \"Not specified\",\"sourceFileNew\": \"LC Name Authority file (LCNAF)\",\"lbTotal\": 105,\"lbUpdated\": 55,\"lbFailed\": 50,\"metadata\": {\"startedAt\": \"2023-10-01T12:00:00Z\",\"completedAt\": \"2023-10-01T15:00:00Z\",\"startedByUserId\": \"c8f721bd-38ac-411d-82c8-a5902fb19deb\",\"startedByUserFirstName\": \"Diku\",\"startedByUserLastName\": \"Admin\"}},{\"id\": \"500f6eca-6276-4993-bfeb-53cbbbba6f08\",\"authorityId\": \"f4d72275-fbca-44d0-9214-72647a812332\",\"action\": \"DELETE\",\"naturalIdOld\": \"n1234567\",\"naturalIdNew\": \"n1234567\",\"headingOld\": \"Charles, Prince of Wales\",\"headingNew\": \"Charles III, King\",\"headingTypeOld\": \"150\",\"headingTypeNew\": \"110\",\"sourceFileOld\": \"Not specified\",\"sourceFileNew\": \"LC Name Authority file (LCNAF)\",\"lbTotal\": 105,\"lbUpdated\": 55,\"lbFailed\": 50,\"metadata\": {\"startedAt\": \"2023-10-01T12:00:00Z\",\"completedAt\": \"2023-09-01T15:00:00Z\",\"startedByUserId\": \"c8f721bd-38ac-411d-82c8-a5902fb19deb\",\"startedByUserFirstName\": \"Diku\",\"startedByUserLastName\": \"Admin\"}}],\"next\": \"2023-08-01T12:00:00.000000Z\"}", + "body": "{\"stats\": [{\"id\": \"497f6eca-6276-4993-bfeb-53cbbbba6f08\",\"authorityId\": \"f4d72275-fbca-44d0-9214-72647a812332\",\"action\": \"UPDATE_HEADING\",\"naturalIdOld\": \"n1234567\",\"naturalIdNew\": \"n1234567\",\"headingOld\": \"Charles, Prince of Wales\",\"headingNew\": \"Charles III, King\",\"headingTypeOld\": \"150\",\"headingTypeNew\": \"110\",\"sourceFileOld\": \"Not specified\",\"sourceFileNew\": \"LC Name Authority file (LCNAF)\",\"metadata\": {\"startedAt\": \"2023-10-01T12:00:00Z\",\"completedAt\": \"2023-10-01T15:00:00Z\",\"startedByUserId\": \"c8f721bd-38ac-411d-82c8-a5902fb19deb\",\"startedByUserFirstName\": \"Diku\",\"startedByUserLastName\": \"Admin\"}},{\"id\": \"500f6eca-6276-4993-bfeb-53cbbbba6f08\",\"authorityId\": \"f4d72275-fbca-44d0-9214-72647a812332\",\"action\": \"DELETE\",\"naturalIdOld\": \"n1234567\",\"naturalIdNew\": \"n1234567\",\"headingOld\": \"Charles, Prince of Wales\",\"headingNew\": \"Charles III, King\",\"headingTypeOld\": \"150\",\"headingTypeNew\": \"110\",\"sourceFileOld\": \"Not specified\",\"sourceFileNew\": \"LC Name Authority file (LCNAF)\",\"metadata\": {\"startedAt\": \"2023-10-01T12:00:00Z\",\"completedAt\": \"2023-09-01T15:00:00Z\",\"startedByUserId\": \"c8f721bd-38ac-411d-82c8-a5902fb19deb\",\"startedByUserFirstName\": \"Diku\",\"startedByUserLastName\": \"Admin\"}}],\"next\": \"2023-08-01T12:00:00.000000Z\"}", "headers": { "Content-Type": "application/json" } @@ -30,7 +30,7 @@ }, "response": { "status": 200, - "body": "{\"stats\": [{\"id\": \"0b1e3760-f689-493e-a98e-9cc9dadb7e83\",\"authorityId\": \"c8f721bd-38ac-411d-82c8-a5902fb20deb\",\"action\": \"UPDATE_HEADING\",\"naturalIdOld\": \"mo34056\",\"naturalIdNew\": \"mo34056\",\"headingOld\": \"Charles III, King\",\"headingNew\": \"King Charles III\",\"headingTypeOld\": \"100\",\"headingTypeNew\": \"100\",\"sourceFileOld\": \"Not specified\",\"sourceFileNew\": \"Not specified\",\"lbTotal\": 10,\"lbUpdated\": 10,\"lbFailed\": 0,\"metadata\": {\"startedAt\": \"2023-08-01T12:00:00Z\",\"completedAt\": \"2023-08-01T15:00:00Z\",\"startedByUserId\": \"f4d72275-fbca-44d0-9214-72647a812333\",\"startedByUserFirstName\": \"Folio\",\"startedByUserLastName\": \"Test\"}},{\"id\": \"1b1e3760-f689-493e-a98e-9cc9dadb7e83\",\"authorityId\": \"c8f721bd-38ac-411d-82c8-a5902fb20deb\",\"action\": \"UPDATE_HEADING\",\"naturalIdOld\": \"mo34056\",\"naturalIdNew\": \"mo34056\",\"headingOld\": \"Charles III, King\",\"headingNew\": \"King Charles III\",\"headingTypeOld\": \"100\",\"headingTypeNew\": \"100\",\"sourceFileOld\": \"Not specified\",\"sourceFileNew\": \"Not specified\",\"lbTotal\": 10,\"lbUpdated\": 10,\"lbFailed\": 0,\"metadata\": {\"startedAt\": \"2023-08-01T12:00:00Z\",\"completedAt\": \"2023-07-01T15:00:00Z\",\"startedByUserId\": \"f4d72275-fbca-44d0-9214-72647a812333\",\"startedByUserLastName\": \"Test\"}},{\"id\": \"1b1e3760-f689-493e-a98e-9cc9dadb7e83\",\"authorityId\": \"c8f721bd-38ac-411d-82c8-a5902fb20deb\",\"action\": \"UPDATE_HEADING\",\"naturalIdOld\": \"mo34056\",\"naturalIdNew\": \"mo34056\",\"headingOld\": \"Charles III, King\",\"headingNew\": \"King Charles III\",\"headingTypeOld\": \"100\",\"headingTypeNew\": \"100\",\"sourceFileOld\": \"Not specified\",\"sourceFileNew\": \"Not specified\",\"lbTotal\": 10,\"lbUpdated\": 10,\"lbFailed\": 0,\"metadata\": {\"startedAt\": \"2023-08-01T12:00:00Z\",\"completedAt\": \"2023-07-01T15:00:00Z\",\"startedByUserId\": \"f4d72275-fbca-44d0-9214-72647a812334\",\"startedByUserLastName\": \"\",\"startedByUserFirstName\": null}}]}", + "body": "{\"stats\": [{\"id\": \"0b1e3760-f689-493e-a98e-9cc9dadb7e83\",\"authorityId\": \"c8f721bd-38ac-411d-82c8-a5902fb20deb\",\"action\": \"UPDATE_HEADING\",\"naturalIdOld\": \"mo34056\",\"naturalIdNew\": \"mo34056\",\"headingOld\": \"Charles III, King\",\"headingNew\": \"King Charles III\",\"headingTypeOld\": \"100\",\"headingTypeNew\": \"100\",\"sourceFileOld\": \"Not specified\",\"sourceFileNew\": \"Not specified\",\"metadata\": {\"startedAt\": \"2023-08-01T12:00:00Z\",\"completedAt\": \"2023-08-01T15:00:00Z\",\"startedByUserId\": \"f4d72275-fbca-44d0-9214-72647a812333\",\"startedByUserFirstName\": \"Folio\",\"startedByUserLastName\": \"Test\"}},{\"id\": \"1b1e3760-f689-493e-a98e-9cc9dadb7e83\",\"authorityId\": \"c8f721bd-38ac-411d-82c8-a5902fb20deb\",\"action\": \"UPDATE_HEADING\",\"naturalIdOld\": \"mo34056\",\"naturalIdNew\": \"mo34056\",\"headingOld\": \"Charles III, King\",\"headingNew\": \"King Charles III\",\"headingTypeOld\": \"100\",\"headingTypeNew\": \"100\",\"sourceFileOld\": \"Not specified\",\"sourceFileNew\": \"Not specified\",\"metadata\": {\"startedAt\": \"2023-08-01T12:00:00Z\",\"completedAt\": \"2023-07-01T15:00:00Z\",\"startedByUserId\": \"f4d72275-fbca-44d0-9214-72647a812333\",\"startedByUserLastName\": \"Test\"}},{\"id\": \"1b1e3760-f689-493e-a98e-9cc9dadb7e83\",\"authorityId\": \"c8f721bd-38ac-411d-82c8-a5902fb20deb\",\"action\": \"UPDATE_HEADING\",\"naturalIdOld\": \"mo34056\",\"naturalIdNew\": \"mo34056\",\"headingOld\": \"Charles III, King\",\"headingNew\": \"King Charles III\",\"headingTypeOld\": \"100\",\"headingTypeNew\": \"100\",\"sourceFileOld\": \"Not specified\",\"sourceFileNew\": \"Not specified\",\"metadata\": {\"startedAt\": \"2023-08-01T12:00:00Z\",\"completedAt\": \"2023-07-01T15:00:00Z\",\"startedByUserId\": \"f4d72275-fbca-44d0-9214-72647a812334\",\"startedByUserLastName\": \"\",\"startedByUserFirstName\": null}}]}", "headers": { "Content-Type": "application/json" } diff --git a/src/test/resources/mappings/entitesLinksStatsConsortium.json b/src/test/resources/mappings/entitesLinksStatsConsortium.json index 05b5c7d20..d11d66bbd 100644 --- a/src/test/resources/mappings/entitesLinksStatsConsortium.json +++ b/src/test/resources/mappings/entitesLinksStatsConsortium.json @@ -1,5 +1,23 @@ { "mappings": [ + { + "request": { + "method": "GET", + "url": "/links/stats/authority?limit=2&action=UPDATE_HEADING&fromDate=2024-01-01T00%3A00Z&toDate=2024-12-01T23%3A59%3A59.999999999Z", + "headers": { + "x-okapi-tenant": { + "equalTo": "college" + } + } + }, + "response": { + "status": 200, + "body": "{\"stats\": [{\"id\": \"497f6eca-6276-4993-bfeb-53cbbbba6f08\",\"authorityId\": \"f4d72275-fbca-44d0-9214-72647a812332\",\"action\": \"UPDATE_HEADING\",\"naturalIdOld\": \"n1234567\",\"naturalIdNew\": \"n1234567\",\"headingOld\": \"Charles, Prince of Wales\",\"headingNew\": \"Charles III, King\",\"headingTypeOld\": \"150\",\"headingTypeNew\": \"110\",\"sourceFileOld\": \"Not specified\",\"sourceFileNew\": \"LC Name Authority file (LCNAF)\",\"metadata\": {\"startedAt\": \"2024-10-01T12:00:01Z\",\"completedAt\": \"2024-10-01T15:00:00Z\",\"startedByUserId\": \"c8f721bd-38ac-411d-82c8-a5902fb19deb\",\"startedByUserFirstName\": \"Folio\",\"startedByUserLastName\": \"Test\"}},{\"id\": \"500f6eca-6276-4993-bfeb-53cbbbba6f08\",\"authorityId\": \"f4d72275-fbca-44d0-9214-72647a812332\",\"action\": \"DELETE\",\"naturalIdOld\": \"n1234567\",\"naturalIdNew\": \"n1234567\",\"headingOld\": \"Charles, Prince of Wales\",\"headingNew\": \"Charles III, King\",\"headingTypeOld\": \"150\",\"headingTypeNew\": \"111\",\"sourceFileOld\": \"Not specified\",\"sourceFileNew\": \"LC Name Authority file (LCNAF)\",\"metadata\": {\"startedAt\": \"2024-10-01T12:00:00Z\",\"completedAt\": \"2024-09-01T15:00:00Z\",\"startedByUserId\": \"c8f721bd-38ac-411d-82c8-a5902fb19deb\",\"startedByUserFirstName\": \"Folio\",\"startedByUserLastName\": \"Admin\"}} ], \"next\": null}", + "headers": { + "Content-Type": "application/json" + } + } + }, { "request": { "method": "GET", @@ -12,7 +30,7 @@ }, "response": { "status": 200, - "body": "{\"stats\": [{\"id\": \"497f6eca-6276-4993-bfeb-53cbbbba6f08\",\"authorityId\": \"f4d72275-fbca-44d0-9214-72647a812332\",\"action\": \"UPDATE_HEADING\",\"naturalIdOld\": \"n1234567\",\"naturalIdNew\": \"n1234567\",\"headingOld\": \"Charles, Prince of Wales\",\"headingNew\": \"Charles III, King\",\"headingTypeOld\": \"150\",\"headingTypeNew\": \"110\",\"sourceFileOld\": \"Not specified\",\"sourceFileNew\": \"LC Name Authority file (LCNAF)\",\"lbTotal\": 105,\"lbUpdated\": 55,\"lbFailed\": 50,\"shared\": false,\"metadata\": {\"startedAt\": \"2024-10-01T12:00:00Z\",\"completedAt\": \"2024-10-01T15:00:00Z\",\"startedByUserId\": \"c8f721bd-38ac-411d-82c8-a5902fb19deb\",\"startedByUserFirstName\": \"Folio\",\"startedByUserLastName\": \"Test\"}},{\"id\": \"500f6eca-6276-4993-bfeb-53cbbbba6f08\",\"authorityId\": \"f4d72275-fbca-44d0-9214-72647a812332\",\"action\": \"DELETE\",\"naturalIdOld\": \"n1234567\",\"naturalIdNew\": \"n1234567\",\"headingOld\": \"Charles, Prince of Wales\",\"headingNew\": \"Charles III, King\",\"headingTypeOld\": \"150\",\"headingTypeNew\": \"111\",\"sourceFileOld\": \"Not specified\",\"sourceFileNew\": \"LC Name Authority file (LCNAF)\",\"lbTotal\": 105,\"lbUpdated\": 55,\"lbFailed\": 50,\"shared\": false,\"metadata\": {\"startedAt\": \"2024-10-01T12:00:00Z\",\"completedAt\": \"2024-09-01T15:00:00Z\",\"startedByUserId\": \"c8f721bd-38ac-411d-82c8-a5902fb19deb\",\"startedByUserFirstName\": \"Folio\",\"startedByUserLastName\": \"Admin\"}} ], \"next\": \"2024-08-01T12:00:00.000000Z\"}", + "body": "{\"stats\": [{\"id\": \"1b1e3760-f689-493e-a98e-9cc9dadb7e83\",\"authorityId\": \"c8f721bd-38ac-411d-82c8-a5902fb20deb\",\"action\": \"UPDATE_HEADING\",\"naturalIdOld\": \"mo34056\",\"naturalIdNew\": \"mo34056\",\"headingOld\": \"Charles III, King\",\"headingNew\": \"King Charles III\",\"headingTypeOld\": \"100\",\"headingTypeNew\": \"110\",\"sourceFileOld\": \"Not specified\",\"sourceFileNew\": \"Not specified\",\"metadata\": {\"startedAt\": \"2024-08-01T12:00:00Z\",\"completedAt\": \"2024-07-01T15:00:00Z\",\"startedByUserId\": \"f4d72275-fbca-44d0-9214-72647a812333\",\"startedByUserLastName\": \"Consortium\"}},{\"id\": \"1b1e3760-f689-493e-a98e-9cc9dadb7e83\",\"authorityId\": \"c8f721bd-38ac-411d-82c8-a5902fb20deb\",\"action\": \"UPDATE_HEADING\",\"naturalIdOld\": \"mo34056\",\"naturalIdNew\": \"mo34056\",\"headingOld\": \"Charles III, King\",\"headingNew\": \"King Charles III\",\"headingTypeOld\": \"100\",\"headingTypeNew\": \"111\",\"sourceFileOld\": \"Not specified\",\"sourceFileNew\": \"Not specified\",\"metadata\": {\"startedAt\": \"2024-08-01T12:00:00Z\",\"completedAt\": \"2024-07-01T15:00:00Z\",\"startedByUserId\": \"f4d72275-fbca-44d0-9214-72647a812334\",\"startedByUserLastName\": \"\",\"startedByUserFirstName\": null}}],\"next\": \"2024-08-01T11:00:00.000000Z\"}", "headers": { "Content-Type": "application/json" } @@ -21,7 +39,7 @@ { "request": { "method": "GET", - "url": "/links/stats/authority?limit=2&action=UPDATE_HEADING&fromDate=2024-01-01T00%3A00Z&toDate=2024-08-01T12%3A00Z", + "url": "/links/stats/authority?limit=2&action=UPDATE_HEADING&fromDate=2024-01-01T00%3A00Z&toDate=2024-08-01T11%3A00Z", "headers": { "x-okapi-tenant": { "equalTo": "consortium" @@ -30,7 +48,7 @@ }, "response": { "status": 200, - "body": "{\"stats\": [{\"id\": \"0b1e3760-f689-493e-a98e-9cc9dadb7e83\",\"authorityId\": \"c8f721bd-38ac-411d-82c8-a5902fb20deb\",\"action\": \"UPDATE_HEADING\",\"naturalIdOld\": \"mo34056\",\"naturalIdNew\": \"mo34056\",\"headingOld\": \"Charles III, King\",\"headingNew\": \"King Charles III\",\"headingTypeOld\": \"100\",\"headingTypeNew\": \"100\",\"sourceFileOld\": \"Not specified\",\"sourceFileNew\": \"Not specified\",\"lbTotal\": 10,\"lbUpdated\": 10,\"lbFailed\": 0,\"shared\": true,\"metadata\": {\"startedAt\": \"2024-08-01T12:00:00Z\",\"completedAt\": \"2024-08-01T15:00:00Z\",\"startedByUserId\": \"f4d72275-fbca-44d0-9214-72647a812333\",\"startedByUserFirstName\": \"Consortium\",\"startedByUserLastName\": \"Admin\"}},{\"id\": \"1b1e3760-f689-493e-a98e-9cc9dadb7e83\",\"authorityId\": \"c8f721bd-38ac-411d-82c8-a5902fb20deb\",\"action\": \"UPDATE_HEADING\",\"naturalIdOld\": \"mo34056\",\"naturalIdNew\": \"mo34056\",\"headingOld\": \"Charles III, King\",\"headingNew\": \"King Charles III\",\"headingTypeOld\": \"100\",\"headingTypeNew\": \"110\",\"sourceFileOld\": \"Not specified\",\"sourceFileNew\": \"Not specified\",\"lbTotal\": 10,\"lbUpdated\": 10,\"lbFailed\": 0,\"shared\": true,\"metadata\": {\"startedAt\": \"2024-08-01T12:00:00Z\",\"completedAt\": \"2024-07-01T15:00:00Z\",\"startedByUserId\": \"f4d72275-fbca-44d0-9214-72647a812333\",\"startedByUserLastName\": \"Consortium\"}},{\"id\": \"1b1e3760-f689-493e-a98e-9cc9dadb7e83\",\"authorityId\": \"c8f721bd-38ac-411d-82c8-a5902fb20deb\",\"action\": \"UPDATE_HEADING\",\"naturalIdOld\": \"mo34056\",\"naturalIdNew\": \"mo34056\",\"headingOld\": \"Charles III, King\",\"headingNew\": \"King Charles III\",\"headingTypeOld\": \"100\",\"headingTypeNew\": \"111\",\"sourceFileOld\": \"Not specified\",\"sourceFileNew\": \"Not specified\",\"lbTotal\": 10,\"lbUpdated\": 10,\"lbFailed\": 0,\"shared\": true,\"metadata\": {\"startedAt\": \"2024-08-01T12:00:00Z\",\"completedAt\": \"2024-07-01T15:00:00Z\",\"startedByUserId\": \"f4d72275-fbca-44d0-9214-72647a812334\",\"startedByUserLastName\": \"\",\"startedByUserFirstName\": null}}]}", + "body": "{\"stats\": [{\"id\": \"0b1e3760-f689-493e-a98e-9cc9dadb7e83\",\"authorityId\": \"c8f721bd-38ac-411d-82c8-a5902fb20deb\",\"action\": \"UPDATE_HEADING\",\"naturalIdOld\": \"mo34056\",\"naturalIdNew\": \"mo34056\",\"headingOld\": \"Charles III, King\",\"headingNew\": \"King Charles III\",\"headingTypeOld\": \"100\",\"headingTypeNew\": \"100\",\"sourceFileOld\": \"Not specified\",\"sourceFileNew\": \"Not specified\",\"metadata\": {\"startedAt\": \"2024-08-01T11:00:00Z\",\"completedAt\": \"2024-08-01T15:00:00Z\",\"startedByUserId\": \"f4d72275-fbca-44d0-9214-72647a812333\",\"startedByUserFirstName\": \"Consortium\",\"startedByUserLastName\": \"Admin\"}}],\"next\": null}", "headers": { "Content-Type": "application/json" } diff --git a/src/test/resources/output/authority_control/auth_heading_update.csv b/src/test/resources/output/authority_control/auth_heading_update.csv index d17d94d08..da4f81915 100644 --- a/src/test/resources/output/authority_control/auth_heading_update.csv +++ b/src/test/resources/output/authority_control/auth_heading_update.csv @@ -1,6 +1,6 @@ -Last updated,Original heading,New heading,Identifier,Original 1XX,New 1XX,Authority source file name,Number of bibliographic records linked,Updater -2023-10-01 12:00:00.000Z,"Charles, Prince of Wales","Charles III, King",n1234567,150,110,LC Name Authority file (LCNAF),105,"Admin, Diku" -2023-10-01 12:00:00.000Z,"Charles, Prince of Wales","Charles III, King",n1234567,150,110,LC Name Authority file (LCNAF),105,"Admin, Diku" -2023-08-01 12:00:00.000Z,"Charles III, King",King Charles III,mo34056,100,100,Not specified,10,"Test, Folio" -2023-08-01 12:00:00.000Z,"Charles III, King",King Charles III,mo34056,100,100,Not specified,10,Test -2023-08-01 12:00:00.000Z,"Charles III, King",King Charles III,mo34056,100,100,Not specified,10,Unknown User \ No newline at end of file +Last updated,Original heading,New heading,Identifier,Original 1XX,New 1XX,Authority source file name,Updater +2023-10-01 12:00:00.000Z,"Charles, Prince of Wales","Charles III, King",n1234567,150,110,LC Name Authority file (LCNAF),"Admin, Diku" +2023-10-01 12:00:00.000Z,"Charles, Prince of Wales","Charles III, King",n1234567,150,110,LC Name Authority file (LCNAF),"Admin, Diku" +2023-08-01 12:00:00.000Z,"Charles III, King",King Charles III,mo34056,100,100,Not specified,"Test, Folio" +2023-08-01 12:00:00.000Z,"Charles III, King",King Charles III,mo34056,100,100,Not specified,Test +2023-08-01 12:00:00.000Z,"Charles III, King",King Charles III,mo34056,100,100,Not specified,Unknown User diff --git a/src/test/resources/output/authority_control/auth_heading_update_consortium.csv b/src/test/resources/output/authority_control/auth_heading_update_consortium.csv index a47698265..930dd1902 100644 --- a/src/test/resources/output/authority_control/auth_heading_update_consortium.csv +++ b/src/test/resources/output/authority_control/auth_heading_update_consortium.csv @@ -1,6 +1,6 @@ -Last updated,Original heading,New heading,Identifier,Original 1XX,New 1XX,Authority source file name,Number of bibliographic records linked,Authority record type,Updater -2024-10-01 12:00:00.000Z,"Charles, Prince of Wales","Charles III, King",n1234567,150,110,LC Name Authority file (LCNAF),105,local,"Test, Folio" -2024-10-01 12:00:00.000Z,"Charles, Prince of Wales","Charles III, King",n1234567,150,111,LC Name Authority file (LCNAF),105,local,"Admin, Folio" -2024-08-01 12:00:00.000Z,"Charles III, King",King Charles III,mo34056,100,100,Not specified,10,shared,"Admin, Consortium" -2024-08-01 12:00:00.000Z,"Charles III, King",King Charles III,mo34056,100,110,Not specified,10,shared,Consortium -2024-08-01 12:00:00.000Z,"Charles III, King",King Charles III,mo34056,100,111,Not specified,10,shared,Unknown User +Last updated,Original heading,New heading,Identifier,Original 1XX,New 1XX,Authority source file name,Authority record type,Updater +2024-10-01 12:00:01.000Z,"Charles, Prince of Wales","Charles III, King",n1234567,150,110,LC Name Authority file (LCNAF),local,"Test, Folio" +2024-10-01 12:00:00.000Z,"Charles, Prince of Wales","Charles III, King",n1234567,150,111,LC Name Authority file (LCNAF),local,"Admin, Folio" +2024-08-01 12:00:00.000Z,"Charles III, King",King Charles III,mo34056,100,110,Not specified,shared,Consortium +2024-08-01 12:00:00.000Z,"Charles III, King",King Charles III,mo34056,100,111,Not specified,shared,Unknown User +2024-08-01 11:00:00.000Z,"Charles III, King",King Charles III,mo34056,100,100,Not specified,shared,"Admin, Consortium" diff --git a/src/test/resources/output/authority_control/auth_heading_update_empty.csv b/src/test/resources/output/authority_control/auth_heading_update_empty.csv index 2743772f9..d104f00da 100644 --- a/src/test/resources/output/authority_control/auth_heading_update_empty.csv +++ b/src/test/resources/output/authority_control/auth_heading_update_empty.csv @@ -1,2 +1,2 @@ -Last updated,Original heading,New heading,Identifier,Original 1XX,New 1XX,Authority source file name,Number of bibliographic records linked,Updater +Last updated,Original heading,New heading,Identifier,Original 1XX,New 1XX,Authority source file name,Updater No records found From a19e70decd79084cbad480a7b24923501775ea47 Mon Sep 17 00:00:00 2001 From: SvitlanaKovalova1 Date: Tue, 17 Feb 2026 11:20:01 +0100 Subject: [PATCH 2/8] feat[auth-export-report]:fix getDataFromOverflowStats method --- .../readers/AuthUpdateHeadingsItemReader.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/folio/dew/batch/authoritycontrol/readers/AuthUpdateHeadingsItemReader.java b/src/main/java/org/folio/dew/batch/authoritycontrol/readers/AuthUpdateHeadingsItemReader.java index f7524fec4..4e510413a 100644 --- a/src/main/java/org/folio/dew/batch/authoritycontrol/readers/AuthUpdateHeadingsItemReader.java +++ b/src/main/java/org/folio/dew/batch/authoritycontrol/readers/AuthUpdateHeadingsItemReader.java @@ -119,9 +119,9 @@ private AuthorityDataStatDtoCollection getDataFromOverflowStats(int limit) { return getMergedAuthorityStats(null, null, null); } if (overflowStats.size() > limit) { - var mergedStats = overflowStats.stream().limit(limit).toList(); - overflowStats = mergedStats.subList(limit, mergedStats.size() - 1); - return getMergedAuthorityStats(mergedStats, null, null); + var resultStats = new ArrayList<>(overflowStats.subList(0, limit)); + overflowStats.subList(0, limit).clear(); + return getMergedAuthorityStats(resultStats, null, null); } var result = getMergedAuthorityStats(overflowStats, null, null); overflowStats = null; From 19384cd13a543836e7b25763ced47e8f05a18ffa Mon Sep 17 00:00:00 2001 From: SvitlanaKovalova1 Date: Tue, 17 Feb 2026 12:59:58 +0100 Subject: [PATCH 3/8] feat[auth-export-report]:set shared = true for stats records from central tenant --- .../readers/AuthUpdateHeadingsItemReader.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/main/java/org/folio/dew/batch/authoritycontrol/readers/AuthUpdateHeadingsItemReader.java b/src/main/java/org/folio/dew/batch/authoritycontrol/readers/AuthUpdateHeadingsItemReader.java index 4e510413a..9d51bad8f 100644 --- a/src/main/java/org/folio/dew/batch/authoritycontrol/readers/AuthUpdateHeadingsItemReader.java +++ b/src/main/java/org/folio/dew/batch/authoritycontrol/readers/AuthUpdateHeadingsItemReader.java @@ -49,6 +49,9 @@ protected AuthorityDataStatDtoCollection getCollection(int limit) { if (toDate() == null) { return null; } + if (isConsortiumTenant()) { + return getAuthorityStatsFromCentralTenant(limit); + } return entitiesLinksStatsClient.getAuthorityStats(limit, UPDATE_HEADING, fromDate(), toDate()); } @@ -77,6 +80,14 @@ protected AuthorityDataStatDto doRead() { return currentChunk.get(currentChunkOffset++); } + private AuthorityDataStatDtoCollection getAuthorityStatsFromCentralTenant(int limit) { + var result = entitiesLinksStatsClient.getAuthorityStats(limit, UPDATE_HEADING, fromDate(), toDate()); + if (result != null && result.getStats() != null && !result.getStats().isEmpty()) { + result.getStats().forEach(stat -> stat.setShared(true)); + } + return result; + } + private AuthorityDataStatDtoCollection getConsortiumAuthorityStats(int limit) { var memberTenantStats = fetchMemberTenantStats(limit); var centralTenantStats = fetchCentralTenantStats(limit); @@ -95,6 +106,10 @@ private boolean isConsortiumMemberTenant() { return consortiumTenant != null && !consortiumTenant.equals(context.getTenantId()); } + private boolean isConsortiumTenant() { + return consortiumTenant != null && consortiumTenant.equals(context.getTenantId()); + } + private AuthorityDataStatDtoCollection fetchMemberTenantStats(int limit) { if (toDate() != null) { return entitiesLinksStatsClient.getAuthorityStats(limit, UPDATE_HEADING, fromDate(), toDate()); From 55a7c41174372d9b95075b22e8620371d07864a8 Mon Sep 17 00:00:00 2001 From: SvitlanaKovalova1 Date: Tue, 17 Feb 2026 14:52:53 +0100 Subject: [PATCH 4/8] feat[auth-export-report]:fix getDataFromOverflowStats method --- .../authoritycontrol/readers/AuthUpdateHeadingsItemReader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/folio/dew/batch/authoritycontrol/readers/AuthUpdateHeadingsItemReader.java b/src/main/java/org/folio/dew/batch/authoritycontrol/readers/AuthUpdateHeadingsItemReader.java index 9d51bad8f..d6e740c07 100644 --- a/src/main/java/org/folio/dew/batch/authoritycontrol/readers/AuthUpdateHeadingsItemReader.java +++ b/src/main/java/org/folio/dew/batch/authoritycontrol/readers/AuthUpdateHeadingsItemReader.java @@ -135,7 +135,7 @@ private AuthorityDataStatDtoCollection getDataFromOverflowStats(int limit) { } if (overflowStats.size() > limit) { var resultStats = new ArrayList<>(overflowStats.subList(0, limit)); - overflowStats.subList(0, limit).clear(); + overflowStats = new ArrayList<>(overflowStats.subList(limit, overflowStats.size())); return getMergedAuthorityStats(resultStats, null, null); } var result = getMergedAuthorityStats(overflowStats, null, null); From 5b7d53622c7898d03027a552694d0cf793bce63f Mon Sep 17 00:00:00 2001 From: SvitlanaKovalova1 Date: Tue, 17 Feb 2026 19:16:59 +0100 Subject: [PATCH 5/8] update testcontainers version in the pom.xml --- pom.xml | 2 +- .../readers/AuthUpdateHeadingsItemReader.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 7de867cad..e43b55ce5 100644 --- a/pom.xml +++ b/pom.xml @@ -65,7 +65,7 @@ 2.27.2 1.20.5 2.9.1 - 1.17.6 + 1.21.4 2.40.2 3.0.0-SNAPSHOT 1.3 diff --git a/src/main/java/org/folio/dew/batch/authoritycontrol/readers/AuthUpdateHeadingsItemReader.java b/src/main/java/org/folio/dew/batch/authoritycontrol/readers/AuthUpdateHeadingsItemReader.java index d6e740c07..5db8d0263 100644 --- a/src/main/java/org/folio/dew/batch/authoritycontrol/readers/AuthUpdateHeadingsItemReader.java +++ b/src/main/java/org/folio/dew/batch/authoritycontrol/readers/AuthUpdateHeadingsItemReader.java @@ -26,7 +26,7 @@ public class AuthUpdateHeadingsItemReader extends AuthorityControlItemReader overflowStats; + private List overflowStats = new ArrayList<>(); public AuthUpdateHeadingsItemReader(EntitiesLinksStatsClient entitiesLinksStatsClient, AuthorityControlExportConfig exportConfig, @@ -130,7 +130,7 @@ private AuthorityDataStatDtoCollection fetchCentralTenantStats(int limit) { } private AuthorityDataStatDtoCollection getDataFromOverflowStats(int limit) { - if (overflowStats == null) { + if (overflowStats == null || overflowStats.isEmpty()) { return getMergedAuthorityStats(null, null, null); } if (overflowStats.size() > limit) { From 69182087ec8797f1cf480b328a2c6a232aaa538f Mon Sep 17 00:00:00 2001 From: SvitlanaKovalova1 Date: Thu, 19 Feb 2026 10:34:08 +0100 Subject: [PATCH 6/8] feat[auth-export-report]: update AuthUpdateHeadingsItemReader --- folio-export-common | 2 +- .../readers/AuthUpdateHeadingsItemReader.java | 172 ++++++------------ .../readers/AuthorityControlItemReader.java | 6 +- .../dew/AuthorityControlConsortiumTest.java | 35 +++- .../mappings/entitesLinksStatsConsortium.json | 54 ++++++ ...eading_update_consortium_member_tenant.csv | 4 + 6 files changed, 142 insertions(+), 131 deletions(-) create mode 100644 src/test/resources/output/authority_control/auth_heading_update_consortium_member_tenant.csv diff --git a/folio-export-common b/folio-export-common index 7856bc84e..d98ac2205 160000 --- a/folio-export-common +++ b/folio-export-common @@ -1 +1 @@ -Subproject commit 7856bc84e68dc324376330a46c6fb9a8bd920369 +Subproject commit d98ac22051c12c7bbfa5f099f67b8222e6feca0e diff --git a/src/main/java/org/folio/dew/batch/authoritycontrol/readers/AuthUpdateHeadingsItemReader.java b/src/main/java/org/folio/dew/batch/authoritycontrol/readers/AuthUpdateHeadingsItemReader.java index 5db8d0263..3186b62eb 100644 --- a/src/main/java/org/folio/dew/batch/authoritycontrol/readers/AuthUpdateHeadingsItemReader.java +++ b/src/main/java/org/folio/dew/batch/authoritycontrol/readers/AuthUpdateHeadingsItemReader.java @@ -3,9 +3,7 @@ import static org.folio.dew.domain.dto.authority.control.AuthorityDataStatDto.ActionEnum.UPDATE_HEADING; import java.util.ArrayList; -import java.util.Comparator; import java.util.List; -import lombok.extern.log4j.Log4j2; import org.folio.dew.client.EntitiesLinksStatsClient; import org.folio.dew.config.properties.AuthorityControlJobProperties; import org.folio.dew.domain.dto.authority.control.AuthorityControlExportConfig; @@ -17,7 +15,6 @@ import org.springframework.batch.core.configuration.annotation.StepScope; import org.springframework.stereotype.Component; -@Log4j2 @StepScope @Component public class AuthUpdateHeadingsItemReader extends AuthorityControlItemReader { @@ -25,8 +22,11 @@ public class AuthUpdateHeadingsItemReader extends AuthorityControlItemReader overflowStats = new ArrayList<>(); + private final List memberTenantStats = new ArrayList<>(); + private final List consortiumTenantStats = new ArrayList<>(); public AuthUpdateHeadingsItemReader(EntitiesLinksStatsClient entitiesLinksStatsClient, AuthorityControlExportConfig exportConfig, @@ -38,150 +38,82 @@ public AuthUpdateHeadingsItemReader(EntitiesLinksStatsClient entitiesLinksStatsC this.context = context; this.executionService = executionService; this.consortiumTenant = folioTenantService.getConsortiumTenant(); + this.isConsortiumMemberTenant = consortiumTenant != null && !consortiumTenant.equals(this.context.getTenantId()); + this.isConsortiumTenant = consortiumTenant != null && consortiumTenant.equals(this.context.getTenantId()); } @Override - protected AuthorityDataStatDtoCollection getCollection(int limit) { - log.debug("Fetching authority stats for tenant [{}] ", context.getTenantId()); - if (isConsortiumMemberTenant()) { - return getConsortiumAuthorityStats(limit); - } - if (toDate() == null) { - return null; + protected AuthorityDataStatDto doRead() { + if (!isConsortiumMemberTenant) { + return super.doRead(); } - if (isConsortiumTenant()) { + return getConsortiumAuthorityDataStat(limit); + } + + @Override + protected AuthorityDataStatDtoCollection getCollection(int limit) { + if (isConsortiumTenant) { return getAuthorityStatsFromCentralTenant(limit); } return entitiesLinksStatsClient.getAuthorityStats(limit, UPDATE_HEADING, fromDate(), toDate()); } - @Override - protected AuthorityDataStatDto doRead() { - if (currentChunk == null || currentChunkOffset >= currentChunk.size()) { - if (toDate == null && toConsortiumDate == null && currentChunk != null) { - var collection = getCollection(limit); - if (collection == null || collection.getStats() == null || collection.getStats().isEmpty()) { - return null; - } - currentChunk = collection.getStats(); - currentChunkOffset = 0; - } else { - var collection = getCollection(limit); - currentChunk = collection.getStats(); - toDate = collection.getNext(); - toConsortiumDate = collection.getConsortiumNext(); - currentChunkOffset = 0; - } + private AuthorityDataStatDto getConsortiumAuthorityDataStat(int limit) { + if (memberTenantStats.isEmpty()) { + loadNextMemberTenantStatsPage(limit); } - - if (currentChunk.isEmpty()) { + if (consortiumTenantStats.isEmpty()) { + loadNextCentralTenantStatsPage(limit); + } + // if there are no stats in both member and central tenant return null to finish reading + if (memberTenantStats.isEmpty() && consortiumTenantStats.isEmpty()) { return null; } - return currentChunk.get(currentChunkOffset++); - } - private AuthorityDataStatDtoCollection getAuthorityStatsFromCentralTenant(int limit) { - var result = entitiesLinksStatsClient.getAuthorityStats(limit, UPDATE_HEADING, fromDate(), toDate()); - if (result != null && result.getStats() != null && !result.getStats().isEmpty()) { - result.getStats().forEach(stat -> stat.setShared(true)); + if (memberTenantStats.isEmpty()) { + return consortiumTenantStats.removeFirst(); } - return result; - } - private AuthorityDataStatDtoCollection getConsortiumAuthorityStats(int limit) { - var memberTenantStats = fetchMemberTenantStats(limit); - var centralTenantStats = fetchCentralTenantStats(limit); - - if (memberTenantStats == null && centralTenantStats == null) { - return getDataFromOverflowStats(limit); - } - var mergedStats = mergeStats(memberTenantStats, centralTenantStats); - if (mergedStats != null && mergedStats.size() > limit) { - return createStatsPage(limit, mergedStats, memberTenantStats, centralTenantStats); + if (consortiumTenantStats.isEmpty()) { + return memberTenantStats.removeFirst(); } - return getMergedAuthorityStats(mergedStats, memberTenantStats, centralTenantStats); - } - private boolean isConsortiumMemberTenant() { - return consortiumTenant != null && !consortiumTenant.equals(context.getTenantId()); - } + var memberStat = memberTenantStats.getFirst(); + var consortiumStat = consortiumTenantStats.getFirst(); - private boolean isConsortiumTenant() { - return consortiumTenant != null && consortiumTenant.equals(context.getTenantId()); + return memberStat.getMetadata().getStartedAt().isAfter(consortiumStat.getMetadata().getStartedAt()) + ? memberTenantStats.removeFirst() + : consortiumTenantStats.removeFirst(); } - private AuthorityDataStatDtoCollection fetchMemberTenantStats(int limit) { - if (toDate() != null) { - return entitiesLinksStatsClient.getAuthorityStats(limit, UPDATE_HEADING, fromDate(), toDate()); + private AuthorityDataStatDtoCollection getAuthorityStatsFromCentralTenant(int limit) { + var authorityStats = entitiesLinksStatsClient.getAuthorityStats(limit, UPDATE_HEADING, fromDate(), toDate()); + if (authorityStats != null && authorityStats.getStats() != null && !authorityStats.getStats().isEmpty()) { + authorityStats.getStats().forEach(stat -> stat.setShared(true)); } - return null; + return authorityStats; } - private AuthorityDataStatDtoCollection fetchCentralTenantStats(int limit) { - if (toConsortiumDate() != null) { - var centralTenantStats = executionService.execute(consortiumTenant, context, () -> - entitiesLinksStatsClient.getAuthorityStats(limit, UPDATE_HEADING, fromDate(), toConsortiumDate())); - if (centralTenantStats != null && !centralTenantStats.getStats().isEmpty()) { - centralTenantStats.getStats().forEach(stat -> stat.setShared(true)); + private void loadNextMemberTenantStatsPage(int limit) { + if (toDate() != null) { + var authorityStats = entitiesLinksStatsClient.getAuthorityStats(limit, UPDATE_HEADING, fromDate(), toDate()); + if (authorityStats != null) { + toDate = authorityStats.getNext(); + memberTenantStats.addAll(authorityStats.getStats()); } - return centralTenantStats; - } - return null; - } - - private AuthorityDataStatDtoCollection getDataFromOverflowStats(int limit) { - if (overflowStats == null || overflowStats.isEmpty()) { - return getMergedAuthorityStats(null, null, null); - } - if (overflowStats.size() > limit) { - var resultStats = new ArrayList<>(overflowStats.subList(0, limit)); - overflowStats = new ArrayList<>(overflowStats.subList(limit, overflowStats.size())); - return getMergedAuthorityStats(resultStats, null, null); } - var result = getMergedAuthorityStats(overflowStats, null, null); - overflowStats = null; - return result; } - private List mergeStats(AuthorityDataStatDtoCollection memberTenantStats, - AuthorityDataStatDtoCollection centralTenantStats) { - - List mergedStats = new ArrayList<>(); + private void loadNextCentralTenantStatsPage(int limit) { + if (toConsortiumDate() != null) { + var authorityStats = executionService.execute(consortiumTenant, context, () -> + entitiesLinksStatsClient.getAuthorityStats(limit, UPDATE_HEADING, fromDate(), toConsortiumDate())); - if (memberTenantStats != null) { - mergedStats.addAll(memberTenantStats.getStats()); - } - if (centralTenantStats != null) { - mergedStats.addAll(centralTenantStats.getStats()); - } - if (overflowStats != null && !overflowStats.isEmpty()) { - mergedStats.addAll(overflowStats); - overflowStats = null; + if (authorityStats != null && !authorityStats.getStats().isEmpty()) { + authorityStats.getStats().forEach(stat -> stat.setShared(true)); + toConsortiumDate = authorityStats.getNext(); + consortiumTenantStats.addAll(authorityStats.getStats()); + } } - return sortStatsByStartedAtDesc(mergedStats); - } - - private AuthorityDataStatDtoCollection createStatsPage(int limit, List mergedStats, - AuthorityDataStatDtoCollection memberTenantStats, - AuthorityDataStatDtoCollection centralTenantStats) { - var resultStats = mergedStats.stream().limit(limit).toList(); - overflowStats = mergedStats.subList(limit, mergedStats.size()); - return getMergedAuthorityStats(resultStats, memberTenantStats, centralTenantStats); - } - - private AuthorityDataStatDtoCollection getMergedAuthorityStats(List mergedStats, - AuthorityDataStatDtoCollection memberTenantStats, - AuthorityDataStatDtoCollection centralTenantStats) { - return new AuthorityDataStatDtoCollection() - .stats(mergedStats) - .next(memberTenantStats != null ? memberTenantStats.getNext() : null) - .consortiumNext(centralTenantStats != null ? centralTenantStats.getNext() : null); - } - - private List sortStatsByStartedAtDesc(List stats) { - return stats.stream() - .sorted(Comparator.comparing(tenantStats -> tenantStats.getMetadata().getStartedAt())) - .toList() - .reversed(); } } diff --git a/src/main/java/org/folio/dew/batch/authoritycontrol/readers/AuthorityControlItemReader.java b/src/main/java/org/folio/dew/batch/authoritycontrol/readers/AuthorityControlItemReader.java index f0ba41d8d..90f2ae1ea 100644 --- a/src/main/java/org/folio/dew/batch/authoritycontrol/readers/AuthorityControlItemReader.java +++ b/src/main/java/org/folio/dew/batch/authoritycontrol/readers/AuthorityControlItemReader.java @@ -17,10 +17,10 @@ public abstract class AuthorityControlItemReader extends AbstractItemCountingItemStreamItemReader { protected final EntitiesLinksStatsClient entitiesLinksStatsClient; protected final int limit; - protected OffsetDateTime fromDate; + private int currentChunkOffset; + private List currentChunk; + private OffsetDateTime fromDate; protected OffsetDateTime toDate; - protected int currentChunkOffset; - protected List currentChunk; protected OffsetDateTime toConsortiumDate; protected AuthorityControlItemReader(EntitiesLinksStatsClient entitiesLinksStatsClient, diff --git a/src/test/java/org/folio/dew/AuthorityControlConsortiumTest.java b/src/test/java/org/folio/dew/AuthorityControlConsortiumTest.java index 22988eecb..c62e02cef 100644 --- a/src/test/java/org/folio/dew/AuthorityControlConsortiumTest.java +++ b/src/test/java/org/folio/dew/AuthorityControlConsortiumTest.java @@ -41,8 +41,10 @@ class AuthorityControlConsortiumTest extends BaseBatchTest { - private static final String EXPECTED_AUTH_HEADING_UPDATE_OUTPUT = + private static final String EXPECTED_CONSORTIUM_OUTPUT = "src/test/resources/output/authority_control/auth_heading_update_consortium.csv"; + private static final String EXPECTED_CONSORTIUM_MEMBER_TENANT_OUTPUT = + "src/test/resources/output/authority_control/auth_heading_update_consortium_member_tenant.csv"; private static final String EXPECTED_S3_FILE_PATH = "mod-data-export-worker/authority_control_export/college/"; @Autowired @@ -61,7 +63,7 @@ static void beforeAll() { @Test @DisplayName("Run AuthHeadingJob export in the consortium tenant successfully") - void authHeadingJobTest() throws Exception { + void authHeadingJobTestReceiveStatsFromMemberAndCentralTenantTest() throws Exception { var exportConfig = buildExportConfig("2024-01-01", "2024-12-01"); var testLauncher = createTestLauncher(getAuthHeadingJob); var jobParameters = prepareJobParameters(exportConfig); @@ -70,7 +72,7 @@ void authHeadingJobTest() throws Exception { assertThat(jobExecution.getExitStatus()).isEqualTo(ExitStatus.COMPLETED); - verifyFile(jobExecution); + verifyFile(jobExecution, EXPECTED_CONSORTIUM_OUTPUT); wireMockServer.verify(getRequestedFor(urlEqualTo( "/links/stats/authority?limit=2&action=UPDATE_HEADING&fromDate=2024-01-01T00%3A00Z&toDate=2024-12-01T23%3A59%3A59.999999999Z"))); wireMockServer.verify(getRequestedFor(urlEqualTo( @@ -78,19 +80,38 @@ void authHeadingJobTest() throws Exception { verifyJobEvent(); } - private void verifyFile(JobExecution jobExecution) throws Exception { + @Test + @DisplayName("Run AuthHeadingJob export in the consortium tenant successfully") + void authHeadingJobReceiveStatsOnlyFromMemberTenantTest() throws Exception { + var exportConfig = buildExportConfig("2025-02-14", "2025-02-17"); + var testLauncher = createTestLauncher(getAuthHeadingJob); + var jobParameters = prepareJobParameters(exportConfig); + + var jobExecution = testLauncher.launchJob(jobParameters); + + assertThat(jobExecution.getExitStatus()).isEqualTo(ExitStatus.COMPLETED); + + verifyFile(jobExecution, EXPECTED_CONSORTIUM_MEMBER_TENANT_OUTPUT); + wireMockServer.verify(getRequestedFor(urlEqualTo( + "/links/stats/authority?limit=2&action=UPDATE_HEADING&fromDate=2025-02-14T00%3A00Z&toDate=2025-02-17T23%3A59%3A59.999999999Z"))); + wireMockServer.verify(getRequestedFor(urlEqualTo( + "/links/stats/authority?limit=2&action=UPDATE_HEADING&fromDate=2025-02-14T00%3A00Z&toDate=2025-02-15T11%3A00Z"))); + verifyJobEvent(); + } + + private void verifyFile(JobExecution jobExecution, String expectedReportOutput) throws Exception { var executionContext = jobExecution.getExecutionContext(); var fileInStorage = executionContext.getString(OUTPUT_FILES_IN_STORAGE); var fileName = executionContext.getString(AUTHORITY_CONTROL_FILE_NAME); assertEquals(EXPECTED_S3_FILE_PATH + fileName, fileInStorage); - verifyFileOutput(fileInStorage); + verifyFileOutput(fileInStorage, expectedReportOutput); } - private void verifyFileOutput(String fileInStorage) throws Exception { + private void verifyFileOutput(String fileInStorage, String expectedReportOutput) throws Exception { var presignedUrl = remoteFilesStorage.objectToPresignedObjectUrl(fileInStorage); var actualOutput = actualFileOutput(presignedUrl); - var expectedOutput = new FileSystemResource(EXPECTED_AUTH_HEADING_UPDATE_OUTPUT); + var expectedOutput = new FileSystemResource(expectedReportOutput); assertTrue(FileUtils.contentEqualsIgnoreEOL(expectedOutput.getFile(), actualOutput.getFile(), "UTF-8") , "Files are not identical!"); } diff --git a/src/test/resources/mappings/entitesLinksStatsConsortium.json b/src/test/resources/mappings/entitesLinksStatsConsortium.json index d11d66bbd..cb37992c6 100644 --- a/src/test/resources/mappings/entitesLinksStatsConsortium.json +++ b/src/test/resources/mappings/entitesLinksStatsConsortium.json @@ -53,6 +53,60 @@ "Content-Type": "application/json" } } + }, + { + "request": { + "method": "GET", + "url": "/links/stats/authority?limit=2&action=UPDATE_HEADING&fromDate=2025-02-14T00%3A00Z&toDate=2025-02-17T23%3A59%3A59.999999999Z", + "headers": { + "x-okapi-tenant": { + "equalTo": "college" + } + } + }, + "response": { + "status": 200, + "body": "{\"stats\": [{\"id\": \"497f6eca-6276-4993-bfeb-53cbbbba6f08\",\"authorityId\": \"f4d72275-fbca-44d0-9214-72647a812332\",\"action\": \"UPDATE_HEADING\",\"naturalIdOld\": \"n1234567\",\"naturalIdNew\": \"n1234567\",\"headingOld\": \"Charles III, King upd2\",\"headingNew\": \"Charles III, King upd3\",\"headingTypeOld\": \"150\",\"headingTypeNew\": \"110\",\"sourceFileOld\": \"Not specified\",\"sourceFileNew\": \"LC Name Authority file (LCNAF)\",\"metadata\": {\"startedAt\": \"2025-02-16T12:00:01Z\",\"completedAt\": \"2022-02-16T15:00:00Z\",\"startedByUserId\": \"c8f721bd-38ac-411d-82c8-a5902fb19deb\",\"startedByUserFirstName\": \"Folio\",\"startedByUserLastName\": \"Test\"}},{\"id\": \"500f6eca-6276-4993-bfeb-53cbbbba6f08\",\"authorityId\": \"f4d72275-fbca-44d0-9214-72647a812332\",\"action\": \"UPDATE_HEADING\",\"naturalIdOld\": \"n1234567\",\"naturalIdNew\": \"n1234567\",\"headingOld\": \"Charles III, King upd1\",\"headingNew\": \"Charles III, King upd2\",\"headingTypeOld\": \"150\",\"headingTypeNew\": \"111\",\"sourceFileOld\": \"Not specified\",\"sourceFileNew\": \"LC Name Authority file (LCNAF)\",\"metadata\": {\"startedAt\": \"2025-02-16T12:00:00Z\",\"completedAt\": \"2025-02-16T15:00:00Z\",\"startedByUserId\": \"c8f721bd-38ac-411d-82c8-a5902fb19deb\",\"startedByUserFirstName\": \"Folio\",\"startedByUserLastName\": \"Admin\"}} ], \"next\": \"2025-02-15T11:00:00.000000Z\"}", + "headers": { + "Content-Type": "application/json" + } + } + }, + { + "request": { + "method": "GET", + "url": "/links/stats/authority?limit=2&action=UPDATE_HEADING&fromDate=2025-02-14T00%3A00Z&toDate=2025-02-15T11%3A00Z", + "headers": { + "x-okapi-tenant": { + "equalTo": "college" + } + } + }, + "response": { + "status": 200, + "body": "{\"stats\": [{\"id\": \"497f6eca-6276-4993-bfeb-53cbbbba6f08\",\"authorityId\": \"f4d72275-fbca-44d0-9214-72647a812332\",\"action\": \"UPDATE_HEADING\",\"naturalIdOld\": \"n12345678\",\"naturalIdNew\": \"n12345678\",\"headingOld\": \"Charles III, King\",\"headingNew\": \"Charles III, King upd1\",\"headingTypeOld\": \"150\",\"headingTypeNew\": \"110\",\"sourceFileOld\": \"Not specified\",\"sourceFileNew\": \"LC Name Authority file (LCNAF)\",\"metadata\": {\"startedAt\": \"2025-02-15T12:05:00Z\",\"completedAt\": \"2025-02-15T15:00:00Z\",\"startedByUserId\": \"c8f721bd-38ac-411d-82c8-a5902fb19deb\",\"startedByUserFirstName\": \"Folio\",\"startedByUserLastName\": \"Test\"}}], \"next\": null}", + "headers": { + "Content-Type": "application/json" + } + } + }, + { + "request": { + "method": "GET", + "url": "/links/stats/authority?limit=2&action=UPDATE_HEADING&fromDate=2025-02-14T00%3A00Z&toDate=2025-02-17T23%3A59%3A59.999999999Z", + "headers": { + "x-okapi-tenant": { + "equalTo": "consortium" + } + } + }, + "response": { + "status": 200, + "body": "{\"stats\": []}", + "headers": { + "Content-Type": "application/json" + } + } } ] } diff --git a/src/test/resources/output/authority_control/auth_heading_update_consortium_member_tenant.csv b/src/test/resources/output/authority_control/auth_heading_update_consortium_member_tenant.csv new file mode 100644 index 000000000..ca794fd63 --- /dev/null +++ b/src/test/resources/output/authority_control/auth_heading_update_consortium_member_tenant.csv @@ -0,0 +1,4 @@ +Last updated,Original heading,New heading,Identifier,Original 1XX,New 1XX,Authority source file name,Authority record type,Updater +2025-02-16 12:00:01.000Z,"Charles III, King upd2","Charles III, King upd3",n1234567,150,110,LC Name Authority file (LCNAF),local,"Test, Folio" +2025-02-16 12:00:00.000Z,"Charles III, King upd1","Charles III, King upd2",n1234567,150,111,LC Name Authority file (LCNAF),local,"Admin, Folio" +2025-02-15 12:05:00.000Z,"Charles III, King","Charles III, King upd1",n12345678,150,110,LC Name Authority file (LCNAF),local,"Test, Folio" From 9bec9339110f56432f3a01c1895ba3b2faa677ee Mon Sep 17 00:00:00 2001 From: SvitlanaKovalova1 Date: Thu, 19 Feb 2026 15:52:46 +0100 Subject: [PATCH 7/8] feat[auth-export-report]: replace ArrayList with ArrayDeque for FIFO buffering --- .../readers/AuthUpdateHeadingsItemReader.java | 69 +++++++++---------- .../readers/AuthorityControlItemReader.java | 6 -- .../folio/dew/service/FolioTenantService.java | 6 +- 3 files changed, 37 insertions(+), 44 deletions(-) diff --git a/src/main/java/org/folio/dew/batch/authoritycontrol/readers/AuthUpdateHeadingsItemReader.java b/src/main/java/org/folio/dew/batch/authoritycontrol/readers/AuthUpdateHeadingsItemReader.java index 3186b62eb..13fb15c5e 100644 --- a/src/main/java/org/folio/dew/batch/authoritycontrol/readers/AuthUpdateHeadingsItemReader.java +++ b/src/main/java/org/folio/dew/batch/authoritycontrol/readers/AuthUpdateHeadingsItemReader.java @@ -2,8 +2,9 @@ import static org.folio.dew.domain.dto.authority.control.AuthorityDataStatDto.ActionEnum.UPDATE_HEADING; -import java.util.ArrayList; -import java.util.List; +import java.time.OffsetDateTime; +import java.util.ArrayDeque; +import java.util.Deque; import org.folio.dew.client.EntitiesLinksStatsClient; import org.folio.dew.config.properties.AuthorityControlJobProperties; import org.folio.dew.domain.dto.authority.control.AuthorityControlExportConfig; @@ -21,12 +22,13 @@ public class AuthUpdateHeadingsItemReader extends AuthorityControlItemReader memberTenantStats = new ArrayList<>(); - private final List consortiumTenantStats = new ArrayList<>(); + private final Deque memberTenantStats = new ArrayDeque<>(); + private final Deque centralTenantStats = new ArrayDeque<>(); public AuthUpdateHeadingsItemReader(EntitiesLinksStatsClient entitiesLinksStatsClient, AuthorityControlExportConfig exportConfig, @@ -37,9 +39,10 @@ public AuthUpdateHeadingsItemReader(EntitiesLinksStatsClient entitiesLinksStatsC super(entitiesLinksStatsClient, exportConfig, jobProperties); this.context = context; this.executionService = executionService; - this.consortiumTenant = folioTenantService.getConsortiumTenant(); - this.isConsortiumMemberTenant = consortiumTenant != null && !consortiumTenant.equals(this.context.getTenantId()); - this.isConsortiumTenant = consortiumTenant != null && consortiumTenant.equals(this.context.getTenantId()); + this.consortiumCentralTenant = folioTenantService.getConsortiumCentralTenant(); + this.isConsortiumMemberTenant = consortiumCentralTenant != null && !consortiumCentralTenant.equals(this.context.getTenantId()); + this.isConsortiumCentralTenant = consortiumCentralTenant != null && consortiumCentralTenant.equals(this.context.getTenantId()); + this.toConsortiumDate = this.toDate; } @Override @@ -52,46 +55,38 @@ protected AuthorityDataStatDto doRead() { @Override protected AuthorityDataStatDtoCollection getCollection(int limit) { - if (isConsortiumTenant) { - return getAuthorityStatsFromCentralTenant(limit); + var authorityStats = entitiesLinksStatsClient.getAuthorityStats(limit, UPDATE_HEADING, fromDate(), toDate()); + if (isConsortiumCentralTenant && authorityStats != null && !authorityStats.getStats().isEmpty()) { + authorityStats.getStats().forEach(stat -> stat.setShared(true)); } - return entitiesLinksStatsClient.getAuthorityStats(limit, UPDATE_HEADING, fromDate(), toDate()); + return authorityStats; } private AuthorityDataStatDto getConsortiumAuthorityDataStat(int limit) { if (memberTenantStats.isEmpty()) { loadNextMemberTenantStatsPage(limit); } - if (consortiumTenantStats.isEmpty()) { + if (centralTenantStats.isEmpty()) { loadNextCentralTenantStatsPage(limit); } // if there are no stats in both member and central tenant return null to finish reading - if (memberTenantStats.isEmpty() && consortiumTenantStats.isEmpty()) { + if (memberTenantStats.isEmpty() && centralTenantStats.isEmpty()) { return null; } if (memberTenantStats.isEmpty()) { - return consortiumTenantStats.removeFirst(); + return centralTenantStats.pollFirst(); } - - if (consortiumTenantStats.isEmpty()) { - return memberTenantStats.removeFirst(); + if (centralTenantStats.isEmpty()) { + return memberTenantStats.pollFirst(); } - var memberStat = memberTenantStats.getFirst(); - var consortiumStat = consortiumTenantStats.getFirst(); - - return memberStat.getMetadata().getStartedAt().isAfter(consortiumStat.getMetadata().getStartedAt()) - ? memberTenantStats.removeFirst() - : consortiumTenantStats.removeFirst(); - } + var memberStat = memberTenantStats.peekFirst(); + var centralStat = centralTenantStats.peekFirst(); - private AuthorityDataStatDtoCollection getAuthorityStatsFromCentralTenant(int limit) { - var authorityStats = entitiesLinksStatsClient.getAuthorityStats(limit, UPDATE_HEADING, fromDate(), toDate()); - if (authorityStats != null && authorityStats.getStats() != null && !authorityStats.getStats().isEmpty()) { - authorityStats.getStats().forEach(stat -> stat.setShared(true)); - } - return authorityStats; + return memberStat.getMetadata().getStartedAt().isAfter(centralStat.getMetadata().getStartedAt()) + ? memberTenantStats.pollFirst() + : centralTenantStats.pollFirst(); } private void loadNextMemberTenantStatsPage(int limit) { @@ -105,15 +100,19 @@ private void loadNextMemberTenantStatsPage(int limit) { } private void loadNextCentralTenantStatsPage(int limit) { - if (toConsortiumDate() != null) { - var authorityStats = executionService.execute(consortiumTenant, context, () -> - entitiesLinksStatsClient.getAuthorityStats(limit, UPDATE_HEADING, fromDate(), toConsortiumDate())); + if (formatConsortiumDate() != null) { + var authorityStats = executionService.execute(consortiumCentralTenant, context, () -> + entitiesLinksStatsClient.getAuthorityStats(limit, UPDATE_HEADING, fromDate(), formatConsortiumDate())); if (authorityStats != null && !authorityStats.getStats().isEmpty()) { authorityStats.getStats().forEach(stat -> stat.setShared(true)); toConsortiumDate = authorityStats.getNext(); - consortiumTenantStats.addAll(authorityStats.getStats()); + centralTenantStats.addAll(authorityStats.getStats()); } } } + + private String formatConsortiumDate() { + return toConsortiumDate == null ? null : toConsortiumDate.toString(); + } } diff --git a/src/main/java/org/folio/dew/batch/authoritycontrol/readers/AuthorityControlItemReader.java b/src/main/java/org/folio/dew/batch/authoritycontrol/readers/AuthorityControlItemReader.java index 90f2ae1ea..eefdfb10f 100644 --- a/src/main/java/org/folio/dew/batch/authoritycontrol/readers/AuthorityControlItemReader.java +++ b/src/main/java/org/folio/dew/batch/authoritycontrol/readers/AuthorityControlItemReader.java @@ -21,7 +21,6 @@ public abstract class AuthorityControlItemReader private List currentChunk; private OffsetDateTime fromDate; protected OffsetDateTime toDate; - protected OffsetDateTime toConsortiumDate; protected AuthorityControlItemReader(EntitiesLinksStatsClient entitiesLinksStatsClient, AuthorityControlExportConfig exportConfig, @@ -31,7 +30,6 @@ protected AuthorityControlItemReader(EntitiesLinksStatsClient entitiesLinksStats } if (exportConfig.getToDate() != null) { this.toDate = OffsetDateTime.of(exportConfig.getToDate(), LocalTime.MAX, ZoneOffset.UTC); - this.toConsortiumDate = this.toDate; } this.entitiesLinksStatsClient = entitiesLinksStatsClient; this.limit = jobProperties.getEntitiesLinksChunkSize(); @@ -69,10 +67,6 @@ protected String toDate() { return Objects.isNull(toDate) ? null : toDate.toString(); } - protected String toConsortiumDate() { - return Objects.isNull(toConsortiumDate) ? null : toConsortiumDate.toString(); - } - @Override protected void doOpen() { // Nothing to do diff --git a/src/main/java/org/folio/dew/service/FolioTenantService.java b/src/main/java/org/folio/dew/service/FolioTenantService.java index 87f3f88c7..fb547b3c5 100644 --- a/src/main/java/org/folio/dew/service/FolioTenantService.java +++ b/src/main/java/org/folio/dew/service/FolioTenantService.java @@ -68,11 +68,11 @@ public boolean isConsortiumTenant() { } /** - * Get consortium tenant if current context tenant is a part of consortium + * Get consortium central tenant if current context tenant is a part of consortium * - * @return consortium tenant or null if current context tenant is not a part of consortium + * @return consortium central tenant or null if current context tenant is not a part of consortium */ - public String getConsortiumTenant() { + public String getConsortiumCentralTenant() { var centralTenant = userTenantsService.getCentralTenant(context.getTenantId()); return centralTenant.orElse(null); } From a64bda8d892a4e9a2df31eec5ff24452ee9656c3 Mon Sep 17 00:00:00 2001 From: SvitlanaKovalova1 Date: Fri, 20 Feb 2026 14:16:58 +0200 Subject: [PATCH 8/8] feat[auth-export-report]: add support for API version 2.1 of "instance-authority-links-statistics" --- descriptors/ModuleDescriptor-template.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/descriptors/ModuleDescriptor-template.json b/descriptors/ModuleDescriptor-template.json index 84974a679..59268f17b 100644 --- a/descriptors/ModuleDescriptor-template.json +++ b/descriptors/ModuleDescriptor-template.json @@ -40,7 +40,7 @@ }, { "id": "instance-authority-links-statistics", - "version": "3.0" + "version": "2.1 3.0" }, { "id": "holdings-note-types",