FINERACT-2119: Address jobs by name

This commit is contained in:
Marta Jankovics
2024-08-28 16:46:20 +02:00
committed by Adam Saghy
parent eef6a8da5e
commit 3aaa2ffe9c
27 changed files with 761 additions and 185 deletions
@@ -0,0 +1,74 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.infrastructure.core.api;
import jakarta.validation.constraints.NotNull;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.infrastructure.core.service.DefaultOption;
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
public abstract class IdTypeResolver {
public enum IdType implements DefaultOption {
ID, //
EXTERNAL_ID, //
SHORT_NAME,; //
@Override
public boolean isDefault() {
return this == ID;
}
}
@NotNull
public static IdType resolveDefault() {
return IdType.ID;
}
public static IdType resolve(String idType) {
return resolve(IdType.class, idType);
}
public static <T extends Enum<T>> T resolve(@NotNull Class<T> clazz, String idType) {
if (idType == null) {
return clazz.isAssignableFrom(DefaultOption.class) ? (T) DefaultOption.getDefault((Class) clazz) : null;
}
idType = formatIdType(idType);
try {
return Enum.valueOf(clazz, idType);
} catch (IllegalArgumentException e) {
throw resolveFailed(idType, e);
}
}
public static String formatIdType(String idType) {
return idType == null ? null : idType.replaceAll("-", "_").toUpperCase();
}
public static RuntimeException resolveFailed(String idType, Exception e) {
return new PlatformApiDataValidationException("error.msg.id.type.not.found", "Provided type " + idType + " is not supported",
"idType", e, idType);
}
}
@@ -51,14 +51,18 @@ public class MultiException extends Exception {
private final List<Throwable> throwables;
@SuppressFBWarnings("CT_CONSTRUCTOR_THROW")
public MultiException(List<Throwable> problems) {
super("MultiException with " + problems.size() + " contained causes (details available)");
public MultiException(String message, List<Throwable> problems) {
super(message);
if (problems.isEmpty()) {
throw new IllegalArgumentException("List of Throwables must not be empty");
}
this.throwables = new ArrayList<>(problems);
}
public MultiException(List<Throwable> problems) {
this("MultiException with " + problems.size() + " contained causes (details available)", problems);
}
public List<Throwable> getCauses() {
return Collections.unmodifiableList(throwables);
}
@@ -56,6 +56,18 @@ public class PlatformApiDataValidationException extends AbstractPlatformExceptio
this.errors = errors;
}
public PlatformApiDataValidationException(String messageCode, String userMessage, String parameterName, Throwable cause,
final Object... userMessageArgs) {
this("validation.msg.validation.errors.exist", "Validation errors exist.",
List.of(ApiParameterError.parameterError(messageCode, userMessage, parameterName, userMessageArgs)), cause);
}
public PlatformApiDataValidationException(String messageCode, String userMessage, String parameterName,
final Object... userMessageArgs) {
this("validation.msg.validation.errors.exist", "Validation errors exist.",
List.of(ApiParameterError.parameterError(messageCode, userMessage, parameterName, userMessageArgs)), null);
}
public List<ApiParameterError> getErrors() {
return this.errors;
}
@@ -0,0 +1,33 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.infrastructure.core.service;
public interface DefaultOption {
boolean isDefault();
static <T extends Enum<T> & DefaultOption> T getDefault(Class<T> clazz) {
for (T enumConstant : clazz.getEnumConstants()) {
if (enumConstant.isDefault()) {
return enumConstant;
}
}
return null;
}
}
@@ -34,6 +34,9 @@ public class JobDetailData {
@SuppressWarnings("unused")
private String displayName;
@SuppressWarnings("unused")
private String shortName;
@SuppressWarnings("unused")
private Date nextRunTime;
@@ -50,4 +53,22 @@ public class JobDetailData {
@SuppressWarnings("unused")
private JobDetailHistoryData lastRunHistory;
public JobDetailData(Long jobId, String displayName, String shortName, Date nextRunTime, String initializingError,
String cronExpression, boolean active, boolean currentlyRunning, Long version, Date jobRunStartTime, Date jobRunEndTime,
String status, String jobRunErrorMessage, String triggerType, String jobRunErrorLog) {
this.jobId = jobId;
this.displayName = displayName;
this.shortName = shortName;
this.nextRunTime = nextRunTime;
this.initializingError = initializingError;
this.cronExpression = cronExpression;
this.active = active;
this.currentlyRunning = currentlyRunning;
if (version != null) {
this.lastRunHistory = new JobDetailHistoryData().setVersion(version).setJobRunStartTime(jobRunStartTime)
.setJobRunEndTime(jobRunEndTime).setStatus(status).setJobRunErrorMessage(jobRunErrorMessage).setTriggerType(triggerType)
.setJobRunErrorLog(jobRunErrorLog);
}
}
}
@@ -18,11 +18,23 @@
*/
package org.apache.fineract.infrastructure.jobs.exception;
import static java.util.stream.Collectors.mapping;
import static java.util.stream.Collectors.toList;
import jakarta.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.fineract.infrastructure.core.exception.MultiException;
public class JobExecutionException extends MultiException {
public JobExecutionException(String message, List<Throwable> problems) {
super(message, problems);
}
public JobExecutionException(List<Throwable> problems) {
super(problems);
}
@@ -30,4 +42,27 @@ public class JobExecutionException extends MultiException {
public JobExecutionException(MultiException multiException) {
super(multiException.getCauses());
}
public static void throwErrors(@NotNull Map<Throwable, List<String>> errorMap) throws JobExecutionException {
int size = errorMap.size();
if (size == 0) {
return;
}
List<Throwable> errors;
StringBuilder msg = new StringBuilder("Job failed on ");
Stream<Map.Entry<Throwable, List<String>>> entryStream = errorMap.entrySet().stream().filter(e -> e.getValue() != null);
if (size < 10) {
errors = new ArrayList<>(errorMap.keySet());
Map<String, List<List<String>>> errorTypes = entryStream
.collect(Collectors.groupingBy(e -> e.getKey().getClass().getSimpleName(), mapping(Map.Entry::getValue, toList())));
errorTypes.forEach((key, value) -> msg.append(key).append(':').append(value.size()).append(':')
.append(value.stream().flatMap(List::stream).collect(Collectors.joining(","))).append(";\n"));
} else {
errors = List.of(errorMap.keySet().iterator().next());
Map<String, Long> errorTypes = entryStream
.collect(Collectors.groupingBy(e -> e.getKey().getClass().getSimpleName(), Collectors.counting()));
errorTypes.forEach((key, value) -> msg.append(key).append(':').append(value).append(";\n"));
}
throw new JobExecutionException(msg.toString(), errors);
}
}
@@ -18,7 +18,9 @@
*/
package org.apache.fineract.infrastructure.jobs.service;
import jakarta.validation.constraints.NotNull;
import java.util.List;
import org.apache.fineract.infrastructure.core.api.IdTypeResolver;
import org.apache.fineract.infrastructure.core.service.Page;
import org.apache.fineract.infrastructure.core.service.SearchParameters;
import org.apache.fineract.infrastructure.jobs.data.JobDetailData;
@@ -26,14 +28,15 @@ import org.apache.fineract.infrastructure.jobs.data.JobDetailHistoryData;
public interface SchedulerJobRunnerReadService {
List<JobDetailData> findAllJobDeatils();
List<JobDetailData> findAllJobDetails();
JobDetailData retrieveOne(Long jobId);
JobDetailData retrieveOne(@NotNull IdTypeResolver.IdType idType, String identifier);
JobDetailData retrieveOneByName(String jobName);
Page<JobDetailHistoryData> retrieveJobHistory(@NotNull IdTypeResolver.IdType idType, String identifier,
SearchParameters searchParameters);
Page<JobDetailHistoryData> retrieveJobHistory(Long jobId, SearchParameters searchParameters);
@NotNull
Long retrieveId(@NotNull IdTypeResolver.IdType idType, String identifier);
boolean isUpdatesAllowed();
}