diff --git a/descriptors/ModuleDescriptor-template.json b/descriptors/ModuleDescriptor-template.json
index 44a5d255..4aeadaf6 100644
--- a/descriptors/ModuleDescriptor-template.json
+++ b/descriptors/ModuleDescriptor-template.json
@@ -68,7 +68,32 @@
"permissionsRequired": [
"data-export.job.item.post"
],
- "modulePermissions": []
+ "modulePermissions": [
+ "accounts.transfer.post",
+ "accounts.collection.get",
+ "circulation-logs.collection.get",
+ "configuration.entries.collection.get",
+ "configuration.entries.item.get",
+ "configuration.entries.item.post",
+ "configuration.entries.item.put",
+ "configuration.entries.item.delete",
+ "data-export.job.collection.get",
+ "data-export.config.collection.get",
+ "feefineactions.collection.get",
+ "finance.expense-classes.item.get",
+ "inventory-storage.holdings.item.get",
+ "inventory-storage.identifier-types.item.get",
+ "inventory-storage.locations.item.get",
+ "inventory-storage.material-types.item.get",
+ "inventory-storage.service-points.collection.get",
+ "organizations-storage.organizations.item.get",
+ "orders-storage.po-lines.collection.get",
+ "orders-storage.purchase-orders.collection.get",
+ "transfers.collection.get",
+ "users.collection.get",
+ "users.item.post",
+ "users.item.put"
+ ]
},
{
"methods": [
@@ -151,6 +176,41 @@
"modulePermissions": [
"configuration.entries.item.delete"
]
+ },
+ {
+ "methods": [
+ "POST"
+ ],
+ "pathPattern": "/data-export-spring/jobs/send",
+ "permissionsRequired": [
+ "data-export.job.item.send"
+ ],
+ "modulePermissions": [
+ "accounts.transfer.post",
+ "accounts.collection.get",
+ "circulation-logs.collection.get",
+ "configuration.entries.collection.get",
+ "configuration.entries.item.get",
+ "configuration.entries.item.post",
+ "configuration.entries.item.put",
+ "configuration.entries.item.delete",
+ "data-export.job.collection.get",
+ "data-export.config.collection.get",
+ "feefineactions.collection.get",
+ "finance.expense-classes.item.get",
+ "inventory-storage.holdings.item.get",
+ "inventory-storage.identifier-types.item.get",
+ "inventory-storage.locations.item.get",
+ "inventory-storage.material-types.item.get",
+ "inventory-storage.service-points.collection.get",
+ "organizations-storage.organizations.item.get",
+ "orders-storage.po-lines.collection.get",
+ "orders-storage.purchase-orders.collection.get",
+ "transfers.collection.get",
+ "users.collection.get",
+ "users.item.post",
+ "users.item.put"
+ ]
}
]
},
@@ -250,6 +310,11 @@
"displayName": "get data export jobs",
"description": "Get data export jobs"
},
+ {
+ "permissionName": "data-export.job.item.send",
+ "displayName": "send job to kafka",
+ "description": "Send job to Kafka"
+ },
{
"permissionName": "data-export.config.all",
"displayName": "data export configurations - all permissions",
@@ -271,7 +336,8 @@
"data-export.job.item.get",
"data-export.job.collection.get",
"data-export.job.item.download",
- "data-export.job.item.resend"
+ "data-export.job.item.resend",
+ "data-export.job.item.send"
]
},
{
diff --git a/pom.xml b/pom.xml
index 721d317d..bebb77ae 100644
--- a/pom.xml
+++ b/pom.xml
@@ -89,6 +89,11 @@
folio-spring-cql
${folio-spring-base.version}
+
+ org.folio
+ folio-spring-system-user
+ ${folio-spring-base.version}
+
org.springframework.boot
spring-boot-properties-migrator
diff --git a/src/main/java/org/folio/des/client/AuthClient.java b/src/main/java/org/folio/des/client/AuthClient.java
deleted file mode 100644
index ec988ed4..00000000
--- a/src/main/java/org/folio/des/client/AuthClient.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package org.folio.des.client;
-
-import org.folio.des.domain.dto.SystemUserParameters;
-import org.springframework.cloud.openfeign.FeignClient;
-import org.springframework.http.MediaType;
-import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.DeleteMapping;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestParam;
-
-@FeignClient("authn")
-public interface AuthClient {
-
- @PostMapping(value = "/login", consumes = MediaType.APPLICATION_JSON_VALUE)
- ResponseEntity getApiKey(@RequestBody SystemUserParameters authObject);
-
- @PostMapping(value = "/credentials", consumes = MediaType.APPLICATION_JSON_VALUE)
- void saveCredentials(@RequestBody SystemUserParameters systemUserParameters);
-
- @DeleteMapping(value = "/credentials", consumes = MediaType.APPLICATION_JSON_VALUE)
- void deleteCredentials(@RequestParam("userId") String userId);
-}
diff --git a/src/main/java/org/folio/des/client/DataExportSpringClient.java b/src/main/java/org/folio/des/client/DataExportSpringClient.java
new file mode 100644
index 00000000..deba06ba
--- /dev/null
+++ b/src/main/java/org/folio/des/client/DataExportSpringClient.java
@@ -0,0 +1,16 @@
+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;
+
+@FeignClient(name = "data-export-spring", configuration = FeignClientConfiguration.class)
+public interface DataExportSpringClient {
+ @PostMapping(value = "/jobs")
+ Job upsertJob(@RequestBody Job job);
+
+ @PostMapping(value = "/jobs/send")
+ void sendJob(@RequestBody Job job);
+}
diff --git a/src/main/java/org/folio/des/client/PermissionsClient.java b/src/main/java/org/folio/des/client/PermissionsClient.java
deleted file mode 100644
index 56d8a90b..00000000
--- a/src/main/java/org/folio/des/client/PermissionsClient.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package org.folio.des.client;
-
-import org.folio.des.domain.dto.permissions.Permission;
-import org.folio.des.domain.dto.permissions.PermissionUser;
-import org.folio.des.domain.dto.permissions.PermissionUserCollection;
-import org.springframework.cloud.openfeign.FeignClient;
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.*;
-
-@FeignClient("perms/users")
-public interface PermissionsClient {
-
- @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
- PermissionUserCollection get(@RequestParam("query") String query);
-
- @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
- PermissionUser create(@RequestBody PermissionUser permissionUser);
-
- @PostMapping(value = "/{userId}/permissions?indexField=userId", consumes = MediaType.APPLICATION_JSON_VALUE)
- void addPermission(@PathVariable("userId") String userId, Permission permission);
-
-}
diff --git a/src/main/java/org/folio/des/client/UsersClient.java b/src/main/java/org/folio/des/client/UsersClient.java
deleted file mode 100644
index 506ec9bb..00000000
--- a/src/main/java/org/folio/des/client/UsersClient.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package org.folio.des.client;
-
-import org.folio.des.domain.dto.User;
-import org.folio.des.domain.dto.UserCollection;
-import org.springframework.cloud.openfeign.FeignClient;
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.PutMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestParam;
-
-@FeignClient("users")
-public interface UsersClient {
-
- @GetMapping
- UserCollection getUsersByQuery(@RequestParam("query") String query);
-
- @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE)
- void saveUser(@RequestBody User user);
-
- @PutMapping(value = "/{id}", consumes = MediaType.APPLICATION_JSON_VALUE)
- void updateUser(@PathVariable String id, @RequestBody User user);
-}
diff --git a/src/main/java/org/folio/des/config/FolioExecutionContextHelper.java b/src/main/java/org/folio/des/config/FolioExecutionContextHelper.java
deleted file mode 100644
index 944d9daa..00000000
--- a/src/main/java/org/folio/des/config/FolioExecutionContextHelper.java
+++ /dev/null
@@ -1,88 +0,0 @@
-package org.folio.des.config;
-
-import static java.util.Objects.nonNull;
-
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.UUID;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.log4j.Log4j2;
-import org.apache.commons.lang3.StringUtils;
-import org.folio.des.security.AuthService;
-import org.folio.des.security.JWTokenUtils;
-import org.folio.des.security.SecurityManagerService;
-import org.folio.spring.DefaultFolioExecutionContext;
-import org.folio.spring.FolioExecutionContext;
-import org.folio.spring.FolioModuleMetadata;
-import org.folio.spring.integration.XOkapiHeaders;
-import org.folio.spring.scope.FolioExecutionContextSetter;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.stereotype.Component;
-
-@Component
-@Log4j2
-@RequiredArgsConstructor
-public class FolioExecutionContextHelper {
-
- private final FolioModuleMetadata folioModuleMetadata;
- private final FolioExecutionContext folioExecutionContext;
- private final AuthService authService;
- private final SecurityManagerService securityManagerService;
- @Value("${folio.okapi.url}")
- private String okapiUrl;
-
- public void registerTenant() {
- securityManagerService.prepareSystemUser(folioExecutionContext.getOkapiUrl(), folioExecutionContext.getTenantId());
- }
-
- public FolioExecutionContext getFolioExecutionContext(String tenantId) {
- Map> tenantOkapiHeaders = new HashMap<>() {{
- put(XOkapiHeaders.TENANT, List.of(tenantId));
- put(XOkapiHeaders.URL, List.of(okapiUrl));
- }};
-
- // We only have headers['tenant', 'url'] to set up 'execution context' with minimum headers['tenant', 'url', 'token', 'user'].
- // We will do it in two steps: Calling 'auth/login' does not require any permission, so in first one we create 'execution context' with headers['tenant', 'url']
- // and should be able to get a 'token' with required permissions. And then we start second 'execution context' with headers['tenant', 'url', 'token']
- // to get 'system-user-id', at this point we already have 'token' so request is authorized. ('system-user' is created when 'tenant' is registered)
- try (var context = new FolioExecutionContextSetter(new DefaultFolioExecutionContext(folioModuleMetadata, tenantOkapiHeaders))) {
- String systemUserToken = authService.getTokenForSystemUser(tenantId, okapiUrl);
- if (StringUtils.isNotBlank(systemUserToken)) {
- tenantOkapiHeaders.put(XOkapiHeaders.TOKEN, List.of(systemUserToken));
- } else {
- // If we do not get a 'token' we will not have required permissions to do further requests so stop the process
- throw new IllegalStateException(String.format("Cannot create FolioExecutionContext for Tenant: %s because of absent token", tenantId));
- }
- }
-
- try (var context = new FolioExecutionContextSetter(new DefaultFolioExecutionContext(folioModuleMetadata, tenantOkapiHeaders))) {
- String systemUserId = authService.getSystemUserId();
- if (nonNull(systemUserId)) {
- tenantOkapiHeaders.put(XOkapiHeaders.USER_ID, List.of(systemUserId));
- }
- }
- return new DefaultFolioExecutionContext(folioModuleMetadata, tenantOkapiHeaders);
- }
-
- public static String getUserName(FolioExecutionContext context) {
- String jwt = context.getToken();
- Optional userInfo = StringUtils.isBlank(jwt) ? Optional.empty() : JWTokenUtils.parseToken(jwt);
- return StringUtils.substring(userInfo.map(JWTokenUtils.UserInfo::getUserName).orElse(null), 0, 50);
- }
-
- public static UUID getUserId(FolioExecutionContext context) {
- var userIdStr = context.getUserId();
- UUID result = null;
- if (nonNull(userIdStr)) {
- try {
- result = userIdStr;
- } catch (Exception ignore) {
- // Nothing to do
- }
- }
- return result;
- }
-}
diff --git a/src/main/java/org/folio/des/controller/JobsController.java b/src/main/java/org/folio/des/controller/JobsController.java
index b53e042f..3b32db02 100644
--- a/src/main/java/org/folio/des/controller/JobsController.java
+++ b/src/main/java/org/folio/des/controller/JobsController.java
@@ -10,10 +10,12 @@
import java.util.UUID;
import lombok.extern.log4j.Log4j2;
+import org.folio.des.builder.job.JobCommandSchedulerBuilder;
import org.folio.des.domain.dto.ExportTypeSpecificParameters;
import org.folio.des.domain.dto.Job;
import org.folio.des.domain.dto.JobCollection;
import org.folio.des.rest.resource.JobsApi;
+import org.folio.des.service.JobExecutionService;
import org.folio.des.service.JobService;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;
@@ -32,6 +34,8 @@
public class JobsController implements JobsApi {
private final JobService service;
+ private final JobCommandSchedulerBuilder jobCommandSchedulerBuilder;
+ private final JobExecutionService jobExecutionService;
@Override
public ResponseEntity getJobById(UUID id) {
@@ -69,6 +73,13 @@ public ResponseEntity downloadExportedFileByJobId(UUID id) {
return ResponseEntity.ok(new InputStreamResource(service.downloadExportedFile(id)));
}
+ @Override
+ public ResponseEntity sendJob(Job job) {
+ log.info("sendJob:: with job={}.", job);
+ jobExecutionService.sendJobCommand(jobCommandSchedulerBuilder.buildJobCommand(job));
+ return new ResponseEntity<>(HttpStatus.OK);
+ }
+
private boolean isMissingRequiredParameters(Job job) {
var exportTypeParameters = job.getExportTypeSpecificParameters();
return (BULK_EDIT_QUERY == job.getType() && (isNull(job.getEntityType()) || isBlank(exportTypeParameters.getQuery()))) ||
diff --git a/src/main/java/org/folio/des/scheduling/quartz/job/OldDeleteJob.java b/src/main/java/org/folio/des/scheduling/quartz/job/OldDeleteJob.java
index 40eba62e..da96f9e6 100644
--- a/src/main/java/org/folio/des/scheduling/quartz/job/OldDeleteJob.java
+++ b/src/main/java/org/folio/des/scheduling/quartz/job/OldDeleteJob.java
@@ -2,9 +2,11 @@
import static org.folio.des.scheduling.quartz.QuartzConstants.TENANT_ID_PARAM;
-import org.folio.des.config.FolioExecutionContextHelper;
import org.folio.des.service.JobService;
+import org.folio.spring.FolioExecutionContext;
+import org.folio.spring.context.ExecutionContextBuilder;
import org.folio.spring.scope.FolioExecutionContextSetter;
+import org.folio.spring.service.SystemUserService;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
@@ -16,13 +18,14 @@
public class OldDeleteJob implements org.quartz.Job {
private final JobService jobService;
- private final FolioExecutionContextHelper contextHelper;
+ private final ExecutionContextBuilder contextBuilder;
+ private final SystemUserService systemUserService;
private static final String PARAM_NOT_FOUND_MESSAGE = "'%s' param is missing in the jobExecutionContext";
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
String tenantId = getTenantId(jobExecutionContext);
- try (var context = new FolioExecutionContextSetter(contextHelper.getFolioExecutionContext(tenantId))) {
+ try (var context = new FolioExecutionContextSetter(folioExecutionContext(tenantId))) {
jobService.deleteOldJobs();
}
log.info("execute:: deleteOldJobs executed");
@@ -35,4 +38,8 @@ private String getTenantId(JobExecutionContext jobExecutionContext) {
}
return tenantId;
}
+
+ private FolioExecutionContext folioExecutionContext(String tenantId) {
+ return contextBuilder.forSystemUser(systemUserService.getAuthedSystemUser(tenantId));
+ }
}
diff --git a/src/main/java/org/folio/des/scheduling/quartz/job/acquisition/EdifactJob.java b/src/main/java/org/folio/des/scheduling/quartz/job/acquisition/EdifactJob.java
index fe1df9b8..045293d4 100644
--- a/src/main/java/org/folio/des/scheduling/quartz/job/acquisition/EdifactJob.java
+++ b/src/main/java/org/folio/des/scheduling/quartz/job/acquisition/EdifactJob.java
@@ -3,16 +3,17 @@
import static org.folio.des.scheduling.quartz.QuartzConstants.EXPORT_CONFIG_ID_PARAM;
import static org.folio.des.scheduling.quartz.QuartzConstants.TENANT_ID_PARAM;
-import org.folio.des.builder.job.JobCommandSchedulerBuilder;
-import org.folio.des.config.FolioExecutionContextHelper;
+import org.folio.des.client.DataExportSpringClient;
import org.folio.des.domain.dto.ExportConfig;
import org.folio.des.domain.dto.Job;
import org.folio.des.exceptions.SchedulingException;
-import org.folio.des.service.JobExecutionService;
import org.folio.des.service.JobService;
import org.folio.des.service.config.impl.ExportTypeBasedConfigManager;
+import org.folio.spring.FolioExecutionContext;
+import org.folio.spring.context.ExecutionContextBuilder;
import org.folio.spring.exception.NotFoundException;
import org.folio.spring.scope.FolioExecutionContextSetter;
+import org.folio.spring.service.SystemUserService;
import org.quartz.JobExecutionContext;
import org.quartz.JobKey;
@@ -24,22 +25,21 @@
public class EdifactJob implements org.quartz.Job {
private static final String PARAM_NOT_FOUND_MESSAGE = "'%s' param is missing in the jobExecutionContext";
private final ExportTypeBasedConfigManager exportTypeBasedConfigManager;
- private final JobExecutionService jobExecutionService;
private final JobService jobService;
- private final JobCommandSchedulerBuilder jobSchedulerCommandBuilder;
- private final FolioExecutionContextHelper contextHelper;
+ private final ExecutionContextBuilder contextBuilder;
+ private final SystemUserService systemUserService;
+ private final DataExportSpringClient dataExportSpringClient;
@Override
public void execute(JobExecutionContext jobExecutionContext) {
try {
String tenantId = getTenantId(jobExecutionContext);
- try (var context = new FolioExecutionContextSetter(contextHelper.getFolioExecutionContext(tenantId))) {
+ try (var context = new FolioExecutionContextSetter(folioExecutionContext(tenantId))) {
Job job = getJob(jobExecutionContext);
Job resultJob = jobService.upsertAndSendToKafka(job, false, false);
log.info("execute:: configured task saved in DB jobId: {}", resultJob.getId());
if (resultJob.getId() != null) {
- var jobCommand = jobSchedulerCommandBuilder.buildJobCommand(resultJob);
- jobExecutionService.sendJobCommand(jobCommand);
+ dataExportSpringClient.sendJob(resultJob);
log.info("execute:: configured task scheduled and sent to kafka for jobId: {}", resultJob.getId());
}
}
@@ -95,4 +95,8 @@ private void deleteJob(JobExecutionContext jobExecutionContext) {
log.warn("deleteJob:: exception deleting job '{}'", jobKey, e);
}
}
+
+ private FolioExecutionContext folioExecutionContext(String tenantId) {
+ return contextBuilder.forSystemUser(systemUserService.getAuthedSystemUser(tenantId));
+ }
}
diff --git a/src/main/java/org/folio/des/scheduling/quartz/job/bursar/BursarJob.java b/src/main/java/org/folio/des/scheduling/quartz/job/bursar/BursarJob.java
index 4fce06d8..03dd0684 100644
--- a/src/main/java/org/folio/des/scheduling/quartz/job/bursar/BursarJob.java
+++ b/src/main/java/org/folio/des/scheduling/quartz/job/bursar/BursarJob.java
@@ -5,14 +5,16 @@
import java.util.Date;
-import org.folio.des.config.FolioExecutionContextHelper;
+import org.folio.des.client.DataExportSpringClient;
import org.folio.des.domain.dto.ExportConfig;
import org.folio.des.domain.dto.Job;
import org.folio.des.exceptions.SchedulingException;
-import org.folio.des.service.JobService;
import org.folio.des.service.config.impl.ExportTypeBasedConfigManager;
+import org.folio.spring.FolioExecutionContext;
+import org.folio.spring.context.ExecutionContextBuilder;
import org.folio.spring.exception.NotFoundException;
import org.folio.spring.scope.FolioExecutionContextSetter;
+import org.folio.spring.service.SystemUserService;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobKey;
@@ -23,9 +25,10 @@
@Log4j2
@RequiredArgsConstructor
public class BursarJob implements org.quartz.Job {
- private final FolioExecutionContextHelper contextHelper;
+ private final ExecutionContextBuilder contextBuilder;
+ private final SystemUserService systemUserService;
private final ExportTypeBasedConfigManager exportTypeBasedConfigManager;
- private final JobService jobService;
+ private final DataExportSpringClient dataExportSpringClient;
private static final String PARAM_NOT_FOUND_MESSAGE = "'%s' param is missing in the jobExecutionContext";
@Override
@@ -34,9 +37,9 @@ public void execute(JobExecutionContext jobExecutionContext) throws JobExecution
String tenantId = getTenantId(jobExecutionContext);
var current = new Date();
- try (var context = new FolioExecutionContextSetter(contextHelper.getFolioExecutionContext(tenantId))) {
+ try (var context = new FolioExecutionContextSetter(folioExecutionContext(tenantId))) {
Job scheduledJob = getJob(jobExecutionContext);
- Job resultJob = jobService.upsertAndSendToKafka(scheduledJob, true);
+ Job resultJob = dataExportSpringClient.upsertJob(scheduledJob);
log.info("execute:: configureTasks executed for jobId: {} at: {}", resultJob.getId(), current);
}
}
@@ -87,4 +90,8 @@ private void deleteJob(JobExecutionContext jobExecutionContext) {
log.warn("deleteJob:: exception deleting job '{}'", jobKey, e);
}
}
+
+ private FolioExecutionContext folioExecutionContext(String tenantId) {
+ return contextBuilder.forSystemUser(systemUserService.getAuthedSystemUser(tenantId));
+ }
}
diff --git a/src/main/java/org/folio/des/security/AuthService.java b/src/main/java/org/folio/des/security/AuthService.java
deleted file mode 100644
index 8fa4918a..00000000
--- a/src/main/java/org/folio/des/security/AuthService.java
+++ /dev/null
@@ -1,79 +0,0 @@
-package org.folio.des.security;
-
-import lombok.RequiredArgsConstructor;
-import lombok.extern.log4j.Log4j2;
-import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.folio.des.client.AuthClient;
-import org.folio.des.client.UsersClient;
-import org.folio.des.domain.dto.SystemUserParameters;
-import org.folio.des.domain.dto.User;
-import org.folio.spring.integration.XOkapiHeaders;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.http.ResponseEntity;
-import org.springframework.stereotype.Service;
-
-import java.util.Optional;
-
-@Service
-@Log4j2
-@RequiredArgsConstructor
-public class AuthService {
-
- private final AuthClient authClient;
- private final UsersClient usersClient;
-
- @Value("${folio.system.username}")
- private String username;
- @Value("${folio.system.password}")
- private String password;
-
- public String getTokenForSystemUser(String tenant, String url) {
- SystemUserParameters userParameters =
- SystemUserParameters.builder()
- .okapiUrl(url)
- .tenantId(tenant)
- .username(username)
- .password(password)
- .build();
-
- log.info("Attempt login with url={} tenant={} username={}.", url, tenant, username);
-
- ResponseEntity authResponse = authClient.getApiKey(userParameters);
-
- var token = authResponse.getHeaders().get(XOkapiHeaders.TOKEN);
- if (isNotEmpty(token)) {
- log.info("Logged in as {}.", username);
- userParameters.setOkapiToken(token.get(0));
- } else {
- log.error("Can't get token logging in as {}.", username);
- }
- return userParameters.getOkapiToken();
- }
-
- public String getSystemUserId() {
- Optional optionalUser = usersClient.getUsersByQuery("username==" + username).getUsers().stream().findFirst();
-
- if (optionalUser.isEmpty()) {
- log.error("Can't find user id by username {}.", username);
- return null;
- }
- return optionalUser.get().getId();
- }
-
- private boolean isNotEmpty(java.util.List token) {
- return CollectionUtils.isNotEmpty(token) && StringUtils.isNotBlank(token.get(0));
- }
-
- public void deleteCredentials(String userId) {
- authClient.deleteCredentials(userId);
-
- log.info("Removed credentials for user {}.", userId);
- }
-
- public void saveCredentials(SystemUserParameters systemUserParameters) {
- authClient.saveCredentials(systemUserParameters);
-
- log.info("Saved credentials for user {}.", systemUserParameters.getUsername());
- }
-}
diff --git a/src/main/java/org/folio/des/security/SecurityManagerService.java b/src/main/java/org/folio/des/security/SecurityManagerService.java
deleted file mode 100644
index 0bc0abc1..00000000
--- a/src/main/java/org/folio/des/security/SecurityManagerService.java
+++ /dev/null
@@ -1,162 +0,0 @@
-package org.folio.des.security;
-
-import com.google.common.io.Resources;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
-import java.util.UUID;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.log4j.Log4j2;
-import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.folio.des.client.PermissionsClient;
-import org.folio.des.client.UsersClient;
-import org.folio.des.domain.dto.Personal;
-import org.folio.des.domain.dto.SystemUserParameters;
-import org.folio.des.domain.dto.User;
-import org.folio.des.domain.dto.permissions.Permission;
-import org.folio.des.domain.dto.permissions.PermissionUser;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.stereotype.Component;
-
-@Component
-@Log4j2
-@RequiredArgsConstructor
-public class SecurityManagerService {
-
- private static final String PERMISSIONS_FILE_PATH = "permissions/system-user-permissions.csv";
- private static final String USER_LAST_NAME = "SystemDataExportS";
-
- private final PermissionsClient permissionsClient;
- private final UsersClient usersClient;
- private final AuthService authService;
-
- @Value("${folio.system.username}")
- private String username;
- @Value("${folio.system.password}")
- private String password;
-
- public void prepareSystemUser(String okapiUrl, String tenantId) {
- Optional userOptional = getUser(username);
-
- User user;
- if (userOptional.isPresent()) {
- user = userOptional.get();
- updateUser(user);
- } else {
- user = createUser(username);
- }
-
- try {
- authService.deleteCredentials(user.getId());
- } catch (feign.FeignException.NotFound e) {
- // ignore if not exist
- }
- authService.saveCredentials(SystemUserParameters.builder()
- .id(UUID.randomUUID())
- .username(username)
- .password(password)
- .okapiUrl(okapiUrl)
- .tenantId(tenantId)
- .build());
-
- Optional permissionUserOptional = permissionsClient.get("userId==" + user.getId())
- .getPermissionUsers()
- .stream()
- .findFirst();
- if (permissionUserOptional.isPresent()) {
- addPermissions(permissionUserOptional.get());
- } else {
- createPermissionUser(user.getId());
- }
- }
-
- private Optional getUser(String username) {
- return usersClient.getUsersByQuery("username==" + username).getUsers().stream().findFirst();
- }
-
- private User createUser(String username) {
- var result = createUserObject(username);
- log.info("Creating {}.", result);
- usersClient.saveUser(result);
- return result;
- }
-
- private void updateUser(User user) {
- if (existingUserUpToDate(user)) {
- log.info("{} is up to date.", user);
- } else {
- populateMissingUserProperties(user);
- log.info("Updating {}.", user);
- usersClient.updateUser(user.getId(), user);
- }
- }
-
- private PermissionUser createPermissionUser(String userId) {
- List perms = readPermissionsFromResource(PERMISSIONS_FILE_PATH);
- if (CollectionUtils.isEmpty(perms)) {
- throw new IllegalStateException("No user permissions found in " + PERMISSIONS_FILE_PATH);
- }
-
- var permissionUser = PermissionUser.of(UUID.randomUUID().toString(), userId, perms);
- log.info("Creating {}.", permissionUser);
- return permissionsClient.create(permissionUser);
- }
-
- private void addPermissions(PermissionUser permissionUser) {
- var permissions = readPermissionsFromResource(PERMISSIONS_FILE_PATH);
- if (CollectionUtils.isEmpty(permissions)) {
- throw new IllegalStateException("No user permissions found in " + PERMISSIONS_FILE_PATH);
- }
-
- permissions.removeAll(permissionUser.getPermissions());
- permissions.forEach(permission -> {
- var p = new Permission();
- p.setPermissionName(permission);
- try {
- log.info("Adding to user {} permission {}.", permissionUser.getUserId(), p);
- permissionsClient.addPermission(permissionUser.getUserId(), p);
- } catch (Exception e) {
- log.error(String.format("Error adding permission %s to %s.", permission, username), e);
- }
- });
- }
-
- private List readPermissionsFromResource(String permissionsFilePath) {
- List result = new ArrayList<>();
- var url = Resources.getResource(permissionsFilePath);
-
- try {
- result = Resources.readLines(url, StandardCharsets.UTF_8);
- } catch (IOException e) {
- log.error(String.format("Can't read user permissions from %s.", permissionsFilePath), e);
- }
-
- return result;
- }
-
- private User createUserObject(String username) {
- final var result = new User();
-
- result.setId(UUID.randomUUID().toString());
- result.setActive(true);
- result.setUsername(username);
-
- populateMissingUserProperties(result);
-
- return result;
- }
-
- private boolean existingUserUpToDate(User user) {
- return user.getPersonal() != null && StringUtils.isNotBlank(user.getPersonal().getLastName());
- }
-
- private User populateMissingUserProperties(User user) {
- user.setPersonal(new Personal());
- user.getPersonal().setLastName(USER_LAST_NAME);
- return user;
- }
-
-}
diff --git a/src/main/java/org/folio/des/service/FolioTenantService.java b/src/main/java/org/folio/des/service/FolioTenantService.java
index d94f3019..47254427 100644
--- a/src/main/java/org/folio/des/service/FolioTenantService.java
+++ b/src/main/java/org/folio/des/service/FolioTenantService.java
@@ -1,6 +1,5 @@
package org.folio.des.service;
-import org.folio.des.config.FolioExecutionContextHelper;
import org.folio.des.config.kafka.KafkaService;
import org.folio.des.scheduling.acquisition.EdifactScheduledJobInitializer;
import org.folio.des.scheduling.bursar.BursarScheduledJobInitializer;
@@ -9,6 +8,7 @@
import org.folio.des.service.config.BulkEditConfigService;
import org.folio.spring.FolioExecutionContext;
import org.folio.spring.liquibase.FolioSpringLiquibase;
+import org.folio.spring.service.PrepareSystemUserService;
import org.folio.spring.service.TenantService;
import org.folio.tenant.domain.dto.TenantAttributes;
import org.springframework.context.annotation.Primary;
@@ -22,21 +22,21 @@
@Primary
public class FolioTenantService extends TenantService {
- private final FolioExecutionContextHelper contextHelper;
private final KafkaService kafka;
private final BulkEditConfigService bulkEditConfigService;
private final EdifactScheduledJobInitializer edifactScheduledJobInitializer;
private final ScheduledJobsRemover scheduledJobsRemover;
private final BursarScheduledJobInitializer bursarScheduledJobInitializer;
private final OldJobDeleteScheduler oldJobDeleteScheduler;
+ private final PrepareSystemUserService prepareSystemUserService;
public FolioTenantService(JdbcTemplate jdbcTemplate, FolioExecutionContext context, FolioSpringLiquibase folioSpringLiquibase,
- FolioExecutionContextHelper contextHelper, KafkaService kafka,
+ PrepareSystemUserService prepareSystemUserService, KafkaService kafka,
BulkEditConfigService bulkEditConfigService, EdifactScheduledJobInitializer edifactScheduledJobInitializer,
ScheduledJobsRemover scheduledJobsRemover, BursarScheduledJobInitializer bursarScheduledJobInitializer,
OldJobDeleteScheduler oldJobDeleteScheduler) {
super(jdbcTemplate, context, folioSpringLiquibase);
- this.contextHelper = contextHelper;
+ this.prepareSystemUserService = prepareSystemUserService;
this.kafka = kafka;
this.bulkEditConfigService = bulkEditConfigService;
this.edifactScheduledJobInitializer = edifactScheduledJobInitializer;
@@ -48,7 +48,7 @@ public FolioTenantService(JdbcTemplate jdbcTemplate, FolioExecutionContext conte
@Override
protected void afterTenantUpdate(TenantAttributes tenantAttributes) {
try {
- contextHelper.registerTenant();
+ prepareSystemUserService.setupSystemUser();
bursarScheduledJobInitializer.initAllScheduledJob(tenantAttributes);
bulkEditConfigService.checkBulkEditConfiguration();
edifactScheduledJobInitializer.initAllScheduledJob(tenantAttributes);
diff --git a/src/main/java/org/folio/des/service/impl/JobServiceImpl.java b/src/main/java/org/folio/des/service/impl/JobServiceImpl.java
index c5bab28e..8ba224d4 100644
--- a/src/main/java/org/folio/des/service/impl/JobServiceImpl.java
+++ b/src/main/java/org/folio/des/service/impl/JobServiceImpl.java
@@ -1,5 +1,6 @@
package org.folio.des.service.impl;
+import static java.util.Objects.nonNull;
import static org.folio.des.domain.dto.ExportType.BULK_EDIT_IDENTIFIERS;
import static org.folio.des.domain.dto.ExportType.BULK_EDIT_QUERY;
import static org.folio.des.domain.dto.ExportType.BULK_EDIT_UPDATE;
@@ -25,7 +26,7 @@
import org.apache.commons.lang3.StringUtils;
import org.folio.des.client.ConfigurationClient;
import org.folio.des.client.ExportWorkerClient;
-import org.folio.des.config.FolioExecutionContextHelper;
+import org.folio.des.security.JWTokenUtils;
import org.folio.des.domain.dto.PresignedUrl;
import org.folio.des.domain.dto.ExportType;
import org.folio.des.domain.dto.ExportTypeSpecificParameters;
@@ -147,7 +148,7 @@ public org.folio.des.domain.dto.Job upsertAndSendToKafka(org.folio.des.domain.dt
if (StringUtils.isBlank(result.getName())) {
result.setName(String.format("%06d", repository.getNextJobNumber()));
}
- String userName = FolioExecutionContextHelper.getUserName(context);
+ String userName = getUserName(context);
if (StringUtils.isBlank(result.getSource())) {
result.setSource(userName);
}
@@ -161,7 +162,7 @@ public org.folio.des.domain.dto.Job upsertAndSendToKafka(org.folio.des.domain.dt
if (result.getCreatedDate() == null) {
result.setCreatedDate(now);
}
- UUID userId = FolioExecutionContextHelper.getUserId(context);
+ UUID userId = context.getUserId();
if (result.getCreatedByUserId() == null) {
result.setCreatedByUserId(userId);
}
@@ -339,4 +340,9 @@ public static Job dtoToEntity(org.folio.des.domain.dto.Job dto) {
return result;
}
+ private String getUserName(FolioExecutionContext context) {
+ String jwt = context.getToken();
+ Optional userInfo = StringUtils.isBlank(jwt) ? Optional.empty() : JWTokenUtils.parseToken(jwt);
+ return StringUtils.substring(userInfo.map(JWTokenUtils.UserInfo::getUserName).orElse(null), 0, 50);
+ }
}
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 5271f85d..bbd06851 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -1,9 +1,11 @@
folio:
- system:
+ system-user:
username: ${SYSTEM_USER_NAME:data-export-system-user}
password: ${SYSTEM_USER_PASSWORD}
- okapi:
- url: ${OKAPI_URL:http://okapi:9130}
+ lastname: System
+ permissionsFilePath: permissions/system-user-permissions.csv
+ okapi-url: ${OKAPI_URL:http://okapi:9130}
+ environment: ${ENV:folio}
tenant:
validation:
enabled: true
diff --git a/src/main/resources/permissions/system-user-permissions.csv b/src/main/resources/permissions/system-user-permissions.csv
index da9d8f35..ade71b20 100644
--- a/src/main/resources/permissions/system-user-permissions.csv
+++ b/src/main/resources/permissions/system-user-permissions.csv
@@ -1,3 +1,5 @@
+data-export.job.item.post
+data-export.job.item.send
accounts.transfer.post
accounts.collection.get
circulation-logs.collection.get
@@ -21,4 +23,4 @@ orders-storage.purchase-orders.collection.get
transfers.collection.get
users.collection.get
users.item.post
-users.item.put
+users.item.put
\ No newline at end of file
diff --git a/src/main/resources/swagger.api/jobs.yaml b/src/main/resources/swagger.api/jobs.yaml
index 90444b40..38a6fb5e 100644
--- a/src/main/resources/swagger.api/jobs.yaml
+++ b/src/main/resources/swagger.api/jobs.yaml
@@ -185,6 +185,35 @@ paths:
schema:
type: string
format: binary
+ /jobs/send:
+ post:
+ description: Send job via Kafka
+ operationId: sendJob
+ responses:
+ "200":
+ description: Job was sent
+ "400":
+ description: Bad Request
+ content:
+ application/json:
+ example:
+ $ref: "#/components/examples/errors"
+ schema:
+ $ref: "#/components/schemas/errors"
+ "500":
+ description: Internal server errors, e.g. due to misconfiguration
+ content:
+ application/json:
+ example:
+ $ref: "#/components/examples/errors"
+ schema:
+ $ref: "#/components/schemas/errors"
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/job"
+ required: true
components:
schemas:
jobStatus:
diff --git a/src/test/java/org/folio/des/config/FolioExecutionContextHelperTest.java b/src/test/java/org/folio/des/config/FolioExecutionContextHelperTest.java
deleted file mode 100644
index e0869207..00000000
--- a/src/test/java/org/folio/des/config/FolioExecutionContextHelperTest.java
+++ /dev/null
@@ -1,86 +0,0 @@
-package org.folio.des.config;
-
-import org.folio.des.support.BaseTest;
-import org.folio.spring.FolioExecutionContext;
-import org.folio.spring.integration.XOkapiHeaders;
-import org.junit.jupiter.api.Test;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.MediaType;
-
-import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
-import static com.github.tomakehurst.wiremock.client.WireMock.get;
-import static com.github.tomakehurst.wiremock.client.WireMock.post;
-import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-
-class FolioExecutionContextHelperTest extends BaseTest {
-
- @Autowired
- private FolioExecutionContextHelper contextHelper;
-
- private static final String SYSTEM_USER = """
- {
- "users": [
- {
- "username": "data-export-system-user",
- "id": "a85c45b7-d427-4122-8532-5570219c5e59",
- "active": true,
- "departments": [],
- "proxyFor": [],
- "personal": {
- "addresses": []
- },
- "createdDate": "2021-03-17T15:30:07.106+00:00",
- "updatedDate": "2021-03-17T15:30:07.106+00:00",
- "metadata": {
- "createdDate": "2021-03-17T15:21:26.064+00:00",
- "updatedDate": "2021-03-17T15:30:07.043+00:00"
- }
- }
- ],
- "totalRecords": 1,
- "resultInfo": {
- "totalRecords": 1,
- "facets": [],
- "diagnostics": []
- }
- }
- """;
-
- @Test
- void shouldGetFolioExecutionContext() {
- // request to get token for 'data-export-system-user'
- wireMockServer.stubFor(
- post(urlEqualTo("/authn/login"))
- .willReturn(aResponse()
- .withHeader(XOkapiHeaders.TOKEN, TOKEN)));
-
- // request to get list of users by 'username' (='data-export-system-user')
- wireMockServer.stubFor(
- get(urlEqualTo("/users?query=username%3D%3Ddata-export-system-user"))
- .willReturn(aResponse()
- .withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE)
- .withBody(SYSTEM_USER)));
-
- // 'execution context' should be created according to 'data-export-system-user' headers
- FolioExecutionContext executionContext = contextHelper.getFolioExecutionContext(TENANT);
-
- assertEquals(TENANT, executionContext.getTenantId());
- assertEquals(wireMockServer.baseUrl(), executionContext.getOkapiUrl());
- assertEquals(TOKEN, executionContext.getToken());
- assertEquals("a85c45b7-d427-4122-8532-5570219c5e59", executionContext.getUserId().toString());
- }
-
- @Test
- void shouldGetExceptionWhenThereIsNoToken() {
- // request to get response without 'XOkapiHeaders.TOKEN'
- wireMockServer.stubFor(
- post(urlEqualTo("/authn/login"))
- .willReturn(aResponse()));
-
- // should get exception: verify exception type and message
- Exception exception = assertThrows(IllegalStateException.class, () -> contextHelper.getFolioExecutionContext(TENANT));
- assertEquals(String.format("Cannot create FolioExecutionContext for Tenant: %s because of absent token", TENANT), exception.getMessage());
- }
-}
diff --git a/src/test/java/org/folio/des/scheduling/quartz/EdifactExportJobSchedulerTest.java b/src/test/java/org/folio/des/scheduling/quartz/EdifactExportJobSchedulerTest.java
index cbe09ea8..fd4bdcec 100644
--- a/src/test/java/org/folio/des/scheduling/quartz/EdifactExportJobSchedulerTest.java
+++ b/src/test/java/org/folio/des/scheduling/quartz/EdifactExportJobSchedulerTest.java
@@ -2,6 +2,7 @@
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
+import static com.github.tomakehurst.wiremock.client.WireMock.post;
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
@@ -148,6 +149,12 @@ void testJobRemovedForRemovedConfiguration() throws SchedulerException {
.scheduleFrequency(1)
.timeZone(ZoneId.systemDefault().getId());
+ wireMockServer.stubFor(post(urlEqualTo("/authn/login-with-expiry"))
+ .willReturn(aResponse().withStatus(201)
+ .withBody("{\"accessTokenExpiration\":\"2050-01-01T23:59:59Z\", \"refreshTokenExpiration\":\"2050-01-01T23:59:59Z\"}")
+ .withHeader("Content-Type", "application/json")
+ .withHeader("Set-Cookie", "folioAccessToken=AAA-BBB-CCC; Max-Age=600; Expires=Fri, 01 Sep 2030 13:04:35 GMT; Path=/; Secure; HTTPOnly; SameSite=None")));
+
edifactExportJobScheduler.scheduleExportJob(config);
// job should be scheduled
var jobKeys = scheduler.getJobKeys(GroupMatcher.anyJobGroup());
diff --git a/src/test/java/org/folio/des/scheduling/quartz/job/OldDeleteJobTest.java b/src/test/java/org/folio/des/scheduling/quartz/job/OldDeleteJobTest.java
index dd2a9f61..b9142ea0 100644
--- a/src/test/java/org/folio/des/scheduling/quartz/job/OldDeleteJobTest.java
+++ b/src/test/java/org/folio/des/scheduling/quartz/job/OldDeleteJobTest.java
@@ -7,10 +7,12 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import org.folio.des.config.FolioExecutionContextHelper;
import org.folio.des.service.JobService;
import org.folio.spring.FolioExecutionContext;
import org.folio.spring.FolioModuleMetadata;
+import org.folio.spring.context.ExecutionContextBuilder;
+import org.folio.spring.model.SystemUser;
+import org.folio.spring.service.SystemUserService;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
@@ -31,7 +33,9 @@ class OldDeleteJobTest {
@Mock
private JobService jobService;
@Mock
- private FolioExecutionContextHelper contextHelper;
+ private ExecutionContextBuilder contextBuilder;
+ @Mock
+ private SystemUserService systemUserService;
@InjectMocks
private OldDeleteJob oldDeleteJob;
@Mock
@@ -41,10 +45,11 @@ class OldDeleteJobTest {
@Test
void testSuccessfulExecute() throws JobExecutionException {
when(jobExecutionContext.getJobDetail()).thenReturn(getJobDetail());
- when(contextHelper.getFolioExecutionContext(any())).thenReturn(folioExecutionContext);
+ when(systemUserService.getAuthedSystemUser(any())).thenReturn(SystemUser.builder().build());
+ when(contextBuilder.forSystemUser(any())).thenReturn(folioExecutionContext);
doNothing().when(jobService).deleteOldJobs();
oldDeleteJob.execute(jobExecutionContext);
- verify(contextHelper).getFolioExecutionContext(TENANT_ID);
+ verify(jobService).deleteOldJobs();
}
@Test
diff --git a/src/test/java/org/folio/des/scheduling/quartz/job/acquisition/EdifactJobTest.java b/src/test/java/org/folio/des/scheduling/quartz/job/acquisition/EdifactJobTest.java
index 44d7a8c4..28b5e899 100644
--- a/src/test/java/org/folio/des/scheduling/quartz/job/acquisition/EdifactJobTest.java
+++ b/src/test/java/org/folio/des/scheduling/quartz/job/acquisition/EdifactJobTest.java
@@ -5,6 +5,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -12,7 +13,7 @@
import java.util.UUID;
import org.folio.des.builder.job.JobCommandSchedulerBuilder;
-import org.folio.des.config.FolioExecutionContextHelper;
+import org.folio.des.client.DataExportSpringClient;
import org.folio.des.domain.dto.EdiSchedule;
import org.folio.des.domain.dto.ExportConfig;
import org.folio.des.domain.dto.ExportType;
@@ -26,7 +27,10 @@
import org.folio.des.service.config.impl.ExportTypeBasedConfigManager;
import org.folio.spring.FolioExecutionContext;
import org.folio.spring.FolioModuleMetadata;
+import org.folio.spring.context.ExecutionContextBuilder;
import org.folio.spring.exception.NotFoundException;
+import org.folio.spring.model.SystemUser;
+import org.folio.spring.service.SystemUserService;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
@@ -48,7 +52,9 @@ class EdifactJobTest {
@Mock
private JobCommandSchedulerBuilder jobSchedulerCommandBuilder;
@Mock
- private FolioExecutionContextHelper contextHelper;
+ private ExecutionContextBuilder contextBuilder;
+ @Mock
+ private SystemUserService systemUserService;
@Mock
private ExportTypeBasedConfigManager exportTypeBasedConfigManager;
@InjectMocks
@@ -57,6 +63,8 @@ class EdifactJobTest {
private JobExecutionContext jobExecutionContext;
@Mock
private Scheduler scheduler;
+ @Mock
+ private DataExportSpringClient dataExportSpringClient;
private FolioExecutionContext folioExecutionContext = new TestFolioExecutionContext();
private static final String TENANT_ID = "some_test_tenant";
private static final String EXPORT_CONFIG_ID = "some_test_export_config_id";
@@ -65,29 +73,29 @@ class EdifactJobTest {
@Test
void testExecuteSuccessful() {
+ when(systemUserService.getAuthedSystemUser(any())).thenReturn(SystemUser.builder().build());
+ when(contextBuilder.forSystemUser(any())).thenReturn(folioExecutionContext);
when(jobExecutionContext.getJobDetail()).thenReturn(getJobDetail());
when(exportTypeBasedConfigManager.getConfigById(EXPORT_CONFIG_ID)).thenReturn(getExportConfig());
- when(contextHelper.getFolioExecutionContext(any())).thenReturn(folioExecutionContext);
when(jobService.upsertAndSendToKafka(any(), eq(false), eq(false))).thenReturn(new Job().id(UUID.randomUUID()));
+ doNothing().when(dataExportSpringClient).sendJob(any());
edifactJob.execute(jobExecutionContext);
- verify(contextHelper).getFolioExecutionContext(TENANT_ID);
verify(jobService).upsertAndSendToKafka(any(), eq(false), eq(false));
- verify(jobSchedulerCommandBuilder).buildJobCommand(any());
- verify(jobExecutionService).sendJobCommand(any());
+ verify(dataExportSpringClient).sendJob(any());
}
@Test
void testExecuteSuccessfulSkipKafkaWhenNoJobId() {
+ when(systemUserService.getAuthedSystemUser(any())).thenReturn(SystemUser.builder().build());
+ when(contextBuilder.forSystemUser(any())).thenReturn(folioExecutionContext);
when(jobExecutionContext.getJobDetail()).thenReturn(getJobDetail());
when(exportTypeBasedConfigManager.getConfigById(EXPORT_CONFIG_ID)).thenReturn(getExportConfig());
- when(contextHelper.getFolioExecutionContext(any())).thenReturn(folioExecutionContext);
when(jobService.upsertAndSendToKafka(any(), eq(false), eq(false))).thenReturn(new Job());
edifactJob.execute(jobExecutionContext);
- verify(contextHelper).getFolioExecutionContext(TENANT_ID);
verify(jobService).upsertAndSendToKafka(any(), eq(false), eq(false));
verify(jobSchedulerCommandBuilder, times(0)).buildJobCommand(any());
verify(jobExecutionService, times(0)).sendJobCommand(any());
@@ -105,10 +113,11 @@ void testExecuteFailureWhenNoTenantIdPassed() {
@Test
void testExecuteFailureWhenNoExportConfigIdPassed() {
+ when(systemUserService.getAuthedSystemUser(any())).thenReturn(SystemUser.builder().build());
+ when(contextBuilder.forSystemUser(any())).thenReturn(folioExecutionContext);
JobDetail jobDetail = getJobDetail();
jobDetail.getJobDataMap().remove("exportConfigId");
when(jobExecutionContext.getJobDetail()).thenReturn(jobDetail);
- when(contextHelper.getFolioExecutionContext(any())).thenReturn(folioExecutionContext);
verifyExceptionThrownAndJobNotExecuted(IllegalArgumentException.class,
"'exportConfigId' param is missing in the jobExecutionContext", jobExecutionContext);
@@ -116,9 +125,10 @@ void testExecuteFailureWhenNoExportConfigIdPassed() {
@Test
void testExecuteFailureAndJobDeletedWhenExportConfigNotFound() throws SchedulerException {
+ when(systemUserService.getAuthedSystemUser(any())).thenReturn(SystemUser.builder().build());
+ when(contextBuilder.forSystemUser(any())).thenReturn(folioExecutionContext);
when(jobExecutionContext.getJobDetail()).thenReturn(getJobDetail());
when(jobExecutionContext.getScheduler()).thenReturn(scheduler);
- when(contextHelper.getFolioExecutionContext(any())).thenReturn(folioExecutionContext);
when(exportTypeBasedConfigManager.getConfigById(EXPORT_CONFIG_ID))
.thenThrow(new NotFoundException("config not found"));
diff --git a/src/test/java/org/folio/des/scheduling/quartz/job/bursar/BursarJobTest.java b/src/test/java/org/folio/des/scheduling/quartz/job/bursar/BursarJobTest.java
index 5d1ec339..c58673ea 100644
--- a/src/test/java/org/folio/des/scheduling/quartz/job/bursar/BursarJobTest.java
+++ b/src/test/java/org/folio/des/scheduling/quartz/job/bursar/BursarJobTest.java
@@ -4,13 +4,14 @@
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.UUID;
import org.folio.des.builder.job.JobCommandSchedulerBuilder;
-import org.folio.des.config.FolioExecutionContextHelper;
+import org.folio.des.client.DataExportSpringClient;
import org.folio.des.domain.dto.EdiSchedule;
import org.folio.des.domain.dto.ExportConfig;
import org.folio.des.domain.dto.ExportType;
@@ -24,7 +25,10 @@
import org.folio.des.service.config.impl.ExportTypeBasedConfigManager;
import org.folio.spring.FolioExecutionContext;
import org.folio.spring.FolioModuleMetadata;
+import org.folio.spring.context.ExecutionContextBuilder;
import org.folio.spring.exception.NotFoundException;
+import org.folio.spring.model.SystemUser;
+import org.folio.spring.service.SystemUserService;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
@@ -53,7 +57,9 @@ class BursarJobTest {
@Mock
private JobCommandSchedulerBuilder jobSchedulerCommandBuilder;
@Mock
- private FolioExecutionContextHelper contextHelper;
+ private ExecutionContextBuilder contextBuilder;
+ @Mock
+ private SystemUserService systemUserService;
@Mock
private ExportTypeBasedConfigManager exportTypeBasedConfigManager;
@InjectMocks
@@ -62,17 +68,19 @@ class BursarJobTest {
private JobExecutionContext jobExecutionContext;
@Mock
private Scheduler scheduler;
+ @Mock
+ private DataExportSpringClient dataExportSpringClient;
private final FolioExecutionContext folioExecutionContext = new TestFolioExecutionContext();
@Test
void testSuccessfulExecute() throws JobExecutionException {
when(jobExecutionContext.getJobDetail()).thenReturn(getJobDetail());
when(exportTypeBasedConfigManager.getConfigById(EXPORT_CONFIG_ID)).thenReturn(getExportConfig());
- when(contextHelper.getFolioExecutionContext(any())).thenReturn(folioExecutionContext);
- when(jobService.upsertAndSendToKafka(any(), eq(true))).thenReturn(new Job().id(UUID.randomUUID()));
+ when(systemUserService.getAuthedSystemUser(any())).thenReturn(SystemUser.builder().build());
+ when(contextBuilder.forSystemUser(any())).thenReturn(folioExecutionContext);
+ when(dataExportSpringClient.upsertJob(any())).thenReturn(new Job().id(UUID.randomUUID()));
bursarJob.execute(jobExecutionContext);
- verify(contextHelper).getFolioExecutionContext(TENANT_ID);
- verify(jobService).upsertAndSendToKafka(any(), eq(true));
+ verify(dataExportSpringClient).upsertJob(any());
}
@Test
@@ -90,7 +98,8 @@ void testExecuteFailureWhenConfigIdNotFoundInJobDetail() {
JobDetail jobDetail = getJobDetail();
jobDetail.getJobDataMap().remove("exportConfigId");
when(jobExecutionContext.getJobDetail()).thenReturn(jobDetail);
- when(contextHelper.getFolioExecutionContext(any())).thenReturn(folioExecutionContext);
+ when(systemUserService.getAuthedSystemUser(any())).thenReturn(SystemUser.builder().build());
+ when(contextBuilder.forSystemUser(any())).thenReturn(folioExecutionContext);
String message = assertThrows(IllegalArgumentException.class, () -> bursarJob.execute(jobExecutionContext)).getMessage();
assertEquals("'exportConfigId' param is missing in the jobExecutionContext", message);
@@ -99,7 +108,8 @@ void testExecuteFailureWhenConfigIdNotFoundInJobDetail() {
@Test
void testExecuteFailureWhenWhenConfigIdNotFoundInSettings() throws SchedulerException {
when(jobExecutionContext.getJobDetail()).thenReturn(getJobDetail());
- when(contextHelper.getFolioExecutionContext(any())).thenReturn(folioExecutionContext);
+ when(systemUserService.getAuthedSystemUser(any())).thenReturn(SystemUser.builder().build());
+ when(contextBuilder.forSystemUser(any())).thenReturn(folioExecutionContext);
when(jobExecutionContext.getScheduler()).thenReturn(scheduler);
when(exportTypeBasedConfigManager.getConfigById(EXPORT_CONFIG_ID))
.thenThrow(new NotFoundException("config not found"));
diff --git a/src/test/java/org/folio/des/security/SecurityManagerServiceTest.java b/src/test/java/org/folio/des/security/SecurityManagerServiceTest.java
deleted file mode 100644
index 36c7259c..00000000
--- a/src/test/java/org/folio/des/security/SecurityManagerServiceTest.java
+++ /dev/null
@@ -1,151 +0,0 @@
-package org.folio.des.security;
-
-import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
-import static com.github.tomakehurst.wiremock.client.WireMock.delete;
-import static com.github.tomakehurst.wiremock.client.WireMock.deleteRequestedFor;
-import static com.github.tomakehurst.wiremock.client.WireMock.get;
-import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor;
-import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor;
-import static com.github.tomakehurst.wiremock.client.WireMock.putRequestedFor;
-import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
-
-import org.folio.des.support.BaseTest;
-import org.folio.spring.DefaultFolioExecutionContext;
-import org.folio.spring.FolioModuleMetadata;
-import org.folio.spring.integration.XOkapiHeaders;
-import org.folio.spring.scope.FolioExecutionContextSetter;
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Test;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
-
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-class SecurityManagerServiceTest extends BaseTest {
-
- @Autowired private SecurityManagerService securityManagerService;
- @Autowired private FolioModuleMetadata folioModuleMetadata;
- private static final String SYS_USER_EXIST_RESPONSE =
- "{\n"
- + " \"users\": [\n"
- + " {\n"
- + " \"username\": \"data-export-system-user\",\n"
- + " \"id\": \"a85c45b7-d427-4122-8532-5570219c5e59\",\n"
- + " \"active\": true,\n"
- + " \"departments\": [],\n"
- + " \"proxyFor\": [],\n"
- + " \"personal\": {\n"
- + " \"addresses\": []\n"
- + " },\n"
- + " \"createdDate\": \"2021-03-17T15:30:07.106+00:00\",\n"
- + " \"updatedDate\": \"2021-03-17T15:30:07.106+00:00\",\n"
- + " \"metadata\": {\n"
- + " \"createdDate\": \"2021-03-17T15:21:26.064+00:00\",\n"
- + " \"updatedDate\": \"2021-03-17T15:30:07.043+00:00\"\n"
- + " }\n"
- + " }\n"
- + " ],\n"
- + " \"totalRecords\": 1,\n"
- + " \"resultInfo\": {\n"
- + " \"totalRecords\": 1,\n"
- + " \"facets\": [],\n"
- + " \"diagnostics\": []\n"
- + " }\n"
- + "}";
-
- private static final String USER_PERMS_RESPONSE =
- "{ \"permissionUsers\": [],\n \"totalRecords\": 0,\n \"resultInfo\": {\n \"totalRecords\": 0,\n \"facets\": [],\n \"diagnostics\": []\n }\n}";
-
-
- @Test
- @DisplayName("Update user")
- void prepareSystemUser() {
-
- wireMockServer.stubFor(
- get(urlEqualTo("/users?query=username%3D%3Ddata-export-system-user"))
- .willReturn(
- aResponse()
- .withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE)
- .withBody(SYS_USER_EXIST_RESPONSE)));
-
- wireMockServer.stubFor(
- get(urlEqualTo("/perms/users?query=userId%3D%3Da85c45b7-d427-4122-8532-5570219c5e59"))
- .willReturn(
- aResponse()
- .withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE)
- .withBody(USER_PERMS_RESPONSE)));
-
- wireMockServer.stubFor(
- delete(urlEqualTo("/authn/credentials?userId=a85c45b7-d427-4122-8532-5570219c5e59"))
- .willReturn(
- aResponse()
- .withStatus(HttpStatus.NO_CONTENT.value())));
-
- Map> tenantOkapiHeaders = new HashMap<>() {{
- put(XOkapiHeaders.TENANT, List.of(TENANT));
- put(XOkapiHeaders.URL, List.of(wireMockServer.baseUrl()));
- put(XOkapiHeaders.TOKEN, List.of(TOKEN));
- }};
-
- try (var context = new FolioExecutionContextSetter(new DefaultFolioExecutionContext(folioModuleMetadata, tenantOkapiHeaders))) {
- securityManagerService.prepareSystemUser(wireMockServer.baseUrl(), TENANT);
- }
-
- wireMockServer.verify(
- getRequestedFor(urlEqualTo("/users?query=username%3D%3Ddata-export-system-user")));
- wireMockServer.verify(
- putRequestedFor(urlEqualTo("/users/a85c45b7-d427-4122-8532-5570219c5e59")));
- wireMockServer.verify(
- deleteRequestedFor(urlEqualTo("/authn/credentials?userId=a85c45b7-d427-4122-8532-5570219c5e59")));
- wireMockServer.verify(
- postRequestedFor(urlEqualTo("/authn/credentials")));
- }
-
- @Test
- @DisplayName("Update user without previous password")
- void prepareSystemUserWithoutPreviousPassword() {
-
- wireMockServer.stubFor(
- get(urlEqualTo("/users?query=username%3D%3Ddata-export-system-user"))
- .willReturn(
- aResponse()
- .withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE)
- .withBody(SYS_USER_EXIST_RESPONSE)));
-
- wireMockServer.stubFor(
- get(urlEqualTo("/perms/users?query=userId%3D%3Da85c45b7-d427-4122-8532-5570219c5e59"))
- .willReturn(
- aResponse()
- .withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE)
- .withBody(USER_PERMS_RESPONSE)));
-
- wireMockServer.stubFor(
- delete(urlEqualTo("/authn/credentials?userId=a85c45b7-d427-4122-8532-5570219c5e59"))
- .willReturn(
- aResponse()
- .withStatus(HttpStatus.NOT_FOUND.value())));
-
- Map> tenantOkapiHeaders = new HashMap<>() {{
- put(XOkapiHeaders.TENANT, List.of(TENANT));
- put(XOkapiHeaders.URL, List.of(wireMockServer.baseUrl()));
- put(XOkapiHeaders.TOKEN, List.of(TOKEN));
- }};
-
- try (var context = new FolioExecutionContextSetter(new DefaultFolioExecutionContext(folioModuleMetadata, tenantOkapiHeaders))) {
- securityManagerService.prepareSystemUser(wireMockServer.baseUrl(), TENANT);
- }
-
- wireMockServer.verify(
- getRequestedFor(urlEqualTo("/users?query=username%3D%3Ddata-export-system-user")));
- wireMockServer.verify(
- putRequestedFor(urlEqualTo("/users/a85c45b7-d427-4122-8532-5570219c5e59")));
- wireMockServer.verify(
- deleteRequestedFor(urlEqualTo("/authn/credentials?userId=a85c45b7-d427-4122-8532-5570219c5e59")));
- wireMockServer.verify(
- postRequestedFor(urlEqualTo("/authn/credentials")));
- }
-}
diff --git a/src/test/java/org/folio/des/service/FolioTenantServiceTest.java b/src/test/java/org/folio/des/service/FolioTenantServiceTest.java
index ea061e76..a1e3cc74 100644
--- a/src/test/java/org/folio/des/service/FolioTenantServiceTest.java
+++ b/src/test/java/org/folio/des/service/FolioTenantServiceTest.java
@@ -6,7 +6,6 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import org.folio.des.config.FolioExecutionContextHelper;
import org.folio.des.config.kafka.KafkaService;
import org.folio.des.scheduling.acquisition.EdifactScheduledJobInitializer;
import org.folio.des.scheduling.bursar.BursarScheduledJobInitializer;
@@ -14,6 +13,7 @@
import org.folio.des.scheduling.quartz.ScheduledJobsRemover;
import org.folio.des.service.config.BulkEditConfigService;
import org.folio.spring.FolioExecutionContext;
+import org.folio.spring.service.PrepareSystemUserService;
import org.folio.tenant.domain.dto.TenantAttributes;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -27,8 +27,6 @@ class FolioTenantServiceTest {
@InjectMocks
FolioTenantService folioTenantService;
- @Mock
- FolioExecutionContextHelper contextHelper;
@Mock
KafkaService kafka;
@Mock
@@ -45,12 +43,14 @@ class FolioTenantServiceTest {
@Mock
OldJobDeleteScheduler oldJobDeleteScheduler;
+ @Mock
+ PrepareSystemUserService prepareSystemUserService;
@Test
void shouldDoProcessAfterTenantUpdating() {
TenantAttributes tenantAttributes = createTenantAttributes();
- doNothing().when(contextHelper).registerTenant();
+ doNothing().when(prepareSystemUserService).setupSystemUser();
doNothing().when(bulkEditConfigService).checkBulkEditConfiguration();
doNothing().when(edifactScheduledJobInitializer).initAllScheduledJob(tenantAttributes);
doNothing().when(kafka).createKafkaTopics();
@@ -60,7 +60,7 @@ void shouldDoProcessAfterTenantUpdating() {
folioTenantService.afterTenantUpdate(tenantAttributes);
- verify(contextHelper, times(1)).registerTenant();
+ verify(prepareSystemUserService).setupSystemUser();
verify(bulkEditConfigService, times(1)).checkBulkEditConfiguration();
verify(edifactScheduledJobInitializer, times(1)).initAllScheduledJob(tenantAttributes);
verify(bursarScheduledJobInitializer, times(1)).initAllScheduledJob(tenantAttributes);
diff --git a/src/test/java/org/folio/des/support/BaseTest.java b/src/test/java/org/folio/des/support/BaseTest.java
index 5fbcbaf8..a24a18dd 100644
--- a/src/test/java/org/folio/des/support/BaseTest.java
+++ b/src/test/java/org/folio/des/support/BaseTest.java
@@ -6,6 +6,7 @@
import java.util.List;
+import org.folio.spring.config.properties.FolioEnvironment;
import org.folio.spring.integration.XOkapiHeaders;
import org.folio.tenant.domain.dto.TenantAttributes;
import org.junit.jupiter.api.AfterAll;
@@ -59,6 +60,8 @@ public abstract class BaseTest {
protected MockMvc mockMvc;
@Autowired
protected Scheduler scheduler;
+ @Autowired
+ private FolioEnvironment folioEnvironment;
static {
postgreDBContainer.start();
@@ -85,6 +88,7 @@ static void beforeAll(@Autowired MockMvc mockMvc) {
@BeforeEach
void beforeEach() throws SchedulerException {
+ folioEnvironment.setOkapiUrl(wireMockServer.baseUrl());
scheduler.clear();
}
diff --git a/src/test/resources/mappings/authn.json b/src/test/resources/mappings/authn.json
index 0c071848..620dd38e 100644
--- a/src/test/resources/mappings/authn.json
+++ b/src/test/resources/mappings/authn.json
@@ -3,14 +3,15 @@
{
"request": {
"method": "POST",
- "url": "/authn/login"
+ "url": "/authn/login-with-expiry"
},
"response": {
- "status": 200,
+ "status": 201,
"headers": {
"Content-Type": "application/json",
- "x-okapi-token": "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJkaWt1X2FkbWluIiwidXNlcl9pZCI6IjFkM2I1OGNiLTA3YjUtNWZjZC04YTJhLTNjZTA2YTBlYjkwZiIsImlhdCI6MTYxNjQyMDM5MywidGVuYW50IjoiZGlrdSJ9.2nvEYQBbJP1PewEgxixBWLHSX_eELiBEBpjufWiJZRs"
- }
+ "set-cookie": "folioAccessToken=AAA-BBB-CCC; Max-Age=600; Expires=Fri, 01 Sep 2030 13:04:35 GMT; Path=/; Secure; HTTPOnly; SameSite=None"
+ },
+ "body": "{ \n \"accessTokenExpiration\": \"2030-09-01T13:04:35Z\",\n \"refreshTokenExpiration\": \"2030-09-08T12:54:35Z\"\n}"
}
},
{