Repository: kestra-io/kestra (java)

Issue:
Purge Logs fails for MYSQL backend
### Describe the issue

Sample flow
```
id: purgelogs
namespace: demo
tasks:
 - id: purge_logs
   type: io.kestra.plugin.core.log.PurgeLogs
   endDate: "{{ now() |dateAdd(-1, 'MONTHS')}}"
   logLevels:
    - TRACE
    - DEBUG
```

With MYSQL backend, this fails with the following error
```
2024-08-08 10:07:10.733Error while writing value at bind index: 4
 Type class org.slf4j.event.Level is not supported in dialect MYSQL_8_0_19
2024-08-08 10:07:10.733SQL [null]; Error while writing value at bind index: 4
2024-08-08 10:07:10.733org.jooq.exception.DataAccessException: SQL [null]; Error while writing value at bind index: 4
 at org.jooq_3.19.10.MYSQL_8_0_19.debug(Unknown Source)
 at org.jooq.impl.Tools.translate(Tools.java:3607)
 at org.jooq.impl.Tools.translate(Tools.java:3595)
 at org.jooq.impl.AbstractBindContext.bindValue(AbstractBindContext.java:77)
 at org.jooq.impl.Val.accept(Val.java:231)
 at org.jooq.impl.AbstractBindContext.bindInternal(AbstractBindContext.java:258)
 at org.jooq.impl.AbstractBindContext.visit0(AbstractBindContext.java:68)
 at org.jooq.impl.AbstractContext.visit(AbstractContext.java:381)
 at org.jooq.impl.QueryPartCollectionView.acceptElement(QueryPartCollectionView.java:256)
 at org.jooq.impl.QueryPartCollectionView.accept(QueryPartCollectionView.java:226)
 at org.jooq.impl.AbstractBindContext.bindInternal(AbstractBindContext.java:258)
 at org.jooq.impl.AbstractBindContext.visit0(AbstractBindContext.java:68)
 at org.jooq.impl.AbstractContext.visit(AbstractContext.java:381)
 at org.jooq.impl.AbstractQuery.execute(AbstractQuery.java:344)
 at org.jooq.impl.AbstractDelegatingQuery.execute(AbstractDelegatingQuery.java:115)
 at io.kestra.jdbc.repository.AbstractJdbcLogRepository.lambda$deleteByQuery$9(AbstractJdbcLogRepository.java:447)
 at org.jooq.impl.DefaultDSLContext.lambda$transactionResult0$3(DefaultDSLContext.java:530)
 at org.jooq.impl.Tools$3$1.block(Tools.java:6325)
 at java.base/java.util.concurrent.ForkJoinPool.unmanagedBlock(Unknown Source)
 at java.base/java.util.concurrent.ForkJoinPool.managedBlock(Unknown Source)
 at org.jooq.impl.Tools$3.get(Tools.java:6322)
 at org.jooq.impl.DefaultDSLContext.transactionResult0(DefaultDSLContext.java:578)
 at org.jooq.impl.DefaultDSLContext.transactionResult(DefaultDSLContext.java:502)
 at io.kestra.jdbc.JooqDSLContextWrapper.lambda$transactionResult$2(JooqDSLContextWrapper.java:67)
 at dev.failsafe.Functions.lambda$toCtxSupplier$11(Functions.java:243)
 at dev.failsafe.Functions.lambda$get$0(Functions.java:46)
 at dev.failsafe.internal.RetryPolicyExecutor.lambda$apply$0(RetryPolicyExecutor.java:74)
 at dev.failsafe.internal.FallbackExecutor.lambda$apply$0(FallbackExecutor.java:51)
 at dev.failsafe.SyncExecutionImpl.executeSync(SyncExecutionImpl.java:187)
 at dev.failsafe.FailsafeExecutor.call(FailsafeExecutor.java:376)
 at dev.failsafe.FailsafeExecutor.get(FailsafeExecutor.java:112)
 at io.kestra.core.utils.RetryUtils$Instance.wrap(RetryUtils.java:144)
 at io.kestra.core.utils.RetryUtils$Instance.runRetryIf(RetryUtils.java:103)
 at io.kestra.jdbc.JooqDSLContextWrapper.transactionResult(JooqDSLContextWrapper.java:65)
 at io.kestra.jdbc.repository.AbstractJdbcLogRepository.deleteByQuery(AbstractJdbcLogRepository.java:423)
 at io.kestra.core.services.LogService.purge(LogService.java:79)
 at io.kestra.plugin.core.log.PurgeLogs.run(PurgeLogs.java:104)
 at io.kestra.plugin.core.log.PurgeLogs.run(PurgeLogs.java:24)
 at io.kestra.core.runners.WorkerTaskThread.doRun(WorkerTaskThread.java:76)
 at io.kestra.core.runners.AbstractWorkerThread.run(AbstractWorkerThread.java:57)
 Caused by: java.sql.SQLException: Error while writing value at bind index: 4
 at org.jooq.impl.DefaultBindContext.bindValue0(DefaultBindContext.java:85)
 at org.jooq.impl.AbstractBindContext.bindValue(AbstractBindContext.java:74)
 … 36 more
 Caused by: org.jooq.exception.SQLDialectNotSupportedException: Type class org.slf4j.event.Level is not supported in dialect MYSQL_8_0_19
 at org.jooq.impl.DefaultDataType.getDataType0(DefaultDataType.java:957)
 at org.jooq.impl.DefaultDataType.getDataType(DefaultDataType.java:837)
 at org.jooq.impl.DefaultDataType.getDataType(DefaultDataType.java:830)
 at org.jooq.impl.DefaultBinding$DefaultOtherBinding.set0(DefaultBinding.java:3803)
 at org.jooq.impl.DefaultBinding$InternalBinding.set(DefaultBinding.java:1114)
 at org.jooq.impl.DefaultBindContext.bindValue0(DefaultBindContext.java:65)
```

