diff --git a/pom.xml b/pom.xml
index 218d5da3..f1ae6145 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
org.springframework.boot
spring-boot-starter-parent
- 3.4.3
+ 4.0.2
@@ -34,10 +34,9 @@
${project.basedir}/src/main/resources/swagger.api/export-configs.yaml
${project.basedir}/src/main/resources/swagger.api/job-deletion-intervals.yaml
- 9.0.0
+ 10.0.0-RC1
4.1.1
1.0.0
- 3.9.2
1.18.42
1.6.3
0.2.0
@@ -45,9 +44,9 @@
6.2.1
- 1.20.5
+ 1.21.4
2.4.0
- 2.27.2
+ 3.13.0
5.15.0
5.2.0
@@ -111,12 +110,6 @@
postgresql
-
- io.hypersistence
- hypersistence-utils-hibernate-63
- ${hypersistence-utils-hibernate-63.version}
-
-
org.springframework.batch
spring-batch-core
@@ -139,8 +132,8 @@
- org.springframework.kafka
- spring-kafka
+ org.springframework.boot
+ spring-boot-starter-kafka
@@ -215,6 +208,11 @@
+
+ org.springframework.boot
+ spring-boot-starter-webmvc-test
+ test
+
org.mockito
mockito-inline
@@ -257,16 +255,10 @@
mockserver-client-java
${mockserver-client-java.version}
test
-
-
- org.bouncycastle
- bcprov-jdk18on
-
-
- com.github.tomakehurst
+ org.wiremock
wiremock-standalone
${wiremock-standalone.version}
test
@@ -275,6 +267,7 @@
io.rest-assured
rest-assured
+ 6.0.0
test
diff --git a/src/main/java/org/folio/de/entity/BaseJob.java b/src/main/java/org/folio/de/entity/BaseJob.java
index cc85cda8..c5a33e06 100644
--- a/src/main/java/org/folio/de/entity/BaseJob.java
+++ b/src/main/java/org/folio/de/entity/BaseJob.java
@@ -1,6 +1,5 @@
package org.folio.de.entity;
-import io.hypersistence.utils.hibernate.type.json.JsonBinaryType;
import jakarta.persistence.Column;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
@@ -15,7 +14,9 @@
import org.folio.des.domain.dto.IdentifierType;
import org.folio.des.domain.dto.JobStatus;
import org.folio.des.domain.dto.Progress;
+import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.annotations.Type;
+import org.hibernate.type.SqlTypes;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.ExitStatus;
@@ -42,11 +43,11 @@ public abstract class BaseJob {
@Enumerated(EnumType.STRING)
private JobStatus status;
- @Type(JsonBinaryType.class)
+ @JdbcTypeCode(SqlTypes.JSON)
@Column(columnDefinition = "jsonb")
private List files = null;
- @Type(JsonBinaryType.class)
+ @JdbcTypeCode(SqlTypes.JSON)
@Column(columnDefinition = "jsonb")
private List fileNames = null;
@@ -73,7 +74,7 @@ public abstract class BaseJob {
@Enumerated(EnumType.STRING)
private BatchStatus batchStatus;
- @Type(JsonBinaryType.class)
+ @JdbcTypeCode(SqlTypes.JSON)
@Column(columnDefinition = "jsonb")
private ExitStatus exitStatus;
@@ -83,7 +84,7 @@ public abstract class BaseJob {
@Enumerated(EnumType.STRING)
private EntityType entityType;
- @Type(JsonBinaryType.class)
+ @JdbcTypeCode(SqlTypes.JSON)
@Column(columnDefinition = "jsonb")
private Progress progress;
}
diff --git a/src/main/java/org/folio/de/entity/ExportConfigEntity.java b/src/main/java/org/folio/de/entity/ExportConfigEntity.java
index f9b5a40c..49112f3f 100644
--- a/src/main/java/org/folio/de/entity/ExportConfigEntity.java
+++ b/src/main/java/org/folio/de/entity/ExportConfigEntity.java
@@ -4,9 +4,8 @@
import java.util.UUID;
import org.folio.de.entity.base.AuditableEntity;
-import org.hibernate.annotations.Type;
+import org.hibernate.annotations.JdbcTypeCode;
-import io.hypersistence.utils.hibernate.type.json.JsonBinaryType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
@@ -14,6 +13,7 @@
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
+import org.hibernate.type.SqlTypes;
@Entity
@Table(name = "export_config")
@@ -35,7 +35,7 @@ public class ExportConfigEntity extends AuditableEntity {
@Column(name = "tenant", nullable = false)
private String tenant;
- @Type(JsonBinaryType.class)
+ @JdbcTypeCode(SqlTypes.JSON)
@Column(name = "export_type_specific_parameters", columnDefinition = "jsonb", nullable = false)
private Object exportTypeSpecificParameters;
@@ -48,7 +48,7 @@ public class ExportConfigEntity extends AuditableEntity {
@Column(name = "schedule_time")
private String scheduleTime;
- @Type(JsonBinaryType.class)
+ @JdbcTypeCode(SqlTypes.JSON)
@Column(name = "week_days", columnDefinition = "jsonb")
private List weekDays;
diff --git a/src/main/java/org/folio/de/entity/Job.java b/src/main/java/org/folio/de/entity/Job.java
index 30a3b9cd..e5e0c9c5 100644
--- a/src/main/java/org/folio/de/entity/Job.java
+++ b/src/main/java/org/folio/de/entity/Job.java
@@ -1,17 +1,17 @@
package org.folio.de.entity;
-import io.hypersistence.utils.hibernate.type.json.JsonBinaryType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import lombok.Data;
import org.folio.des.domain.dto.ExportTypeSpecificParameters;
-import org.hibernate.annotations.Type;
+import org.hibernate.annotations.JdbcTypeCode;
+import org.hibernate.type.SqlTypes;
@Entity
@Data
public class Job extends BaseJob {
- @Type(JsonBinaryType.class)
+ @JdbcTypeCode(SqlTypes.JSON)
@Column(columnDefinition = "jsonb")
private ExportTypeSpecificParameters exportTypeSpecificParameters;
}
diff --git a/src/main/java/org/folio/de/entity/JobCommand.java b/src/main/java/org/folio/de/entity/JobCommand.java
index 3c0a377c..74ba81c8 100644
--- a/src/main/java/org/folio/de/entity/JobCommand.java
+++ b/src/main/java/org/folio/de/entity/JobCommand.java
@@ -5,7 +5,7 @@
import org.folio.des.domain.dto.ExportType;
import org.folio.des.domain.dto.IdentifierType;
import org.folio.des.domain.dto.Progress;
-import org.springframework.batch.core.JobParameters;
+import org.springframework.batch.core.job.parameters.JobParameters;
import java.util.UUID;
diff --git a/src/main/java/org/folio/de/entity/bursarlegacy/JobWithLegacyBursarParameters.java b/src/main/java/org/folio/de/entity/bursarlegacy/JobWithLegacyBursarParameters.java
index 8897ab2a..2e890598 100644
--- a/src/main/java/org/folio/de/entity/bursarlegacy/JobWithLegacyBursarParameters.java
+++ b/src/main/java/org/folio/de/entity/bursarlegacy/JobWithLegacyBursarParameters.java
@@ -1,20 +1,20 @@
package org.folio.de.entity.bursarlegacy;
-import io.hypersistence.utils.hibernate.type.json.JsonBinaryType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
import lombok.Data;
import org.folio.de.entity.BaseJob;
import org.folio.des.domain.dto.ExportTypeSpecificParametersWithLegacyBursar;
-import org.hibernate.annotations.Type;
+import org.hibernate.annotations.JdbcTypeCode;
+import org.hibernate.type.SqlTypes;
@Entity
@Table(name = "job")
@Data
public class JobWithLegacyBursarParameters extends BaseJob {
- @Type(JsonBinaryType.class)
+ @JdbcTypeCode(SqlTypes.JSON)
@Column(columnDefinition = "jsonb")
private ExportTypeSpecificParametersWithLegacyBursar exportTypeSpecificParameters;
}
diff --git a/src/main/java/org/folio/des/ModDataExportSpringApplication.java b/src/main/java/org/folio/des/ModDataExportSpringApplication.java
index 23ebe405..743bde4e 100644
--- a/src/main/java/org/folio/des/ModDataExportSpringApplication.java
+++ b/src/main/java/org/folio/des/ModDataExportSpringApplication.java
@@ -6,12 +6,9 @@
import org.folio.de.entity.JobCommand;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration;
-import org.springframework.boot.autoconfigure.domain.EntityScan;
-import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.boot.persistence.autoconfigure.EntityScan;
-@SpringBootApplication(exclude = BatchAutoConfiguration.class)
-@EnableFeignClients
+@SpringBootApplication
@EntityScan(basePackageClasses = JobCommand.class)
public class ModDataExportSpringApplication {
public static final String SYSTEM_USER_PASSWORD = "SYSTEM_USER_PASSWORD";
@@ -19,7 +16,7 @@ public class ModDataExportSpringApplication {
public static void main(String[] args) {
String systemUserEnabled = Optional.ofNullable(System.getenv(SYSTEM_USER_ENABLED)).orElse("true");
-
+
if (
Boolean.parseBoolean(systemUserEnabled)
&& StringUtils.isEmpty(System.getenv(SYSTEM_USER_PASSWORD))
diff --git a/src/main/java/org/folio/des/builder/job/AuthorityControlJobCommandBuilder.java b/src/main/java/org/folio/des/builder/job/AuthorityControlJobCommandBuilder.java
index d897b092..4e00a290 100644
--- a/src/main/java/org/folio/des/builder/job/AuthorityControlJobCommandBuilder.java
+++ b/src/main/java/org/folio/des/builder/job/AuthorityControlJobCommandBuilder.java
@@ -5,8 +5,8 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.folio.de.entity.Job;
-import org.springframework.batch.core.JobParameters;
-import org.springframework.batch.core.JobParametersBuilder;
+import org.springframework.batch.core.job.parameters.JobParameters;
+import org.springframework.batch.core.job.parameters.JobParametersBuilder;
import org.springframework.stereotype.Service;
@Service
diff --git a/src/main/java/org/folio/des/builder/job/BursarFeeFinesJobCommandBuilder.java b/src/main/java/org/folio/des/builder/job/BursarFeeFinesJobCommandBuilder.java
index ba6f11ef..98d76eb6 100644
--- a/src/main/java/org/folio/des/builder/job/BursarFeeFinesJobCommandBuilder.java
+++ b/src/main/java/org/folio/des/builder/job/BursarFeeFinesJobCommandBuilder.java
@@ -5,8 +5,8 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.folio.de.entity.Job;
-import org.springframework.batch.core.JobParameters;
-import org.springframework.batch.core.JobParametersBuilder;
+import org.springframework.batch.core.job.parameters.JobParameters;
+import org.springframework.batch.core.job.parameters.JobParametersBuilder;
import org.springframework.stereotype.Service;
@Service
diff --git a/src/main/java/org/folio/des/builder/job/CirculationLogJobCommandBuilder.java b/src/main/java/org/folio/des/builder/job/CirculationLogJobCommandBuilder.java
index 858f8149..4811cd82 100644
--- a/src/main/java/org/folio/des/builder/job/CirculationLogJobCommandBuilder.java
+++ b/src/main/java/org/folio/des/builder/job/CirculationLogJobCommandBuilder.java
@@ -1,8 +1,8 @@
package org.folio.des.builder.job;
import org.folio.de.entity.Job;
-import org.springframework.batch.core.JobParameters;
-import org.springframework.batch.core.JobParametersBuilder;
+import org.springframework.batch.core.job.parameters.JobParameters;
+import org.springframework.batch.core.job.parameters.JobParametersBuilder;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
diff --git a/src/main/java/org/folio/des/builder/job/EHoldingsJobCommandBuilder.java b/src/main/java/org/folio/des/builder/job/EHoldingsJobCommandBuilder.java
index 0d9bd55d..3b4d17df 100644
--- a/src/main/java/org/folio/des/builder/job/EHoldingsJobCommandBuilder.java
+++ b/src/main/java/org/folio/des/builder/job/EHoldingsJobCommandBuilder.java
@@ -5,8 +5,8 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.folio.de.entity.Job;
-import org.springframework.batch.core.JobParameters;
-import org.springframework.batch.core.JobParametersBuilder;
+import org.springframework.batch.core.job.parameters.JobParameters;
+import org.springframework.batch.core.job.parameters.JobParametersBuilder;
import org.springframework.stereotype.Service;
@Service
diff --git a/src/main/java/org/folio/des/builder/job/EdifactOrdersJobCommandBuilder.java b/src/main/java/org/folio/des/builder/job/EdifactOrdersJobCommandBuilder.java
index ba45d363..39308e29 100644
--- a/src/main/java/org/folio/des/builder/job/EdifactOrdersJobCommandBuilder.java
+++ b/src/main/java/org/folio/des/builder/job/EdifactOrdersJobCommandBuilder.java
@@ -5,8 +5,8 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.folio.de.entity.Job;
-import org.springframework.batch.core.JobParameters;
-import org.springframework.batch.core.JobParametersBuilder;
+import org.springframework.batch.core.job.parameters.JobParameters;
+import org.springframework.batch.core.job.parameters.JobParametersBuilder;
import org.springframework.stereotype.Service;
@Service
diff --git a/src/main/java/org/folio/des/builder/job/EdifactOrdersJobCommandSchedulerBuilder.java b/src/main/java/org/folio/des/builder/job/EdifactOrdersJobCommandSchedulerBuilder.java
index 18cc19e0..0db5369f 100644
--- a/src/main/java/org/folio/des/builder/job/EdifactOrdersJobCommandSchedulerBuilder.java
+++ b/src/main/java/org/folio/des/builder/job/EdifactOrdersJobCommandSchedulerBuilder.java
@@ -1,7 +1,7 @@
package org.folio.des.builder.job;
import org.folio.de.entity.JobCommand;
-import org.springframework.batch.core.JobParametersBuilder;
+import org.springframework.batch.core.job.parameters.JobParametersBuilder;
import org.springframework.stereotype.Service;
import com.fasterxml.jackson.core.JsonProcessingException;
diff --git a/src/main/java/org/folio/des/builder/job/JobCommandBuilder.java b/src/main/java/org/folio/des/builder/job/JobCommandBuilder.java
index 8f35bdec..b1142f27 100644
--- a/src/main/java/org/folio/des/builder/job/JobCommandBuilder.java
+++ b/src/main/java/org/folio/des/builder/job/JobCommandBuilder.java
@@ -1,7 +1,7 @@
package org.folio.des.builder.job;
import org.folio.de.entity.Job;
-import org.springframework.batch.core.JobParameters;
+import org.springframework.batch.core.job.parameters.JobParameters;
@FunctionalInterface
public interface JobCommandBuilder {
diff --git a/src/main/java/org/folio/des/client/DataExportSpringClient.java b/src/main/java/org/folio/des/client/DataExportSpringClient.java
index deba06ba..d7362d60 100644
--- a/src/main/java/org/folio/des/client/DataExportSpringClient.java
+++ b/src/main/java/org/folio/des/client/DataExportSpringClient.java
@@ -1,16 +1,15 @@
package org.folio.des.client;
-import org.folio.des.config.feign.FeignClientConfiguration;
import org.folio.des.domain.dto.Job;
-import org.springframework.cloud.openfeign.FeignClient;
-import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.service.annotation.HttpExchange;
+import org.springframework.web.service.annotation.PostExchange;
-@FeignClient(name = "data-export-spring", configuration = FeignClientConfiguration.class)
+@HttpExchange(url = "data-export-spring")
public interface DataExportSpringClient {
- @PostMapping(value = "/jobs")
+ @PostExchange(value = "/jobs")
Job upsertJob(@RequestBody Job job);
- @PostMapping(value = "/jobs/send")
+ @PostExchange(value = "/jobs/send")
void sendJob(@RequestBody Job job);
}
diff --git a/src/main/java/org/folio/des/client/ExportWorkerClient.java b/src/main/java/org/folio/des/client/ExportWorkerClient.java
index a5ec00d2..547f9dc4 100644
--- a/src/main/java/org/folio/des/client/ExportWorkerClient.java
+++ b/src/main/java/org/folio/des/client/ExportWorkerClient.java
@@ -1,13 +1,13 @@
package org.folio.des.client;
import org.folio.des.domain.dto.PresignedUrl;
-import org.springframework.cloud.openfeign.FeignClient;
-import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.service.annotation.GetExchange;
+import org.springframework.web.service.annotation.HttpExchange;
-@FeignClient("refresh-presigned-url")
+@HttpExchange("refresh-presigned-url")
public interface ExportWorkerClient {
- @GetMapping
+ @GetExchange
PresignedUrl getRefreshedPresignedUrl(@RequestParam("filePath") String filePath);
}
diff --git a/src/main/java/org/folio/des/config/HttpClientConfiguration.java b/src/main/java/org/folio/des/config/HttpClientConfiguration.java
new file mode 100644
index 00000000..dbcc0674
--- /dev/null
+++ b/src/main/java/org/folio/des/config/HttpClientConfiguration.java
@@ -0,0 +1,46 @@
+package org.folio.des.config;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.log4j.Log4j2;
+import org.folio.des.client.DataExportSpringClient;
+import org.folio.des.client.ExportWorkerClient;
+import org.folio.des.exceptions.RestClientErrorHandler;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatusCode;
+import org.springframework.web.client.RestClient;
+import org.springframework.web.service.invoker.HttpServiceProxyFactory;
+
+@Configuration
+@Log4j2
+@RequiredArgsConstructor
+public class HttpClientConfiguration {
+
+ private final RestClientErrorHandler errorHandler;
+
+ @Bean
+ public DataExportSpringClient dataExportSpringClient(HttpServiceProxyFactory factory) {
+ return factory.createClient(DataExportSpringClient.class);
+ }
+
+ @Bean
+ public ExportWorkerClient exportWorkerClient(HttpServiceProxyFactory factory) {
+ return factory.createClient(ExportWorkerClient.class);
+ }
+
+ @Primary
+ @Bean
+ public RestClient restClient(RestClient.Builder builder) {
+ return builder
+ .requestInterceptor(
+ (request, body, execution) -> {
+ log.info("Request URL: {}", request.getURI());
+ request.getHeaders().add(HttpHeaders.ACCEPT_ENCODING, "identity");
+ return execution.execute(request, body);
+ })
+ .defaultStatusHandler(HttpStatusCode::isError, errorHandler::handle)
+ .build();
+ }
+}
diff --git a/src/main/java/org/folio/des/config/JacksonConfiguration.java b/src/main/java/org/folio/des/config/JacksonConfiguration.java
index 48e2f163..fb1bb9a6 100644
--- a/src/main/java/org/folio/des/config/JacksonConfiguration.java
+++ b/src/main/java/org/folio/des/config/JacksonConfiguration.java
@@ -13,22 +13,23 @@
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
-import io.hypersistence.utils.hibernate.type.util.ObjectMapperSupplier;
import java.io.IOException;
import java.sql.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
+import org.hibernate.type.format.jackson.JacksonJsonFormatMapper;
import org.springframework.batch.core.ExitStatus;
-import org.springframework.batch.core.JobParameter;
+import org.springframework.batch.core.job.parameters.JobParameter;
import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.boot.hibernate.autoconfigure.HibernatePropertiesCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
@Configuration
-public class JacksonConfiguration implements ObjectMapperSupplier {
+public class JacksonConfiguration {
private static final ObjectMapper OBJECT_MAPPER;
private static final ObjectMapper ENTITY_OBJECT_MAPPER;
@@ -94,11 +95,11 @@ public JobParameter> deserialize(JsonParser jp, DeserializationContext ctxt) t
JsonNode jsonNode = jp.getCodec().readTree(jp);
var identifying = jsonNode.get("identifying").asBoolean();
switch (jsonNode.get("type").asText()) {
- case "STRING" -> new JobParameter<>(jsonNode.get(VALUE_PARAMETER_PROPERTY).asText(), String.class, identifying);
+ case "STRING" -> new JobParameter<>("STRING", jsonNode.get(VALUE_PARAMETER_PROPERTY).asText(), String.class, identifying);
case "DATE" -> new JobParameter<>(
- Date.valueOf(jsonNode.get(VALUE_PARAMETER_PROPERTY).asText()), Date.class, identifying);
- case "LONG" -> new JobParameter<>(jsonNode.get(VALUE_PARAMETER_PROPERTY).asLong(), Long.class, identifying);
- case "DOUBLE" -> new JobParameter<>(jsonNode.get(VALUE_PARAMETER_PROPERTY).asDouble(), Double.class, identifying);
+ "DATE", Date.valueOf(jsonNode.get(VALUE_PARAMETER_PROPERTY).asText()), Date.class, identifying);
+ case "LONG" -> new JobParameter<>("LONG", jsonNode.get(VALUE_PARAMETER_PROPERTY).asLong(), Long.class, identifying);
+ case "DOUBLE" -> new JobParameter<>("DOUBLE", jsonNode.get(VALUE_PARAMETER_PROPERTY).asDouble(), Double.class, identifying);
}
return null;
}
@@ -124,12 +125,15 @@ public ObjectMapper objectMapper() {
@Bean
@Qualifier("entityObjectMapper")
public ObjectMapper entityObjectMapper() {
- return ENTITY_OBJECT_MAPPER;
+ return ENTITY_OBJECT_MAPPER;
}
- @Override
- public ObjectMapper get() {
- return OBJECT_MAPPER;
+ @Bean
+ public HibernatePropertiesCustomizer hibernatePropertiesCustomizer(ObjectMapper objectMapper) {
+ return hibernateProperties -> hibernateProperties.put(
+ "hibernate.type.json_format_mapper",
+ new JacksonJsonFormatMapper(objectMapper)
+ );
}
}
diff --git a/src/main/java/org/folio/des/config/feign/CustomFeignErrorDecoder.java b/src/main/java/org/folio/des/config/feign/CustomFeignErrorDecoder.java
deleted file mode 100644
index 315e1502..00000000
--- a/src/main/java/org/folio/des/config/feign/CustomFeignErrorDecoder.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package org.folio.des.config.feign;
-
-import static feign.FeignException.errorStatus;
-
-import org.folio.spring.exception.NotFoundException;
-import org.springframework.http.HttpStatus;
-
-import feign.Response;
-import feign.codec.ErrorDecoder;
-
-public class CustomFeignErrorDecoder implements ErrorDecoder {
-
- @Override
- public Exception decode(String methodKey, Response response) {
- String requestUrl = response.request().url();
- if (HttpStatus.NOT_FOUND.value() == response.status()) {
- return new NotFoundException(requestUrl);
- }
- return errorStatus(methodKey, response);
- }
-
-}
diff --git a/src/main/java/org/folio/des/config/feign/FeignClientConfiguration.java b/src/main/java/org/folio/des/config/feign/FeignClientConfiguration.java
deleted file mode 100644
index cf72fd6c..00000000
--- a/src/main/java/org/folio/des/config/feign/FeignClientConfiguration.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package org.folio.des.config.feign;
-
-import org.springframework.context.annotation.Bean;
-
-import feign.codec.ErrorDecoder;
-
-public class FeignClientConfiguration {
- @Bean
- public ErrorDecoder errorDecoder() {
- return new CustomFeignErrorDecoder();
- }
-}
diff --git a/src/main/java/org/folio/des/config/kafka/KafkaConfiguration.java b/src/main/java/org/folio/des/config/kafka/KafkaConfiguration.java
index 3cbb7e68..f0c84a0d 100644
--- a/src/main/java/org/folio/des/config/kafka/KafkaConfiguration.java
+++ b/src/main/java/org/folio/des/config/kafka/KafkaConfiguration.java
@@ -10,7 +10,7 @@
import org.apache.kafka.common.serialization.StringSerializer;
import org.folio.spring.FolioExecutionContext;
import org.folio.spring.FolioModuleMetadata;
-import org.springframework.boot.autoconfigure.kafka.KafkaProperties;
+import org.springframework.boot.kafka.autoconfigure.KafkaProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
diff --git a/src/main/java/org/folio/des/config/scheduling/QuartzSchedulerFactoryBeanCustomizer.java b/src/main/java/org/folio/des/config/scheduling/QuartzSchedulerFactoryBeanCustomizer.java
index ecfeedf4..bd1d65a2 100644
--- a/src/main/java/org/folio/des/config/scheduling/QuartzSchedulerFactoryBeanCustomizer.java
+++ b/src/main/java/org/folio/des/config/scheduling/QuartzSchedulerFactoryBeanCustomizer.java
@@ -1,7 +1,7 @@
package org.folio.des.config.scheduling;
import org.folio.spring.config.DataSourceFolioWrapper;
-import org.springframework.boot.autoconfigure.quartz.SchedulerFactoryBeanCustomizer;
+import org.springframework.boot.quartz.autoconfigure.SchedulerFactoryBeanCustomizer;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Component;
diff --git a/src/main/java/org/folio/des/config/scheduling/QuartzSchemaInitializer.java b/src/main/java/org/folio/des/config/scheduling/QuartzSchemaInitializer.java
index 6fc88a4d..41fd70df 100644
--- a/src/main/java/org/folio/des/config/scheduling/QuartzSchemaInitializer.java
+++ b/src/main/java/org/folio/des/config/scheduling/QuartzSchemaInitializer.java
@@ -3,7 +3,7 @@
import org.folio.spring.liquibase.FolioSpringLiquibase;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
-import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties;
+import org.springframework.boot.liquibase.autoconfigure.LiquibaseProperties;
import org.springframework.stereotype.Component;
import liquibase.exception.LiquibaseException;
diff --git a/src/main/java/org/folio/des/controller/ControllerExceptionHandler.java b/src/main/java/org/folio/des/controller/ControllerExceptionHandler.java
index 4e68b7fc..2508c47e 100644
--- a/src/main/java/org/folio/des/controller/ControllerExceptionHandler.java
+++ b/src/main/java/org/folio/des/controller/ControllerExceptionHandler.java
@@ -17,9 +17,9 @@
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.client.RestClientException;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
-import feign.FeignException;
import lombok.extern.log4j.Log4j2;
@RestControllerAdvice
@@ -33,7 +33,7 @@ public class ControllerExceptionHandler {
MissingServletRequestParameterException.class,
MethodArgumentTypeMismatchException.class,
MethodArgumentNotValidException.class,
- FeignException.class
+ RestClientException.class
})
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Errors handleIllegalArgumentException(Exception exception) {
diff --git a/src/main/java/org/folio/des/exceptions/RestClientErrorHandler.java b/src/main/java/org/folio/des/exceptions/RestClientErrorHandler.java
new file mode 100644
index 00000000..d4613f9d
--- /dev/null
+++ b/src/main/java/org/folio/des/exceptions/RestClientErrorHandler.java
@@ -0,0 +1,36 @@
+package org.folio.des.exceptions;
+
+import org.folio.spring.exception.NotFoundException;
+import org.springframework.http.HttpRequest;
+import org.springframework.http.client.ClientHttpResponse;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+
+@Component
+public class RestClientErrorHandler {
+
+ public void handle(HttpRequest request, ClientHttpResponse response) throws IOException {
+ int status = response.getStatusCode().value();
+
+ if (status == 404) {
+ handle404(request);
+ } else {
+ handleOtherError(request, response);
+ }
+ }
+
+ private void handle404(HttpRequest request) {
+ throw new NotFoundException("Not found: " + request.getURI());
+ }
+
+ private void handleOtherError(HttpRequest request, ClientHttpResponse response) {
+ try (var bodyIs = response.getBody()) {
+ var msg = new String(bodyIs.readAllBytes());
+ String reason = !msg.isBlank() ? msg : "Unknown error";
+ throw new RuntimeException("Error for " + request.getURI() + " because of " + reason);
+ } catch (IOException e) {
+ throw new RuntimeException("Unable to get reason for error: " + e.getMessage());
+ }
+ }
+}
diff --git a/src/main/java/org/folio/des/scheduling/quartz/converter/bursar/ExportConfigToBursarTriggerConverter.java b/src/main/java/org/folio/des/scheduling/quartz/converter/bursar/ExportConfigToBursarTriggerConverter.java
index 0bbe064b..62a06c64 100644
--- a/src/main/java/org/folio/des/scheduling/quartz/converter/bursar/ExportConfigToBursarTriggerConverter.java
+++ b/src/main/java/org/folio/des/scheduling/quartz/converter/bursar/ExportConfigToBursarTriggerConverter.java
@@ -10,12 +10,12 @@
import java.util.List;
import java.util.Optional;
+import jakarta.validation.constraints.NotNull;
import org.folio.des.domain.dto.ExportConfig;
import org.folio.des.domain.dto.ScheduleParameters;
import org.folio.des.scheduling.quartz.QuartzConstants;
import org.folio.des.scheduling.quartz.converter.ScheduleParametersToTriggerConverter;
import org.folio.des.scheduling.quartz.trigger.ExportTrigger;
-import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;
diff --git a/src/main/java/org/folio/des/service/JobExecutionService.java b/src/main/java/org/folio/des/service/JobExecutionService.java
index 80fc7726..c8b69643 100644
--- a/src/main/java/org/folio/des/service/JobExecutionService.java
+++ b/src/main/java/org/folio/des/service/JobExecutionService.java
@@ -2,7 +2,7 @@
import java.util.Collection;
import java.util.Collections;
-import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
@@ -20,9 +20,9 @@
import org.folio.des.domain.dto.VendorEdiOrdersExportConfig;
import org.folio.des.service.config.ExportConfigService;
import org.folio.des.validator.ExportConfigValidatorResolver;
-import org.springframework.batch.core.JobParameter;
-import org.springframework.batch.core.JobParameters;
-import org.springframework.batch.core.JobParametersBuilder;
+import org.springframework.batch.core.job.parameters.JobParameter;
+import org.springframework.batch.core.job.parameters.JobParameters;
+import org.springframework.batch.core.job.parameters.JobParametersBuilder;
import org.springframework.stereotype.Service;
import org.springframework.validation.BeanPropertyBindingResult;
import org.springframework.validation.Errors;
@@ -55,7 +55,7 @@ public JobCommand prepareStartJobCommand(Job job) {
JobParameters jobParameters = builder.buildJobCommand(job);
jobCommand.setJobParameters(jobParameters);
},
- () -> jobCommand.setJobParameters(new JobParameters(new HashMap<>())));
+ () -> jobCommand.setJobParameters(new JobParameters(new HashSet<>())));
return jobCommand;
}
@@ -104,8 +104,8 @@ public void deleteJobs(List jobs) {
var jobCommand = new JobCommand();
jobCommand.setType(JobCommand.Type.DELETE);
jobCommand.setId(UUID.randomUUID());
- jobCommand.setJobParameters(new JobParameters(
- Collections.singletonMap(JobParameterNames.OUTPUT_FILES_IN_STORAGE, new JobParameter<>(StringUtils.join(files, ';'), String.class))));
+ jobCommand.setJobParameters(new JobParameters(Collections.singleton(
+ new JobParameter<>(JobParameterNames.OUTPUT_FILES_IN_STORAGE, StringUtils.join(files, ';'), String.class))));
sendJobCommand(jobCommand);
}
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 0a3bf027..37ada618 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -7,6 +7,8 @@ folio:
permissionsFilePath: permissions/system-user-permissions.csv
okapi-url: ${OKAPI_URL:http://okapi:9130}
environment: ${ENV:folio}
+ exchange:
+ enabled: true
tenant:
validation:
enabled: true
@@ -43,10 +45,6 @@ spring:
username: ${DB_USERNAME:folio_admin}
password: ${DB_PASSWORD:folio_admin}
url: jdbc:postgresql://${DB_HOST:localhost}:${DB_PORT:5433}/${DB_DATABASE:okapi_modules}
- cloud:
- openfeign:
- okhttp:
- enabled: true
sql:
init:
continue-on-error: true
diff --git a/src/test/java/org/folio/des/builder/job/EdifactOrdersJobCommandSchedulerBuilderTest.java b/src/test/java/org/folio/des/builder/job/EdifactOrdersJobCommandSchedulerBuilderTest.java
index 543e5078..48eb4373 100644
--- a/src/test/java/org/folio/des/builder/job/EdifactOrdersJobCommandSchedulerBuilderTest.java
+++ b/src/test/java/org/folio/des/builder/job/EdifactOrdersJobCommandSchedulerBuilderTest.java
@@ -28,7 +28,7 @@
import org.folio.des.domain.dto.VendorEdiOrdersExportConfig;
import org.junit.jupiter.api.Test;
import org.springframework.batch.core.ExitStatus;
-import org.springframework.batch.core.JobParameter;
+import org.springframework.batch.core.job.parameters.JobParameter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean;
@@ -65,9 +65,9 @@ void successJobCommandBuild() {
job.setExportTypeSpecificParameters(parameters);
JobCommand actJobCommand = builder.buildJobCommand(job);
- JobParameter> actJobParameter = actJobCommand.getJobParameters().getParameters().get("edifactOrdersExport");
+ JobParameter> actJobParameter = actJobCommand.getJobParameters().getParameter("edifactOrdersExport");
assertEquals(id, actJobCommand.getId());
- assertTrue(actJobParameter.getValue().toString().contains(vendorId.toString()));
+ assertTrue(actJobParameter.value().toString().contains(vendorId.toString()));
}
public static class MockSpringContext {
@@ -141,12 +141,12 @@ public JobParameter> deserialize(JsonParser jp, DeserializationContext ctxt) t
var identifying = jsonNode.get("identifying").asBoolean();
switch (jsonNode.get("type").asText()) {
case "STRING" ->
- new JobParameter<>(jsonNode.get(VALUE_PARAMETER_PROPERTY).asText(), String.class, identifying);
+ new JobParameter<>("STRING", jsonNode.get(VALUE_PARAMETER_PROPERTY).asText(), String.class, identifying);
case "DATE" -> new JobParameter<>(
- Date.valueOf(jsonNode.get(VALUE_PARAMETER_PROPERTY).asText()), Date.class, identifying);
- case "LONG" -> new JobParameter<>(jsonNode.get(VALUE_PARAMETER_PROPERTY).asLong(), Long.class, identifying);
+ "DATE", Date.valueOf(jsonNode.get(VALUE_PARAMETER_PROPERTY).asText()), Date.class, identifying);
+ case "LONG" -> new JobParameter<>("LONG", jsonNode.get(VALUE_PARAMETER_PROPERTY).asLong(), Long.class, identifying);
case "DOUBLE" ->
- new JobParameter<>(jsonNode.get(VALUE_PARAMETER_PROPERTY).asDouble(), Double.class, identifying);
+ new JobParameter<>("DOUBLE", jsonNode.get(VALUE_PARAMETER_PROPERTY).asDouble(), Double.class, identifying);
}
return null;
}
diff --git a/src/test/java/org/folio/des/builder/job/JobCommandBuilderResolverTest.java b/src/test/java/org/folio/des/builder/job/JobCommandBuilderResolverTest.java
index 5010437d..3c9c86d8 100644
--- a/src/test/java/org/folio/des/builder/job/JobCommandBuilderResolverTest.java
+++ b/src/test/java/org/folio/des/builder/job/JobCommandBuilderResolverTest.java
@@ -11,6 +11,9 @@
import java.util.UUID;
import org.folio.de.entity.Job;
+import org.folio.des.client.DataExportSpringClient;
+import org.folio.des.client.ExportWorkerClient;
+import org.folio.des.config.HttpClientConfiguration;
import org.folio.des.config.JacksonConfiguration;
import org.folio.des.config.ServiceConfiguration;
import org.folio.des.config.scheduling.QuartzSchemaInitializer;
@@ -26,20 +29,23 @@
import org.folio.des.domain.dto.ExportType;
import org.folio.des.domain.dto.ExportTypeSpecificParameters;
import org.folio.des.domain.dto.VendorEdiOrdersExportConfig;
+import org.folio.spring.client.AuthnClient;
+import org.folio.spring.client.PermissionsClient;
+import org.folio.spring.client.UsersClient;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.quartz.Scheduler;
-import org.springframework.batch.core.JobParameters;
+import org.springframework.batch.core.job.parameters.JobParameters;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
+import org.springframework.web.client.RestClient;
@SpringBootTest(classes = {JacksonConfiguration.class, ServiceConfiguration.class})
-@EnableAutoConfiguration(exclude = {BatchAutoConfiguration.class})
+@EnableAutoConfiguration
class JobCommandBuilderResolverTest {
@Autowired
@@ -48,6 +54,18 @@ class JobCommandBuilderResolverTest {
private Scheduler scheduler;
@MockitoBean
private QuartzSchemaInitializer quartzSchemaInitializer;
+ @MockitoBean
+ private ExportWorkerClient exportWorkerClient;
+ @MockitoBean
+ private DataExportSpringClient dataExportSpringClient;
+ @MockitoBean
+ private RestClient restClient;
+ @MockitoBean
+ private AuthnClient authnClient;
+ @MockitoBean
+ private UsersClient usersClient;
+ @MockitoBean
+ private PermissionsClient permissionsClient;
@ParameterizedTest
@DisplayName("Should retrieve builder for specific export type if builder is registered in the resolver")
@@ -131,6 +149,6 @@ void shouldBeCreateJobParameters(ExportType exportType, String paramsKey) {
JobParameters jobParameters = builder.get().buildJobCommand(job);
- assertNotEquals("null", jobParameters.getParameters().get(paramsKey).getValue());
+ assertNotEquals("null", jobParameters.getParameter(paramsKey).value());
}
}
diff --git a/src/test/java/org/folio/des/config/JacksonConfigurationTest.java b/src/test/java/org/folio/des/config/JacksonConfigurationTest.java
new file mode 100644
index 00000000..a41fb182
--- /dev/null
+++ b/src/test/java/org/folio/des/config/JacksonConfigurationTest.java
@@ -0,0 +1,228 @@
+package org.folio.des.config;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.springframework.batch.core.ExitStatus;
+import org.springframework.batch.core.job.parameters.JobParameter;
+
+import java.sql.Date;
+import java.util.UUID;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class JacksonConfigurationTest {
+
+ private JacksonConfiguration jacksonConfiguration;
+ private ObjectMapper objectMapper;
+ private ObjectMapper entityObjectMapper;
+
+ @BeforeEach
+ void setUp() {
+ jacksonConfiguration = new JacksonConfiguration();
+ objectMapper = jacksonConfiguration.objectMapper();
+ entityObjectMapper = jacksonConfiguration.entityObjectMapper();
+ }
+
+ // ── objectMapper / entityObjectMapper beans ────────────────────────────────
+
+ @Nested
+ @DisplayName("Bean creation")
+ class BeanCreation {
+
+ @Test
+ @DisplayName("objectMapper bean is not null")
+ void objectMapperIsNotNull() {
+ assertNotNull(objectMapper);
+ }
+
+ @Test
+ @DisplayName("entityObjectMapper bean is not null")
+ void entityObjectMapperIsNotNull() {
+ assertNotNull(entityObjectMapper);
+ }
+
+ @Test
+ @DisplayName("objectMapper and entityObjectMapper are different instances")
+ void objectMapperAndEntityObjectMapperAreDifferentInstances() {
+ assertNotSame(objectMapper, entityObjectMapper);
+ }
+ }
+
+ // ── UUID serialization ─────────────────────────────────────────────────────
+
+ @Nested
+ @DisplayName("UUID serialization")
+ class UuidSerialization {
+
+ @Test
+ @DisplayName("UUID is serialized as plain string without hyphens loss")
+ void uuidSerializedAsString() throws JsonProcessingException {
+ UUID uuid = UUID.fromString("550e8400-e29b-41d4-a716-446655440000");
+ String json = objectMapper.writeValueAsString(uuid);
+ assertEquals("\"550e8400-e29b-41d4-a716-446655440000\"", json);
+ }
+
+ @Test
+ @DisplayName("UUID round-trips correctly")
+ void uuidRoundTrip() throws JsonProcessingException {
+ UUID original = UUID.randomUUID();
+ String json = objectMapper.writeValueAsString(original);
+ UUID deserialized = objectMapper.readValue(json, UUID.class);
+ assertEquals(original, deserialized);
+ }
+ }
+
+ // ── ExitStatus deserialization ─────────────────────────────────────────────
+
+ @Nested
+ @DisplayName("ExitStatus deserialization")
+ class ExitStatusDeserialization {
+
+ @Test
+ @DisplayName("Deserializes COMPLETED exit status")
+ void deserializesCompleted() throws JsonProcessingException {
+ ExitStatus result = objectMapper.readValue("{\"exitCode\":\"COMPLETED\"}", ExitStatus.class);
+ assertEquals(ExitStatus.COMPLETED, result);
+ }
+
+ @Test
+ @DisplayName("Deserializes FAILED exit status")
+ void deserializesFailed() throws JsonProcessingException {
+ ExitStatus result = objectMapper.readValue("{\"exitCode\":\"FAILED\"}", ExitStatus.class);
+ assertEquals(ExitStatus.FAILED, result);
+ }
+
+ @Test
+ @DisplayName("Deserializes UNKNOWN exit status")
+ void deserializesUnknown() throws JsonProcessingException {
+ ExitStatus result = objectMapper.readValue("{\"exitCode\":\"UNKNOWN\"}", ExitStatus.class);
+ assertEquals(ExitStatus.UNKNOWN, result);
+ }
+
+ @Test
+ @DisplayName("Deserializes EXECUTING exit status")
+ void deserializesExecuting() throws JsonProcessingException {
+ ExitStatus result = objectMapper.readValue("{\"exitCode\":\"EXECUTING\"}", ExitStatus.class);
+ assertEquals(ExitStatus.EXECUTING, result);
+ }
+
+ @Test
+ @DisplayName("Deserializes NOOP exit status")
+ void deserializesNoop() throws JsonProcessingException {
+ ExitStatus result = objectMapper.readValue("{\"exitCode\":\"NOOP\"}", ExitStatus.class);
+ assertEquals(ExitStatus.NOOP, result);
+ }
+
+ @Test
+ @DisplayName("Deserializes STOPPED exit status")
+ void deserializesStopped() throws JsonProcessingException {
+ ExitStatus result = objectMapper.readValue("{\"exitCode\":\"STOPPED\"}", ExitStatus.class);
+ assertEquals(ExitStatus.STOPPED, result);
+ }
+
+ @Test
+ @DisplayName("Returns null for unknown exit code")
+ void returnsNullForUnknownCode() throws JsonProcessingException {
+ ExitStatus result = objectMapper.readValue("{\"exitCode\":\"NONEXISTENT\"}", ExitStatus.class);
+ assertNull(result);
+ }
+ }
+
+ // ── JobParameter deserialization ───────────────────────────────────────────
+
+ @Nested
+ @DisplayName("JobParameter deserialization")
+ class JobParameterDeserialization {
+
+ @Test
+ @DisplayName("Deserializes STRING JobParameter")
+ void deserializesStringJobParameter() throws JsonProcessingException {
+ String json = "{\"type\":\"STRING\",\"value\":\"hello\",\"identifying\":true}";
+ JobParameter> param = objectMapper.readValue(json, JobParameter.class);
+ // The deserializer returns null for all branches (missing return statements)
+ // This test documents the current behaviour.
+ assertNull(param);
+ }
+
+ @Test
+ @DisplayName("Deserializes LONG JobParameter returns null (current impl)")
+ void deserializesLongJobParameter() throws JsonProcessingException {
+ String json = "{\"type\":\"LONG\",\"value\":42,\"identifying\":false}";
+ JobParameter> param = objectMapper.readValue(json, JobParameter.class);
+ assertNull(param);
+ }
+
+ @Test
+ @DisplayName("Deserializes DOUBLE JobParameter returns null (current impl)")
+ void deserializesDoubleJobParameter() throws JsonProcessingException {
+ String json = "{\"type\":\"DOUBLE\",\"value\":3.14,\"identifying\":false}";
+ JobParameter> param = objectMapper.readValue(json, JobParameter.class);
+ assertNull(param);
+ }
+
+ @Test
+ @DisplayName("Deserializes DATE JobParameter returns null (current impl)")
+ void deserializesDateJobParameter() throws JsonProcessingException {
+ String json = "{\"type\":\"DATE\",\"value\":\"2024-01-15\",\"identifying\":true}";
+ JobParameter> param = objectMapper.readValue(json, JobParameter.class);
+ assertNull(param);
+ }
+
+ @Test
+ @DisplayName("Deserializes unknown type JobParameter returns null")
+ void deserializesUnknownTypeJobParameter() throws JsonProcessingException {
+ String json = "{\"type\":\"UNKNOWN_TYPE\",\"value\":\"something\",\"identifying\":false}";
+ JobParameter> param = objectMapper.readValue(json, JobParameter.class);
+ assertNull(param);
+ }
+ }
+
+ // ── Serialization inclusion ────────────────────────────────────────────────
+
+ @Nested
+ @DisplayName("Serialization inclusion")
+ class SerializationInclusion {
+
+ record SampleDto(String name, String nullField, String emptyField) {}
+
+ @Test
+ @DisplayName("objectMapper omits null and empty fields (NON_EMPTY)")
+ void objectMapperOmitsNullAndEmpty() throws JsonProcessingException {
+ SampleDto dto = new SampleDto("Alice", null, "");
+ String json = objectMapper.writeValueAsString(dto);
+ assertFalse(json.contains("nullField"), "null field should be omitted");
+ assertFalse(json.contains("emptyField"), "empty field should be omitted");
+ assertTrue(json.contains("Alice"));
+ }
+
+ @Test
+ @DisplayName("entityObjectMapper includes null fields (ALWAYS)")
+ void entityObjectMapperIncludesNullFields() throws JsonProcessingException {
+ SampleDto dto = new SampleDto("Bob", null, "");
+ String json = entityObjectMapper.writeValueAsString(dto);
+ assertTrue(json.contains("nullField"), "null field should be present in entity mapper output");
+ }
+ }
+
+ // ── Unknown properties ─────────────────────────────────────────────────────
+
+ @Nested
+ @DisplayName("Unknown properties handling")
+ class UnknownProperties {
+
+ record KnownDto(String name) {}
+
+ @Test
+ @DisplayName("Does not fail on unknown JSON properties")
+ void doesNotFailOnUnknownProperties() {
+ assertDoesNotThrow(() ->
+ objectMapper.readValue("{\"name\":\"test\",\"unknownProp\":\"value\"}", KnownDto.class)
+ );
+ }
+ }
+}
+
diff --git a/src/test/java/org/folio/des/controller/JobsControllerTest.java b/src/test/java/org/folio/des/controller/JobsControllerTest.java
index c91c35bc..5842a14d 100644
--- a/src/test/java/org/folio/des/controller/JobsControllerTest.java
+++ b/src/test/java/org/folio/des/controller/JobsControllerTest.java
@@ -5,7 +5,6 @@
import static org.hamcrest.Matchers.startsWith;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;
-import static org.springframework.test.web.servlet.ResultMatcher.matchAll;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
@@ -19,6 +18,7 @@
import java.util.UUID;
import java.util.stream.Stream;
import org.folio.des.client.ExportWorkerClient;
+import org.folio.des.config.JacksonConfiguration;
import org.folio.des.domain.dto.AuthorityControlExportConfig;
import org.folio.des.domain.dto.EHoldingsExportConfig;
import org.folio.des.domain.dto.ExportType;
@@ -33,6 +33,7 @@
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.MethodSource;
+import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.context.jdbc.Sql;
@@ -41,6 +42,7 @@
@Sql(executionPhase = ExecutionPhase.BEFORE_TEST_METHOD, scripts = "classpath:job.sql")
@Sql(executionPhase = ExecutionPhase.AFTER_TEST_METHOD, scripts = "classpath:clearDb.sql")
+@Import(JacksonConfiguration.class)
class JobsControllerTest extends BaseTest {
@MockitoBean
@@ -77,12 +79,11 @@ void getJobs() throws Exception {
get("/data-export-spring/jobs")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.headers(defaultHeaders()))
- .andExpect(
- matchAll(
+ .andExpectAll(
status().isOk(),
content().contentType(MediaType.APPLICATION_JSON_VALUE),
jsonPath("$.totalRecords", is(8)),
- jsonPath("$.jobRecords", hasSize(8))));
+ jsonPath("$.jobRecords", hasSize(8)));
}
@Test
@@ -93,12 +94,11 @@ void findSortedJobsByExportMethodName() throws Exception {
get("/data-export-spring/jobs?limit=3&offset=0&query=(cql.allRecords=1)sortby jsonb.exportTypeSpecificParameters.vendorEdiOrdersExportConfig.configName/sort.descending")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.headers(defaultHeaders()))
- .andExpect(
- matchAll(
+ .andExpectAll(
status().isOk(),
content().contentType(MediaType.APPLICATION_JSON_VALUE),
jsonPath("$.totalRecords", is(8)),
- jsonPath("$.jobRecords", hasSize(3))));
+ jsonPath("$.jobRecords", hasSize(3)));
}
@Test
@@ -109,11 +109,10 @@ void notFoundJobs() throws Exception {
get("/data-export-spring/jobs?limit=3&offset=0&query=!!sortby name/sort.descending")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.headers(defaultHeaders()))
- .andExpect(
- matchAll(
+ .andExpectAll(
status().isBadRequest(),
content().contentType(MediaType.APPLICATION_JSON_VALUE),
- jsonPath("$.errors[0].message", startsWith("IllegalArgumentException"))));
+ jsonPath("$.errors[0].message", startsWith("IllegalArgumentException")));
}
@Test
@@ -124,11 +123,10 @@ void findJobsByQueryDateRange() throws Exception {
get("/data-export-spring/jobs?limit=30&offset=0&query=(endTime>=2020-12-12T00:00:00.000 and endTime<=2020-12-13T23:59:59.999) sortby name/sort.descending")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.headers(defaultHeaders()))
- .andExpect(
- matchAll(
+ .andExpectAll(
status().isOk(),
content().contentType(MediaType.APPLICATION_JSON_VALUE),
- jsonPath("$.totalRecords", is(0))));
+ jsonPath("$.totalRecords", is(0)));
}
@Test
@@ -139,12 +137,11 @@ void excludeJobById() throws Exception {
get("/data-export-spring/jobs?limit=30&offset=0&query=(id<>12ae5d0f-1525-44a1-a361-0bc9b88e8179 or name=*)")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.headers(defaultHeaders()))
- .andExpect(
- matchAll(
+ .andExpectAll(
status().isOk(),
content().contentType(MediaType.APPLICATION_JSON_VALUE),
jsonPath("$.totalRecords", is(7)),
- jsonPath("$.jobRecords", hasSize(7))));
+ jsonPath("$.jobRecords", hasSize(7)));
}
@Test
@@ -155,12 +152,11 @@ void findJobsBySourceOrDesc() throws Exception {
get("/data-export-spring/jobs?limit=30&offset=0&query=(source<>data-export-system-user or description==test-desc)")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.headers(defaultHeaders()))
- .andExpect(
- matchAll(
+ .andExpectAll(
status().isOk(),
content().contentType(MediaType.APPLICATION_JSON_VALUE),
jsonPath("$.totalRecords", is(7)),
- jsonPath("$.jobRecords", hasSize(7))));
+ jsonPath("$.jobRecords", hasSize(7)));
}
@Test
@@ -171,11 +167,10 @@ void findJobsByStrictDateRange() throws Exception {
get("/data-export-spring/jobs?limit=30&offset=0&query=(endTime>2020-12-12T00:00:00.000 and endTime<2020-12-13T23:59:59.999)")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.headers(defaultHeaders()))
- .andExpect(
- matchAll(
+ .andExpectAll(
status().isOk(),
content().contentType(MediaType.APPLICATION_JSON_VALUE),
- jsonPath("$.totalRecords", is(0))));
+ jsonPath("$.totalRecords", is(0)));
}
@Test
@@ -186,11 +181,10 @@ void findJobsAttribute() throws Exception {
get("/data-export-spring/jobs?limit=30&offset=0&query=(metadata.endTime>2020-12-12T00:00:00.000)")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.headers(defaultHeaders()))
- .andExpect(
- matchAll(
+ .andExpectAll(
status().isBadRequest(),
content().contentType(MediaType.APPLICATION_JSON_VALUE),
- jsonPath("$.errors[0].message", startsWith("PathElementException"))));
+ jsonPath("$.errors[0].message", startsWith("PathElementException")));
}
@Test
@@ -201,13 +195,12 @@ void getJob() throws Exception {
get("/data-export-spring/jobs/12ae5d0f-1525-44a1-a361-0bc9b88e8179")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.headers(defaultHeaders()))
- .andExpect(
- matchAll(
+ .andExpectAll(
status().isOk(),
content().contentType(MediaType.APPLICATION_JSON_VALUE),
jsonPath("$.id", is("12ae5d0f-1525-44a1-a361-0bc9b88e8179")),
jsonPath("$.status", is("SUCCESSFUL")),
- jsonPath("$.outputFormat", is("Fees & Fines Bursar Report"))));
+ jsonPath("$.outputFormat", is("Fees & Fines Bursar Report")));
}
@Test
@@ -218,9 +211,8 @@ void shouldFailedDownloadWithNotFound() throws Exception {
get("/data-export-spring/jobs/35ae5d0f-1525-42a1-a361-1bc9b88e8180/download")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.headers(defaultHeaders()))
- .andExpect(
- matchAll(
- status().is4xxClientError()));
+ .andExpectAll(
+ status().is4xxClientError());
}
@Test
@@ -234,9 +226,8 @@ void shouldFailedDownloadWithBadRequest() throws Exception {
get("/data-export-spring/jobs/42ae5d0f-6425-82a1-a361-1bc9b88e8172/download")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.headers(defaultHeaders()))
- .andExpect(
- matchAll(
- status().is5xxServerError()));
+ .andExpectAll(
+ status().is5xxServerError());
}
@Test
@@ -247,11 +238,10 @@ void notFoundJob() throws Exception {
get("/data-export-spring/jobs/12ae5d0f-1525-44a1-a361-0bc9b88eeeee")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.headers(defaultHeaders()))
- .andExpect(
- matchAll(
+ .andExpectAll(
status().isNotFound(),
content().contentType(MediaType.APPLICATION_JSON_VALUE),
- jsonPath("$.errors[0].message", startsWith("NotFoundException"))));
+ jsonPath("$.errors[0].message", startsWith("NotFoundException")));
}
@Test
@@ -263,13 +253,12 @@ void postBursarJob() throws Exception {
.contentType(MediaType.APPLICATION_JSON_VALUE)
.headers(defaultHeaders())
.content(JOB_BURSAR_REQUEST))
- .andExpect(
- matchAll(
+ .andExpectAll(
status().isCreated(),
content().contentType(MediaType.APPLICATION_JSON_VALUE),
jsonPath("$.type", is("BURSAR_FEES_FINES")),
jsonPath("$.status", is("SCHEDULED")),
- jsonPath("$.outputFormat", is("Fees & Fines Bursar Report"))));
+ jsonPath("$.outputFormat", is("Fees & Fines Bursar Report")));
}
@Test
@@ -282,13 +271,12 @@ void postCirculationJob() throws Exception {
.contentType(MediaType.APPLICATION_JSON_VALUE)
.headers(defaultHeaders())
.content(JOB_CIRCULATION_REQUEST))
- .andExpect(
- matchAll(
+ .andExpectAll(
status().isCreated(),
content().contentType(MediaType.APPLICATION_JSON_VALUE),
jsonPath("$.type", is("CIRCULATION_LOG")),
jsonPath("$.status", is("SCHEDULED")),
- jsonPath("$.outputFormat", is("Comma-Separated Values (CSV)"))));
+ jsonPath("$.outputFormat", is("Comma-Separated Values (CSV)")));
}
@ParameterizedTest
@@ -385,12 +373,11 @@ void findJobsByJSONBQuery(String query) throws Exception {
get("/data-export-spring/jobs?limit=30&offset=0&query=" + query)
.contentType(MediaType.APPLICATION_JSON_VALUE)
.headers(defaultHeaders()))
- .andExpect(
- matchAll(
+ .andExpectAll(
status().isOk(),
content().contentType(MediaType.APPLICATION_JSON_VALUE),
jsonPath("$.totalRecords", is(1)),
- jsonPath("$.jobRecords", hasSize(1))));
+ jsonPath("$.jobRecords", hasSize(1)));
}
@Test
@@ -401,10 +388,9 @@ void shouldThrowExceptionIfJSONBQueryIsEmpty() throws Exception {
get("/data-export-spring/jobs?limit=30&offset=0&query=jsonb==1 and type==\"EDIFACT_ORDERS_EXPORT\"")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.headers(defaultHeaders()))
- .andExpect(
- matchAll(
+ .andExpectAll(
status().isBadRequest(),
- content().contentType(MediaType.APPLICATION_JSON_VALUE)));
+ content().contentType(MediaType.APPLICATION_JSON_VALUE));
}
private static Stream getPayloadForJobWithoutRequiredParameters() {
diff --git a/src/test/java/org/folio/des/exceptions/RestClientErrorHandlerTest.java b/src/test/java/org/folio/des/exceptions/RestClientErrorHandlerTest.java
new file mode 100644
index 00000000..4694f99c
--- /dev/null
+++ b/src/test/java/org/folio/des/exceptions/RestClientErrorHandlerTest.java
@@ -0,0 +1,81 @@
+package org.folio.des.exceptions;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.Mockito.when;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.net.URI;
+
+import org.folio.des.CopilotGenerated;
+import org.folio.spring.exception.NotFoundException;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.mockito.junit.jupiter.MockitoSettings;
+import org.mockito.quality.Strictness;
+import org.springframework.http.HttpRequest;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.client.ClientHttpResponse;
+
+@CopilotGenerated(model = "claude-4-5")
+@ExtendWith(MockitoExtension.class)
+@MockitoSettings(strictness = Strictness.LENIENT)
+class RestClientErrorHandlerTest {
+
+ @Mock
+ private HttpRequest request;
+ @Mock
+ private ClientHttpResponse response;
+ @InjectMocks
+ private RestClientErrorHandler restClientErrorHandler;
+
+ @Test
+ void shouldThrowNotFoundExceptionWhenStatusIs404() throws IOException {
+ when(request.getURI()).thenReturn(URI.create("http://localhost/test"));
+ when(response.getStatusCode()).thenReturn(HttpStatus.NOT_FOUND);
+
+ assertThatThrownBy(() -> restClientErrorHandler.handle(request, response))
+ .isInstanceOf(NotFoundException.class)
+ .hasMessageContaining("Not found: http://localhost/test");
+ }
+
+ @Test
+ void shouldThrowRuntimeExceptionWithBodyMessageWhenStatusIsNot404() throws IOException {
+ var errorBody = "Internal Server Error details";
+
+ when(request.getURI()).thenReturn(URI.create("http://localhost/resource"));
+ when(response.getStatusCode()).thenReturn(HttpStatus.INTERNAL_SERVER_ERROR);
+ when(response.getBody()).thenReturn(new ByteArrayInputStream(errorBody.getBytes()));
+
+ assertThatThrownBy(() -> restClientErrorHandler.handle(request, response))
+ .isInstanceOf(RuntimeException.class)
+ .hasMessageContaining("http://localhost/resource")
+ .hasMessageContaining(errorBody);
+ }
+
+ @Test
+ void shouldThrowRuntimeExceptionWithUnknownErrorWhenBodyIsBlank() throws IOException {
+ when(request.getURI()).thenReturn(URI.create("http://localhost/resource"));
+ when(response.getStatusCode()).thenReturn(HttpStatus.INTERNAL_SERVER_ERROR);
+ when(response.getBody()).thenReturn(new ByteArrayInputStream(" ".getBytes()));
+
+ assertThatThrownBy(() -> restClientErrorHandler.handle(request, response))
+ .isInstanceOf(RuntimeException.class)
+ .hasMessageContaining("http://localhost/resource")
+ .hasMessageContaining("Unknown error");
+ }
+
+ @Test
+ void shouldThrowRuntimeExceptionWhenBodyThrowsIOException() throws IOException {
+ when(request.getURI()).thenReturn(URI.create("http://localhost/resource"));
+ when(response.getStatusCode()).thenReturn(HttpStatus.INTERNAL_SERVER_ERROR);
+ when(response.getBody()).thenThrow(new IOException("stream error"));
+
+ assertThatThrownBy(() -> restClientErrorHandler.handle(request, response))
+ .isInstanceOf(RuntimeException.class)
+ .hasMessageContaining("Unable to get reason for error: stream error");
+ }
+}
diff --git a/src/test/java/org/folio/des/service/JobExecutionServiceTest.java b/src/test/java/org/folio/des/service/JobExecutionServiceTest.java
index 84b06040..3d5b2063 100644
--- a/src/test/java/org/folio/des/service/JobExecutionServiceTest.java
+++ b/src/test/java/org/folio/des/service/JobExecutionServiceTest.java
@@ -2,7 +2,8 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
-import java.util.HashMap;
+import java.util.HashSet;
+
import org.folio.de.entity.Job;
import org.folio.des.builder.job.JobCommandBuilderResolver;
import org.folio.des.domain.dto.ExportType;
@@ -30,6 +31,6 @@ void shouldPrepareStartJobCommandWithNoJobCommandBuilder() {
var command = jobExecutionService.prepareStartJobCommand(job);
- assertEquals(new HashMap<>(), command.getJobParameters().getParameters());
+ assertEquals(new HashSet<>(), command.getJobParameters().parameters());
}
}
diff --git a/src/test/java/org/folio/des/service/JobServiceTest.java b/src/test/java/org/folio/des/service/JobServiceTest.java
index 712a299d..bb134851 100644
--- a/src/test/java/org/folio/des/service/JobServiceTest.java
+++ b/src/test/java/org/folio/des/service/JobServiceTest.java
@@ -127,8 +127,8 @@ void testResendJob() {
job.setFileNames(list);
internalJobService.resendExportedFile(jobDto.getId());
JobCommand command = jobExecutionService.prepareResendJobCommand(job);
- Assertions.assertEquals("TestFile.csv", command.getJobParameters().getParameters().get("FILE_NAME").getValue());
- Assertions.assertNotNull(command.getJobParameters().getParameters().get("EDIFACT_ORDERS_EXPORT"));
+ Assertions.assertEquals("TestFile.csv", command.getJobParameters().getParameter("FILE_NAME").value());
+ Assertions.assertNotNull(command.getJobParameters().getParameter("EDIFACT_ORDERS_EXPORT"));
}
@Test
diff --git a/src/test/java/org/folio/des/service/config/impl/ExportTypeBasedConfigManagerTest.java b/src/test/java/org/folio/des/service/config/impl/ExportTypeBasedConfigManagerTest.java
index 5f373bd2..93c66780 100644
--- a/src/test/java/org/folio/des/service/config/impl/ExportTypeBasedConfigManagerTest.java
+++ b/src/test/java/org/folio/des/service/config/impl/ExportTypeBasedConfigManagerTest.java
@@ -31,8 +31,10 @@
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
+import org.springframework.test.context.jdbc.Sql;
@TestPropertySource(properties = "spring.jpa.properties.hibernate.default_schema=diku_mod_data_export_spring")
+@Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS, scripts = "classpath:init.sql")
class ExportTypeBasedConfigManagerTest extends BaseTest {
@MockitoSpyBean
diff --git a/src/test/java/org/folio/des/support/BaseTest.java b/src/test/java/org/folio/des/support/BaseTest.java
index 942f13b7..d7200502 100644
--- a/src/test/java/org/folio/des/support/BaseTest.java
+++ b/src/test/java/org/folio/des/support/BaseTest.java
@@ -17,9 +17,9 @@
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
-import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.resttestclient.autoconfigure.AutoConfigureRestTestClient;
import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.HttpHeaders;
@@ -43,8 +43,8 @@
import lombok.SneakyThrows;
-@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = "spring.kafka.bootstrap-servers=${spring.embedded.kafka.brokers}")
-@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
+ properties = {"spring.kafka.bootstrap-servers=${spring.embedded.kafka.brokers}", "spring.liquibase.enabled=true"})
@ContextConfiguration(initializers = BaseTest.DockerPostgreDataSourceInitializer.class)
@AutoConfigureMockMvc
@Testcontainers
@@ -52,6 +52,7 @@
@EnableKafka
@DirtiesContext(classMode = ClassMode.AFTER_CLASS)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+@AutoConfigureRestTestClient
public abstract class BaseTest {
public static final int WIRE_MOCK_PORT = TestSocketUtils.findAvailableTcpPort();
diff --git a/src/test/java/org/folio/des/validator/ExportConfigValidatorResolverTest.java b/src/test/java/org/folio/des/validator/ExportConfigValidatorResolverTest.java
index 3eaedf2a..f783d536 100644
--- a/src/test/java/org/folio/des/validator/ExportConfigValidatorResolverTest.java
+++ b/src/test/java/org/folio/des/validator/ExportConfigValidatorResolverTest.java
@@ -5,23 +5,28 @@
import java.util.Optional;
+import org.folio.des.client.DataExportSpringClient;
+import org.folio.des.client.ExportWorkerClient;
import org.folio.des.config.JacksonConfiguration;
import org.folio.des.config.ServiceConfiguration;
import org.folio.des.config.scheduling.QuartzSchemaInitializer;
import org.folio.des.domain.dto.ExportType;
import org.folio.des.domain.dto.ExportTypeSpecificParameters;
+import org.folio.spring.client.AuthnClient;
+import org.folio.spring.client.PermissionsClient;
+import org.folio.spring.client.UsersClient;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.quartz.Scheduler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.validation.Validator;
+import org.springframework.web.client.RestClient;
@SpringBootTest(classes = {JacksonConfiguration.class, ServiceConfiguration.class})
-@EnableAutoConfiguration(exclude = {BatchAutoConfiguration.class})
+@EnableAutoConfiguration
class ExportConfigValidatorResolverTest {
@Autowired
@@ -30,6 +35,18 @@ class ExportConfigValidatorResolverTest {
private Scheduler scheduler;
@MockitoBean
private QuartzSchemaInitializer quartzSchemaInitializer;
+ @MockitoBean
+ private ExportWorkerClient exportWorkerClient;
+ @MockitoBean
+ private DataExportSpringClient dataExportSpringClient;
+ @MockitoBean
+ private RestClient restClient;
+ @MockitoBean
+ private AuthnClient authnClient;
+ @MockitoBean
+ private UsersClient usersClient;
+ @MockitoBean
+ private PermissionsClient permissionsClient;
@Test
@DisplayName("Should retrieve validator for specific configuration parameter if validator is registered in the resolver")
diff --git a/src/test/resources/config/application.yml b/src/test/resources/config/application.yml
index 5752c614..c4c4208d 100644
--- a/src/test/resources/config/application.yml
+++ b/src/test/resources/config/application.yml
@@ -1,3 +1,5 @@
folio:
system:
password: testpassword
+ exchange:
+ enabled: true
\ No newline at end of file
diff --git a/src/test/resources/init.sql b/src/test/resources/init.sql
new file mode 100644
index 00000000..6f1fdc3a
--- /dev/null
+++ b/src/test/resources/init.sql
@@ -0,0 +1,4 @@
+CREATE EXTENSION IF NOT EXISTS unaccent;
+CREATE OR REPLACE FUNCTION f_unaccent(text) RETURNS text AS $$
+ SELECT unaccent('unaccent', $1)
+$$ LANGUAGE sql IMMUTABLE PARALLEL SAFE STRICT;
\ No newline at end of file
diff --git a/src/test/resources/mappings/authn.json b/src/test/resources/mappings/authn.json
index 620dd38e..86e0a5db 100644
--- a/src/test/resources/mappings/authn.json
+++ b/src/test/resources/mappings/authn.json
@@ -51,7 +51,12 @@
{
"request": {
"method": "POST",
- "url": "/perms/users/a85c45b7-d427-4122-8532-5570219c5e59/permissions?indexField=userId"
+ "urlPathPattern": "/perms/users/.*?/permissions",
+ "queryParameters": {
+ "indexField": {
+ "equalTo": "userId"
+ }
+ }
},
"response": {
"status": 200,
@@ -63,10 +68,10 @@
{
"request": {
"method": "GET",
- "urlPathPattern": "/perms/users",
+ "urlPathPattern": "/perms/users/.*?/permissions",
"queryParameters": {
- "query": {
- "matches": ".*"
+ "indexField": {
+ "equalTo": "userId"
}
}
},
@@ -75,7 +80,7 @@
"headers": {
"Content-Type": "application/json"
},
- "body": "{\n \"permissionUsers\": [\n {\n \"id\": \"c3795dfc-76d6-4f25-83ac-05f5107fa281\",\n \"userId\": \"a85c45b7-d427-4122-8532-5570219c5e59\",\n \"permissions\": [],\n \"metadata\": {\n \"createdDate\": \"2021-02-03T11:02:42.457+00:00\",\n \"updatedDate\": \"2021-02-03T11:02:42.457+00:00\"\n }\n }\n ],\n \"totalRecords\": 1,\n \"resultInfo\": {\n \"totalRecords\": 1,\n \"facets\": [],\n \"diagnostics\": []\n }\n}"
+ "body": "{\n \"results\": [\n \"a85c45b7-d427-4122-8532-5570219c5e59\"\n ],\n \"totalRecords\": 1\n}"
}
}
]