### Environment

- Kestra Version: v0.18
- Operating System: Docker
- MySQL backend: 8.0.30


Pull request:
fix(core,jdbc): PurgeLog with levels in postgres
Fixes #4604

Patch:
diff --git a/core/src/main/java/io/kestra/core/models/executions/LogEntry.java b/core/src/main/java/io/kestra/core/models/executions/LogEntry.java
index f45b99785..2029c03a8 100644
--- a/core/src/main/java/io/kestra/core/models/executions/LogEntry.java
+++ b/core/src/main/java/io/kestra/core/models/executions/LogEntry.java
@@ -61,14 +61,13 @@ public class LogEntry implements DeletedInterface, TenantInterface {
     @Builder.Default
     boolean deleted = false;
 
-    public static List<String> findLevelsByMin(Level minLevel) {
+    public static List<Level> findLevelsByMin(Level minLevel) {
         if (minLevel == null) {
-            return Arrays.stream(Level.values()).map(Enum::name).toList();
+            return Arrays.asList(Level.values());
         }
 
         return Arrays.stream(Level.values())
             .filter(level -> level.toInt() >= minLevel.toInt())
-            .map(Enum::name)
             .toList();
     }
 
diff --git a/core/src/main/java/io/kestra/plugin/core/storage/LocalFiles.java b/core/src/main/java/io/kestra/plugin/core/storage/LocalFiles.java
index 157cf106c..1e442b612 100644
--- a/core/src/main/java/io/kestra/plugin/core/storage/LocalFiles.java
+++ b/core/src/main/java/io/kestra/plugin/core/storage/LocalFiles.java
@@ -22,8 +22,8 @@ import java.util.Map;
 @Getter
 @NoArgsConstructor
 @Schema(
-    title = "Allow to create files in the local filesystem or to send files from the local filesystem to the Kestra's internal storage.",
-    description = "Replaced by `inputFiles` property on the WorkingDirectory task - migration information [here](https://kestra.io/docs/migration-guide/local-files). Previously, this task should be used with the WorkingDirectory task to be able to access the same local filesystem within multiple tasks. Note that this task cannot be skipped, so setting `disabled: true` will not work on this task."
+    title = "This task is deprecated and replaced by `inputFiles` property available in all script tasks and in the [WorkingDirectory](https://kestra.io/plugins/core/tasks/io.kestra.plugin.core.flow.workingdirectory) task. Check the [migration guide](https://kestra.io/docs/migration-guide/0.17.0/local-files) for more details. ",
+    description = "This task was intended to be used along with the `WorkingDirectory` task to create temporary files. This task suffers from multiple limitations e.g. it cannot be skipped, so setting `disabled: true` will have no effect. Overall, the WorkingDirectory task is more flexible and should be used instead of this task. This task will be removed in a future version of Kestra."
 )
 @Deprecated
 @Plugin(examples = {
diff --git a/jdbc-postgres/src/main/java/io/kestra/repository/postgres/PostgresLogRepository.java b/jdbc-postgres/src/main/java/io/kestra/repository/postgres/PostgresLogRepository.java
index 7d5d4bf80..e58e2597a 100644
--- a/jdbc-postgres/src/main/java/io/kestra/repository/postgres/PostgresLogRepository.java
+++ b/jdbc-postgres/src/main/java/io/kestra/repository/postgres/PostgresLogRepository.java
@@ -10,6 +10,7 @@ import org.jooq.impl.DSL;
 import org.slf4j.event.Level;
 
 import java.util.Collections;
+import java.util.List;
 import java.util.stream.Collectors;
 
 
@@ -27,10 +28,9 @@ public class PostgresLogRepository extends AbstractJdbcLogRepository {
     }
 
     @Override
-    protected Condition minLevel(Level minLevel) {
+    protected Condition levelsCondition(List<Level> levels) {
         return DSL.condition("level in (" +
-            LogEntry
-                .findLevelsByMin(minLevel)
+            levels
                 .stream()
                 .map(s -> "'" + s + "'::log_level")
                 .collect(Collectors.joining(", ")) +
diff --git a/jdbc/src/main/java/io/kestra/jdbc/repository/AbstractJdbcLogRepository.java b/jdbc/src/main/java/io/kestra/jdbc/repository/AbstractJdbcLogRepository.java
index a834bb4e8..c17238017 100644
--- a/jdbc/src/main/java/io/kestra/jdbc/repository/AbstractJdbcLogRepository.java
+++ b/jdbc/src/main/java/io/kestra/jdbc/repository/AbstractJdbcLogRepository.java
@@ -441,7 +441,7 @@ public abstract class AbstractJdbcLogRepository extends AbstractJdbcRepository i
                 }
 
                 if (logLevels != null) {
-                    delete = delete.and(field("level").in(logLevels));
+                    delete = delete.and(levelsCondition(logLevels));
                 }
 
                 return delete.execute();
@@ -493,7 +493,11 @@ public abstract class AbstractJdbcLogRepository extends AbstractJdbcRepository i
             });
     }
 
-    protected Condition minLevel(Level minLevel) {
-        return field("level").in(LogEntry.findLevelsByMin(minLevel));
+    private Condition minLevel(Level minLevel) {
+        return levelsCondition(LogEntry.findLevelsByMin(minLevel));
+    }
+
+    protected Condition levelsCondition(List<Level> levels) {
+        return field("level").in(levels.stream().map(level -> level.name()).toList());
     }
 }
diff --git a/webserver/src/main/java/io/kestra/webserver/controllers/api/LogController.java b/webserver/src/main/java/io/kestra/webserver/controllers/api/LogController.java
index ec2105713..027318c06 100644
--- a/webserver/src/main/java/io/kestra/webserver/controllers/api/LogController.java
+++ b/webserver/src/main/java/io/kestra/webserver/controllers/api/LogController.java
@@ -126,7 +126,7 @@ public class LogController {
         @Parameter(description = "The min log level filter") @Nullable @QueryValue Level minLevel
     ) {
         AtomicReference<Runnable> cancel = new AtomicReference<>();
-        List<String> levels = LogEntry.findLevelsByMin(minLevel);
+        List<String> levels = LogEntry.findLevelsByMin(minLevel).stream().map(level -> level.name()).toList();
 
         return Flux
             .<Event<LogEntry>>create(emitter -> {


Test patch:
diff --git a/core/src/test/java/io/kestra/core/repositories/AbstractLogRepositoryTest.java b/core/src/test/java/io/kestra/core/repositories/AbstractLogRepositoryTest.java
index 7581ff18a..de7a8f4bf 100644
--- a/core/src/test/java/io/kestra/core/repositories/AbstractLogRepositoryTest.java
+++ b/core/src/test/java/io/kestra/core/repositories/AbstractLogRepositoryTest.java
@@ -149,7 +149,7 @@ public abstract class AbstractLogRepositoryTest {
 
         logRepository.save(log1);
 
-        logRepository.deleteByQuery(null, "io.kestra.unittest", "flowId", null, null, ZonedDateTime.now().plusMinutes(1));
+        logRepository.deleteByQuery(null, "io.kestra.unittest", "flowId", List.of(Level.TRACE, Level.DEBUG, Level.INFO), null, ZonedDateTime.now().plusMinutes(1));
 
         find = logRepository.findByExecutionId(null, log1.getExecutionId(), null, Pageable.from(1, 50));
         assertThat(find.size(), is(0));