From c959568583ef6164f873d03756585127fe84b413 Mon Sep 17 00:00:00 2001 From: Antonio Perez Dieppa Date: Wed, 11 Feb 2026 18:52:26 +0000 Subject: [PATCH] refactor: remove cli modules --- cli/flamingock-cli-executor/build.gradle.kts | 91 ---- .../cli/executor/FlamingockExecutorCli.java | 105 ---- .../cli/executor/command/ApplyCommand.java | 167 ------ .../cli/executor/command/AuditCommand.java | 46 -- .../cli/executor/command/ExecuteCommand.java | 52 -- .../cli/executor/command/FixCommand.java | 150 ------ .../cli/executor/command/GetIssueCommand.java | 166 ------ .../cli/executor/command/IssueCommand.java | 45 -- .../cli/executor/command/ListCommand.java | 202 -------- .../executor/command/ListIssueCommand.java | 147 ------ .../handler/ExecutorExceptionHandler.java | 60 --- .../orchestration/CommandExecutor.java | 129 ----- .../executor/orchestration/CommandResult.java | 336 ------------- .../orchestration/ExecutionOptions.java | 138 ----- .../cli/executor/output/ConsoleFormatter.java | 94 ---- .../output/ExecutionResultFormatter.java | 236 --------- .../cli/executor/output/IssueFormatter.java | 195 ------- .../cli/executor/output/JsonFormatter.java | 74 --- .../cli/executor/output/TableColumn.java | 181 ------- .../cli/executor/output/TableFormatter.java | 317 ------------ .../process/JarDetectionException.java | 41 -- .../cli/executor/process/JarType.java | 43 -- .../cli/executor/process/JarTypeDetector.java | 133 ----- .../cli/executor/process/JvmLauncher.java | 476 ------------------ .../cli/executor/process/LaunchResult.java | 154 ------ .../cli/executor/process/LaunchStatus.java | 61 --- .../executor/result/ResponseResultReader.java | 176 ------- .../cli/executor/util/VersionProvider.java | 91 ---- .../executor/process/JarTypeDetectorTest.java | 254 ---------- .../cli/executor/process/JvmLauncherTest.java | 277 ---------- .../result/ResponseResultReaderTest.java | 168 ------- cli/flamingock-cli/CLI-README.md | 250 --------- cli/flamingock-cli/CLI_SPECIFICATION.md | 96 ---- cli/flamingock-cli/DEVELOPMENT.md | 314 ------------ cli/flamingock-cli/README.md | 267 ---------- cli/flamingock-cli/TEST_INTEGRATION.md | 103 ---- cli/flamingock-cli/build.gradle.kts | 273 ---------- .../src/dist/flamingock-cli.yml | 36 -- .../java/io/flamingock/cli/FlamingockCli.java | 86 ---- .../cli/command/audit/AuditCommand.java | 38 -- .../cli/command/audit/FixCommand.java | 93 ---- .../cli/command/audit/ListCommand.java | 135 ----- .../cli/command/issue/GetIssueCommand.java | 222 -------- .../cli/command/issue/IssueCommand.java | 38 -- .../cli/command/issue/ListIssueCommand.java | 95 ---- .../flamingock/cli/config/ConfigLoader.java | 159 ------ .../flamingock/cli/config/DatabaseConfig.java | 337 ------------- .../cli/config/FlamingockConfig.java | 37 -- .../cli/factory/CouchbaseClusterFactory.java | 55 -- .../cli/factory/DynamoDBClientFactory.java | 70 --- .../cli/factory/MongoClientFactory.java | 95 ---- .../cli/factory/SqlDataSourceFactory.java | 81 --- .../cli/handler/CliExceptionHandler.java | 172 ------- .../flamingock/cli/logging/CliLogLevel.java | 60 --- .../cli/logging/CliLoggerFactory.java | 74 --- .../flamingock/cli/logging/CliLoggerImpl.java | 447 ---------------- .../cli/logging/CliSLF4JLoggerFactory.java | 57 --- .../cli/logging/CliSLF4JServiceProvider.java | 70 --- .../flamingock/cli/logging/LoggingMixin.java | 73 --- .../io/flamingock/cli/logging/NoOpLogger.java | 355 ------------- .../flamingock/cli/service/AuditService.java | 198 -------- .../flamingock/cli/service/JsonFormatter.java | 115 ----- .../flamingock/cli/service/TableColumn.java | 105 ---- .../cli/service/TableFormatter.java | 286 ----------- .../io/flamingock/cli/util/ASCIIColors.java | 27 - .../org.slf4j.spi.SLF4JServiceProvider | 1 - .../java/io/flamingock/cli/SimpleCLITest.java | 98 ---- .../cli/config/SimpleConfigLoaderTest.java | 203 -------- .../integration/CLICommandExecutionTest.java | 164 ------ .../CLICouchbaseIntegrationTest.java | 151 ------ .../integration/CLIDynamoIntegrationTest.java | 142 ------ .../integration/CLIMongoIntegrationTest.java | 145 ------ .../integration/CLISqlIntegrationTest.java | 205 -------- .../io/flamingock/cli/test/TestUtils.java | 180 ------- .../api/annotations/Categories.java | 32 -- .../api/annotations/ChangeTemplate.java | 34 -- .../io/flamingock/api/annotations/Stage.java | 2 - .../external/{targets => }/TargetSystem.java | 4 +- .../flamingock/api/task/ChangeCategory.java | 20 - .../api/task/ChangeCategoryAware.java | 20 - core/flamingock-core-commons/build.gradle.kts | 2 +- core/flamingock-core/build.gradle.kts | 2 +- .../core/builder/AbstractBuilder.java | 2 +- .../builder/AbstractChangeRunnerBuilder.java | 2 +- .../configuration/core/CoreConfigurator.java | 2 +- .../targets/AbstractTargetSystem.java | 2 +- .../external/targets/TargetSystemManager.java | 2 +- .../targets/operations/TargetSystemOps.java | 2 +- .../core/task/loaded/AbstractLoadedTask.java | 2 - .../core/task/loaded/CodeLoadedChange.java | 1 - core/flamingock-graalvm/build.gradle.kts | 1 - core/flamingock-processor/build.gradle.kts | 6 +- .../couchbase/CouchbaseTargetSystemTest.java | 2 +- .../MongoDBSpringDataTargetSystemTest.java | 2 +- .../sync/MongoDBSyncTargetSystemTest.java | 2 +- legacy/mongock-support/build.gradle.kts | 4 +- .../build.gradle.kts | 1 - .../FlamingockAutoConfiguration.java | 2 +- .../FlamingockAutoConfigurationTests.java | 2 +- .../build.gradle.kts | 1 - .../FlamingockTestConfiguration.java | 2 +- settings.gradle.kts | 10 - utils/general-util/build.gradle.kts | 2 +- 103 files changed, 21 insertions(+), 11160 deletions(-) delete mode 100644 cli/flamingock-cli-executor/build.gradle.kts delete mode 100644 cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/FlamingockExecutorCli.java delete mode 100644 cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/command/ApplyCommand.java delete mode 100644 cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/command/AuditCommand.java delete mode 100644 cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/command/ExecuteCommand.java delete mode 100644 cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/command/FixCommand.java delete mode 100644 cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/command/GetIssueCommand.java delete mode 100644 cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/command/IssueCommand.java delete mode 100644 cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/command/ListCommand.java delete mode 100644 cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/command/ListIssueCommand.java delete mode 100644 cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/handler/ExecutorExceptionHandler.java delete mode 100644 cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/orchestration/CommandExecutor.java delete mode 100644 cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/orchestration/CommandResult.java delete mode 100644 cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/orchestration/ExecutionOptions.java delete mode 100644 cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/output/ConsoleFormatter.java delete mode 100644 cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/output/ExecutionResultFormatter.java delete mode 100644 cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/output/IssueFormatter.java delete mode 100644 cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/output/JsonFormatter.java delete mode 100644 cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/output/TableColumn.java delete mode 100644 cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/output/TableFormatter.java delete mode 100644 cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/process/JarDetectionException.java delete mode 100644 cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/process/JarType.java delete mode 100644 cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/process/JarTypeDetector.java delete mode 100644 cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/process/JvmLauncher.java delete mode 100644 cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/process/LaunchResult.java delete mode 100644 cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/process/LaunchStatus.java delete mode 100644 cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/result/ResponseResultReader.java delete mode 100644 cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/util/VersionProvider.java delete mode 100644 cli/flamingock-cli-executor/src/test/java/io/flamingock/cli/executor/process/JarTypeDetectorTest.java delete mode 100644 cli/flamingock-cli-executor/src/test/java/io/flamingock/cli/executor/process/JvmLauncherTest.java delete mode 100644 cli/flamingock-cli-executor/src/test/java/io/flamingock/cli/executor/result/ResponseResultReaderTest.java delete mode 100644 cli/flamingock-cli/CLI-README.md delete mode 100644 cli/flamingock-cli/CLI_SPECIFICATION.md delete mode 100644 cli/flamingock-cli/DEVELOPMENT.md delete mode 100644 cli/flamingock-cli/README.md delete mode 100644 cli/flamingock-cli/TEST_INTEGRATION.md delete mode 100644 cli/flamingock-cli/build.gradle.kts delete mode 100644 cli/flamingock-cli/src/dist/flamingock-cli.yml delete mode 100644 cli/flamingock-cli/src/main/java/io/flamingock/cli/FlamingockCli.java delete mode 100644 cli/flamingock-cli/src/main/java/io/flamingock/cli/command/audit/AuditCommand.java delete mode 100644 cli/flamingock-cli/src/main/java/io/flamingock/cli/command/audit/FixCommand.java delete mode 100644 cli/flamingock-cli/src/main/java/io/flamingock/cli/command/audit/ListCommand.java delete mode 100644 cli/flamingock-cli/src/main/java/io/flamingock/cli/command/issue/GetIssueCommand.java delete mode 100644 cli/flamingock-cli/src/main/java/io/flamingock/cli/command/issue/IssueCommand.java delete mode 100644 cli/flamingock-cli/src/main/java/io/flamingock/cli/command/issue/ListIssueCommand.java delete mode 100644 cli/flamingock-cli/src/main/java/io/flamingock/cli/config/ConfigLoader.java delete mode 100644 cli/flamingock-cli/src/main/java/io/flamingock/cli/config/DatabaseConfig.java delete mode 100644 cli/flamingock-cli/src/main/java/io/flamingock/cli/config/FlamingockConfig.java delete mode 100644 cli/flamingock-cli/src/main/java/io/flamingock/cli/factory/CouchbaseClusterFactory.java delete mode 100644 cli/flamingock-cli/src/main/java/io/flamingock/cli/factory/DynamoDBClientFactory.java delete mode 100644 cli/flamingock-cli/src/main/java/io/flamingock/cli/factory/MongoClientFactory.java delete mode 100644 cli/flamingock-cli/src/main/java/io/flamingock/cli/factory/SqlDataSourceFactory.java delete mode 100644 cli/flamingock-cli/src/main/java/io/flamingock/cli/handler/CliExceptionHandler.java delete mode 100644 cli/flamingock-cli/src/main/java/io/flamingock/cli/logging/CliLogLevel.java delete mode 100644 cli/flamingock-cli/src/main/java/io/flamingock/cli/logging/CliLoggerFactory.java delete mode 100644 cli/flamingock-cli/src/main/java/io/flamingock/cli/logging/CliLoggerImpl.java delete mode 100644 cli/flamingock-cli/src/main/java/io/flamingock/cli/logging/CliSLF4JLoggerFactory.java delete mode 100644 cli/flamingock-cli/src/main/java/io/flamingock/cli/logging/CliSLF4JServiceProvider.java delete mode 100644 cli/flamingock-cli/src/main/java/io/flamingock/cli/logging/LoggingMixin.java delete mode 100644 cli/flamingock-cli/src/main/java/io/flamingock/cli/logging/NoOpLogger.java delete mode 100644 cli/flamingock-cli/src/main/java/io/flamingock/cli/service/AuditService.java delete mode 100644 cli/flamingock-cli/src/main/java/io/flamingock/cli/service/JsonFormatter.java delete mode 100644 cli/flamingock-cli/src/main/java/io/flamingock/cli/service/TableColumn.java delete mode 100644 cli/flamingock-cli/src/main/java/io/flamingock/cli/service/TableFormatter.java delete mode 100644 cli/flamingock-cli/src/main/java/io/flamingock/cli/util/ASCIIColors.java delete mode 100644 cli/flamingock-cli/src/main/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider delete mode 100644 cli/flamingock-cli/src/test/java/io/flamingock/cli/SimpleCLITest.java delete mode 100644 cli/flamingock-cli/src/test/java/io/flamingock/cli/config/SimpleConfigLoaderTest.java delete mode 100644 cli/flamingock-cli/src/test/java/io/flamingock/cli/integration/CLICommandExecutionTest.java delete mode 100644 cli/flamingock-cli/src/test/java/io/flamingock/cli/integration/CLICouchbaseIntegrationTest.java delete mode 100644 cli/flamingock-cli/src/test/java/io/flamingock/cli/integration/CLIDynamoIntegrationTest.java delete mode 100644 cli/flamingock-cli/src/test/java/io/flamingock/cli/integration/CLIMongoIntegrationTest.java delete mode 100644 cli/flamingock-cli/src/test/java/io/flamingock/cli/integration/CLISqlIntegrationTest.java delete mode 100644 cli/flamingock-cli/src/test/java/io/flamingock/cli/test/TestUtils.java delete mode 100644 core/flamingock-core-api/src/main/java/io/flamingock/api/annotations/Categories.java delete mode 100644 core/flamingock-core-api/src/main/java/io/flamingock/api/annotations/ChangeTemplate.java rename core/flamingock-core-api/src/main/java/io/flamingock/api/external/{targets => }/TargetSystem.java (87%) delete mode 100644 core/flamingock-core-api/src/main/java/io/flamingock/api/task/ChangeCategory.java delete mode 100644 core/flamingock-core-api/src/main/java/io/flamingock/api/task/ChangeCategoryAware.java diff --git a/cli/flamingock-cli-executor/build.gradle.kts b/cli/flamingock-cli-executor/build.gradle.kts deleted file mode 100644 index 5aa5c0513..000000000 --- a/cli/flamingock-cli-executor/build.gradle.kts +++ /dev/null @@ -1,91 +0,0 @@ -plugins { - `java-library` - `maven-publish` -} - -description = "Flamingock CLI for executing changes in applications" - -val jacksonVersion = "2.16.0" - -dependencies { - // CLI Framework - implementation("info.picocli:picocli:4.7.5") - annotationProcessor("info.picocli:picocli-codegen:4.7.5") - - // Core dependencies for response handling - implementation(project(":core:flamingock-core-commons")) - implementation(project(":utils:general-util")) - implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jacksonVersion") - - // Test dependencies - testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.2") - testImplementation("org.junit.jupiter:junit-jupiter-engine:5.9.2") - testImplementation("org.mockito:mockito-core:4.11.0") - testImplementation("org.assertj:assertj-core:3.24.2") -} - -java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 -} - -// Create UberJar with all dependencies -val uberJar by tasks.registering(Jar::class) { - group = "build" - description = "Create a self-contained JAR with all dependencies" - - archiveBaseName.set("flamingock-cli-executor") - archiveClassifier.set("uber") - archiveVersion.set(project.version.toString()) - isZip64 = true - - duplicatesStrategy = DuplicatesStrategy.EXCLUDE - - manifest { - attributes["Main-Class"] = "io.flamingock.cli.executor.FlamingockExecutorCli" - attributes["Implementation-Title"] = project.name - attributes["Implementation-Version"] = project.version - } - - from(sourceSets.main.get().output) - dependsOn(configurations.runtimeClasspath) - from({ - configurations.runtimeClasspath.get().filter { it.name.endsWith("jar") }.map { zipTree(it) } - }) { - exclude("META-INF/*.SF", "META-INF/*.DSA", "META-INF/*.RSA") - } -} - -// Test configuration -tasks.test { - useJUnitPlatform() - testLogging { - events("passed", "skipped", "failed") - exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL - showStandardStreams = false - } -} - -// Add uber jar as additional artifact to existing maven publication -afterEvaluate { - publishing { - publications { - named("maven") { - // Add the uber jar as additional artifact - artifact(uberJar.get()) { - classifier = "uber" - } - - // Override description for CLI executor module - pom { - name.set("Flamingock CLI") - description.set("Flamingock CLI for executing changes in applications") - } - } - } - } -} - -tasks.named("assemble").configure { - dependsOn(uberJar) -} diff --git a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/FlamingockExecutorCli.java b/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/FlamingockExecutorCli.java deleted file mode 100644 index 7cc047c15..000000000 --- a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/FlamingockExecutorCli.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.executor; - -import io.flamingock.cli.executor.command.AuditCommand; -import io.flamingock.cli.executor.command.ExecuteCommand; -import io.flamingock.cli.executor.command.IssueCommand; -import io.flamingock.cli.executor.handler.ExecutorExceptionHandler; -import io.flamingock.cli.executor.util.VersionProvider; -import picocli.CommandLine; -import picocli.CommandLine.Command; -import picocli.CommandLine.Option; - -import java.util.Optional; - -/** - * Main entry point for the Flamingock CLI. - * - *

This CLI tool spawns the user's application with Flamingock - * and executes changes, returning the result via exit codes.

- * - *

Usage: flamingock execute apply --jar ./app.jar

- */ -@Command( - name = "flamingock", - description = "Flamingock CLI - Execute Flamingock changes", - header = { - "@|bold,cyan Flamingock CLI|@ - Execute Flamingock changes", - "" - }, - footer = { - "", - "@|bold Examples:|@", - " flamingock execute apply --jar ./app.jar", - " flamingock audit list --jar ./app.jar", - " flamingock audit fix --jar ./app.jar -c my-change-id -r APPLIED", - " flamingock issue list --jar ./app.jar", - " flamingock issue get --jar ./app.jar -c my-change-id --guidance", - " flamingock --log-level=debug execute apply --jar ./my-app.jar", - "", - "For detailed help on any command, use: flamingock --help" - }, - subcommands = {ExecuteCommand.class, AuditCommand.class, IssueCommand.class}, - mixinStandardHelpOptions = true, - versionProvider = VersionProvider.class -) -public class FlamingockExecutorCli implements Runnable { - - @Option(names = {"--log-level", "-l"}, - description = "Application log level: debug, info, warn, error", - scope = CommandLine.ScopeType.INHERIT) - private String logLevel; - - @Option(names = {"--quiet", "-q"}, - description = "Suppress non-essential output", - scope = CommandLine.ScopeType.INHERIT) - private boolean quiet; - - @Option(names = {"--no-color"}, - description = "Disable colored output", - scope = CommandLine.ScopeType.INHERIT) - private boolean noColor; - - public static void main(String[] args) { - FlamingockExecutorCli cli = new FlamingockExecutorCli(); - CommandLine cmd = new CommandLine(cli); - - // Use custom exception handler for better error messages - cmd.setExecutionExceptionHandler(new ExecutorExceptionHandler()); - - int exitCode = cmd.execute(args); - System.exit(exitCode); - } - - @Override - public void run() { - // This runs when no subcommand is specified - show help - new CommandLine(this).usage(System.out); - } - - public Optional getLogLevel() { - return Optional.ofNullable(logLevel); - } - - public boolean isQuiet() { - return quiet; - } - - public boolean isNoColor() { - return noColor; - } -} diff --git a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/command/ApplyCommand.java b/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/command/ApplyCommand.java deleted file mode 100644 index 88c96a383..000000000 --- a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/command/ApplyCommand.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.executor.command; - -import io.flamingock.cli.executor.FlamingockExecutorCli; -import io.flamingock.cli.executor.orchestration.CommandExecutor; -import io.flamingock.cli.executor.orchestration.CommandResult; -import io.flamingock.cli.executor.orchestration.ExecutionOptions; -import io.flamingock.cli.executor.output.ConsoleFormatter; -import io.flamingock.cli.executor.output.ExecutionResultFormatter; -import io.flamingock.cli.executor.util.VersionProvider; -import io.flamingock.internal.common.core.operation.OperationType; -import io.flamingock.internal.common.core.response.data.ExecuteResponseData; -import picocli.CommandLine; -import picocli.CommandLine.Command; -import picocli.CommandLine.Option; -import picocli.CommandLine.ParentCommand; - -import java.io.File; -import java.util.Optional; -import java.util.concurrent.Callable; - -/** - * Command to apply pending Flamingock changes. - * - *

This command spawns the user's application JAR with special flags - * that enable CLI mode in Flamingock, executes all pending changes, - * and returns the result via exit code.

- * - *

Exit codes:

- *
    - *
  • 0 - Success (all changes applied)
  • - *
  • 1 - Failure (execution error or change failed)
  • - *
  • 2 - Usage error (invalid CLI arguments)
  • - *
  • 126 - JAR not found
  • - *
- */ -@Command( - name = "apply", - description = "Apply pending Flamingock changes", - mixinStandardHelpOptions = true -) -public class ApplyCommand implements Callable { - - /** - * Exit code when JAR file is not found. - */ - public static final int EXIT_JAR_NOT_FOUND = 126; - - @ParentCommand - private ExecuteCommand parent; - - @Option(names = {"--jar", "-j"}, - description = "Path to the application JAR", - required = true) - private File jarFile; - - private final CommandExecutor commandExecutor; - - /** - * Creates a new ApplyCommand with default dependencies. - */ - public ApplyCommand() { - this(new CommandExecutor()); - } - - /** - * Creates a new ApplyCommand with the specified CommandExecutor. - * - * @param commandExecutor the command executor to use - */ - public ApplyCommand(CommandExecutor commandExecutor) { - this.commandExecutor = commandExecutor; - } - - @Override - public Integer call() { - FlamingockExecutorCli root = getRootCommand(); - boolean quiet = root != null && root.isQuiet(); - Optional logLevel = root != null ? root.getLogLevel() : Optional.empty(); - - // Print header unless quiet mode - if (!quiet) { - ConsoleFormatter.printHeader(VersionProvider.getVersionString()); - } - - // Validate JAR exists - if (!jarFile.exists()) { - ConsoleFormatter.printError("JAR file not found: " + jarFile.getAbsolutePath()); - return EXIT_JAR_NOT_FOUND; - } - - if (!jarFile.isFile()) { - ConsoleFormatter.printError("Path is not a file: " + jarFile.getAbsolutePath()); - return EXIT_JAR_NOT_FOUND; - } - - // Execution ops: always stream output by default - ExecutionOptions options = ExecutionOptions.builder() - .logLevel(logLevel.orElse(null)) - .streamOutput(true) - .build(); - - CommandResult result = commandExecutor.execute( - jarFile.getAbsolutePath(), - OperationType.EXECUTE_APPLY, - ExecuteResponseData.class, - options - ); - - if (result.isSuccess()) { - if (!quiet) { - // Print detailed execution summary - ExecuteResponseData data = result.getData(); - if (data != null) { - ExecutionResultFormatter.print(data); - } else { - ConsoleFormatter.printSuccess(result.getDurationMs()); - } - } - return 0; - } else { - // Print execution summary if available (even on failure, shows what was applied) - if (!quiet && result.getData() != null) { - ExecutionResultFormatter.print(result.getData()); - } - ConsoleFormatter.printFailure(result.getErrorCode(), result.getErrorMessage()); - return result.getExitCode(); - } - } - - private FlamingockExecutorCli getRootCommand() { - if (parent == null) { - return null; - } - // Navigate up the command hierarchy to find the root - CommandLine.Model.CommandSpec spec = parent.getClass().getAnnotation(Command.class) != null - ? CommandLine.Model.CommandSpec.forAnnotatedObject(parent) - : null; - - // Try to get the parent directly through field access - try { - java.lang.reflect.Field parentField = ExecuteCommand.class.getDeclaredField("parent"); - parentField.setAccessible(true); - Object grandParent = parentField.get(parent); - if (grandParent instanceof FlamingockExecutorCli) { - return (FlamingockExecutorCli) grandParent; - } - } catch (Exception e) { - // Fall through - } - return null; - } -} diff --git a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/command/AuditCommand.java b/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/command/AuditCommand.java deleted file mode 100644 index 7893d2a37..000000000 --- a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/command/AuditCommand.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.executor.command; - -import picocli.CommandLine; -import picocli.CommandLine.Command; - -/** - * Parent command for audit operations. - * - *

Groups subcommands related to audit inspection:

- *
    - *
  • {@code list} - List audit entries
  • - *
  • {@code fix} - Fix audit state for a change with issues
  • - *
- */ -@Command( - name = "audit", - description = "Audit operations for inspecting change history", - subcommands = {ListCommand.class, FixCommand.class}, - mixinStandardHelpOptions = true -) -public class AuditCommand implements Runnable { - - @CommandLine.ParentCommand - private Object parent; - - @Override - public void run() { - // Show help when no subcommand is specified - new CommandLine(this).usage(System.out); - } -} diff --git a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/command/ExecuteCommand.java b/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/command/ExecuteCommand.java deleted file mode 100644 index b2b0a39ea..000000000 --- a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/command/ExecuteCommand.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.executor.command; - -import picocli.CommandLine; -import picocli.CommandLine.Command; - -/** - * Parent command for execution operations. - * - *

Groups subcommands related to executing Flamingock changes:

- *
    - *
  • {@code apply} - Apply pending changes
  • - *
- * - *

Future milestones may add additional subcommands such as:

- *
    - *
  • {@code undo} - Undo last change (M1)
  • - *
  • {@code dry-run} - Preview changes without applying (M1)
  • - *
  • {@code validate} - Validate change state (M1)
  • - *
- */ -@Command( - name = "execute", - description = "Execute Flamingock operations on an application", - subcommands = {ApplyCommand.class}, - mixinStandardHelpOptions = true -) -public class ExecuteCommand implements Runnable { - - @CommandLine.ParentCommand - private Object parent; - - @Override - public void run() { - // Show help when no subcommand is specified - new CommandLine(this).usage(System.out); - } -} diff --git a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/command/FixCommand.java b/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/command/FixCommand.java deleted file mode 100644 index 76191f758..000000000 --- a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/command/FixCommand.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright 2026 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.executor.command; - -import io.flamingock.cli.executor.FlamingockExecutorCli; -import io.flamingock.cli.executor.orchestration.CommandExecutor; -import io.flamingock.cli.executor.orchestration.CommandResult; -import io.flamingock.cli.executor.orchestration.ExecutionOptions; -import io.flamingock.cli.executor.output.ConsoleFormatter; -import io.flamingock.cli.executor.util.VersionProvider; -import io.flamingock.internal.common.core.operation.OperationType; -import io.flamingock.internal.common.core.recovery.Resolution; -import io.flamingock.internal.common.core.response.data.AuditFixResponseData; -import picocli.CommandLine.Command; -import picocli.CommandLine.Option; -import picocli.CommandLine.ParentCommand; - -import java.io.File; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.Callable; - -/** - * Command to fix audit state for a change with issues. - * - *

This command marks a change as either APPLIED or ROLLED_BACK, - * resolving any audit issues that are blocking execution.

- */ -@Command( - name = "fix", - description = "Fix audit state for a change with issues", - mixinStandardHelpOptions = true -) -public class FixCommand implements Callable { - - public static final int EXIT_JAR_NOT_FOUND = 126; - - @ParentCommand - private AuditCommand parent; - - @Option(names = {"--jar", "-j"}, - description = "Path to the application JAR", - required = true) - private File jarFile; - - @Option(names = {"-c", "--change-id"}, - description = "The change ID to fix", - required = true) - private String changeId; - - @Option(names = {"-r", "--resolution"}, - description = "Resolution type: APPLIED or ROLLED_BACK", - required = true) - private Resolution resolution; - - private final CommandExecutor commandExecutor; - - public FixCommand() { - this(new CommandExecutor()); - } - - public FixCommand(CommandExecutor commandExecutor) { - this.commandExecutor = commandExecutor; - } - - @Override - public Integer call() { - FlamingockExecutorCli root = getRootCommand(); - boolean quiet = root != null && root.isQuiet(); - Optional logLevel = root != null ? root.getLogLevel() : Optional.empty(); - - if (!quiet) { - ConsoleFormatter.printHeader(VersionProvider.getVersionString()); - } - - if (!jarFile.exists()) { - ConsoleFormatter.printError("JAR file not found: " + jarFile.getAbsolutePath()); - return EXIT_JAR_NOT_FOUND; - } - - if (!jarFile.isFile()) { - ConsoleFormatter.printError("Path is not a file: " + jarFile.getAbsolutePath()); - return EXIT_JAR_NOT_FOUND; - } - - Map operationArgs = new HashMap<>(); - operationArgs.put("flamingock.change-id", changeId); - operationArgs.put("flamingock.resolution", resolution.name()); - - ExecutionOptions options = ExecutionOptions.builder() - .logLevel(logLevel.orElse(null)) - .streamOutput(logLevel.isPresent()) - .operationArgs(operationArgs) - .build(); - - CommandResult result = commandExecutor.execute( - jarFile.getAbsolutePath(), - OperationType.AUDIT_FIX, - AuditFixResponseData.class, - options - ); - - if (result.isSuccess()) { - AuditFixResponseData data = result.getData(); - if (data != null) { - if ("APPLIED".equals(data.getResult())) { - ConsoleFormatter.printSuccess(0); - System.out.println(data.getMessage()); - } else { - ConsoleFormatter.printInfo(data.getMessage()); - } - } - return 0; - } else { - ConsoleFormatter.printFailure(result.getErrorCode(), result.getErrorMessage()); - return result.getExitCode(); - } - } - - private FlamingockExecutorCli getRootCommand() { - if (parent == null) { - return null; - } - try { - java.lang.reflect.Field parentField = AuditCommand.class.getDeclaredField("parent"); - parentField.setAccessible(true); - Object grandParent = parentField.get(parent); - if (grandParent instanceof FlamingockExecutorCli) { - return (FlamingockExecutorCli) grandParent; - } - } catch (Exception e) { - // Fall through - } - return null; - } -} diff --git a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/command/GetIssueCommand.java b/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/command/GetIssueCommand.java deleted file mode 100644 index c599b8122..000000000 --- a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/command/GetIssueCommand.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright 2026 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.executor.command; - -import io.flamingock.cli.executor.FlamingockExecutorCli; -import io.flamingock.cli.executor.orchestration.CommandExecutor; -import io.flamingock.cli.executor.orchestration.CommandResult; -import io.flamingock.cli.executor.orchestration.ExecutionOptions; -import io.flamingock.cli.executor.output.ConsoleFormatter; -import io.flamingock.cli.executor.output.IssueFormatter; -import io.flamingock.cli.executor.output.JsonFormatter; -import io.flamingock.cli.executor.util.VersionProvider; -import io.flamingock.internal.common.core.operation.OperationType; -import io.flamingock.internal.common.core.response.data.IssueGetResponseData; -import picocli.CommandLine.Command; -import picocli.CommandLine.Option; -import picocli.CommandLine.ParentCommand; - -import java.io.File; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.Callable; - -/** - * Command to get detailed information about a specific audit issue. - */ -@Command( - name = "get", - aliases = "describe", - description = "Get detailed information about an audit issue", - mixinStandardHelpOptions = true -) -public class GetIssueCommand implements Callable { - - public static final int EXIT_JAR_NOT_FOUND = 126; - - @ParentCommand - private IssueCommand parent; - - @Option(names = {"--jar", "-j"}, - description = "Path to the application JAR", - required = true) - private File jarFile; - - @Option(names = {"-c", "--change-id"}, - description = "The change ID to inspect (if not provided, shows first issue)") - private String changeId; - - @Option(names = {"--json"}, - description = "Output in JSON format") - private boolean json; - - @Option(names = {"-g", "--guidance"}, - description = "Show resolution guidance") - private boolean guidance; - - private final CommandExecutor commandExecutor; - - public GetIssueCommand() { - this(new CommandExecutor()); - } - - public GetIssueCommand(CommandExecutor commandExecutor) { - this.commandExecutor = commandExecutor; - } - - @Override - public Integer call() { - FlamingockExecutorCli root = getRootCommand(); - boolean quiet = root != null && root.isQuiet(); - Optional logLevel = root != null ? root.getLogLevel() : Optional.empty(); - - if (!quiet && !json) { - ConsoleFormatter.printHeader(VersionProvider.getVersionString()); - } - - if (!jarFile.exists()) { - ConsoleFormatter.printError("JAR file not found: " + jarFile.getAbsolutePath()); - return EXIT_JAR_NOT_FOUND; - } - - if (!jarFile.isFile()) { - ConsoleFormatter.printError("Path is not a file: " + jarFile.getAbsolutePath()); - return EXIT_JAR_NOT_FOUND; - } - - Map operationArgs = new HashMap<>(); - if (changeId != null && !changeId.isEmpty()) { - operationArgs.put("flamingock.change-id", changeId); - } - if (guidance) { - operationArgs.put("flamingock.guidance", "true"); - } - - ExecutionOptions options = ExecutionOptions.builder() - .logLevel(logLevel.orElse(null)) - .streamOutput(logLevel.isPresent()) - .operationArgs(operationArgs) - .build(); - - CommandResult result = commandExecutor.execute( - jarFile.getAbsolutePath(), - OperationType.ISSUE_GET, - IssueGetResponseData.class, - options - ); - - if (result.isSuccess()) { - IssueGetResponseData data = result.getData(); - if (json) { - JsonFormatter.print(data); - } else { - displayIssue(data, quiet); - } - return 0; - } else { - ConsoleFormatter.printFailure(result.getErrorCode(), result.getErrorMessage()); - return result.getExitCode(); - } - } - - private void displayIssue(IssueGetResponseData data, boolean quiet) { - if (data == null || !data.isFound()) { - if (changeId != null) { - ConsoleFormatter.printInfo("No issue found for change ID: " + changeId); - } else { - ConsoleFormatter.printInfo("No issues found. All changes are healthy."); - } - return; - } - - System.out.println(); - IssueFormatter.printDetail(data, guidance); - } - - private FlamingockExecutorCli getRootCommand() { - if (parent == null) { - return null; - } - try { - java.lang.reflect.Field parentField = IssueCommand.class.getDeclaredField("parent"); - parentField.setAccessible(true); - Object grandParent = parentField.get(parent); - if (grandParent instanceof FlamingockExecutorCli) { - return (FlamingockExecutorCli) grandParent; - } - } catch (Exception e) { - // Fall through - } - return null; - } -} diff --git a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/command/IssueCommand.java b/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/command/IssueCommand.java deleted file mode 100644 index 4dbfff99b..000000000 --- a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/command/IssueCommand.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2026 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.executor.command; - -import picocli.CommandLine; -import picocli.CommandLine.Command; - -/** - * Parent command for issue operations. - * - *

Groups subcommands related to audit issues:

- *
    - *
  • {@code list} - List audit issues
  • - *
  • {@code get} - Get details for a specific issue
  • - *
- */ -@Command( - name = "issue", - description = "Issue operations for inspecting audit problems", - subcommands = {ListIssueCommand.class, GetIssueCommand.class}, - mixinStandardHelpOptions = true -) -public class IssueCommand implements Runnable { - - @CommandLine.ParentCommand - private Object parent; - - @Override - public void run() { - new CommandLine(this).usage(System.out); - } -} diff --git a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/command/ListCommand.java b/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/command/ListCommand.java deleted file mode 100644 index 54ee99544..000000000 --- a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/command/ListCommand.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.executor.command; - -import io.flamingock.cli.executor.FlamingockExecutorCli; -import io.flamingock.cli.executor.orchestration.CommandExecutor; -import io.flamingock.cli.executor.orchestration.CommandResult; -import io.flamingock.cli.executor.orchestration.ExecutionOptions; -import io.flamingock.cli.executor.output.ConsoleFormatter; -import io.flamingock.cli.executor.output.TableFormatter; -import io.flamingock.cli.executor.util.VersionProvider; -import io.flamingock.internal.common.core.operation.OperationType; -import io.flamingock.internal.common.core.response.data.AuditListResponseData; -import io.flamingock.internal.common.core.response.data.AuditListResponseData.AuditEntryDto; -import picocli.CommandLine.Command; -import picocli.CommandLine.Option; -import picocli.CommandLine.ParentCommand; - -import java.io.File; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.Callable; - -/** - * Command to list audit entries from the Flamingock audit store. - * - *

This command spawns the user's application JAR with special flags - * that enable CLI mode in Flamingock and executes the LIST operation, - * which retrieves the audit history.

- * - *

Exit codes:

- *
    - *
  • 0 - Success
  • - *
  • 1 - Failure (execution error)
  • - *
  • 2 - Usage error (invalid CLI arguments)
  • - *
  • 126 - JAR not found
  • - *
- */ -@Command( - name = "list", - description = "List audit entries from the change history", - mixinStandardHelpOptions = true -) -public class ListCommand implements Callable { - - /** - * Exit code when JAR file is not found. - */ - public static final int EXIT_JAR_NOT_FOUND = 126; - - @ParentCommand - private AuditCommand parent; - - @Option(names = {"--jar", "-j"}, - description = "Path to the application JAR", - required = true) - private File jarFile; - - @Option(names = {"--history"}, - description = "Show full chronological history instead of snapshot") - private boolean history; - - @Option(names = {"--since"}, - description = "Filter entries since date (ISO-8601: yyyy-MM-dd or yyyy-MM-ddTHH:mm:ss)") - private String since; - - @Option(names = {"-e", "--extended"}, - description = "Show extended information (execution ID, class, method, hostname)") - private boolean extended; - - private final CommandExecutor commandExecutor; - - /** - * Creates a new ListCommand with default dependencies. - */ - public ListCommand() { - this(new CommandExecutor()); - } - - /** - * Creates a new ListCommand with the specified CommandExecutor. - * - * @param commandExecutor the command executor to use - */ - public ListCommand(CommandExecutor commandExecutor) { - this.commandExecutor = commandExecutor; - } - - @Override - public Integer call() { - FlamingockExecutorCli root = getRootCommand(); - boolean quiet = root != null && root.isQuiet(); - Optional logLevel = root != null ? root.getLogLevel() : Optional.empty(); - - // Print header unless quiet mode - if (!quiet) { - ConsoleFormatter.printHeader(VersionProvider.getVersionString()); - } - - // Validate JAR exists - if (!jarFile.exists()) { - ConsoleFormatter.printError("JAR file not found: " + jarFile.getAbsolutePath()); - return EXIT_JAR_NOT_FOUND; - } - - if (!jarFile.isFile()) { - ConsoleFormatter.printError("Path is not a file: " + jarFile.getAbsolutePath()); - return EXIT_JAR_NOT_FOUND; - } - - // Build operation-specific arguments - Map operationArgs = new HashMap<>(); - if (history) { - operationArgs.put("flamingock.audit.history", "true"); - } - if (since != null && !since.isEmpty()) { - operationArgs.put("flamingock.audit.since", since); - } - if (extended) { - operationArgs.put("flamingock.audit.extended", "true"); - } - - // Non-execution ops: only stream output if log level is explicitly set - ExecutionOptions options = ExecutionOptions.builder() - .logLevel(logLevel.orElse(null)) - .streamOutput(logLevel.isPresent()) - .operationArgs(operationArgs) - .build(); - - CommandResult result = commandExecutor.execute( - jarFile.getAbsolutePath(), - OperationType.AUDIT_LIST, - AuditListResponseData.class, - options - ); - - if (result.isSuccess()) { - if (result.getData() != null) { - displayAuditEntries(result.getData().getEntries(), quiet); - } - return 0; - } else { - ConsoleFormatter.printFailure(result.getErrorCode(), result.getErrorMessage()); - return result.getExitCode(); - } - } - - private void displayAuditEntries(List entries, boolean quiet) { - if (entries == null || entries.isEmpty()) { - if (!quiet) { - ConsoleFormatter.printInfo("No audit entries found."); - } - return; - } - - System.out.println(); - TableFormatter tableFormatter = new TableFormatter(); - if (extended) { - tableFormatter.printExtendedTable(entries); - } else { - tableFormatter.printBasicTable(entries); - } - - TableFormatter.printStateLegend(); - - System.out.println(); - System.out.println("Total: " + entries.size() + " entries"); - } - - private FlamingockExecutorCli getRootCommand() { - if (parent == null) { - return null; - } - // Navigate up the command hierarchy to find the root - try { - java.lang.reflect.Field parentField = AuditCommand.class.getDeclaredField("parent"); - parentField.setAccessible(true); - Object grandParent = parentField.get(parent); - if (grandParent instanceof FlamingockExecutorCli) { - return (FlamingockExecutorCli) grandParent; - } - } catch (Exception e) { - // Fall through - } - return null; - } -} diff --git a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/command/ListIssueCommand.java b/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/command/ListIssueCommand.java deleted file mode 100644 index aad31d90e..000000000 --- a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/command/ListIssueCommand.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2026 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.executor.command; - -import io.flamingock.cli.executor.FlamingockExecutorCli; -import io.flamingock.cli.executor.orchestration.CommandExecutor; -import io.flamingock.cli.executor.orchestration.CommandResult; -import io.flamingock.cli.executor.orchestration.ExecutionOptions; -import io.flamingock.cli.executor.output.ConsoleFormatter; -import io.flamingock.cli.executor.output.IssueFormatter; -import io.flamingock.cli.executor.output.JsonFormatter; -import io.flamingock.cli.executor.util.VersionProvider; -import io.flamingock.internal.common.core.operation.OperationType; -import io.flamingock.internal.common.core.response.data.IssueListResponseData; -import picocli.CommandLine.Command; -import picocli.CommandLine.Option; -import picocli.CommandLine.ParentCommand; - -import java.io.File; -import java.util.Optional; -import java.util.concurrent.Callable; - -/** - * Command to list audit issues from the Flamingock audit store. - */ -@Command( - name = "list", - aliases = "ls", - description = "List changes with audit issues", - mixinStandardHelpOptions = true -) -public class ListIssueCommand implements Callable { - - public static final int EXIT_JAR_NOT_FOUND = 126; - - @ParentCommand - private IssueCommand parent; - - @Option(names = {"--jar", "-j"}, - description = "Path to the application JAR", - required = true) - private File jarFile; - - @Option(names = {"--json"}, - description = "Output in JSON format") - private boolean json; - - private final CommandExecutor commandExecutor; - - public ListIssueCommand() { - this(new CommandExecutor()); - } - - public ListIssueCommand(CommandExecutor commandExecutor) { - this.commandExecutor = commandExecutor; - } - - @Override - public Integer call() { - FlamingockExecutorCli root = getRootCommand(); - boolean quiet = root != null && root.isQuiet(); - Optional logLevel = root != null ? root.getLogLevel() : Optional.empty(); - - if (!quiet && !json) { - ConsoleFormatter.printHeader(VersionProvider.getVersionString()); - } - - if (!jarFile.exists()) { - ConsoleFormatter.printError("JAR file not found: " + jarFile.getAbsolutePath()); - return EXIT_JAR_NOT_FOUND; - } - - if (!jarFile.isFile()) { - ConsoleFormatter.printError("Path is not a file: " + jarFile.getAbsolutePath()); - return EXIT_JAR_NOT_FOUND; - } - - ExecutionOptions options = ExecutionOptions.builder() - .logLevel(logLevel.orElse(null)) - .streamOutput(logLevel.isPresent()) - .build(); - - CommandResult result = commandExecutor.execute( - jarFile.getAbsolutePath(), - OperationType.ISSUE_LIST, - IssueListResponseData.class, - options - ); - - if (result.isSuccess()) { - IssueListResponseData data = result.getData(); - if (json) { - JsonFormatter.print(data); - } else { - displayIssues(data, quiet); - } - return 0; - } else { - ConsoleFormatter.printFailure(result.getErrorCode(), result.getErrorMessage()); - return result.getExitCode(); - } - } - - private void displayIssues(IssueListResponseData data, boolean quiet) { - if (data == null || data.getIssues() == null || data.getIssues().isEmpty()) { - if (!quiet) { - ConsoleFormatter.printInfo("No issues found. All changes are healthy."); - } - return; - } - - System.out.println(); - IssueFormatter.printList(data); - System.out.println(); - System.out.println("Total: " + data.getIssues().size() + " issues"); - } - - private FlamingockExecutorCli getRootCommand() { - if (parent == null) { - return null; - } - try { - java.lang.reflect.Field parentField = IssueCommand.class.getDeclaredField("parent"); - parentField.setAccessible(true); - Object grandParent = parentField.get(parent); - if (grandParent instanceof FlamingockExecutorCli) { - return (FlamingockExecutorCli) grandParent; - } - } catch (Exception e) { - // Fall through - } - return null; - } -} diff --git a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/handler/ExecutorExceptionHandler.java b/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/handler/ExecutorExceptionHandler.java deleted file mode 100644 index bd8d37fe9..000000000 --- a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/handler/ExecutorExceptionHandler.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.executor.handler; - -import picocli.CommandLine; -import picocli.CommandLine.IExecutionExceptionHandler; -import picocli.CommandLine.ParseResult; - -import java.io.PrintWriter; - -/** - * CLI exception handler that provides helpful guidance for common errors. - */ -public class ExecutorExceptionHandler implements IExecutionExceptionHandler { - - private static final String SEPARATOR = "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"; - - @Override - public int handleExecutionException(Exception ex, CommandLine commandLine, ParseResult parseResult) { - PrintWriter err = commandLine.getErr(); - - err.println(); - err.println(SEPARATOR); - err.println("Error: " + ex.getMessage()); - err.println(SEPARATOR); - - // Show stack trace in verbose mode - if (isVerboseMode(parseResult)) { - err.println(); - err.println("Stack trace:"); - ex.printStackTrace(err); - } else { - err.println(); - err.println("For detailed error information, run with --verbose flag"); - } - - return 1; - } - - private boolean isVerboseMode(ParseResult parseResult) { - if (parseResult == null) { - return false; - } - // Check if --verbose was specified - return parseResult.hasMatchedOption("--verbose"); - } -} diff --git a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/orchestration/CommandExecutor.java b/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/orchestration/CommandExecutor.java deleted file mode 100644 index 72d1b3b8a..000000000 --- a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/orchestration/CommandExecutor.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.executor.orchestration; - -import io.flamingock.cli.executor.process.JvmLauncher; -import io.flamingock.cli.executor.process.LaunchResult; -import io.flamingock.cli.executor.result.ResponseResultReader; -import io.flamingock.cli.executor.result.ResponseResultReader.ResponseResult; -import io.flamingock.internal.common.core.operation.OperationType; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; - -/** - * Orchestrates the execution of CLI commands. - * - *

This class handles the common flow of: - *

    - *
  1. Creating a temporary output file for response communication
  2. - *
  3. Launching the JVM process with the user's JAR
  4. - *
  5. Handling common launch failures (entry point not found, process errors)
  6. - *
  7. Reading and parsing the response file
  8. - *
  9. Cleaning up temporary files
  10. - *
- * - *

By centralizing this logic, individual commands only need to handle - * their specific presentation logic.

- */ -public class CommandExecutor { - - private final JvmLauncher launcher; - private final ResponseResultReader reader; - - /** - * Creates a new CommandExecutor with default dependencies. - */ - public CommandExecutor() { - this(new JvmLauncher(), new ResponseResultReader()); - } - - /** - * Creates a new CommandExecutor with the specified dependencies. - * - * @param launcher the JVM launcher - * @param reader the response result reader - */ - public CommandExecutor(JvmLauncher launcher, ResponseResultReader reader) { - this.launcher = launcher; - this.reader = reader; - } - - /** - * Executes a command by launching the user's JAR and reading the response. - * - *

This method handles all common error scenarios: - *

    - *
  • Entry point not found - JAR missing flamingock-core classes
  • - *
  • Process start failures - java not found, permissions, etc.
  • - *
  • Process interruption
  • - *
  • Response file read errors
  • - *
- * - * @param jarPath the path to the user's JAR file - * @param operation the Flamingock operation to execute - * @param responseType the expected type of the response data - * @param options execution options (log level, stream output, etc.) - * @param the response data type - * @return the command result - */ - public CommandResult execute( - String jarPath, - OperationType operation, - Class responseType, - ExecutionOptions options - ) { - Path outputFile = null; - try { - outputFile = Files.createTempFile("flamingock-response-", ".json"); - - LaunchResult launchResult = launcher.launch( - jarPath, - operation, - outputFile.toString(), - options.getLogLevel(), - options.isStreamOutput(), - options.getOperationArgs() - ); - - // Handle launch-level failures - don't try to read response file - if (launchResult.isFailure()) { - return CommandResult.fromLaunchFailure(launchResult); - } - - // Launch succeeded, read the response file - ResponseResult responseResult = reader.readTyped(outputFile, responseType); - - if (responseResult.isSuccess()) { - return CommandResult.success(responseResult.getData(), responseResult.getDurationMs()); - } else { - return CommandResult.fromResponse(responseResult); - } - - } catch (IOException e) { - return CommandResult.processStartFailed("Failed to create temporary file: " + e.getMessage()); - } finally { - if (outputFile != null) { - try { - Files.deleteIfExists(outputFile); - } catch (IOException ignored) { - // Best effort cleanup - } - } - } - } -} diff --git a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/orchestration/CommandResult.java b/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/orchestration/CommandResult.java deleted file mode 100644 index 8068d7cc0..000000000 --- a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/orchestration/CommandResult.java +++ /dev/null @@ -1,336 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.executor.orchestration; - -import io.flamingock.cli.executor.process.LaunchResult; -import io.flamingock.cli.executor.process.LaunchStatus; -import io.flamingock.cli.executor.result.ResponseResultReader.ResponseResult; - -/** - * Represents the result of a CLI command execution. - * Encapsulates both launch-level and response-level outcomes. - * - * @param the type of the response data - */ -public class CommandResult { - - private final boolean success; - private final T data; - private final String errorCode; - private final String errorMessage; - private final int exitCode; - private final long durationMs; - private final LaunchStatus launchStatus; - - private CommandResult(boolean success, T data, String errorCode, String errorMessage, - int exitCode, long durationMs, LaunchStatus launchStatus) { - this.success = success; - this.data = data; - this.errorCode = errorCode; - this.errorMessage = errorMessage; - this.exitCode = exitCode; - this.durationMs = durationMs; - this.launchStatus = launchStatus; - } - - /** - * Creates a successful result with data. - * - * @param data the response data - * @param durationMs the execution duration - * @param the data type - * @return a success result - */ - public static CommandResult success(T data, long durationMs) { - return new CommandResult<>(true, data, null, null, 0, durationMs, LaunchStatus.SUCCESS); - } - - /** - * Creates a result for entry point not found error. - * - * @param exitCode the process exit code - * @param the data type - * @return an entry point not found result - */ - public static CommandResult entryPointNotFound(int exitCode) { - return new CommandResult<>( - false, - null, - "ENTRY_POINT_NOT_FOUND", - "Flamingock CLI entry point not found in your JAR.\n\n" + - "Your uber/shaded JAR must include 'flamingock-core' classes.\n" + - "If you're using Maven Shade or Gradle Shadow plugin, ensure flamingock-core\n" + - "is not excluded from the shading process.\n\n" + - "Hint: Check that 'io.flamingock.core.cli.FlamingockCliMainEntryPoint' is present:\n" + - " jar tf your-app.jar | grep FlamingockCliMainEntryPoint", - exitCode, - 0, - LaunchStatus.ENTRY_POINT_NOT_FOUND - ); - } - - /** - * Creates a result for JAR analysis failure. - * - * @param errorMessage the error message - * @param the data type - * @return a JAR analysis failed result - */ - public static CommandResult jarAnalysisFailed(String errorMessage) { - return new CommandResult<>( - false, - null, - "JAR_ANALYSIS_FAILED", - "Failed to analyze JAR: " + errorMessage, - 1, - 0, - LaunchStatus.JAR_ANALYSIS_FAILED - ); - } - - /** - * Creates a result for process start failure. - * - * @param errorMessage the error message - * @param the data type - * @return a process start failed result - */ - public static CommandResult processStartFailed(String errorMessage) { - return new CommandResult<>( - false, - null, - "PROCESS_START_FAILED", - "Failed to start process: " + errorMessage, - 1, - 0, - LaunchStatus.PROCESS_START_FAILED - ); - } - - /** - * Creates a result for process interrupted. - * - * @param the data type - * @return a process interrupted result - */ - public static CommandResult processInterrupted() { - return new CommandResult<>( - false, - null, - "PROCESS_INTERRUPTED", - "Process was interrupted", - 1, - 0, - LaunchStatus.PROCESS_INTERRUPTED - ); - } - - /** - * Creates a result for missing Flamingock runtime. - * - *

This occurs when the JAR does not contain the Flamingock CLI entry point, - * which can happen in two scenarios: - *

    - *
  1. User provided a thin JAR without bundled dependencies
  2. - *
  3. User built an uber JAR but flamingock-core was excluded/relocated
  4. - *
- * - * @param the data type - * @return a missing Flamingock runtime result - */ - public static CommandResult missingFlamingockRuntime() { - return new CommandResult<>( - false, - null, - "MISSING_FLAMINGOCK_RUNTIME", - "Your JAR does not include the Flamingock CLI entry point.\n\n" + - "This usually means one of the following:\n\n" + - "1. THIN JAR: You provided a thin JAR without bundled dependencies.\n" + - " Solution: Build an uber/shaded JAR that includes all dependencies.\n\n" + - "2. MISSING DEPENDENCY: You built an uber JAR but flamingock-core\n" + - " was not included (wrong scope, excluded, or relocated).\n" + - " Solution: Ensure flamingock-core is an 'implementation' dependency\n" + - " and not excluded from your shading configuration.\n\n" + - "To create an uber JAR:\n\n" + - " Maven: maven-shade-plugin\n" + - " https://maven.apache.org/plugins/maven-shade-plugin/\n\n" + - " Gradle: Shadow plugin or custom Jar task\n" + - " https://github.com/johnrengelman/shadow\n\n" + - "Example Gradle uber JAR task:\n\n" + - " val uberJar by tasks.registering(Jar::class) {\n" + - " archiveClassifier.set(\"uber\")\n" + - " duplicatesStrategy = DuplicatesStrategy.EXCLUDE\n" + - " from(sourceSets.main.get().output)\n" + - " from(configurations.runtimeClasspath.get().map { zipTree(it) })\n" + - " }\n\n" + - "Verify the entry point is present:\n" + - " jar tf your-app.jar | grep FlamingockCliMainEntryPoint", - 1, - 0, - LaunchStatus.MISSING_FLAMINGOCK_RUNTIME - ); - } - - /** - * Creates a result from a launch failure. - * - * @param launchResult the launch result - * @param the data type - * @return a command result based on the launch failure - */ - public static CommandResult fromLaunchFailure(LaunchResult launchResult) { - switch (launchResult.getStatus()) { - case MISSING_FLAMINGOCK_RUNTIME: - return missingFlamingockRuntime(); - case ENTRY_POINT_NOT_FOUND: - return entryPointNotFound(launchResult.getExitCode()); - case JAR_ANALYSIS_FAILED: - return jarAnalysisFailed(launchResult.getErrorDetail()); - case PROCESS_START_FAILED: - return processStartFailed(launchResult.getErrorDetail()); - case PROCESS_INTERRUPTED: - return processInterrupted(); - case PROCESS_FAILED: - default: - return new CommandResult<>( - false, - null, - "PROCESS_FAILED", - launchResult.getErrorDetail() != null - ? launchResult.getErrorDetail() - : "Process exited with code " + launchResult.getExitCode(), - launchResult.getExitCode(), - 0, - launchResult.getStatus() - ); - } - } - - /** - * Creates a result from a response result. - * - * @param responseResult the response result - * @param the data type - * @return a command result based on the response - */ - public static CommandResult fromResponse(ResponseResult responseResult) { - if (responseResult.isSuccess()) { - return success(responseResult.getData(), responseResult.getDurationMs()); - } else { - return new CommandResult<>( - false, - null, - responseResult.getErrorCode(), - responseResult.getErrorMessage(), - 1, - responseResult.getDurationMs(), - LaunchStatus.SUCCESS // Launch succeeded, but response indicated failure - ); - } - } - - /** - * Creates a result for response file read error. - * - * @param errorMessage the error message - * @param exitCode the process exit code - * @param the data type - * @return a response read error result - */ - public static CommandResult responseReadError(String errorMessage, int exitCode) { - return new CommandResult<>( - false, - null, - "RESPONSE_READ_ERROR", - errorMessage, - exitCode != 0 ? exitCode : 1, - 0, - LaunchStatus.SUCCESS // Launch succeeded, but response couldn't be read - ); - } - - /** - * Returns whether the command was successful. - * - * @return true if successful - */ - public boolean isSuccess() { - return success; - } - - /** - * Returns the response data. - * - * @return the data, or null if not successful - */ - public T getData() { - return data; - } - - /** - * Returns the error code. - * - * @return the error code, or null if successful - */ - public String getErrorCode() { - return errorCode; - } - - /** - * Returns the error message. - * - * @return the error message, or null if successful - */ - public String getErrorMessage() { - return errorMessage; - } - - /** - * Returns the exit code for the command. - * - * @return the exit code - */ - public int getExitCode() { - return exitCode; - } - - /** - * Returns the execution duration in milliseconds. - * - * @return the duration - */ - public long getDurationMs() { - return durationMs; - } - - /** - * Returns the launch status. - * - * @return the launch status - */ - public LaunchStatus getLaunchStatus() { - return launchStatus; - } - - /** - * Checks if this was a launch-level failure (before response could be read). - * - * @return true if launch failed - */ - public boolean isLaunchFailure() { - return launchStatus != LaunchStatus.SUCCESS; - } -} diff --git a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/orchestration/ExecutionOptions.java b/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/orchestration/ExecutionOptions.java deleted file mode 100644 index 2ae7dfa69..000000000 --- a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/orchestration/ExecutionOptions.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.executor.orchestration; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -/** - * Encapsulates options for command execution. - */ -public class ExecutionOptions { - - private final String logLevel; - private final boolean streamOutput; - private final Map operationArgs; - - private ExecutionOptions(Builder builder) { - this.logLevel = builder.logLevel; - this.streamOutput = builder.streamOutput; - this.operationArgs = Collections.unmodifiableMap(new HashMap<>(builder.operationArgs)); - } - - /** - * Returns the log level, or null if not set. - * - * @return the log level - */ - public String getLogLevel() { - return logLevel; - } - - /** - * Returns whether to stream output to console. - * - * @return true if output should be streamed - */ - public boolean isStreamOutput() { - return streamOutput; - } - - /** - * Returns additional operation-specific arguments to pass to the JAR. - * - * @return the operation arguments map (unmodifiable) - */ - public Map getOperationArgs() { - return operationArgs; - } - - /** - * Creates a new builder. - * - * @return a new builder instance - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Builder for ExecutionOptions. - */ - public static class Builder { - private String logLevel; - private boolean streamOutput = true; - private Map operationArgs = new HashMap<>(); - - private Builder() { - } - - /** - * Sets the log level. - * - * @param logLevel the log level - * @return this builder - */ - public Builder logLevel(String logLevel) { - this.logLevel = logLevel; - return this; - } - - /** - * Sets whether to stream output. - * - * @param streamOutput true to stream output - * @return this builder - */ - public Builder streamOutput(boolean streamOutput) { - this.streamOutput = streamOutput; - return this; - } - - /** - * Sets additional operation-specific arguments. - * - * @param operationArgs the operation arguments - * @return this builder - */ - public Builder operationArgs(Map operationArgs) { - this.operationArgs = operationArgs != null ? operationArgs : new HashMap<>(); - return this; - } - - /** - * Adds a single operation argument. - * - * @param key the argument key - * @param value the argument value - * @return this builder - */ - public Builder operationArg(String key, String value) { - this.operationArgs.put(key, value); - return this; - } - - /** - * Builds the ExecutionOptions. - * - * @return the built options - */ - public ExecutionOptions build() { - return new ExecutionOptions(this); - } - } -} diff --git a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/output/ConsoleFormatter.java b/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/output/ConsoleFormatter.java deleted file mode 100644 index 4f7d98a5c..000000000 --- a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/output/ConsoleFormatter.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.executor.output; - -/** - * Provides formatted console output for the CLI executor. - * Follows professional CLI conventions (docker, kubectl, terraform style). - */ -public final class ConsoleFormatter { - - private ConsoleFormatter() { - } - - /** - * Prints the CLI header with version information. - * - * @param version the CLI version - */ - public static void printHeader(String version) { - System.out.println("flamingock v" + version); - } - - /** - * Prints a success message after successful execution. - * - * @param durationMs the execution duration in milliseconds - */ - public static void printSuccess(long durationMs) { - System.out.println("Completed successfully (" + durationMs + "ms)"); - } - - /** - * Prints a success message after successful execution. - */ - public static void printSuccess() { - System.out.println("Completed successfully"); - } - - /** - * Prints a failure message after failed execution. - */ - public static void printFailure() { - printFailure(null, null); - } - - /** - * Prints a failure message with error details. - * - * @param errorCode the error code (optional) - * @param errorMessage the error message (optional) - */ - public static void printFailure(String errorCode, String errorMessage) { - if (errorMessage != null) { - if (errorCode != null) { - System.err.println("Error (" + errorCode + "): " + errorMessage); - } else { - System.err.println("Error: " + errorMessage); - } - } else { - System.err.println("Error: operation failed"); - } - } - - /** - * Prints an error message. - * - * @param message the error message - */ - public static void printError(String message) { - System.err.println("Error: " + message); - } - - /** - * Prints an informational message. - * - * @param message the message - */ - public static void printInfo(String message) { - System.out.println(message); - } -} diff --git a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/output/ExecutionResultFormatter.java b/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/output/ExecutionResultFormatter.java deleted file mode 100644 index 5653678dd..000000000 --- a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/output/ExecutionResultFormatter.java +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright 2026 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.executor.output; - -import io.flamingock.internal.common.core.response.data.ChangeResult; -import io.flamingock.internal.common.core.response.data.ChangeStatus; -import io.flamingock.internal.common.core.response.data.ErrorInfo; -import io.flamingock.internal.common.core.response.data.ExecuteResponseData; -import io.flamingock.internal.common.core.response.data.ExecutionStatus; -import io.flamingock.internal.common.core.response.data.StageResult; - -/** - * Formats execution results for CLI output. - * Provides professional, scannable output following CLI conventions. - */ -public final class ExecutionResultFormatter { - - private static final String SEPARATOR = "--------------------------------------------------------------------------------"; - private static final int CHANGE_ID_WIDTH = 30; - private static final int AUTHOR_WIDTH = 20; - - private ExecutionResultFormatter() { - } - - /** - * Formats the complete execution result for CLI display. - * - * @param result the execution result data - * @return formatted string for display - */ - public static String format(ExecuteResponseData result) { - StringBuilder sb = new StringBuilder("\n"); - - for (StageResult stage : result.getStages()) { - sb.append(formatStage(stage)); - } - - // Print summary - sb.append("\n").append(SEPARATOR).append("\n"); - sb.append(centerText("EXECUTION SUMMARY", SEPARATOR.length())).append("\n"); - sb.append(SEPARATOR).append("\n"); - - sb.append(String.format(" Status: %s%n", formatStatus(result.getStatus()))); - sb.append(String.format(" Duration: %s%n", formatDuration(result.getTotalDurationMs()))); - sb.append(formatStagesSummary(result)); - sb.append(formatChangesSummary(result)); - - // Print error details if failed - if (result.isFailed() && result.getError() != null) { - sb.append(formatErrorDetails(result.getError())); - } - - sb.append(SEPARATOR).append("\n"); - - return sb.toString(); - } - - /** - * Formats a single stage with its changes. - */ - private static String formatStage(StageResult stage) { - StringBuilder sb = new StringBuilder(); - sb.append(String.format("%n Stage: %s%n", stage.getStageName())); - - for (ChangeResult change : stage.getChanges()) { - sb.append(formatChange(change)); - } - - return sb.toString(); - } - - /** - * Formats a single change result line. - */ - private static String formatChange(ChangeResult change) { - String statusLabel = formatChangeStatus(change.getStatus()); - String changeId = truncateOrPad(change.getChangeId(), CHANGE_ID_WIDTH); - String author = change.getAuthor() != null - ? truncateOrPad("(author: " + change.getAuthor() + ")", AUTHOR_WIDTH) - : truncateOrPad("", AUTHOR_WIDTH); - String duration = formatChangeDuration(change); - - return String.format(" [%s] %s %s %s%n", statusLabel, changeId, author, duration); - } - - /** - * Formats the change status for display. - */ - private static String formatChangeStatus(ChangeStatus status) { - switch (status) { - case APPLIED: - return "APPLIED"; - case ALREADY_APPLIED: - return "SKIPPED"; - case FAILED: - return "FAILED "; - case ROLLED_BACK: - return "ROLLBCK"; - case NOT_REACHED: - return "SKIPPED"; - default: - return "UNKNOWN"; - } - } - - /** - * Formats the execution status for summary display. - */ - private static String formatStatus(ExecutionStatus status) { - switch (status) { - case SUCCESS: - return "SUCCESS"; - case FAILED: - return "FAILED"; - case PARTIAL: - return "PARTIAL"; - case NO_CHANGES: - return "NO CHANGES"; - default: - return "UNKNOWN"; - } - } - - /** - * Formats the stages summary line. - */ - private static String formatStagesSummary(ExecuteResponseData result) { - if (result.getFailedStages() > 0) { - return String.format(" Stages: %d completed, %d failed%n", - result.getCompletedStages(), result.getFailedStages()); - } else { - return String.format(" Stages: %d completed%n", result.getCompletedStages()); - } - } - - /** - * Formats the changes summary line. - */ - private static String formatChangesSummary(ExecuteResponseData result) { - return String.format(" Changes: %d applied, %d skipped, %d failed%n", - result.getAppliedChanges(), result.getSkippedChanges(), result.getFailedChanges()); - } - - /** - * Formats error details section. - */ - private static String formatErrorDetails(ErrorInfo error) { - StringBuilder sb = new StringBuilder(); - sb.append("\n Error:\n"); - if (error.getChangeId() != null) { - sb.append(String.format(" Change: %s%n", error.getChangeId())); - } - if (error.getStageId() != null) { - sb.append(String.format(" Stage: %s%n", error.getStageId())); - } - if (error.getErrorType() != null) { - sb.append(String.format(" Type: %s%n", error.getErrorType())); - } - if (error.getMessage() != null) { - sb.append(String.format(" Message: %s%n", error.getMessage())); - } - return sb.toString(); - } - - /** - * Formats duration for change line. - */ - private static String formatChangeDuration(ChangeResult change) { - if (change.getStatus() == ChangeStatus.ALREADY_APPLIED) { - return "Already applied"; - } else if (change.getStatus() == ChangeStatus.NOT_REACHED) { - return "Not executed"; - } else { - return formatDuration(change.getDurationMs()); - } - } - - /** - * Formats duration in human-readable format. - */ - private static String formatDuration(long millis) { - if (millis < 1000) { - return millis + "ms"; - } else if (millis < 60000) { - return String.format("%.1fs", millis / 1000.0); - } else { - return String.format("%.1fm", millis / 60000.0); - } - } - - /** - * Truncates or pads a string to the specified width. - */ - private static String truncateOrPad(String s, int width) { - if (s == null) { - s = ""; - } - if (s.length() > width) { - return s.substring(0, width - 3) + "..."; - } - return String.format("%-" + width + "s", s); - } - - /** - * Centers text within a given width. - */ - private static String centerText(String text, int width) { - if (text.length() >= width) { - return text; - } - int padding = (width - text.length()) / 2; - return String.format("%" + padding + "s%s%" + padding + "s", "", text, ""); - } - - /** - * Prints the execution result to standard output. - * - * @param result the execution result data - */ - public static void print(ExecuteResponseData result) { - System.out.print(format(result)); - } -} diff --git a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/output/IssueFormatter.java b/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/output/IssueFormatter.java deleted file mode 100644 index 9c5812060..000000000 --- a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/output/IssueFormatter.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright 2026 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.executor.output; - -import io.flamingock.internal.common.core.response.data.IssueGetResponseData; -import io.flamingock.internal.common.core.response.data.IssueListResponseData; -import io.flamingock.internal.common.core.response.data.IssueListResponseData.IssueSummaryDto; - -import java.time.format.DateTimeFormatter; - -/** - * Formats issue information for console output. - */ -public class IssueFormatter { - - private static final String RESET = "\u001B[0m"; - private static final String RED = "\u001B[31m"; - private static final String YELLOW = "\u001B[33m"; - private static final String CYAN = "\u001B[36m"; - private static final String BOLD = "\u001B[1m"; - - private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); - - private IssueFormatter() { - } - - /** - * Prints a table of issue summaries. - * - * @param data the issue list response data - */ - public static void printList(IssueListResponseData data) { - if (data == null || data.getIssues() == null || data.getIssues().isEmpty()) { - return; - } - - // Simple table format - String format = "%-32s %-15s %-21s %s%n"; - System.out.printf(format, "Change ID", "State", "Time", "Error Summary"); - System.out.println(repeat("-", 100)); - - for (IssueSummaryDto issue : data.getIssues()) { - String state = issue.getState() != null ? colorState(issue.getState()) : "UNKNOWN"; - String time = issue.getCreatedAt() != null ? issue.getCreatedAt().format(TIME_FORMATTER) : "-"; - String errorSummary = issue.getErrorSummary() != null ? issue.getErrorSummary() : "-"; - - System.out.printf("%-32s %s %-21s %s%n", - truncate(issue.getChangeId(), 30), - padState(state, issue.getState(), 15), - time, - errorSummary); - } - } - - /** - * Prints detailed information about a single issue. - * - * @param data the issue details - * @param withGuidance whether to include resolution guidance - */ - public static void printDetail(IssueGetResponseData data, boolean withGuidance) { - if (data == null || !data.isFound()) { - return; - } - - // Overview section - System.out.println(BOLD + "📋 OVERVIEW" + RESET); - System.out.println(" Change ID: " + data.getChangeId()); - System.out.println(" State: " + colorState(data.getState())); - System.out.println(" Author: " + (data.getAuthor() != null ? data.getAuthor() : "-")); - System.out.println(" Created At: " + (data.getCreatedAt() != null ? data.getCreatedAt().format(TIME_FORMATTER) : "-")); - System.out.println(" Execution ID: " + (data.getExecutionId() != null ? data.getExecutionId() : "-")); - System.out.println(" Duration: " + formatDuration(data.getExecutionMillis())); - System.out.println(" Target System: " + (data.getTargetSystemId() != null ? data.getTargetSystemId() : "-")); - System.out.println(" Recovery Strategy: " + (data.getRecoveryStrategy() != null ? data.getRecoveryStrategy() : "-")); - System.out.println(); - - // Execution details - System.out.println(BOLD + "📍 EXECUTION DETAILS" + RESET); - System.out.println(" Class: " + (data.getClassName() != null ? data.getClassName() : "-")); - System.out.println(" Method: " + (data.getMethodName() != null ? data.getMethodName() : "-")); - System.out.println(" Hostname: " + (data.getExecutionHostname() != null ? data.getExecutionHostname() : "-")); - System.out.println(); - - // Error details - System.out.println(BOLD + "⚠️ ERROR DETAILS" + RESET); - if (data.getErrorTrace() != null && !data.getErrorTrace().isEmpty()) { - // Print error trace with indentation - String[] lines = data.getErrorTrace().split("\n"); - for (String line : lines) { - System.out.println(" " + RED + line + RESET); - } - } else { - System.out.println(" No error trace available"); - } - System.out.println(); - - // Resolution guidance - if (withGuidance) { - printGuidance(data); - } - } - - private static void printGuidance(IssueGetResponseData data) { - System.out.println(BOLD + "🔧 RESOLUTION GUIDANCE" + RESET); - System.out.println(); - System.out.println(" To resolve this issue, follow these steps:"); - System.out.println(); - System.out.println(" 1. " + CYAN + "Review the error" + RESET + " - Examine the error trace above to understand what went wrong"); - System.out.println(); - System.out.println(" 2. " + CYAN + "Verify actual state" + RESET + " - Check your target system to determine if the change was:"); - System.out.println(" - Successfully applied (despite the error)"); - System.out.println(" - Partially applied (requires manual cleanup)"); - System.out.println(" - Not applied at all (can be retried)"); - System.out.println(); - System.out.println(" 3. " + CYAN + "Fix the audit state" + RESET + " - Once you've verified the actual state, mark it accordingly:"); - System.out.println(); - System.out.println(" If the change WAS successfully applied:"); - System.out.println(" " + YELLOW + "flamingock audit fix --jar app.jar -c " + data.getChangeId() + " -r APPLIED" + RESET); - System.out.println(); - System.out.println(" If the change was NOT applied (or you've rolled it back):"); - System.out.println(" " + YELLOW + "flamingock audit fix --jar app.jar -c " + data.getChangeId() + " -r ROLLED_BACK" + RESET); - System.out.println(); - System.out.println(" 4. " + CYAN + "Retry execution" + RESET + " - After fixing, run the application again to continue"); - System.out.println(); - } - - private static String colorState(String state) { - if (state == null) { - return "UNKNOWN"; - } - switch (state.toUpperCase()) { - case "FAILED": - case "ROLLBACK_FAILED": - return RED + state + RESET; - case "STARTED": - return YELLOW + state + RESET; - default: - return state; - } - } - - private static String padState(String coloredState, String rawState, int width) { - // Calculate actual display width (without ANSI codes) - int displayLen = rawState != null ? rawState.length() : 7; - int padding = width - displayLen; - if (padding > 0) { - return coloredState + repeat(" ", padding); - } - return coloredState; - } - - private static String truncate(String value, int maxLen) { - if (value == null) { - return "-"; - } - if (value.length() <= maxLen) { - return value; - } - return value.substring(0, maxLen - 3) + "..."; - } - - private static String formatDuration(long millis) { - if (millis < 1000) { - return millis + "ms"; - } else if (millis < 60000) { - return String.format("%.2fs", millis / 1000.0); - } else { - long minutes = millis / 60000; - long seconds = (millis % 60000) / 1000; - return String.format("%dm %ds", minutes, seconds); - } - } - - private static String repeat(String str, int count) { - StringBuilder sb = new StringBuilder(count); - for (int i = 0; i < count; i++) { - sb.append(str); - } - return sb.toString(); - } -} diff --git a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/output/JsonFormatter.java b/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/output/JsonFormatter.java deleted file mode 100644 index f78b1cb92..000000000 --- a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/output/JsonFormatter.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2026 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.executor.output; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; - -/** - * Formats objects as JSON for console output. - */ -public class JsonFormatter { - - private static final ObjectMapper MAPPER; - - static { - MAPPER = new ObjectMapper(); - MAPPER.registerModule(new JavaTimeModule()); - MAPPER.enable(SerializationFeature.INDENT_OUTPUT); - MAPPER.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); - } - - private JsonFormatter() { - } - - /** - * Prints an object as pretty-printed JSON. - * - * @param object the object to print - */ - public static void print(Object object) { - if (object == null) { - System.out.println("null"); - return; - } - try { - String json = MAPPER.writeValueAsString(object); - System.out.println(json); - } catch (JsonProcessingException e) { - System.err.println("Error serializing to JSON: " + e.getMessage()); - } - } - - /** - * Converts an object to a JSON string. - * - * @param object the object to convert - * @return JSON string representation - */ - public static String toJson(Object object) { - if (object == null) { - return "null"; - } - try { - return MAPPER.writeValueAsString(object); - } catch (JsonProcessingException e) { - return "{\"error\": \"" + e.getMessage() + "\"}"; - } - } -} diff --git a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/output/TableColumn.java b/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/output/TableColumn.java deleted file mode 100644 index d5d15ef77..000000000 --- a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/output/TableColumn.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.executor.output; - -/** - * Represents a table column definition with title, width and alignment. - */ -public class TableColumn { - - public enum Alignment { - LEFT, CENTER, RIGHT - } - - private final String title; - private final int width; - private final Alignment alignment; - - public TableColumn(String title, int width, Alignment alignment) { - this.title = title; - this.width = width; - this.alignment = alignment; - } - - public TableColumn(String title, int width) { - this(title, width, Alignment.LEFT); - } - - public String getTitle() { - return title; - } - - public int getWidth() { - return width; - } - - public Alignment getAlignment() { - return alignment; - } - - /** - * Format text according to column width and alignment. - * - * @param text the text to format - * @return formatted text that fits within the column width and alignment - */ - public String format(String text) { - String truncated = truncate(text); - return align(truncated); - } - - /** - * Truncate text if it exceeds column width. - */ - private String truncate(String text) { - if (text == null) { - return "-"; - } - // Calculate visual length (emojis count as 2 chars visually) - int visualLength = getVisualLength(text); - if (visualLength <= width) { - return text; - } - // Account for "..." when truncating - if (width <= 3) { - return "...".substring(0, width); - } - // Truncate based on visual length - return truncateToVisualWidth(text, width - 3) + "..."; - } - - /** - * Get the visual length of a string, accounting for emojis and variation selectors. - */ - private int getVisualLength(String text) { - int length = 0; - for (int i = 0; i < text.length(); ) { - int codePoint = text.codePointAt(i); - // Skip variation selectors (they're invisible modifiers) - if (isVariationSelector(codePoint)) { - i += Character.charCount(codePoint); - continue; - } - // Emojis and other wide characters take 2 visual spaces - if (Character.isSupplementaryCodePoint(codePoint) || isEmojiCodePoint(codePoint)) { - length += 2; - } else { - length += 1; - } - i += Character.charCount(codePoint); - } - return length; - } - - /** - * Check if a code point is a variation selector (invisible modifier). - */ - private boolean isVariationSelector(int codePoint) { - // U+FE0E (text style) and U+FE0F (emoji style) variation selectors - return codePoint == 0xFE0E || codePoint == 0xFE0F; - } - - /** - * Check if a code point is likely an emoji. - */ - private boolean isEmojiCodePoint(int codePoint) { - return (codePoint >= 0x1F300 && codePoint <= 0x1F9FF) // Misc Symbols, Emoticons, etc. - || (codePoint >= 0x2600 && codePoint <= 0x26FF) // Misc Symbols - || (codePoint >= 0x2700 && codePoint <= 0x27BF) // Dingbats - || (codePoint >= 0x2300 && codePoint <= 0x23FF); // Misc Technical - } - - /** - * Truncate string to a visual width. - */ - private String truncateToVisualWidth(String text, int maxWidth) { - StringBuilder result = new StringBuilder(); - int currentWidth = 0; - for (int i = 0; i < text.length(); ) { - int codePoint = text.codePointAt(i); - // Always include variation selectors (they follow their base character) - if (isVariationSelector(codePoint)) { - result.appendCodePoint(codePoint); - i += Character.charCount(codePoint); - continue; - } - int charWidth = (Character.isSupplementaryCodePoint(codePoint) || isEmojiCodePoint(codePoint)) ? 2 : 1; - if (currentWidth + charWidth > maxWidth) { - break; - } - result.appendCodePoint(codePoint); - currentWidth += charWidth; - i += Character.charCount(codePoint); - } - return result.toString(); - } - - /** - * Align text within column width. - */ - private String align(String text) { - int visualLength = getVisualLength(text); - int padding = width - visualLength; - - if (padding <= 0) { - return text; - } - - switch (alignment) { - case RIGHT: - return spaces(padding) + text; - case CENTER: - int leftPad = padding / 2; - int rightPad = padding - leftPad; - return spaces(leftPad) + text + spaces(rightPad); - case LEFT: - default: - return text + spaces(padding); - } - } - - private String spaces(int count) { - StringBuilder sb = new StringBuilder(count); - for (int i = 0; i < count; i++) { - sb.append(' '); - } - return sb.toString(); - } -} diff --git a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/output/TableFormatter.java b/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/output/TableFormatter.java deleted file mode 100644 index 930e918b2..000000000 --- a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/output/TableFormatter.java +++ /dev/null @@ -1,317 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.executor.output; - -import io.flamingock.internal.common.core.response.data.AuditListResponseData.AuditEntryDto; - -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.List; - -/** - * Formats audit entries as professional box-drawing tables with colored state text. - */ -public class TableFormatter { - - // Unicode box-drawing characters - private static final String VERTICAL = "│"; - private static final String HORIZONTAL = "─"; - private static final String TOP_LEFT = "┌"; - private static final String TOP_RIGHT = "┐"; - private static final String BOTTOM_LEFT = "└"; - private static final String BOTTOM_RIGHT = "┘"; - private static final String T_DOWN = "┬"; - private static final String T_UP = "┴"; - private static final String T_RIGHT = "├"; - private static final String T_LEFT = "┤"; - private static final String CROSS = "┼"; - - // ANSI color codes - private static final String RESET = "\u001B[0m"; - private static final String GREEN = "\u001B[32m"; - private static final String RED = "\u001B[31m"; - private static final String YELLOW = "\u001B[33m"; - private static final String CYAN = "\u001B[36m"; - - // Column widths - private static final int CHANGE_ID_WIDTH = 30; - private static final int STATE_WIDTH = 13; - private static final int AUTHOR_WIDTH = 18; - private static final int TIME_WIDTH = 21; - // Extended column widths - private static final int EXECUTION_ID_WIDTH = 15; - private static final int CLASS_WIDTH = 25; - private static final int METHOD_WIDTH = 15; - private static final int HOSTNAME_WIDTH = 15; - - private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); - - /** - * Print basic audit table with 4 columns: Change ID, State, Author, Time. - * - * @param entries the audit entries to display - */ - public void printBasicTable(List entries) { - List columns = new ArrayList<>(); - columns.add(new TableColumn("Change ID", CHANGE_ID_WIDTH)); - columns.add(new TableColumn("State", STATE_WIDTH, TableColumn.Alignment.CENTER)); - columns.add(new TableColumn("Author", AUTHOR_WIDTH)); - columns.add(new TableColumn("Time", TIME_WIDTH)); - - printTable(entries, columns, false); - } - - /** - * Print extended audit table with additional columns for debugging. - * - * @param entries the audit entries to display - */ - public void printExtendedTable(List entries) { - List columns = new ArrayList<>(); - columns.add(new TableColumn("Change ID", CHANGE_ID_WIDTH)); - columns.add(new TableColumn("State", STATE_WIDTH, TableColumn.Alignment.CENTER)); - columns.add(new TableColumn("Exec ID", EXECUTION_ID_WIDTH)); - columns.add(new TableColumn("Author", AUTHOR_WIDTH)); - columns.add(new TableColumn("Time", TIME_WIDTH)); - columns.add(new TableColumn("Class", CLASS_WIDTH)); - columns.add(new TableColumn("Method", METHOD_WIDTH)); - columns.add(new TableColumn("Hostname", HOSTNAME_WIDTH)); - - printTable(entries, columns, true); - } - - private void printTable(List entries, List columns, boolean extended) { - // Print top border - printTopBorder(columns); - - // Print header row - printHeaderRow(columns); - - // Print header separator - printMiddleBorder(columns); - - // Print data rows - for (AuditEntryDto entry : entries) { - printDataRow(entry, columns, extended); - } - - // Print bottom border - printBottomBorder(columns); - } - - private void printTopBorder(List columns) { - System.out.print(TOP_LEFT); - for (int i = 0; i < columns.size(); i++) { - System.out.print(repeat(HORIZONTAL, columns.get(i).getWidth())); - if (i < columns.size() - 1) { - System.out.print(T_DOWN); - } - } - System.out.println(TOP_RIGHT); - } - - private void printMiddleBorder(List columns) { - System.out.print(T_RIGHT); - for (int i = 0; i < columns.size(); i++) { - System.out.print(repeat(HORIZONTAL, columns.get(i).getWidth())); - if (i < columns.size() - 1) { - System.out.print(CROSS); - } - } - System.out.println(T_LEFT); - } - - private void printBottomBorder(List columns) { - System.out.print(BOTTOM_LEFT); - for (int i = 0; i < columns.size(); i++) { - System.out.print(repeat(HORIZONTAL, columns.get(i).getWidth())); - if (i < columns.size() - 1) { - System.out.print(T_UP); - } - } - System.out.println(BOTTOM_RIGHT); - } - - private void printHeaderRow(List columns) { - System.out.print(VERTICAL); - for (TableColumn column : columns) { - // Always center headers - String title = column.getTitle(); - int padding = column.getWidth() - title.length(); - int leftPad = padding / 2; - int rightPad = padding - leftPad; - System.out.print(spaces(leftPad) + title + spaces(rightPad)); - System.out.print(VERTICAL); - } - System.out.println(); - } - - private void printDataRow(AuditEntryDto entry, List columns, boolean extended) { - System.out.print(VERTICAL); - for (int i = 0; i < columns.size(); i++) { - if (i == 1) { - // State column: handle ANSI codes separately - String stateText = getStateText(entry.getState()); - int displayLen = getStateDisplayLength(entry.getState()); - int padding = columns.get(i).getWidth() - displayLen; - int leftPad = padding / 2; - int rightPad = padding - leftPad; - System.out.print(spaces(leftPad) + stateText + spaces(rightPad)); - } else { - String value = extended ? getExtendedColumnValue(entry, i) : getColumnValue(entry, i); - System.out.print(columns.get(i).format(value)); - } - System.out.print(VERTICAL); - } - System.out.println(); - } - - private String getColumnValue(AuditEntryDto entry, int columnIndex) { - switch (columnIndex) { - case 0: // Change ID - return entry.getTaskId(); - case 2: // Author - return entry.getAuthor(); - case 3: // Time - return formatTime(entry.getCreatedAt()); - default: - return ""; - } - } - - private String getExtendedColumnValue(AuditEntryDto entry, int columnIndex) { - switch (columnIndex) { - case 0: // Change ID - return entry.getTaskId(); - case 2: // Exec ID - return truncate(entry.getExecutionId(), EXECUTION_ID_WIDTH); - case 3: // Author - return entry.getAuthor(); - case 4: // Time - return formatTime(entry.getCreatedAt()); - case 5: // Class - return truncateClassName(entry.getClassName(), CLASS_WIDTH); - case 6: // Method - return truncate(entry.getMethodName(), METHOD_WIDTH); - case 7: // Hostname - return truncate(entry.getExecutionHostname(), HOSTNAME_WIDTH); - default: - return ""; - } - } - - private String truncate(String value, int maxLen) { - if (value == null) { - return "-"; - } - if (value.length() <= maxLen) { - return value; - } - return value.substring(0, maxLen - 3) + "..."; - } - - private String truncateClassName(String className, int maxLen) { - if (className == null) { - return "-"; - } - // Get simple class name - int lastDot = className.lastIndexOf('.'); - String simpleName = lastDot >= 0 ? className.substring(lastDot + 1) : className; - return truncate(simpleName, maxLen); - } - - private String spaces(int count) { - StringBuilder sb = new StringBuilder(count); - for (int i = 0; i < count; i++) { - sb.append(' '); - } - return sb.toString(); - } - - private String getStateText(String state) { - if (state == null) { - return "UNKNOWN"; - } - - switch (state.toUpperCase()) { - case "APPLIED": - case "MANUAL_MARKED_AS_APPLIED": - return GREEN + "APPLIED" + RESET; - case "ROLLED_BACK": - case "MANUAL_MARKED_AS_ROLLED_BACK": - return CYAN + "ROLLED_BACK" + RESET; - case "FAILED": - case "ROLLBACK_FAILED": - return RED + "FAILED" + RESET; - case "STARTED": - return YELLOW + "STARTED" + RESET; - default: - return state; - } - } - - /** - * Get the display length of the state (without ANSI codes). - */ - private int getStateDisplayLength(String state) { - if (state == null) { - return 7; // "UNKNOWN" - } - switch (state.toUpperCase()) { - case "APPLIED": - case "MANUAL_MARKED_AS_APPLIED": - return 7; // "APPLIED" - case "ROLLED_BACK": - case "MANUAL_MARKED_AS_ROLLED_BACK": - return 11; // "ROLLED_BACK" - case "FAILED": - case "ROLLBACK_FAILED": - return 6; // "FAILED" - case "STARTED": - return 7; // "STARTED" - default: - return state.length(); - } - } - - private String formatTime(LocalDateTime time) { - if (time == null) { - return "-"; - } - return time.format(TIME_FORMATTER); - } - - private String repeat(String str, int count) { - StringBuilder sb = new StringBuilder(count); - for (int i = 0; i < count; i++) { - sb.append(str); - } - return sb.toString(); - } - - /** - * Print the state legend explaining what each state means. - */ - public static void printStateLegend() { - System.out.println(); - System.out.println("State Legend:"); - System.out.println(GREEN + "APPLIED" + RESET + " - Successfully completed"); - System.out.println(CYAN + "ROLLED_BACK" + RESET + " - Reverted, needs reapplication"); - System.out.println(RED + "FAILED" + RESET + " - Execution failed"); - System.out.println(YELLOW + "STARTED" + RESET + " - Incomplete state"); - } -} diff --git a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/process/JarDetectionException.java b/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/process/JarDetectionException.java deleted file mode 100644 index eb0802846..000000000 --- a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/process/JarDetectionException.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.executor.process; - -/** - * Exception thrown when JAR type detection fails. - */ -public class JarDetectionException extends Exception { - - /** - * Constructs a new exception with the specified message. - * - * @param message the detail message - */ - public JarDetectionException(String message) { - super(message); - } - - /** - * Constructs a new exception with the specified message and cause. - * - * @param message the detail message - * @param cause the cause of this exception - */ - public JarDetectionException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/process/JarType.java b/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/process/JarType.java deleted file mode 100644 index 8c772f3da..000000000 --- a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/process/JarType.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.executor.process; - -/** - * Represents the type of JAR file for determining the appropriate execution strategy. - */ -public enum JarType { - - /** - * A Spring Boot executable JAR. - * Detected by presence of BOOT-INF/, Spring Boot loader entries, - * or Spring Boot Main-Class in manifest. - */ - SPRING_BOOT, - - /** - * A standard (non-Spring Boot) uber/shaded JAR with Flamingock runtime. - * Executed using classpath with explicit main class. - */ - PLAIN_UBER, - - /** - * A JAR that does not contain the Flamingock CLI entry point. - * This can occur when: - * - User provided a thin JAR (dependencies not bundled) - * - User built an uber JAR but flamingock-core was excluded/relocated - */ - MISSING_FLAMINGOCK_RUNTIME -} diff --git a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/process/JarTypeDetector.java b/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/process/JarTypeDetector.java deleted file mode 100644 index b83f32f33..000000000 --- a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/process/JarTypeDetector.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.executor.process; - -import java.io.File; -import java.io.IOException; -import java.util.Enumeration; -import java.util.jar.Attributes; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; -import java.util.jar.Manifest; - -/** - * Detects the type of a JAR file to determine the appropriate execution strategy. - * - *

Detection logic for Spring Boot: - *

    - *
  • Presence of any entry starting with {@code BOOT-INF/}
  • - *
  • Presence of any entry starting with {@code org/springframework/boot/loader/}
  • - *
  • Main-Class manifest attribute pointing to Spring Boot loader
  • - *
- * - *

For non-Spring Boot JARs, checks for the Flamingock CLI entry point class. - * If the entry point is missing, returns {@link JarType#MISSING_FLAMINGOCK_RUNTIME}. - */ -public class JarTypeDetector { - - private static final String BOOT_INF_PREFIX = "BOOT-INF/"; - private static final String SPRING_BOOT_LOADER_PREFIX = "org/springframework/boot/loader/"; - private static final String SPRING_BOOT_LOADER_MAIN_CLASS_PREFIX = "org.springframework.boot.loader."; - private static final String FLAMINGOCK_ENTRY_POINT = "io/flamingock/core/cli/FlamingockCliMainEntryPoint.class"; - - /** - * Detects the type of the specified JAR file. - * - * @param jarPath the path to the JAR file - * @return the detected JAR type - * @throws JarDetectionException if the JAR cannot be analyzed - */ - public JarType detect(String jarPath) throws JarDetectionException { - return detect(new File(jarPath)); - } - - /** - * Detects the type of the specified JAR file. - * - *

For non-Spring Boot JARs, also verifies the Flamingock CLI entry point is present. - * If missing, returns {@link JarType#MISSING_FLAMINGOCK_RUNTIME}. - * - * @param jarFile the JAR file - * @return the detected JAR type - * @throws JarDetectionException if the JAR cannot be analyzed - */ - public JarType detect(File jarFile) throws JarDetectionException { - validateJarFile(jarFile); - - try (JarFile jar = new JarFile(jarFile)) { - // Check manifest Main-Class first (fastest check) - if (hasSpringBootMainClass(jar)) { - return JarType.SPRING_BOOT; - } - - // Check for Spring Boot structure and Flamingock entry point in single scan - boolean isSpringBoot = false; - boolean hasFlamingockEntryPoint = false; - - Enumeration entries = jar.entries(); - while (entries.hasMoreElements()) { - JarEntry entry = entries.nextElement(); - String name = entry.getName(); - - if (name.startsWith(BOOT_INF_PREFIX) || name.startsWith(SPRING_BOOT_LOADER_PREFIX)) { - isSpringBoot = true; - } - - if (name.equals(FLAMINGOCK_ENTRY_POINT)) { - hasFlamingockEntryPoint = true; - } - - // Early exit if we found Spring Boot (no need to check entry point) - if (isSpringBoot) { - return JarType.SPRING_BOOT; - } - } - - // For non-Spring Boot JARs, verify Flamingock entry point is present - if (!hasFlamingockEntryPoint) { - return JarType.MISSING_FLAMINGOCK_RUNTIME; - } - - return JarType.PLAIN_UBER; - - } catch (IOException e) { - throw new JarDetectionException( - "Cannot read JAR file: " + jarFile.getAbsolutePath() + " - " + e.getMessage(), e); - } - } - - private void validateJarFile(File jarFile) throws JarDetectionException { - if (!jarFile.exists()) { - throw new JarDetectionException("JAR file not found: " + jarFile.getAbsolutePath()); - } - if (!jarFile.isFile()) { - throw new JarDetectionException("Path is not a file: " + jarFile.getAbsolutePath()); - } - } - - private boolean hasSpringBootMainClass(JarFile jar) throws IOException { - Manifest manifest = jar.getManifest(); - if (manifest == null) { - return false; - } - - Attributes mainAttributes = manifest.getMainAttributes(); - String mainClass = mainAttributes.getValue(Attributes.Name.MAIN_CLASS); - - return mainClass != null && mainClass.startsWith(SPRING_BOOT_LOADER_MAIN_CLASS_PREFIX); - } - -} diff --git a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/process/JvmLauncher.java b/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/process/JvmLauncher.java deleted file mode 100644 index 3d9920031..000000000 --- a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/process/JvmLauncher.java +++ /dev/null @@ -1,476 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.executor.process; - -import io.flamingock.internal.common.core.operation.OperationType; - -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -/** - * Launches a JVM process to execute an application with Flamingock CLI mode enabled. - * - *

This class handles:

- *
    - *
  • Detecting JAR type (Spring Boot vs plain uber JAR)
  • - *
  • Building the java command with appropriate flags
  • - *
  • Starting the process via ProcessBuilder
  • - *
  • Streaming stdout/stderr in real-time (when enabled)
  • - *
  • Returning structured launch results
  • - *
- */ -public class JvmLauncher { - - /** - * The fully qualified name of the Flamingock CLI entry point for non-Spring Boot JARs. - */ - static final String FLAMINGOCK_CLI_ENTRY_POINT = "io.flamingock.core.cli.FlamingockCliMainEntryPoint"; - - private final JarTypeDetector jarTypeDetector; - - /** - * Creates a new JvmLauncher with the default JarTypeDetector. - */ - public JvmLauncher() { - this(new JarTypeDetector()); - } - - /** - * Creates a new JvmLauncher with the specified JarTypeDetector. - * - * @param jarTypeDetector the detector to use for JAR type detection - */ - public JvmLauncher(JarTypeDetector jarTypeDetector) { - this.jarTypeDetector = jarTypeDetector; - } - - /** - * Launches the application with Flamingock CLI mode enabled. - * Uses the default EXECUTE operation. - * - * @param jarPath absolute path to the application JAR - * @return the launch result - */ - public LaunchResult launch(String jarPath) { - return launch(jarPath, null, null, null, true, Collections.emptyMap()); - } - - /** - * Launches the application with Flamingock CLI mode enabled, a specific operation, - * an optional output file for result communication, optional log level, and output streaming control. - * - * @param jarPath absolute path to the application JAR - * @param operation the Flamingock operation to execute, or null for default - * @param outputFile path to the output file for result communication, or null if not needed - * @param logLevel the application log level (debug, info, warn, error), or null for app default - * @param streamOutput whether to stream stdout/stderr to console (false = consume silently) - * @param operationArgs additional operation-specific arguments to pass - * @return the launch result - */ - public LaunchResult launch(String jarPath, OperationType operation, String outputFile, String logLevel, boolean streamOutput, Map operationArgs) { - String operationName = operation != null ? operation.name() : null; - List command; - JarType jarType; - - try { - jarType = jarTypeDetector.detect(jarPath); - } catch (JarDetectionException e) { - return LaunchResult.jarAnalysisFailed(e.getMessage()); - } - - // Early detection: JAR is missing Flamingock runtime - if (jarType == JarType.MISSING_FLAMINGOCK_RUNTIME) { - return LaunchResult.missingFlamingockRuntime(); - } - - command = buildCommand(jarPath, operationName, outputFile, logLevel, jarType, operationArgs); - - ProcessBuilder processBuilder = new ProcessBuilder(command); - processBuilder.directory(new File(jarPath).getParentFile()); - processBuilder.redirectErrorStream(false); - - try { - Process process = processBuilder.start(); - - Thread stdoutThread; - Thread stderrThread; - StringBuilder stderrCapture = new StringBuilder(); - - if (streamOutput) { - // Stream stdout and stderr in parallel - stdoutThread = streamOutput(process.getInputStream(), System.out); - stderrThread = streamAndCaptureOutput(process.getErrorStream(), System.err, stderrCapture); - } else { - // Consume streams silently to prevent blocking, but still capture stderr - stdoutThread = consumeSilently(process.getInputStream()); - stderrThread = captureOutput(process.getErrorStream(), stderrCapture); - } - - // Wait for the process to complete - int exitCode = process.waitFor(); - - // Wait for output streaming to complete - stdoutThread.join(); - stderrThread.join(); - - // Check for entry point not found error in non-Spring Boot path - if (exitCode != 0 && jarType == JarType.PLAIN_UBER) { - String stderr = stderrCapture.toString(); - if (isEntryPointNotFoundError(stderr)) { - return LaunchResult.entryPointNotFound(exitCode); - } - } - - if (exitCode == 0) { - return LaunchResult.success(); - } else { - return LaunchResult.processFailed(exitCode); - } - - } catch (IOException e) { - return LaunchResult.processStartFailed(e.getMessage()); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - return LaunchResult.processInterrupted(); - } - } - - /** - * Checks if the stderr output indicates the entry point class was not found. - * - * @param stderr the captured stderr output - * @return true if entry point was not found - */ - private boolean isEntryPointNotFoundError(String stderr) { - return stderr.contains("Could not find or load main class") && - stderr.contains(FLAMINGOCK_CLI_ENTRY_POINT); - } - - /** - * Builds the command line for launching the application. - * Detects the JAR type and routes to the appropriate command builder. - * - * @param jarPath path to the JAR file - * @param operation the Flamingock operation to execute, or null for default - * @param outputFile path to the output file for result communication, or null if not needed - * @param logLevel the application log level (debug, info, warn, error), or null for app default - * @return the command as a list of strings - * @throws JarDetectionException if the JAR type cannot be determined - */ - List buildCommand(String jarPath, String operation, String outputFile, String logLevel) - throws JarDetectionException { - JarType jarType = jarTypeDetector.detect(jarPath); - return buildCommand(jarPath, operation, outputFile, logLevel, jarType, Collections.emptyMap()); - } - - /** - * Builds the command line for launching the application with the specified JAR type. - * - * @param jarPath path to the JAR file - * @param operation the Flamingock operation to execute, or null for default - * @param outputFile path to the output file for result communication, or null if not needed - * @param logLevel the application log level (debug, info, warn, error), or null for app default - * @param jarType the type of JAR - * @return the command as a list of strings - */ - List buildCommand(String jarPath, String operation, String outputFile, String logLevel, JarType jarType) { - return buildCommand(jarPath, operation, outputFile, logLevel, jarType, Collections.emptyMap()); - } - - /** - * Builds the command line for launching the application with the specified JAR type. - * - * @param jarPath path to the JAR file - * @param operation the Flamingock operation to execute, or null for default - * @param outputFile path to the output file for result communication, or null if not needed - * @param logLevel the application log level (debug, info, warn, error), or null for app default - * @param jarType the type of JAR - * @param operationArgs additional operation-specific arguments - * @return the command as a list of strings - */ - List buildCommand(String jarPath, String operation, String outputFile, String logLevel, JarType jarType, Map operationArgs) { - if (jarType == JarType.SPRING_BOOT) { - return buildSpringBootCommand(jarPath, operation, outputFile, logLevel, operationArgs); - } else { - return buildPlainUberCommand(jarPath, operation, outputFile, logLevel, operationArgs); - } - } - - /** - * Builds the command line for launching a Spring Boot application. - * - * @param jarPath path to the JAR file - * @param operation the Flamingock operation to execute, or null for default - * @param outputFile path to the output file for result communication, or null if not needed - * @param logLevel the application log level (debug, info, warn, error), or null for app default - * @return the command as a list of strings - */ - List buildSpringBootCommand(String jarPath, String operation, String outputFile, String logLevel) { - return buildSpringBootCommand(jarPath, operation, outputFile, logLevel, Collections.emptyMap()); - } - - /** - * Builds the command line for launching a Spring Boot application. - * - * @param jarPath path to the JAR file - * @param operation the Flamingock operation to execute, or null for default - * @param outputFile path to the output file for result communication, or null if not needed - * @param logLevel the application log level (debug, info, warn, error), or null for app default - * @param operationArgs additional operation-specific arguments - * @return the command as a list of strings - */ - List buildSpringBootCommand(String jarPath, String operation, String outputFile, String logLevel, Map operationArgs) { - List command = new ArrayList<>(); - - // Find the java executable - command.add(getJavaExecutable()); - - // Add the JAR - command.add("-jar"); - command.add(jarPath); - - // Spring Boot flags to disable web server - command.add("--spring.main.web-application-type=none"); - - // Disable Spring Boot banner - command.add("--spring.main.banner-mode=off"); - - // Add the flamingock-cli profile - command.add("--spring.profiles.include=flamingock-cli"); - - // Enable Flamingock CLI mode - command.add("--flamingock.cli.mode=true"); - - // Add operation if specified - if (operation != null && !operation.isEmpty()) { - command.add("--flamingock.operation=" + operation); - } - - // Add output file if specified - if (outputFile != null && !outputFile.isEmpty()) { - command.add("--flamingock.output-file=" + outputFile); - } - - // Add log level if specified (Spring Boot style) - if (logLevel != null && !logLevel.isEmpty()) { - command.add("--logging.level.root=" + logLevel.toUpperCase()); - } - - // Add operation-specific arguments - if (operationArgs != null) { - for (Map.Entry entry : operationArgs.entrySet()) { - command.add("--" + entry.getKey() + "=" + entry.getValue()); - } - } - - return command; - } - - /** - * Builds the command line for launching a plain uber JAR application. - * - * @param jarPath path to the JAR file - * @param operation the Flamingock operation to execute, or null for default - * @param outputFile path to the output file for result communication, or null if not needed - * @param logLevel the application log level (debug, info, warn, error), or null for app default - * @return the command as a list of strings - */ - List buildPlainUberCommand(String jarPath, String operation, String outputFile, String logLevel) { - return buildPlainUberCommand(jarPath, operation, outputFile, logLevel, Collections.emptyMap()); - } - - /** - * Builds the command line for launching a plain uber JAR application. - * - * @param jarPath path to the JAR file - * @param operation the Flamingock operation to execute, or null for default - * @param outputFile path to the output file for result communication, or null if not needed - * @param logLevel the application log level (debug, info, warn, error), or null for app default - * @param operationArgs additional operation-specific arguments - * @return the command as a list of strings - */ - List buildPlainUberCommand(String jarPath, String operation, String outputFile, String logLevel, Map operationArgs) { - List command = new ArrayList<>(); - - // Find the java executable - command.add(getJavaExecutable()); - - // Use classpath instead of -jar - command.add("-cp"); - command.add(jarPath); - - // Specify the Flamingock CLI entry point - command.add(FLAMINGOCK_CLI_ENTRY_POINT); - - // Enable Flamingock CLI mode - command.add("--flamingock.cli.mode=true"); - - // Add operation if specified - if (operation != null && !operation.isEmpty()) { - command.add("--flamingock.operation=" + operation); - } - - // Add output file if specified - if (outputFile != null && !outputFile.isEmpty()) { - command.add("--flamingock.output-file=" + outputFile); - } - - // Add log level if specified (Flamingock-specific style) - if (logLevel != null && !logLevel.isEmpty()) { - command.add("--flamingock.log.level=" + logLevel.toUpperCase()); - } - - // Add operation-specific arguments - if (operationArgs != null) { - for (Map.Entry entry : operationArgs.entrySet()) { - command.add("--" + entry.getKey() + "=" + entry.getValue()); - } - } - - return command; - } - - /** - * Gets the path to the java executable. - * - * @return the java executable path - */ - String getJavaExecutable() { - String javaHome = System.getProperty("java.home"); - if (javaHome != null && !javaHome.isEmpty()) { - String separator = File.separator; - String executable = javaHome + separator + "bin" + separator + "java"; - - // On Windows, add .exe extension - if (System.getProperty("os.name", "").toLowerCase().contains("win")) { - executable += ".exe"; - } - - File javaFile = new File(executable); - if (javaFile.exists()) { - return executable; - } - } - - // Fall back to relying on PATH - return "java"; - } - - /** - * Creates a thread that reads from the input stream and writes to the output stream. - * - * @param inputStream the stream to read from - * @param outputStream the stream to write to - * @return the thread (already started) - */ - private Thread streamOutput(InputStream inputStream, java.io.PrintStream outputStream) { - Thread thread = new Thread(() -> { - try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) { - String line; - while ((line = reader.readLine()) != null) { - outputStream.println(line); - } - } catch (IOException e) { - // Stream closed, ignore - } - }); - thread.setDaemon(true); - thread.start(); - return thread; - } - - /** - * Creates a thread that reads from the input stream, writes to the output stream, - * and captures the content to a StringBuilder. - * - * @param inputStream the stream to read from - * @param outputStream the stream to write to - * @param capture the StringBuilder to capture output - * @return the thread (already started) - */ - private Thread streamAndCaptureOutput(InputStream inputStream, java.io.PrintStream outputStream, - StringBuilder capture) { - Thread thread = new Thread(() -> { - try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) { - String line; - while ((line = reader.readLine()) != null) { - outputStream.println(line); - synchronized (capture) { - capture.append(line).append("\n"); - } - } - } catch (IOException e) { - // Stream closed, ignore - } - }); - thread.setDaemon(true); - thread.start(); - return thread; - } - - /** - * Creates a thread that captures the input stream to a StringBuilder without printing. - * - * @param inputStream the stream to read from - * @param capture the StringBuilder to capture output - * @return the thread (already started) - */ - private Thread captureOutput(InputStream inputStream, StringBuilder capture) { - Thread thread = new Thread(() -> { - try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) { - String line; - while ((line = reader.readLine()) != null) { - synchronized (capture) { - capture.append(line).append("\n"); - } - } - } catch (IOException e) { - // Stream closed, ignore - } - }); - thread.setDaemon(true); - thread.start(); - return thread; - } - - /** - * Creates a thread that consumes the input stream silently to prevent blocking. - * - * @param inputStream the stream to consume - * @return the thread (already started) - */ - private Thread consumeSilently(InputStream inputStream) { - Thread thread = new Thread(() -> { - try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) { - while (reader.readLine() != null) { - // Discard output - } - } catch (IOException e) { - // Stream closed, ignore - } - }); - thread.setDaemon(true); - thread.start(); - return thread; - } -} diff --git a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/process/LaunchResult.java b/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/process/LaunchResult.java deleted file mode 100644 index 6ffcdc575..000000000 --- a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/process/LaunchResult.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.executor.process; - -/** - * Represents the result of launching a JVM process. - * Provides structured information about the launch outcome. - */ -public class LaunchResult { - - private final LaunchStatus status; - private final int exitCode; - private final String errorDetail; - - private LaunchResult(LaunchStatus status, int exitCode, String errorDetail) { - this.status = status; - this.exitCode = exitCode; - this.errorDetail = errorDetail; - } - - /** - * Creates a successful launch result. - * - * @return a success result with exit code 0 - */ - public static LaunchResult success() { - return new LaunchResult(LaunchStatus.SUCCESS, 0, null); - } - - /** - * Creates a result indicating the entry point was not found. - * - * @param exitCode the process exit code - * @return an entry point not found result - */ - public static LaunchResult entryPointNotFound(int exitCode) { - return new LaunchResult(LaunchStatus.ENTRY_POINT_NOT_FOUND, exitCode, null); - } - - /** - * Creates a result indicating the process failed to start. - * - * @param errorMessage the error message - * @return a process start failed result - */ - public static LaunchResult processStartFailed(String errorMessage) { - return new LaunchResult(LaunchStatus.PROCESS_START_FAILED, 1, errorMessage); - } - - /** - * Creates a result indicating the process was interrupted. - * - * @return a process interrupted result - */ - public static LaunchResult processInterrupted() { - return new LaunchResult(LaunchStatus.PROCESS_INTERRUPTED, 1, "Process was interrupted"); - } - - /** - * Creates a result indicating the process failed with a non-zero exit code. - * - * @param exitCode the process exit code - * @return a process failed result - */ - public static LaunchResult processFailed(int exitCode) { - return new LaunchResult(LaunchStatus.PROCESS_FAILED, exitCode, null); - } - - /** - * Creates a result indicating JAR analysis failed. - * - * @param errorMessage the error message - * @return a JAR analysis failed result - */ - public static LaunchResult jarAnalysisFailed(String errorMessage) { - return new LaunchResult(LaunchStatus.JAR_ANALYSIS_FAILED, 1, errorMessage); - } - - /** - * Creates a result indicating the JAR is missing the Flamingock runtime. - * - * @return a missing Flamingock runtime result - */ - public static LaunchResult missingFlamingockRuntime() { - return new LaunchResult(LaunchStatus.MISSING_FLAMINGOCK_RUNTIME, 1, null); - } - - /** - * Returns the launch status. - * - * @return the status - */ - public LaunchStatus getStatus() { - return status; - } - - /** - * Returns the process exit code. - * - * @return the exit code - */ - public int getExitCode() { - return exitCode; - } - - /** - * Returns additional error details, if any. - * - * @return the error detail, or null - */ - public String getErrorDetail() { - return errorDetail; - } - - /** - * Checks if the launch was successful. - * - * @return true if successful - */ - public boolean isSuccess() { - return status == LaunchStatus.SUCCESS; - } - - /** - * Checks if the failure was due to entry point not found. - * - * @return true if entry point was not found - */ - public boolean isEntryPointNotFound() { - return status == LaunchStatus.ENTRY_POINT_NOT_FOUND; - } - - /** - * Checks if the launch resulted in any kind of failure. - * - * @return true if any failure occurred - */ - public boolean isFailure() { - return status != LaunchStatus.SUCCESS; - } -} diff --git a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/process/LaunchStatus.java b/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/process/LaunchStatus.java deleted file mode 100644 index 6e1524c50..000000000 --- a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/process/LaunchStatus.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.executor.process; - -/** - * Represents the status of a JVM launch operation. - */ -public enum LaunchStatus { - - /** - * The process launched and completed successfully (exit code 0). - */ - SUCCESS, - - /** - * The Flamingock CLI entry point class was not found in the JAR. - * This typically means flamingock-core is not included in the uber JAR. - */ - ENTRY_POINT_NOT_FOUND, - - /** - * The process failed to start (e.g., java executable not found, IO error). - */ - PROCESS_START_FAILED, - - /** - * The process was interrupted while running. - */ - PROCESS_INTERRUPTED, - - /** - * The process completed but with a non-zero exit code. - */ - PROCESS_FAILED, - - /** - * Failed to analyze the JAR file. - */ - JAR_ANALYSIS_FAILED, - - /** - * The JAR does not contain the Flamingock CLI entry point. - * This can occur when: - * - User provided a thin JAR (dependencies not bundled) - * - User built an uber JAR but flamingock-core was excluded/relocated - */ - MISSING_FLAMINGOCK_RUNTIME -} diff --git a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/result/ResponseResultReader.java b/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/result/ResponseResultReader.java deleted file mode 100644 index da56bc26e..000000000 --- a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/result/ResponseResultReader.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright 2026 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.executor.result; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.jsontype.NamedType; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import io.flamingock.internal.common.core.response.ResponseEnvelope; -import io.flamingock.internal.common.core.response.data.AuditFixResponseData; -import io.flamingock.internal.common.core.response.data.AuditListResponseData; -import io.flamingock.internal.common.core.response.data.ExecuteResponseData; -import io.flamingock.internal.common.core.response.data.IssueGetResponseData; -import io.flamingock.internal.common.core.response.data.IssueListResponseData; -import io.flamingock.internal.util.JsonObjectMapper; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Optional; - -/** - * Reads and parses response files from spawned application processes. - */ -public class ResponseResultReader { - - private final ObjectMapper objectMapper; - - public ResponseResultReader() { - this.objectMapper = JsonObjectMapper.DEFAULT_INSTANCE.copy(); - this.objectMapper.registerModule(new JavaTimeModule()); - registerSubtypes(); - } - - private void registerSubtypes() { - objectMapper.registerSubtypes( - new NamedType(AuditListResponseData.class, "audit_list"), - new NamedType(AuditFixResponseData.class, "audit_fix"), - new NamedType(ExecuteResponseData.class, "execute"), - new NamedType(IssueListResponseData.class, "issue_list"), - new NamedType(IssueGetResponseData.class, "issue_get") - ); - } - - /** - * Reads a response envelope from a file. - * - * @param filePath the path to the response file - * @return the parsed response envelope, or empty if the file doesn't exist or parsing fails - */ - public Optional read(Path filePath) { - if (!Files.exists(filePath)) { - return Optional.empty(); - } - - try { - ResponseEnvelope envelope = objectMapper.readValue(filePath.toFile(), ResponseEnvelope.class); - return Optional.of(envelope); - } catch (IOException e) { - return Optional.empty(); - } - } - - /** - * Reads a response envelope from a file and returns it as a typed result. - * - * @param filePath the path to the response file - * @param dataType the expected type of the data field - * @param the data type - * @return the typed response result - */ - public ResponseResult readTyped(Path filePath, Class dataType) { - Optional envelope = read(filePath); - - if (!envelope.isPresent()) { - return ResponseResult.readError("Response file not found or could not be read: " + filePath); - } - - ResponseEnvelope env = envelope.get(); - - if (!env.isSuccess()) { - return ResponseResult.fromFailure(env); - } - - Object data = env.getData(); - if (data == null) { - return ResponseResult.success(env, null); - } - - if (dataType.isInstance(data)) { - return ResponseResult.success(env, dataType.cast(data)); - } - - try { - T typedData = objectMapper.convertValue(data, dataType); - return ResponseResult.success(env, typedData); - } catch (Exception e) { - return ResponseResult.readError("Failed to convert response data to " + dataType.getSimpleName()); - } - } - - /** - * Represents a typed response result. - * - * @param the data type - */ - public static class ResponseResult { - private final boolean success; - private final T data; - private final String errorCode; - private final String errorMessage; - private final boolean recoverable; - private final long durationMs; - - private ResponseResult(boolean success, T data, String errorCode, String errorMessage, boolean recoverable, long durationMs) { - this.success = success; - this.data = data; - this.errorCode = errorCode; - this.errorMessage = errorMessage; - this.recoverable = recoverable; - this.durationMs = durationMs; - } - - public static ResponseResult success(ResponseEnvelope envelope, T data) { - return new ResponseResult<>(true, data, null, null, false, envelope.getDurationMs()); - } - - public static ResponseResult fromFailure(ResponseEnvelope envelope) { - String code = envelope.getError() != null ? envelope.getError().getCode() : "UNKNOWN_ERROR"; - String message = envelope.getError() != null ? envelope.getError().getMessage() : "Unknown error"; - boolean recoverable = envelope.getError() != null && envelope.getError().isRecoverable(); - return new ResponseResult<>(false, null, code, message, recoverable, envelope.getDurationMs()); - } - - public static ResponseResult readError(String message) { - return new ResponseResult<>(false, null, "READ_ERROR", message, false, 0); - } - - public boolean isSuccess() { - return success; - } - - public T getData() { - return data; - } - - public String getErrorCode() { - return errorCode; - } - - public String getErrorMessage() { - return errorMessage; - } - - public boolean isRecoverable() { - return recoverable; - } - - public long getDurationMs() { - return durationMs; - } - } -} diff --git a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/util/VersionProvider.java b/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/util/VersionProvider.java deleted file mode 100644 index 67ef37bc4..000000000 --- a/cli/flamingock-cli-executor/src/main/java/io/flamingock/cli/executor/util/VersionProvider.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.executor.util; - -import picocli.CommandLine.IVersionProvider; - -import java.io.IOException; -import java.io.InputStream; -import java.util.Properties; -import java.util.jar.Attributes; -import java.util.jar.Manifest; - -/** - * Provides version information from the JAR manifest or build properties. - */ -public class VersionProvider implements IVersionProvider { - - private static final String UNKNOWN_VERSION = "unknown"; - - @Override - public String[] getVersion() { - String version = getVersionFromManifest(); - if (UNKNOWN_VERSION.equals(version)) { - version = getVersionFromProperties(); - } - return new String[]{"Flamingock CLI v" + version}; - } - - /** - * Gets the version string for use in headers and messages. - * - * @return the version string - */ - public static String getVersionString() { - VersionProvider provider = new VersionProvider(); - String version = provider.getVersionFromManifest(); - if (UNKNOWN_VERSION.equals(version)) { - version = provider.getVersionFromProperties(); - } - return version; - } - - private String getVersionFromManifest() { - try { - InputStream manifestStream = getClass().getClassLoader() - .getResourceAsStream("META-INF/MANIFEST.MF"); - if (manifestStream != null) { - Manifest manifest = new Manifest(manifestStream); - Attributes attrs = manifest.getMainAttributes(); - String version = attrs.getValue("Implementation-Version"); - if (version != null && !version.isEmpty()) { - return version; - } - } - } catch (IOException e) { - // Fall through to return unknown - } - return UNKNOWN_VERSION; - } - - private String getVersionFromProperties() { - try { - Properties props = new Properties(); - InputStream is = getClass().getClassLoader() - .getResourceAsStream("flamingock-cli-executor.properties"); - if (is != null) { - props.load(is); - String version = props.getProperty("version"); - if (version != null && !version.isEmpty()) { - return version; - } - } - } catch (IOException e) { - // Fall through to return unknown - } - return UNKNOWN_VERSION; - } -} diff --git a/cli/flamingock-cli-executor/src/test/java/io/flamingock/cli/executor/process/JarTypeDetectorTest.java b/cli/flamingock-cli-executor/src/test/java/io/flamingock/cli/executor/process/JarTypeDetectorTest.java deleted file mode 100644 index c4dd4ac8b..000000000 --- a/cli/flamingock-cli-executor/src/test/java/io/flamingock/cli/executor/process/JarTypeDetectorTest.java +++ /dev/null @@ -1,254 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.executor.process; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.nio.file.Path; -import java.util.jar.Attributes; -import java.util.jar.JarEntry; -import java.util.jar.JarOutputStream; -import java.util.jar.Manifest; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -/** - * Tests for JarTypeDetector. - */ -class JarTypeDetectorTest { - - @TempDir - Path tempDir; - - private JarTypeDetector detector; - - @BeforeEach - void setUp() { - detector = new JarTypeDetector(); - } - - @Test - void detect_springBootJar_withBootInf_returnsSpringBoot() throws Exception { - File jar = createJarWithEntries("spring-boot.jar", - null, - "BOOT-INF/classes/", - "BOOT-INF/lib/"); - - JarType result = detector.detect(jar); - - assertEquals(JarType.SPRING_BOOT, result); - } - - @Test - void detect_springBootJar_withLoaderClasses_returnsSpringBoot() throws Exception { - File jar = createJarWithEntries("spring-boot-loader.jar", - null, - "org/springframework/boot/loader/JarLauncher.class", - "org/springframework/boot/loader/LaunchedURLClassLoader.class"); - - JarType result = detector.detect(jar); - - assertEquals(JarType.SPRING_BOOT, result); - } - - @Test - void detect_springBootJar_byManifest_returnsSpringBoot() throws Exception { - Manifest manifest = createManifest("org.springframework.boot.loader.JarLauncher"); - File jar = createJarWithEntries("spring-boot-manifest.jar", - manifest, - "com/example/Application.class"); - - JarType result = detector.detect(jar); - - assertEquals(JarType.SPRING_BOOT, result); - } - - @Test - void detect_springBootJar_withPropertiesLauncher_returnsSpringBoot() throws Exception { - Manifest manifest = createManifest("org.springframework.boot.loader.PropertiesLauncher"); - File jar = createJarWithEntries("spring-boot-properties.jar", - manifest, - "com/example/Application.class"); - - JarType result = detector.detect(jar); - - assertEquals(JarType.SPRING_BOOT, result); - } - - @Test - void detect_plainUberJar_withFlamingockEntryPoint_returnsPlainUber() throws Exception { - Manifest manifest = createManifest("com.example.Application"); - File jar = createJarWithEntries("plain-uber.jar", - manifest, - "com/example/Application.class", - "com/example/Service.class", - "io/flamingock/core/cli/FlamingockCliMainEntryPoint.class"); - - JarType result = detector.detect(jar); - - assertEquals(JarType.PLAIN_UBER, result); - } - - @Test - void detect_jarWithoutFlamingockEntryPoint_returnsMissingRuntime() throws Exception { - Manifest manifest = createManifest("com.example.Application"); - File jar = createJarWithEntries("missing-runtime.jar", - manifest, - "com/example/Application.class", - "com/example/Service.class"); - - JarType result = detector.detect(jar); - - assertEquals(JarType.MISSING_FLAMINGOCK_RUNTIME, result); - } - - @Test - void detect_emptyJar_returnsMissingRuntime() throws Exception { - File jar = createJarWithEntries("empty.jar", null); - - JarType result = detector.detect(jar); - - assertEquals(JarType.MISSING_FLAMINGOCK_RUNTIME, result); - } - - @Test - void detect_jarWithNoManifest_andNoFlamingockEntryPoint_returnsMissingRuntime() throws Exception { - File jar = createJarWithEntries("no-manifest.jar", - null, - "com/example/Application.class"); - - JarType result = detector.detect(jar); - - assertEquals(JarType.MISSING_FLAMINGOCK_RUNTIME, result); - } - - @Test - void detect_thinJar_withOnlyAppClasses_returnsMissingRuntime() throws Exception { - // Simulates a thin JAR - only app classes, no dependencies bundled - Manifest manifest = createManifest("com.example.Application"); - File jar = createJarWithEntries("thin-jar.jar", - manifest, - "com/example/Application.class", - "com/example/Service.class", - "com/example/Repository.class"); - - JarType result = detector.detect(jar); - - assertEquals(JarType.MISSING_FLAMINGOCK_RUNTIME, result); - } - - @Test - void detect_uberJar_withFlamingockCore_butMissingEntryPoint_returnsMissingRuntime() throws Exception { - // Simulates uber JAR where flamingock-core was relocated or entry point excluded - Manifest manifest = createManifest("com.example.Application"); - File jar = createJarWithEntries("relocated-flamingock.jar", - manifest, - "com/example/Application.class", - "io/flamingock/core/SomeOtherClass.class", // Has flamingock-core but not entry point - "io/flamingock/api/SomeApiClass.class"); - - JarType result = detector.detect(jar); - - assertEquals(JarType.MISSING_FLAMINGOCK_RUNTIME, result); - } - - @Test - void detect_nonExistentJar_throwsException() { - File nonExistent = new File(tempDir.toFile(), "does-not-exist.jar"); - - JarDetectionException exception = assertThrows(JarDetectionException.class, - () -> detector.detect(nonExistent)); - - assertTrue(exception.getMessage().contains("JAR file not found")); - } - - @Test - void detect_directoryInsteadOfFile_throwsException() { - File directory = tempDir.toFile(); - - JarDetectionException exception = assertThrows(JarDetectionException.class, - () -> detector.detect(directory)); - - assertTrue(exception.getMessage().contains("Path is not a file")); - } - - @Test - void detect_corruptJar_throwsException() throws Exception { - File corruptJar = new File(tempDir.toFile(), "corrupt.jar"); - try (FileOutputStream fos = new FileOutputStream(corruptJar)) { - fos.write("not a valid jar file".getBytes()); - } - - JarDetectionException exception = assertThrows(JarDetectionException.class, - () -> detector.detect(corruptJar)); - - assertTrue(exception.getMessage().contains("Cannot read JAR file")); - } - - @Test - void detect_stringPath_works() throws Exception { - Manifest manifest = createManifest("com.example.Application"); - File jar = createJarWithEntries("string-path.jar", - manifest, - "com/example/Application.class", - "io/flamingock/core/cli/FlamingockCliMainEntryPoint.class"); - - JarType result = detector.detect(jar.getAbsolutePath()); - - assertEquals(JarType.PLAIN_UBER, result); - } - - private File createJarWithEntries(String name, Manifest manifest, String... entryNames) throws IOException { - File jarFile = new File(tempDir.toFile(), name); - - Manifest m = manifest; - if (m == null) { - m = new Manifest(); - m.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0"); - } - - try (JarOutputStream jos = new JarOutputStream(new FileOutputStream(jarFile), m)) { - for (String entryName : entryNames) { - JarEntry entry = new JarEntry(entryName); - jos.putNextEntry(entry); - // Write a dummy byte for class files - if (entryName.endsWith(".class")) { - jos.write(0xCA); - jos.write(0xFE); - jos.write(0xBA); - jos.write(0xBE); - } - jos.closeEntry(); - } - } - - return jarFile; - } - - private Manifest createManifest(String mainClass) { - Manifest manifest = new Manifest(); - manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0"); - manifest.getMainAttributes().put(Attributes.Name.MAIN_CLASS, mainClass); - return manifest; - } -} diff --git a/cli/flamingock-cli-executor/src/test/java/io/flamingock/cli/executor/process/JvmLauncherTest.java b/cli/flamingock-cli-executor/src/test/java/io/flamingock/cli/executor/process/JvmLauncherTest.java deleted file mode 100644 index 618ba5332..000000000 --- a/cli/flamingock-cli-executor/src/test/java/io/flamingock/cli/executor/process/JvmLauncherTest.java +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.executor.process; - -import org.junit.jupiter.api.Test; - -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -/** - * Tests for JvmLauncher command building. - */ -class JvmLauncherTest { - - // ================== Spring Boot Command Tests ================== - - @Test - void buildSpringBootCommand_shouldContainJarFlag() { - JvmLauncher launcher = new JvmLauncher(); - List command = launcher.buildSpringBootCommand("/path/to/app.jar", null, null, null); - - assertTrue(command.contains("-jar")); - assertTrue(command.contains("/path/to/app.jar")); - } - - @Test - void buildSpringBootCommand_shouldContainSpringWebDisabled() { - JvmLauncher launcher = new JvmLauncher(); - List command = launcher.buildSpringBootCommand("/path/to/app.jar", null, null, null); - - assertTrue(command.contains("--spring.main.web-application-type=none")); - } - - @Test - void buildSpringBootCommand_shouldContainCliProfile() { - JvmLauncher launcher = new JvmLauncher(); - List command = launcher.buildSpringBootCommand("/path/to/app.jar", null, null, null); - - assertTrue(command.contains("--spring.profiles.include=flamingock-cli")); - } - - @Test - void buildSpringBootCommand_shouldContainCliModeFlag() { - JvmLauncher launcher = new JvmLauncher(); - List command = launcher.buildSpringBootCommand("/path/to/app.jar", null, null, null); - - assertTrue(command.contains("--flamingock.cli.mode=true")); - } - - @Test - void buildSpringBootCommand_shouldDisableBanner() { - JvmLauncher launcher = new JvmLauncher(); - List command = launcher.buildSpringBootCommand("/path/to/app.jar", null, null, null); - - assertTrue(command.contains("--spring.main.banner-mode=off")); - } - - @Test - void buildSpringBootCommand_shouldHaveCorrectFlagCountWithoutOperation() { - JvmLauncher launcher = new JvmLauncher(); - List command = launcher.buildSpringBootCommand("/path/to/app.jar", null, null, null); - - // java -jar + 4 flags = 7 elements - assertEquals(7, command.size()); - } - - @Test - void buildSpringBootCommand_shouldIncludeOperationWhenProvided() { - JvmLauncher launcher = new JvmLauncher(); - List command = launcher.buildSpringBootCommand("/path/to/app.jar", "EXECUTE", null, null); - - assertTrue(command.contains("--flamingock.operation=EXECUTE")); - // java -jar + 4 flags + operation = 8 elements - assertEquals(8, command.size()); - } - - @Test - void buildSpringBootCommand_shouldIncludeListOperation() { - JvmLauncher launcher = new JvmLauncher(); - List command = launcher.buildSpringBootCommand("/path/to/app.jar", "LIST", null, null); - - assertTrue(command.contains("--flamingock.operation=LIST")); - } - - @Test - void buildSpringBootCommand_shouldNotIncludeOperationWhenEmpty() { - JvmLauncher launcher = new JvmLauncher(); - List command = launcher.buildSpringBootCommand("/path/to/app.jar", "", null, null); - - // Should not contain any operation flag - for (String arg : command) { - assertFalse(arg.startsWith("--flamingock.operation=")); - } - assertEquals(7, command.size()); - } - - @Test - void buildSpringBootCommand_shouldIncludeLogLevelWhenProvided() { - JvmLauncher launcher = new JvmLauncher(); - List command = launcher.buildSpringBootCommand("/path/to/app.jar", "EXECUTE", null, "debug"); - - assertTrue(command.contains("--logging.level.root=DEBUG")); - } - - @Test - void buildSpringBootCommand_shouldUppercaseLogLevel() { - JvmLauncher launcher = new JvmLauncher(); - List command = launcher.buildSpringBootCommand("/path/to/app.jar", null, null, "info"); - - assertTrue(command.contains("--logging.level.root=INFO")); - } - - @Test - void buildSpringBootCommand_shouldNotIncludeLogLevelWhenNull() { - JvmLauncher launcher = new JvmLauncher(); - List command = launcher.buildSpringBootCommand("/path/to/app.jar", null, null, null); - - for (String arg : command) { - assertFalse(arg.startsWith("--logging.level.root=")); - } - } - - @Test - void buildSpringBootCommand_shouldNotIncludeLogLevelWhenEmpty() { - JvmLauncher launcher = new JvmLauncher(); - List command = launcher.buildSpringBootCommand("/path/to/app.jar", null, null, ""); - - for (String arg : command) { - assertFalse(arg.startsWith("--logging.level.root=")); - } - } - - @Test - void buildSpringBootCommand_shouldIncludeOutputFileWhenProvided() { - JvmLauncher launcher = new JvmLauncher(); - List command = launcher.buildSpringBootCommand("/path/to/app.jar", null, "/tmp/output.json", null); - - assertTrue(command.contains("--flamingock.output-file=/tmp/output.json")); - } - - // ================== Plain Uber JAR Command Tests ================== - - @Test - void buildPlainUberCommand_usesClasspath() { - JvmLauncher launcher = new JvmLauncher(); - List command = launcher.buildPlainUberCommand("/path/to/app.jar", null, null, null); - - assertTrue(command.contains("-cp")); - assertTrue(command.contains("/path/to/app.jar")); - assertFalse(command.contains("-jar")); - } - - @Test - void buildPlainUberCommand_usesEntryPoint() { - JvmLauncher launcher = new JvmLauncher(); - List command = launcher.buildPlainUberCommand("/path/to/app.jar", null, null, null); - - assertTrue(command.contains(JvmLauncher.FLAMINGOCK_CLI_ENTRY_POINT)); - } - - @Test - void buildPlainUberCommand_noSpringFlags() { - JvmLauncher launcher = new JvmLauncher(); - List command = launcher.buildPlainUberCommand("/path/to/app.jar", null, null, null); - - for (String arg : command) { - assertFalse(arg.startsWith("--spring."), "Should not contain Spring flags: " + arg); - } - } - - @Test - void buildPlainUberCommand_includesFlamingockFlags() { - JvmLauncher launcher = new JvmLauncher(); - List command = launcher.buildPlainUberCommand("/path/to/app.jar", "EXECUTE", "/tmp/output.json", null); - - assertTrue(command.contains("--flamingock.cli.mode=true")); - assertTrue(command.contains("--flamingock.operation=EXECUTE")); - assertTrue(command.contains("--flamingock.output-file=/tmp/output.json")); - } - - @Test - void buildPlainUberCommand_usesFlamingockLogLevel() { - JvmLauncher launcher = new JvmLauncher(); - List command = launcher.buildPlainUberCommand("/path/to/app.jar", null, null, "debug"); - - assertTrue(command.contains("--flamingock.log.level=DEBUG")); - // Should not use Spring-style logging - for (String arg : command) { - assertFalse(arg.startsWith("--logging.level.root=")); - } - } - - @Test - void buildPlainUberCommand_shouldHaveCorrectFlagCount() { - JvmLauncher launcher = new JvmLauncher(); - List command = launcher.buildPlainUberCommand("/path/to/app.jar", null, null, null); - - // java -cp + cli.mode = 5 elements - assertEquals(5, command.size()); - } - - @Test - void buildPlainUberCommand_shouldIncludeOperationWhenProvided() { - JvmLauncher launcher = new JvmLauncher(); - List command = launcher.buildPlainUberCommand("/path/to/app.jar", "LIST", null, null); - - assertTrue(command.contains("--flamingock.operation=LIST")); - // java -cp + cli.mode + operation = 6 elements - assertEquals(6, command.size()); - } - - @Test - void buildPlainUberCommand_shouldNotIncludeOperationWhenEmpty() { - JvmLauncher launcher = new JvmLauncher(); - List command = launcher.buildPlainUberCommand("/path/to/app.jar", "", null, null); - - for (String arg : command) { - assertFalse(arg.startsWith("--flamingock.operation=")); - } - } - - @Test - void buildPlainUberCommand_shouldNotIncludeLogLevelWhenNull() { - JvmLauncher launcher = new JvmLauncher(); - List command = launcher.buildPlainUberCommand("/path/to/app.jar", null, null, null); - - for (String arg : command) { - assertFalse(arg.startsWith("--flamingock.log.level=")); - } - } - - // ================== JAR Type Routing Tests ================== - - @Test - void buildCommand_routesToSpringBootForSpringBootJar() { - JvmLauncher launcher = new JvmLauncher(); - List command = launcher.buildCommand("/path/to/app.jar", null, null, null, JarType.SPRING_BOOT); - - assertTrue(command.contains("-jar")); - assertTrue(command.contains("--spring.main.web-application-type=none")); - } - - @Test - void buildCommand_routesToPlainUberForPlainUberJar() { - JvmLauncher launcher = new JvmLauncher(); - List command = launcher.buildCommand("/path/to/app.jar", null, null, null, JarType.PLAIN_UBER); - - assertTrue(command.contains("-cp")); - assertTrue(command.contains(JvmLauncher.FLAMINGOCK_CLI_ENTRY_POINT)); - } - - // ================== General Tests ================== - - @Test - void getJavaExecutable_shouldReturnNonEmpty() { - JvmLauncher launcher = new JvmLauncher(); - String javaExecutable = launcher.getJavaExecutable(); - - assertTrue(javaExecutable != null && !javaExecutable.isEmpty()); - } -} diff --git a/cli/flamingock-cli-executor/src/test/java/io/flamingock/cli/executor/result/ResponseResultReaderTest.java b/cli/flamingock-cli-executor/src/test/java/io/flamingock/cli/executor/result/ResponseResultReaderTest.java deleted file mode 100644 index d9aa04c1c..000000000 --- a/cli/flamingock-cli-executor/src/test/java/io/flamingock/cli/executor/result/ResponseResultReaderTest.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright 2026 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.executor.result; - -import io.flamingock.internal.common.core.response.ResponseEnvelope; -import io.flamingock.internal.common.core.response.data.AuditListResponseData; -import io.flamingock.internal.common.core.response.data.ExecuteResponseData; -import io.flamingock.internal.common.core.response.data.ExecutionStatus; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Optional; - -import static org.junit.jupiter.api.Assertions.*; - -/** - * Tests for ResponseResultReader - reads and parses JSON response files from spawned processes. - */ -class ResponseResultReaderTest { - - private ResponseResultReader reader; - - @TempDir - Path tempDir; - - @BeforeEach - void setUp() { - reader = new ResponseResultReader(); - } - - @Test - @DisplayName("Should parse valid execute response JSON") - void shouldParseValidExecuteResponse() throws IOException { - // Given - String json = "{\n" + - " \"success\": true,\n" + - " \"operation\": \"EXECUTE\",\n" + - " \"timestamp\": \"2026-02-09T10:00:00Z\",\n" + - " \"durationMs\": 1500,\n" + - " \"data\": {\n" + - " \"@type\": \"execute\",\n" + - " \"status\": \"SUCCESS\",\n" + - " \"totalStages\": 2,\n" + - " \"completedStages\": 2,\n" + - " \"failedStages\": 0,\n" + - " \"totalChanges\": 5,\n" + - " \"appliedChanges\": 3,\n" + - " \"skippedChanges\": 2,\n" + - " \"failedChanges\": 0,\n" + - " \"totalDurationMs\": 1200\n" + - " }\n" + - "}"; - - Path responseFile = tempDir.resolve("response.json"); - Files.write(responseFile, json.getBytes(StandardCharsets.UTF_8)); - - // When - ResponseResultReader.ResponseResult result = - reader.readTyped(responseFile, ExecuteResponseData.class); - - // Then - assertTrue(result.isSuccess()); - assertNotNull(result.getData()); - assertEquals(ExecutionStatus.SUCCESS, result.getData().getStatus()); - assertEquals(2, result.getData().getTotalStages()); - assertEquals(2, result.getData().getCompletedStages()); - assertEquals(5, result.getData().getTotalChanges()); - assertEquals(3, result.getData().getAppliedChanges()); - assertEquals(2, result.getData().getSkippedChanges()); - assertEquals(0, result.getData().getFailedChanges()); - assertEquals(1500, result.getDurationMs()); - } - - @Test - @DisplayName("Should parse valid audit list response JSON") - void shouldParseValidAuditListResponse() throws IOException { - // Given - String json = "{\n" + - " \"success\": true,\n" + - " \"operation\": \"LIST\",\n" + - " \"timestamp\": \"2026-02-09T10:00:00Z\",\n" + - " \"durationMs\": 250,\n" + - " \"data\": {\n" + - " \"@type\": \"audit_list\",\n" + - " \"entries\": [\n" + - " {\n" + - " \"taskId\": \"change-001\",\n" + - " \"author\": \"developer\",\n" + - " \"state\": \"APPLIED\",\n" + - " \"stageId\": \"stage-1\",\n" + - " \"executionMillis\": 100\n" + - " },\n" + - " {\n" + - " \"taskId\": \"change-002\",\n" + - " \"author\": \"developer\",\n" + - " \"state\": \"APPLIED\",\n" + - " \"stageId\": \"stage-1\",\n" + - " \"executionMillis\": 150\n" + - " }\n" + - " ]\n" + - " }\n" + - "}"; - - Path responseFile = tempDir.resolve("response.json"); - Files.write(responseFile, json.getBytes(StandardCharsets.UTF_8)); - - // When - ResponseResultReader.ResponseResult result = - reader.readTyped(responseFile, AuditListResponseData.class); - - // Then - assertTrue(result.isSuccess()); - assertNotNull(result.getData()); - assertNotNull(result.getData().getEntries()); - assertEquals(2, result.getData().getEntries().size()); - assertEquals("change-001", result.getData().getEntries().get(0).getTaskId()); - assertEquals("developer", result.getData().getEntries().get(0).getAuthor()); - assertEquals("APPLIED", result.getData().getEntries().get(0).getState()); - assertEquals(250, result.getDurationMs()); - } - - @Test - @DisplayName("Should return empty when file not found") - void shouldReturnEmptyWhenFileNotFound() { - // Given - Path nonExistentFile = tempDir.resolve("does-not-exist.json"); - - // When - Optional result = reader.read(nonExistentFile); - - // Then - assertFalse(result.isPresent()); - } - - @Test - @DisplayName("Should handle corrupt JSON gracefully") - void shouldHandleCorruptJson() throws IOException { - // Given - String corruptJson = "{ this is not valid json !!!"; - Path responseFile = tempDir.resolve("corrupt.json"); - Files.write(responseFile, corruptJson.getBytes(StandardCharsets.UTF_8)); - - // When - Optional result = reader.read(responseFile); - - // Then - assertFalse(result.isPresent()); - } -} diff --git a/cli/flamingock-cli/CLI-README.md b/cli/flamingock-cli/CLI-README.md deleted file mode 100644 index 5fd02e36b..000000000 --- a/cli/flamingock-cli/CLI-README.md +++ /dev/null @@ -1,250 +0,0 @@ -# Flamingock CLI - -A lightweight command-line interface for Flamingock audit operations in community edition environments. - -## Quick Start - -### Build CLI -```bash -# Build CLI with distribution -./gradlew :cli:flamingock-cli:build - -# Build only (without distribution) -./gradlew :cli:flamingock-cli:classes - -# Create uber JAR only -./gradlew :cli:flamingock-cli:uberJar - -# Generate distribution only -./gradlew :cli:flamingock-cli:generateDistribution -``` - -### Use CLI -```bash -# List conflicted audit entries -./flamingock-cli-dist/flamingock audit list - -# Mark change as applied -./flamingock-cli-dist/flamingock audit mark --change-id ch1 --state APPLIED - -# Mark change as rolled back -./flamingock-cli-dist/flamingock audit mark --change-id ch2 --state ROLLED_BACK - -# Use custom configuration file -./flamingock-cli-dist/flamingock -c /path/to/config.yml audit list - -# Show help -./flamingock-cli-dist/flamingock --help -./flamingock-cli-dist/flamingock audit --help -./flamingock-cli-dist/flamingock audit mark --help -``` - -## Development Workflow - -### When you modify OpsClient or core components: -1. **Build entire project** (includes CLI): - ```bash - ./gradlew build - ``` - -2. **Distribution is automatically updated** in `flamingock-cli-dist/` - -3. **Test your changes**: - ```bash - ./flamingock-cli-dist/flamingock --version - ./flamingock-cli-dist/flamingock audit list - ``` - -No manual steps required - everything is automated! - -### For CLI-specific development: -```bash -# Quick CLI-only build -./gradlew :cli:flamingock-cli:build - -# Clean CLI artifacts -./gradlew :cli:flamingock-cli:clean - -# Run CLI directly (without distribution) -./gradlew :cli:flamingock-cli:run --args="audit list" -``` - -## Distribution Contents - -After building, `flamingock-cli-dist/` contains: - -- **`flamingock-cli.jar`** - Self-contained executable JAR with all dependencies -- **`flamingock`** - Unix/Linux/macOS executable shell script -- **`flamingock.bat`** - Windows executable batch file -- **`flamingock-cli.yml`** - Sample configuration file - -## Configuration - -### MongoDB Example -```yaml -flamingock: - service-identifier: "flamingock-cli" - audit: - mongodb: - connection-string: "mongodb://localhost:27017" - database: "myapp" -``` - -### Alternative MongoDB Configuration -```yaml -flamingock: - service-identifier: "flamingock-cli" - audit: - mongodb: - host: "localhost" - port: 27017 - database: "myapp" - username: "user" - password: "pass" -``` - -### DynamoDB Example -```yaml -flamingock: - service-identifier: "flamingock-cli" - audit: - dynamodb: - region: "us-east-1" - # endpoint: "http://localhost:8000" # Optional for local DynamoDB - # access-key: "your-key" # Optional, uses AWS credential chain - # secret-key: "your-secret" # Optional, uses AWS credential chain -``` - -### Couchbase Example -```yaml -flamingock: - service-identifier: "flamingock-cli" - audit: - couchbase: - endpoint: "couchbase://localhost:12110" - username: "your-username" - password: "your-password" - bucket-name: "test" - table: "flamingockAuditLog" # Optional, defaults to "flamingockAuditLog" -``` - -### SQL Example -```yaml -flamingock: - service-identifier: "flamingock-cli" - audit: - sql: - endpoint: "jdbc:sqlserver://localhost:1433/test-db" - username: "your-username" - password: "your-password" - sql-dialect: "SqlServer" - table: "flamingockAuditLog" # Optional, defaults to "flamingockAuditLog" -``` - -### Configuration File Resolution -1. Command line argument: `--config /path/to/file.yml` -2. Default: `flamingock-cli.yml` in bin directory - -## Architecture - -### OpsClient Integration -The CLI uses `OpsClientBuilder` from Flamingock core: - -```java -// Create context with dependencies -Context context = new SimpleContext(); -context.addDependency(new CommunityConfiguration()); -context.addDependency(MongoClient.class, mongoClient); -context.addDependency(MongoDatabase.class, mongoDatabase); - -// Build OpsClient -AuditStore auditStore = new MongoDBSyncAuditStore(); -OpsClient client = new OpsClientBuilder(coreConfig, context, auditStore).build(); - -// Use OpsClient methods -client.getConflictedAuditEntries(); -client.markAsSuccess(changeId); -client.markAsRolledBack(changeId); -``` - -### Database Support -- **MongoDB**: Uses MongoDB Sync driver (not Spring Data) -- **DynamoDB**: Uses AWS SDK v2 -- **Couchbase**: Uses Couchbase driver v3.7.3 -- **Auto-detection**: Based on YAML configuration structure -- **Validation**: Connection testing during client creation - -### Dependency Injection -- CLI builds its own `Context` with required dependencies -- Database clients are created and injected via `Context.addDependency()` -- No Spring Boot context loading - lightweight and fast - -## Commands - -### `flamingock audit list` -Lists all conflicted audit entries showing: -- Change ID, execution ID, stage ID -- Author, state, type, class, method -- Execution time, hostname, timestamps -- Error traces (if any) - -### `flamingock audit mark` -Marks a change with a specific state: -- **`--state APPLIED`** - Marks as successfully applied -- **`--state ROLLED_BACK`** - Marks as rolled back -- **`--change-id `** - Required change identifier - -## Error Handling -- **Configuration errors**: Missing files, invalid YAML, multiple databases -- **Connection errors**: Database connectivity, authentication failures -- **Validation errors**: Missing required fields, invalid change IDs -- **Debug mode**: Set `flamingock.debug` system property for stack traces - -## Logging -Uses SLF4J with simple implementation for clean, professional output. - -## Build System Integration - -### Gradle Tasks -| Task | Description | -|--------------------------------------------|----------------------------------| -| `:cli:flamingock-cli:build` | Full build with distribution | -| `:cli:flamingock-cli:uberJar` | Create self-contained JAR only | -| `:cli:flamingock-cli:generateDistribution` | Generate executable scripts only | -| `:cli:flamingock-cli:clean` | Remove all CLI artifacts | - -### Automatic Distribution -The build process automatically: -1. Compiles all CLI sources -2. Creates UberJar with dependencies -3. Generates platform-specific executable scripts -4. Sets proper Unix file permissions -5. Creates sample configuration file -6. Places everything in `flamingock-cli-dist/` - -### Integration with Core Changes -When you modify `OpsClient`, `AuditStore`, or any core component: -1. Run `./gradlew build` (builds entire project) -2. CLI distribution automatically reflects your changes -3. Test with `./flamingock-cli-dist/flamingock` - -This ensures CLI stays synchronized with core development. - -## Distribution for Production - -The `flamingock-cli-dist/` directory is ready for production deployment: - -1. **Copy the entire directory** to target system -2. **Add to PATH** (optional): - ```bash - export PATH=$PATH:/path/to/flamingock-cli-dist - ``` -3. **Use anywhere**: - ```bash - flamingock audit list - ``` - -Or use directly without PATH modification: -```bash -/path/to/flamingock-cli-dist/flamingock audit list -``` diff --git a/cli/flamingock-cli/CLI_SPECIFICATION.md b/cli/flamingock-cli/CLI_SPECIFICATION.md deleted file mode 100644 index a79445ef4..000000000 --- a/cli/flamingock-cli/CLI_SPECIFICATION.md +++ /dev/null @@ -1,96 +0,0 @@ -# Flamingock CLI Specification - -The CLI follows a **docker-like style**: - -``` -flamingock [command] [operation] [options] -``` - -## General Principles - -- The CLI is **lightweight**: - - It only builds an `OpsClient` via the `OpsClientBuilder`. - - All business logic is delegated to the `OpsClient` (or components it delegates to). - - The CLI only parses input, calls `OpsClient`, and formats output. - -- Default behavior: - - `flamingock audit list` (with no flags) → shows the **current snapshot** (latest state per change). - - Flags modify or filter the behavior. - ---- - -## Commands - -### 1. `audit list` - -List audits, with optional filters. - -**Base command:** -``` -flamingock audit list -``` - -**Options:** -- `--issues` (boolean, no value) → only audits with issues -- `--history` (boolean, no value) → full chronological audit history (all states, ordered by time) -- `--since ` (string, ISO-8601, e.g. `2025-01-01T00:00:00`) → list audits since a given date -- `--limit ` (integer) → pagination limit -- `--page ` (integer) → pagination page - -**Examples:** -```bash -flamingock audit list -flamingock audit list --issues -flamingock audit list --history -flamingock audit list --since 2025-01-01T00:00:00 -flamingock audit list --limit 50 --page 2 -``` - ---- - -### 2. `audit mark` - -Forcefully mark a given **change** with a new state. - -**Base command:** -``` -flamingock audit mark --change-unit --state -``` - -**Options:** -- `--change-unit ` / `-c ` → the changeId (required) -- `--state ` / `-s ` → the state to mark (`APPLIED` or `ROLLED_BACK`) (required) - -**Examples:** -```bash -flamingock audit mark --change-unit CU123 --state APPLIED -flamingock audit mark -c CU123 -s ROLLED_BACK -``` - ---- - -## Global Options - -- `--config ` / `-c ` → path to configuration file (default: `flamingock-cli.yml`) -- `--help` / `-h` → show help information -- `--version` / `-V` → show version information - ---- - -## Summary - -- Command structure: `flamingock [command] [operation] [options]` -- For now, only the `audit` command is implemented with: - - `list` (default snapshot, optional filters `--issues`, `--history`, `--since`, pagination) - - `mark` (force a state for a change) -- The CLI is a thin layer: config → build OpsClient → call → format result. - ---- - -## Implementation Notes - -- **Default behavior change**: The `audit list` command now shows a **snapshot view** (latest state per change) by default, not just conflicted entries. -- **History flag**: Use `--history` to get the full chronological audit history. -- **Issues flag**: Use `--issues` to filter for only entries with problems. -- **Since flag**: Use `--since` with ISO-8601 format to filter by date. -- **Pagination**: Client-side pagination is supported with `--limit` and `--page` flags. diff --git a/cli/flamingock-cli/DEVELOPMENT.md b/cli/flamingock-cli/DEVELOPMENT.md deleted file mode 100644 index aba86a1a3..000000000 --- a/cli/flamingock-cli/DEVELOPMENT.md +++ /dev/null @@ -1,314 +0,0 @@ -# Flamingock CLI Development Guide - -## Testing & Debugging - -### Quick Test Commands - -```bash -# Run all tests -./gradlew :cli:flamingock-cli:test - -# Run tests with detailed output -./gradlew :cli:flamingock-cli:test --info - -# Run specific test class -./gradlew :cli:flamingock-cli:test --tests "ConfigLoaderTest" - -# Run specific test method -./gradlew :cli:flamingock-cli:test --tests "ConfigLoaderTest.shouldLoadMongoDBConfiguration" -``` - -### Debug CLI Execution - -```bash -# Debug with full logging (creates flamingock-cli-debug.log) -./gradlew :cli:flamingock-cli:debugCli --args="audit list" - -# Debug with custom arguments -./gradlew :cli:flamingock-cli:debugCli --args="--config custom.yml audit mark --change-id test --state APPLIED" - -# Test CLI without database connection attempts -./gradlew :cli:flamingock-cli:testCli --args="--help" - -# Quick debug - just test the CLI structure -./gradlew :cli:flamingock-cli:testCli --args="audit" -``` - -### Remote Debugging - -The `debugCli` task automatically starts JVM debugging on port 5005: - -1. **Start debugger**: `./gradlew :cli:flamingock-cli:debugCli --args="audit list"` -2. **Attach debugger** in your IDE: - - **IntelliJ**: Run → Attach to Process → localhost:5005 - - **VS Code**: Use launch.json configuration (see below) - -### Database Testing - -#### MongoDB Integration Tests -```bash -# Set environment variable to enable MongoDB tests -export MONGO_TEST=true -./gradlew :cli:flamingock-cli:test --tests "*MongoClientFactoryTest*" - -# Or set for integration tests with real MongoDB -export MONGO_INTEGRATION_TEST=true -./gradlew :cli:flamingock-cli:test --tests "*AuditServiceTest*" -``` - -#### Integration Tests with TestContainers - -**MongoDB CLI Integration Tests** - Full CLI testing with real MongoDB: -```bash -# Run MongoDB integration tests (requires Docker) -./gradlew :cli:flamingock-cli:test --tests "*CLIMongoIntegrationTest*" - -# Run single test -./gradlew :cli:flamingock-cli:test --tests "CLIMongoIntegrationTest.shouldRunAuditListCommandWithMongoDB" -``` - -**DynamoDB CLI Integration Tests** - Full CLI testing with local DynamoDB: -```bash -# Run DynamoDB integration tests (requires Docker) -./gradlew :cli:flamingock-cli:test --tests "*CLIDynamoIntegrationTest*" - -# Run single test -./gradlew :cli:flamingock-cli:test --tests "CLIDynamoIntegrationTest.shouldRunAuditListCommandWithDynamoDB" -``` - -**Complete CLI Command Execution Tests**: -```bash -# Test all CLI commands end-to-end (requires Docker) -./gradlew :cli:flamingock-cli:test --tests "*CLICommandExecutionTest*" - -# Test OpsClient creation specifically -./gradlew :cli:flamingock-cli:test --tests "CLICommandExecutionTest.shouldVerifyOpsClientCreation" -``` - -**Run All Integration Tests**: -```bash -# Run all integration tests at once (requires Docker) -./gradlew :cli:flamingock-cli:test --tests "*Integration*" - -# Run all tests including unit and integration tests -./gradlew :cli:flamingock-cli:test -``` - -#### Legacy DynamoDB Integration Tests -```bash -# For AWS DynamoDB tests (requires credentials) -export AWS_TEST=true -./gradlew :cli:flamingock-cli:test --tests "*DynamoDBClientFactoryTest*" - -# For local DynamoDB tests (requires local DynamoDB on port 8000) -export DYNAMO_LOCAL_TEST=true -./gradlew :cli:flamingock-cli:test --tests "*DynamoDBClientFactoryTest*" - -# For integration tests -export DYNAMO_INTEGRATION_TEST=true -./gradlew :cli:flamingock-cli:test --tests "*AuditServiceTest*" -``` - -### Test Categories - -**Unit Tests** - Fast, no external dependencies: -- `ConfigLoaderTest` - YAML parsing and validation -- `*FactoryTest` - Database client creation logic -- `CLIIntegrationTest` - Command line argument parsing - -**Integration Tests** - Require databases (disabled by default): -- `AuditServiceTest` - End-to-end service testing -- Enabled via environment variables only - -### Logging Configuration - -**Development** (with Logback): -- File: `flamingock-cli-debug.log` -- Console: Detailed formatting with timestamps -- Flamingock logs: DEBUG level -- MongoDB driver: INFO level - -**Production** (in UberJAR): -- Uses SLF4J Simple -- Console: Clean output -- Minimal logging levels - -### IDE Configuration - -#### IntelliJ IDEA - -**Debug Configuration:** -``` -Main class: io.flamingock.cli.FlamingockCli -Program arguments: audit list -Working directory: /path/to/flamingock-cli-dist -VM options: -Dflamingock.debug=true -Dlogback.configurationFile=logback-debug.xml -``` - -**Remote Debug Configuration:** -``` -Host: localhost -Port: 5005 -Use module classpath: flamingock-cli.main -``` - -#### VS Code launch.json - -```json -{ - "version": "0.2.0", - "configurations": [ - { - "type": "java", - "name": "Debug CLI", - "request": "launch", - "mainClass": "io.flamingock.cli.FlamingockCli", - "projectName": "flamingock-cli", - "args": ["audit", "list"], - "cwd": "${workspaceFolder}/flamingock-cli-dist", - "vmArgs": "-Dflamingock.debug=true" - }, - { - "type": "java", - "name": "Attach to CLI Debug", - "request": "attach", - "hostName": "localhost", - "port": 5005 - } - ] -} -``` - -### Test Data Setup - -#### MongoDB Test Setup -```bash -# Start MongoDB with Docker -docker run -d --name mongo-test -p 27017:27017 mongo:5.0 - -# Create test data -mongosh --eval " -use test; -db.flamingockAudit.insertOne({ - changeId: 'test-change-001', - status: 'APPLIED', - timestamp: new Date() -}); -" -``` - -#### DynamoDB Local Setup -```bash -# Start DynamoDB Local -docker run -d --name dynamodb-local -p 8000:8000 amazon/dynamodb-local - -# Verify it's running -curl http://localhost:8000/shell/ -``` - -### Error Debugging - -#### Common Issues - -**"Configuration file not found"** -```bash -# Debug: Check working directory -./gradlew :cli:flamingock-cli:debugCli --args="--config $PWD/flamingock-cli-dist/flamingock-cli.yml audit list" -``` - -**"Failed to create OpsClient"** -```bash -# Enable detailed MongoDB logging -export JAVA_OPTS="-Dorg.mongodb.driver.level=DEBUG" -./gradlew :cli:flamingock-cli:debugCli --args="audit list" -``` - -**"Multiple database configurations"** -```bash -# Validate YAML structure -cat flamingock-cli-dist/flamingock-cli.yml | grep -A5 "audit:" -``` - -#### Debug Log Analysis - -The debug log `flamingock-cli-debug.log` contains: -- Configuration loading steps -- Database client creation attempts -- OpsClient builder progression -- Error stack traces with line numbers - -Key log markers to search for: -- `FK-Builder` - Core Flamingock operations -- `io.flamingock.cli` - CLI-specific operations -- `org.mongodb.driver.cluster` - MongoDB connection status -- `software.amazon.awssdk` - DynamoDB operations - -### Performance Testing - -#### Memory Usage -```bash -# Monitor memory during execution -./gradlew :cli:flamingock-cli:debugCli --args="audit list" & -PID=$! -while kill -0 $PID 2>/dev/null; do - ps -p $PID -o pid,rss,vsz,cmd - sleep 1 -done -``` - -#### Startup Time -```bash -# Measure startup time -time ./flamingock-cli-dist/flamingock --help -time ./flamingock-cli-dist/flamingock audit list -``` - -### Development Workflow - -1. **Make Changes** to CLI code -2. **Build**: `./gradlew :cli:flamingock-cli:build` -3. **Test**: `./gradlew :cli:flamingock-cli:test` -4. **Debug**: `./gradlew :cli:flamingock-cli:debugCli --args="your-test-command"` -5. **Fix Issues** based on logs -6. **Integration Test** with real databases (optional) - -### Continuous Integration - -Tests are designed to run in CI without external dependencies: -- Unit tests run by default -- Integration tests require explicit environment variables -- Database tests are conditional on service availability -- All tests use temporary files and cleanup after themselves - -### Adding New Tests - -**Unit Test Template:** -```java -@Test -void shouldTestSpecificBehavior() { - // Given - // Setup test data - - // When - // Execute the operation - - // Then - assertThat(result).isEqualTo(expected); -} -``` - -**Integration Test Template:** -```java -@Test -@EnabledIfEnvironmentVariable(named = "YOUR_TEST", matches = "true") -void shouldTestWithRealDatabase() { - try { - // Test with real database - } catch (Exception e) { - // Handle expected failures gracefully - System.out.println("Database not available: " + e.getMessage()); - } -} -``` - -This comprehensive testing and debugging setup ensures you can efficiently develop, test, and troubleshoot the Flamingock CLI! diff --git a/cli/flamingock-cli/README.md b/cli/flamingock-cli/README.md deleted file mode 100644 index 9cbfc1d53..000000000 --- a/cli/flamingock-cli/README.md +++ /dev/null @@ -1,267 +0,0 @@ -# Flamingock CLI - -Command-line interface for Flamingock audit and issue management operations. - -## Prerequisites - -- Java 8 or higher -- Gradle (for building from source) - -## Building Locally - -```bash -# Clone the repository -git clone https://github.com/flamingock/flamingock-java.git -cd flamingock-java - -# Build the CLI module -./gradlew :cli:flamingock-cli:build - -# Create distribution archives -./gradlew :cli:flamingock-cli:distZip # Creates ZIP distribution -./gradlew :cli:flamingock-cli:distTar # Creates TAR.GZ distribution - -# Install locally for testing -./gradlew :cli:flamingock-cli:installDist -``` - -### Build Outputs - -- **JAR files**: `cli/flamingock-cli/build/libs/` - - `flamingock-cli-{version}.jar` - Standard JAR - - `flamingock-cli.jar` - Uber JAR with all dependencies -- **Distributions**: `cli/flamingock-cli/build/distributions/` - - `flamingock-cli-{version}.zip` - Windows/cross-platform - - `flamingock-cli-{version}.tar.gz` - Linux/macOS -- **Local install**: `cli/flamingock-cli/build/install/flamingock-cli/` - -## Installation - -### Option 1: Using Distribution Archives - -1. Download the appropriate distribution: - - Windows: `flamingock-cli-{version}.zip` - - Linux/macOS: `flamingock-cli-{version}.tar.gz` - -2. Extract the archive: - ```bash - # Linux/macOS - tar -xzf flamingock-cli-{version}.tar.gz - - # Windows (or use any ZIP tool) - unzip flamingock-cli-{version}.zip - ``` - -3. Add the `bin` directory to your PATH or run directly: - ```bash - # Linux/macOS - ./flamingock-cli-{version}/bin/flamingock --help - - # Windows - flamingock-cli-{version}\bin\flamingock.bat --help - ``` - -### Option 2: Using Maven Dependency - -Add to your project's dependencies: -```xml - - io.flamingock - flamingock-cli - {version} - uber - -``` - -### Option 3: Direct JAR Execution - -```bash -java -jar flamingock-cli.jar --help -``` - -## Configuration - -Modify `flamingock-cli.yml` configuration file: - -```yaml -flamingock: - service-identifier: "my-service" - - # For MongoDB - audit: - mongodb: - connection-string: "mongodb://localhost:27017" - database: "myapp" - - # For DynamoDB - # audit: - # dynamodb: - # region: "us-east-1" -``` - -## Usage - -### Basic Commands - -```bash -# Show help -flamingock --help - -# Audit operations -flamingock audit list # List all audit entries -flamingock audit get -c # Get specific audit entry -flamingock audit fix -c # Fix audit issue - -# Issue operations -flamingock issue list # List all issues -flamingock issue get # Get next issue (or specific with -c) -flamingock issue get -c # Get specific issue -flamingock issue get -g # Show resolution guidance - -# Global options -flamingock --config-file # Use specific config file -flamingock --verbose # Verbose output -flamingock --json # JSON output format -``` - -### Common Workflows - -#### Investigating Issues -```bash -# List all issues -flamingock issue list - -# Get details for the next issue with guidance -flamingock issue get --guidance - -# Fix the issue -flamingock audit fix -c -``` - -#### Monitoring Audit State -```bash -# View all audit entries -flamingock audit list - -# Check specific change -flamingock audit get -c user-migration-v2 - -# Export as JSON for processing -flamingock audit list --json > audit-report.json -``` - -## Release Process - -### Local Testing - -1. Build and test locally: - ```bash - ./gradlew :cli:flamingock-cli:clean build test - ./gradlew :cli:flamingock-cli:installDist - # Test the installed CLI - ./cli/flamingock-cli/build/install/flamingock-cli/bin/flamingock --help - ``` - -2. Publish to local Maven: - ```bash - ./gradlew :cli:flamingock-cli:publishToMavenLocal - ``` - -### Remote Release - -The CLI is released as part of the Flamingock core modules: - -1. **Release with core bundle**: - ```bash - ./gradlew -PreleaseBundle=core publish - ./gradlew -PreleaseBundle=core jreleaserFullRelease - ``` - -2. **Release CLI only**: - ```bash - ./gradlew -Pmodule=flamingock-cli publish - ./gradlew -Pmodule=flamingock-cli jreleaserFullRelease - ``` - -### Distribution Channels - -- **Maven Central**: Available as `io.flamingock:flamingock-cli:{version}` - - Standard JAR for library usage - - Uber JAR (classifier: `uber`) for standalone execution -- **GitHub Releases**: Distribution archives (ZIP/TAR.GZ) with launch scripts -- **Direct Download**: Links available in Flamingock documentation - -## Development - -### Project Structure - -``` -cli/flamingock-cli/ -├── src/ -│ ├── main/java/io/flamingock/cli/ -│ │ ├── FlamingockCli.java # Main entry point -│ │ ├── command/ # Command implementations -│ │ │ ├── audit/ # Audit commands -│ │ │ └── issue/ # Issue commands -│ │ ├── config/ # Configuration handling -│ │ └── util/ # Utilities -│ └── dist/ # Distribution resources -│ └── flamingock-cli.yml # Sample configuration -└── build.gradle.kts # Build configuration -``` - -### Adding New Commands - -1. Create command class extending appropriate base -2. Add `@Command` annotation with picocli configuration -3. Implement `Callable` interface -4. Register in parent command group - -Example: -```java -@Command(name = "mycommand", description = "Does something") -public class MyCommand implements Callable { - @Override - public Integer call() { - // Implementation - return 0; // Success - } -} -``` - -### Testing - -```bash -# Run unit tests -./gradlew :cli:flamingock-cli:test - -# Run with test configuration -./gradlew :cli:flamingock-cli:testCli - -# Debug mode -./gradlew :cli:flamingock-cli:debugCli -``` - -## Future Enhancements - -### Native Executables (Planned) - -Future releases will include native executables for: -- Windows (.exe) -- macOS (universal binary) -- Linux (x64, ARM64) - -These will be built using GraalVM Native Image, providing: -- Instant startup (no JVM required) -- Smaller distribution size -- Better system integration - -## License - -Apache License 2.0 - See [LICENSE](../../LICENSE) file for details. - -## Support - -- Documentation: https://docs.flamingock.io/cli -- Issues: https://github.com/flamingock/flamingock-java/issues -- Discussions: https://github.com/flamingock/flamingock-java/discussions diff --git a/cli/flamingock-cli/TEST_INTEGRATION.md b/cli/flamingock-cli/TEST_INTEGRATION.md deleted file mode 100644 index 23813f533..000000000 --- a/cli/flamingock-cli/TEST_INTEGRATION.md +++ /dev/null @@ -1,103 +0,0 @@ -# CLI Integration Testing Guide - -## Overview - -The Flamingock CLI now has comprehensive integration testing using TestContainers that verifies: -- CLI command execution with real databases -- OpsClient creation and connection handling -- Configuration loading and validation -- Database type detection - -## Available Integration Tests - -### 1. MongoDB Integration Tests -Tests the CLI with real MongoDB using TestContainers. - -**Run tests:** -```bash -./gradlew :cli:flamingock-cli:test --tests "*CLIMongoIntegrationTest*" -``` - -**What this verifies:** -- ✅ TestContainers MongoDB spins up correctly -- ✅ CLI loads YAML configuration with MongoDB connection string -- ✅ CLI attempts to create OpsClient with MongoDB driver -- ✅ Database connection setup works (even if no audit entries exist) - -### 2. DynamoDB Integration Tests -Tests the CLI with local DynamoDB using TestContainers. - -**Run tests:** -```bash -./gradlew :cli:flamingock-cli:test --tests "*CLIDynamoIntegrationTest*" -``` - -### 3. Complete CLI Command Execution Tests -Tests all CLI commands end-to-end with database connections. - -**Run tests:** -```bash -./gradlew :cli:flamingock-cli:test --tests "*CLICommandExecutionTest*" -``` - -### Run All Tests -Run everything including unit tests and integration tests: - -**Run all tests:** -```bash -./gradlew :cli:flamingock-cli:test -``` - -## Test Results - -**✅ PASSING:** MongoDB integration test successfully demonstrates: -1. TestContainers MongoDB container startup -2. Dynamic configuration file generation with container connection string -3. CLI command execution (`audit list`) with database connection -4. OpsClient creation attempt (the core functionality you requested) - -**Example output:** -``` -CLIMongoIntegrationTest > shouldRunAuditListCommandWithMongoDB() PASSED -``` - -## Manual Testing - -You can also test the CLI manually: - -**1. Build the CLI:** -```bash -./gradlew :cli:flamingock-cli:build -``` - -**2. Run with debug output:** -```bash -./gradlew :cli:flamingock-cli:debugCli --args="audit list" -``` - -**3. Test with custom config:** -```bash -./gradlew :cli:flamingock-cli:debugCli --args="--config /path/to/config.yml audit list" -``` - -## What This Proves - -✅ **Database Connection Works:** The integration tests prove that the CLI can successfully connect to databases using TestContainers - -✅ **OpsClient Creation:** The CLI creates the OpsClient and attempts database operations - -✅ **Configuration Loading:** YAML configuration parsing works correctly for both MongoDB and DynamoDB - -✅ **Command Execution:** The CLI command structure and argument parsing works as expected - -✅ **Error Handling:** The CLI gracefully handles connection issues and missing audit entries - -## Next Steps - -The integration tests provide a solid foundation for: -- Testing actual audit operations when they become available -- Validating CLI behavior with different database states -- Automated testing in CI/CD pipelines -- Development and debugging workflows - -The setup is ready for production use and further CLI feature development! \ No newline at end of file diff --git a/cli/flamingock-cli/build.gradle.kts b/cli/flamingock-cli/build.gradle.kts deleted file mode 100644 index 30f4c0625..000000000 --- a/cli/flamingock-cli/build.gradle.kts +++ /dev/null @@ -1,273 +0,0 @@ -import java.security.MessageDigest - -plugins { - `java-library` - `maven-publish` -} - -description = "Command-line interface for Flamingock audit and issue management operations" - -dependencies { - implementation(project(":core:flamingock-core")) - implementation(project(":community:flamingock-community")) - implementation(project(":utils:sql-util")) - implementation(project(":core:target-systems:mongodb-external-system-api")) - - // CLI framework - implementation("info.picocli:picocli:4.7.5") - - // YAML configuration - implementation("org.yaml:snakeyaml:2.2") - - // JSON formatting - implementation("com.google.code.gson:gson:2.10.1") - - // Database clients (community edition) - implementation("org.mongodb:mongodb-driver-sync:4.9.1") - implementation("software.amazon.awssdk:dynamodb:2.20.0") - implementation ("com.couchbase.client:java-client:3.7.3") - - // SQL drivers - implementation("mysql:mysql-connector-java:8.0.33") - implementation("com.microsoft.sqlserver:mssql-jdbc:12.4.2.jre8") - implementation("com.oracle.database.jdbc:ojdbc8:21.9.0.0") - implementation("org.postgresql:postgresql:42.7.3") - implementation("org.mariadb.jdbc:mariadb-java-client:3.3.2") - implementation("com.h2database:h2:2.2.224") - implementation("org.xerial:sqlite-jdbc:3.41.2.1") - implementation("com.ibm.informix:jdbc:4.50.10") - implementation("org.firebirdsql.jdbc:jaybird:4.0.10.java8") - - // HikariCP for SQL database connection pooling - implementation("com.zaxxer:HikariCP:3.4.5") - - // SLF4J API - needed for interface compatibility (provided by flamingock-core) - // implementation("org.slf4j:slf4j-api:1.7.36") // Already provided by core dependencies - - // Test dependencies - testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.1") - testImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.1") - testImplementation("org.junit.jupiter:junit-jupiter-params:5.10.1") - testImplementation("org.mockito:mockito-core:5.8.0") - testImplementation("org.mockito:mockito-junit-jupiter:5.8.0") - testImplementation("org.assertj:assertj-core:3.24.2") - testImplementation("org.testcontainers:testcontainers-junit-jupiter:2.0.2") - testImplementation("org.testcontainers:testcontainers-mongodb:2.0.2") - testImplementation("org.testcontainers:testcontainers-couchbase:2.0.2") - testImplementation("com.github.stefanbirkner:system-lambda:1.2.1") - // SQL Testcontainers - testImplementation("org.testcontainers:testcontainers-mysql:2.0.2") - testImplementation("org.testcontainers:testcontainers-mssqlserver:2.0.2") - testImplementation("org.testcontainers:testcontainers-oracle-xe:2.0.2") - testImplementation("org.testcontainers:testcontainers-postgresql:2.0.2") - testImplementation("org.testcontainers:testcontainers-mariadb:2.0.2") - -} - -// Create UberJar with all dependencies -val uberJar by tasks.registering(Jar::class) { - group = "build" - description = "Create a self-contained JAR with all dependencies" - - archiveBaseName.set("flamingock-cli") - archiveClassifier.set("uber") - archiveVersion.set(project.version.toString()) - isZip64 = true - - duplicatesStrategy = DuplicatesStrategy.EXCLUDE - - manifest { - attributes["Main-Class"] = "io.flamingock.cli.FlamingockCli" - attributes["Implementation-Title"] = project.name - attributes["Implementation-Version"] = project.version - } - - from(sourceSets.main.get().output) - dependsOn(configurations.runtimeClasspath) - from({ - configurations.runtimeClasspath.get().filter { it.name.endsWith("jar") }.map { zipTree(it) } - }) { - exclude("META-INF/*.SF", "META-INF/*.DSA", "META-INF/*.RSA") - } -} - -val createScripts by tasks.registering(CreateStartScripts::class) { - mainClass.set("io.flamingock.cli.FlamingockCli") - applicationName = "flamingock" - outputDir = layout.buildDirectory.dir("scripts").get().asFile - classpath = files(uberJar.get().archiveFile) -} - -val distImage by tasks.registering(Sync::class) { - group = "distribution" - description = "Creates the distribution image" - dependsOn(uberJar, createScripts) - - into(layout.buildDirectory.dir("dist-image")) - - from(createScripts) { - into("bin") - fileMode = "755".toInt(8) - } - from(uberJar.get().archiveFile) { - into("lib") - } - from("src/dist") { - into("bin") - } -} - -val distZip by tasks.registering(Zip::class) { - group = "distribution" - description = "Builds the ZIP distribution" - dependsOn(distImage) - - from(distImage.get().destinationDir) { - into("flamingock-cli") - } - - archiveBaseName.set("flamingock-cli") - archiveVersion.set("") - archiveExtension.set("zip") - - destinationDirectory.set(layout.buildDirectory.dir("distributions")) -} - -val distTar by tasks.registering(Tar::class) { - group = "distribution" - description = "Builds the TAR distribution" - dependsOn(distImage) - - from(distImage.get().destinationDir) { - into("flamingock-cli") - } - - archiveBaseName.set("flamingock-cli") - archiveVersion.set("") - archiveExtension.set("tar.gz") - compression = Compression.GZIP - - destinationDirectory.set(layout.buildDirectory.dir("distributions")) -} - -// Test configuration -tasks.test { - useJUnitPlatform() - testLogging { - events("passed", "skipped", "failed") - exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL - showStandardStreams = false - } -} - -// Debug task - run CLI with debugging enabled -val debugCli by tasks.registering(JavaExec::class) { - group = "debug" - description = "Run CLI in debug mode with development logging" - - classpath = sourceSets.main.get().runtimeClasspath - mainClass.set("io.flamingock.cli.FlamingockCli") - - // Enable debug logging using CLI flags - systemProperty("flamingock.debug", "true") - - // JVM debugging - jvmArgs = listOf( - "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005", - "-XX:+ShowCodeDetailsInExceptionMessages" - ) - - // Set working directory to distribution directory - workingDir = file("${project.rootDir}/flamingock-cli-dist") - - // Default arguments with debug flag (can be overridden) - args = listOf("--debug", "--help") -} - -// Quick test task - run CLI without MongoDB dependency -val testCli by tasks.registering(JavaExec::class) { - group = "debug" - description = "Test CLI commands without database connections" - - classpath = sourceSets.main.get().runtimeClasspath - mainClass.set("io.flamingock.cli.FlamingockCli") - - systemProperty("flamingock.debug", "true") - workingDir = file("${project.rootDir}/flamingock-cli-dist") - - args = listOf("--verbose", "--help") -} - -java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 -} - -// Add uber jar as additional artifact to existing maven publication -afterEvaluate { - publishing { - publications { - named("maven") { - // Add the uber jar as additional artifact - artifact(uberJar.get()) { - classifier = "uber" - } - - // Override description for CLI module - pom { - name.set("Flamingock CLI") - description.set("Command-line interface for Flamingock audit and issue management operations") - } - } - } - } -} - -// Generate checksums for distributions -val generateChecksums by tasks.registering { - group = "distribution" - description = "Generate checksums for distribution files" - dependsOn(distZip, distTar) - - doLast { - val distDir = file("${layout.buildDirectory.get()}/distributions") - val checksumFile = file("${distDir}/checksums.txt") - - val checksums = mutableListOf() - distDir.listFiles()?.forEach { distFile -> - if (distFile.extension in listOf("gz", "zip")) { - val sha256 = MessageDigest.getInstance("SHA-256") - distFile.inputStream().use { input -> - val buffer = ByteArray(8192) - var bytesRead = input.read(buffer) - while (bytesRead != -1) { - sha256.update(buffer, 0, bytesRead) - bytesRead = input.read(buffer) - } - } - val checksum = sha256.digest().joinToString("") { byte -> "%02x".format(byte) } - checksums.add("${checksum} ${distFile.name}") - } - } - - checksumFile.writeText(checksums.joinToString("\n")) - println("Checksums written to: ${checksumFile.absolutePath}") - } -} - -// Ensure distributions are built on every build (neccesary?) -tasks.named("assemble").configure { - dependsOn(generateChecksums) -} - -// Ensure distributions are built before JReleaser tasks -tasks.matching { it.name.startsWith("jreleaser") }.configureEach { - dependsOn(generateChecksums) -} - -// Add task to build all distributions -tasks.register("buildDistributions") { - group = "distribution" - description = "Build all distribution archives" - dependsOn(distZip, distTar, generateChecksums) -} diff --git a/cli/flamingock-cli/src/dist/flamingock-cli.yml b/cli/flamingock-cli/src/dist/flamingock-cli.yml deleted file mode 100644 index 9b99267d3..000000000 --- a/cli/flamingock-cli/src/dist/flamingock-cli.yml +++ /dev/null @@ -1,36 +0,0 @@ -# Flamingock CLI Configuration Sample -# Modify this file as needed - -flamingock: - service-identifier: "flamingock-cli" - audit: - - # MongoDB Configuration (comment to use other or modify to use) - mongodb: - connection-string: "mongodb://localhost:27017" - database: "test" - collection: "flamingockAuditLog" # Optional, defaults to "flamingockAuditLog" - - # DynamoDB Configuration (uncomment and modify to use) - # dynamodb: - # region: "us-east-1" - # table: "flamingockAuditLog" # Optional, defaults to "flamingockAuditLog" - # # endpoint: "http://localhost:8000" # Optional for local DynamoDB - # # access-key: "your-access-key" # Optional if using IAM roles - # # secret-key: "your-secret-key" # Optional if using IAM roles - - # Couchbase Configuration (uncomment and modify to use) - # couchbase: - # endpoint: "couchbase://localhost:12110" - # username: "your-username" - # password: "your-password" - # bucket-name: "test" - # table: "flamingockAuditLog" # Optional, defaults to "flamingockAuditLog" - - # SQL Configuration (uncomment and modify to use) - # sql: - # endpoint: "jdbc:sqlserver://localhost:1433/test-db" - # username: "your-username" - # password: "your-password" - # sql-dialect: "SqlServer" - # table: "flamingockAuditLog" # Optional, defaults to "flamingockAuditLog" diff --git a/cli/flamingock-cli/src/main/java/io/flamingock/cli/FlamingockCli.java b/cli/flamingock-cli/src/main/java/io/flamingock/cli/FlamingockCli.java deleted file mode 100644 index 23bd07051..000000000 --- a/cli/flamingock-cli/src/main/java/io/flamingock/cli/FlamingockCli.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli; - -import io.flamingock.cli.command.audit.AuditCommand; -import io.flamingock.cli.command.issue.IssueCommand; -import io.flamingock.cli.handler.CliExceptionHandler; -import io.flamingock.cli.logging.LoggingMixin; -import picocli.CommandLine; -import picocli.CommandLine.Command; -import picocli.CommandLine.Mixin; -import picocli.CommandLine.Option; - -@Command( - name = "flamingock", - description = "Flamingock CLI - Change-as-Code platform for distributed systems evolution", - header = { - "@|bold,cyan Flamingock CLI|@ - Change-as-Code platform for distributed systems", - "" - }, - footer = { - "", - "@|bold Examples:|@", - " flamingock --verbose audit list", - " flamingock --debug -c custom.yml issue list", - " flamingock --quiet audit mark-success change-id", - "", - "@|yellow Note:|@ Global options (--verbose, --debug, etc.) must be placed before commands.", - "For detailed help on any command, use: flamingock --help" - }, - subcommands = {AuditCommand.class, IssueCommand.class}, - mixinStandardHelpOptions = true, - version = "Flamingock CLI 1.0.0" -) -public class FlamingockCli implements Runnable { - - @Option(names = {"-c", "--config"}, - description = "Path to configuration file. Global option - must be placed before commands. (default: flamingock-cli.yml)", - order = 0) - private String configFile = "flamingock-cli.yml"; - - @Mixin - private LoggingMixin loggingMixin; - - public static void main(String[] args) { - FlamingockCli cli = new FlamingockCli(); - CommandLine cmd = new CommandLine(cli); - - cmd.setExecutionStrategy(parseResult -> { - cli.loggingMixin.initializeLogging(); - return new CommandLine.RunLast().execute(parseResult); - }); - - // Use custom exception handler for better error messages - cmd.setExecutionExceptionHandler(new CliExceptionHandler()); - - int exitCode = cmd.execute(args); - System.exit(exitCode); - } - - @Override - public void run() { - // Initialize logging when any command is about to run - loggingMixin.initializeLogging(); - - // This runs when no subcommand is specified - show help - new CommandLine(this).usage(System.out); - } - - public String getConfigFile() { - return configFile; - } -} diff --git a/cli/flamingock-cli/src/main/java/io/flamingock/cli/command/audit/AuditCommand.java b/cli/flamingock-cli/src/main/java/io/flamingock/cli/command/audit/AuditCommand.java deleted file mode 100644 index dd6d73ef0..000000000 --- a/cli/flamingock-cli/src/main/java/io/flamingock/cli/command/audit/AuditCommand.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.command.audit; - -import io.flamingock.cli.FlamingockCli; -import picocli.CommandLine.Command; -import picocli.CommandLine.ParentCommand; - -@Command( - name = "audit", - description = "Audit operations for Flamingock changes", - subcommands = { - ListCommand.class, - FixCommand.class - } -) -public class AuditCommand { - - @ParentCommand - private FlamingockCli flamingockCli; - - public String getConfigFile() { - return flamingockCli.getConfigFile(); - } -} \ No newline at end of file diff --git a/cli/flamingock-cli/src/main/java/io/flamingock/cli/command/audit/FixCommand.java b/cli/flamingock-cli/src/main/java/io/flamingock/cli/command/audit/FixCommand.java deleted file mode 100644 index 9c95599c9..000000000 --- a/cli/flamingock-cli/src/main/java/io/flamingock/cli/command/audit/FixCommand.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.command.audit; - -import io.flamingock.cli.config.ConfigLoader; -import io.flamingock.cli.config.FlamingockConfig; -import io.flamingock.cli.service.AuditService; -import io.flamingock.internal.common.core.recovery.FixResult; -import io.flamingock.internal.common.core.recovery.Resolution; -import io.flamingock.internal.util.log.FlamingockLoggerFactory; -import org.slf4j.Logger; -import picocli.CommandLine.Command; -import picocli.CommandLine.Option; -import picocli.CommandLine.ParentCommand; - -@Command( - name = "fix", - description = "Fix audit state for a change with inconsistent audit" -) -public class FixCommand implements Runnable { - - private static final Logger logger = FlamingockLoggerFactory.getLogger("FixCommand"); - - @ParentCommand - private AuditCommand parent; - - @Option(names = {"-c", "--change-id"}, required = true, description = "Change ID to fix") - private String changeId; - - @Option(names = {"-r", "--resolution"}, required = true, description = "Resolution state: APPLIED or ROLLED_BACK") - private Resolution resolution; - - @Override - public void run() { - try { - logger.info("Fixing audit issue for changeId: {} with resolution: {}", changeId, resolution); - logger.debug("Configuration file: {}", parent.getConfigFile()); - - // Load configuration - FlamingockConfig config = ConfigLoader.loadConfig(parent.getConfigFile()); - - // Create audit service - AuditService auditService = new AuditService(config); - - // Fix the audit issue - logger.debug("Calling fixAuditIssue with changeId: {} and resolution: {}", changeId, resolution); - FixResult result = auditService.fixAuditIssue(changeId, resolution); - - // Handle different results with appropriate messaging - switch (result) { - case APPLIED: - System.out.println("✅ Successfully fixed audit state for change '" + changeId + "'"); - System.out.println(" Resolution applied: " + resolution); - - if (resolution == Resolution.ROLLED_BACK) { - System.out.println(" 📝 Note: Flamingock will retry this change in the next execution."); - } - break; - - case NO_ISSUE_FOUND: - System.out.println("⚠️ Fix not applied for change '" + changeId + "'"); - System.out.println(" Reason: The audit state is already consistent and healthy."); - System.out.println(" No action required - change has no issues to fix."); - break; - - default: - System.err.println("❌ Unexpected result: " + result); - System.exit(1); - } - - } catch (IllegalArgumentException e) { - System.err.println("❌ Error: " + e.getMessage()); - System.exit(1); - } catch (Exception e) { - logger.error("Failed to fix audit issue", e); - System.err.println("❌ Failed to fix audit issue: " + e.getMessage()); - System.exit(1); - } - } -} \ No newline at end of file diff --git a/cli/flamingock-cli/src/main/java/io/flamingock/cli/command/audit/ListCommand.java b/cli/flamingock-cli/src/main/java/io/flamingock/cli/command/audit/ListCommand.java deleted file mode 100644 index 0cad8c3a1..000000000 --- a/cli/flamingock-cli/src/main/java/io/flamingock/cli/command/audit/ListCommand.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.command.audit; - -import io.flamingock.cli.config.ConfigLoader; -import io.flamingock.cli.config.FlamingockConfig; -import io.flamingock.cli.service.AuditService; -import io.flamingock.cli.service.TableFormatter; -import io.flamingock.internal.common.core.audit.AuditEntry; -import io.flamingock.internal.util.log.FlamingockLoggerFactory; -import org.slf4j.Logger; -import picocli.CommandLine; -import picocli.CommandLine.Command; -import picocli.CommandLine.ParentCommand; - -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.time.format.DateTimeParseException; -import java.util.List; - -@Command( - name = "list", - description = "List audit entries" -) -public class ListCommand implements Runnable { - - private static final Logger logger = FlamingockLoggerFactory.getLogger("ListCommand"); - - @ParentCommand - private AuditCommand parent; - - @CommandLine.Option(names = "--history", description = "Show full chronological audit history") - private boolean history; - - @CommandLine.Option(names = "--since", description = "Show audits since date (ISO-8601 format: 2025-01-01T00:00:00)") - private String since; - -// @CommandLine.Option(names = "--limit", description = "Limit number of results") -// private Integer limit; -// -// @CommandLine.Option(names = "--page", description = "Page number for pagination") -// private Integer page; - - @CommandLine.Option(names = {"--extended", "-e"}, description = "Show extended audit information") - private boolean extended; - - @Override - public void run() { - try { - logger.info("Starting list command execution"); - logger.debug("Configuration file: {}", parent.getConfigFile()); - - // Load configuration - FlamingockConfig config = ConfigLoader.loadConfig(parent.getConfigFile()); - - // Create audit service - AuditService auditService = new AuditService(config); - - // Parse since date if provided - LocalDateTime sinceDate = null; - if (since != null && !since.trim().isEmpty()) { - try { - sinceDate = LocalDateTime.parse(since, DateTimeFormatter.ISO_LOCAL_DATE_TIME); - } catch (DateTimeParseException e) { - throw new IllegalArgumentException("Invalid date format for --since. Use ISO-8601 format: 2025-01-01T00:00:00"); - } - } - - // Get audit entries based on flags - List entries; - String listType; - - if (history) { - // Full chronological history - entries = auditService.listAuditEntriesHistory(); - listType = "Full Audit History"; - } else if (sinceDate != null) { - // Entries since a specific date - entries = auditService.listAuditEntriesSince(sinceDate); - listType = "Audit Entries since " + since; - } else { - // Default: snapshot view (latest per change) - entries = auditService.listAuditEntriesSnapshot(); - listType = "Audit Entries Snapshot (Latest per Change)"; - } - - - // Display results - if (entries.isEmpty()) { - System.out.println("No audit entries found."); - } else { - System.out.println(listType + ":"); - StringBuilder separator = new StringBuilder(); - for (int i = 0; i <= listType.length(); i++) { - separator.append("="); - } - System.out.println(separator.toString()); - System.out.println(); - - // Use table formatter - TableFormatter formatter = new TableFormatter(); - if (extended) { - formatter.printExtendedTable(entries); - } else { - formatter.printBasicTable(entries); - } - - // Print state legend - TableFormatter.printStateLegend(); - - System.out.println(); - System.out.println("Total entries: " + entries.size()); - - } - System.out.println(); - - } catch (Exception e) { - throw new RuntimeException("Failed to list audit entries: " + e.getMessage(), e); - } - } - -} \ No newline at end of file diff --git a/cli/flamingock-cli/src/main/java/io/flamingock/cli/command/issue/GetIssueCommand.java b/cli/flamingock-cli/src/main/java/io/flamingock/cli/command/issue/GetIssueCommand.java deleted file mode 100644 index 27a3731d8..000000000 --- a/cli/flamingock-cli/src/main/java/io/flamingock/cli/command/issue/GetIssueCommand.java +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.command.issue; - -import io.flamingock.cli.config.ConfigLoader; -import io.flamingock.cli.config.FlamingockConfig; -import io.flamingock.cli.service.AuditService; -import io.flamingock.cli.service.JsonFormatter; -import io.flamingock.internal.common.core.audit.AuditEntry; -import io.flamingock.internal.common.core.audit.issue.AuditEntryIssue; -import io.flamingock.internal.util.log.FlamingockLoggerFactory; -import org.slf4j.Logger; -import picocli.CommandLine.Command; -import picocli.CommandLine.Option; -import picocli.CommandLine.ParentCommand; - -import java.util.List; -import java.util.Optional; - -import static io.flamingock.cli.util.ASCIIColors.inViolet; - -@Command( - name = "get", - aliases = {"describe"}, - description = "Show details of a specific issue or the next available issue" -) -public class GetIssueCommand implements Runnable { - - private static final Logger logger = FlamingockLoggerFactory.getLogger("GetIssueCommand"); - - @ParentCommand - private IssueCommand parent; - - @Option(names = {"-c", "--change-id"}, required = false, description = "Change ID to inspect (optional - shows next issue if not specified)") - private String changeId; - - @Option(names = {"-j", "--json"}, description = "Output results in JSON format") - private boolean json; - - @Option(names = {"-g", "--guidance"}, description = "Show resolution guidance") - private boolean guidance; - - @Override - public void run() { - try { - logger.debug("Configuration file: {}", parent.getConfigFile()); - - // Load configuration - FlamingockConfig config = ConfigLoader.loadConfig(parent.getConfigFile()); - - // Create audit service - AuditService auditService = new AuditService(config); - - // Determine which issue to show - AuditEntryIssue issueToShow = null; - - if (changeId != null) { - // Specific change-id provided - logger.info("Getting issue details for changeId: {}", changeId); - logger.debug("Fetching issue details for changeId: {}", changeId); - Optional auditEntryIssueOpt = auditService.getAuditEntryIssue(changeId); - - if (auditEntryIssueOpt.isPresent()) { - issueToShow = auditEntryIssueOpt.get(); - } else { - System.err.println("Error: There is no issue with change: " + changeId); - return; - } - } else { - // No change-id provided, get the next available issue - logger.info("Getting next available issue"); - List issues = auditService.listAuditEntriesWithIssues(); - - if (issues.isEmpty()) { - System.out.println("\n✅ No issues found! All changes are in consistent state.\n"); - return; - } else { - issueToShow = issues.get(0); // Get the first issue - logger.debug("Found {} issues, showing first: {}", issues.size(), issueToShow.getChangeId()); - } - } - - // Display the issue - if (json) { - JsonFormatter formatter = new JsonFormatter(); - formatter.printIssueDetails(issueToShow); - } else { - printIssueDetails(issueToShow, guidance); - } - - } catch (UnsupportedOperationException e) { - // Expected for now - stub not implemented - System.err.println("Error: Issue details retrieval not yet implemented."); - System.err.println("This feature will be available in a future release."); - } catch (Exception e) { - throw new RuntimeException("Failed to get issue details: " + e.getMessage(), e); - } - } - - private void printIssueDetails(AuditEntryIssue details, boolean showGuidance) { - AuditEntry entry = details.getAuditEntry(); - String errorMessage = getSmartErrorMessage(details); - String targetSystem = extractTargetSystem(entry); - - System.out.println("\nIssue Details: " + details.getChangeId()); - System.out.println("=================================================="); - System.out.println(); - - // Overview section - System.out.println("📋 OVERVIEW"); - System.out.println(" Change ID: " + entry.getTaskId()); - System.out.println(" State: " + entry.getState() + " (❌)"); - System.out.println(" Target System: " + targetSystem); - System.out.println(" Author: " + (entry.getAuthor() != null ? entry.getAuthor() : "-")); - System.out.println(" Time: " + (entry.getCreatedAt() != null ? entry.getCreatedAt().toString().replace("T", " ") : "-")); - System.out.println(" Execution ID: " + (entry.getExecutionId() != null ? entry.getExecutionId() : "-")); - System.out.println(" Duration: " + entry.getExecutionMillis() + "ms"); - System.out.println(); - - // Error Details section - System.out.println("⚠️ ERROR DETAILS"); - System.out.println(" " + errorMessage); - System.out.println(); - System.out.println(" Technical Details:"); - System.out.println(" - Class: " + shortenClassName(entry.getClassName())); - System.out.println(" - Method: " + (entry.getMethodName() != null ? entry.getMethodName() : "-")); - System.out.println(" - Hostname: " + (entry.getExecutionHostname() != null ? entry.getExecutionHostname() : "-")); - - if (showGuidance) { - System.out.println(); - printResolutionProcess(details.getChangeId(), targetSystem); - } - System.out.println(); - } - - private String getSmartErrorMessage(AuditEntryIssue details) { - String errorMessage = details.getErrorMessage(); - - // If we have a meaningful error message, use it - if (errorMessage != null && !errorMessage.trim().isEmpty() && !errorMessage.equals("unknown")) { - return errorMessage; - } - - // Generate context-aware default based on state - AuditEntry.Status state = details.getAuditEntry().getState(); - if (state != null) { - switch (state) { - case STARTED: - return "Execution interrupted unexpectedly"; - case FAILED: - return "Change execution failed due to unknown reasons"; - case ROLLBACK_FAILED: - return "Rollback operation failed due to unknown reasons"; - default: - return "Unknown error occurred"; - } - } - return "Unknown error occurred"; - } - - private String extractTargetSystem(AuditEntry entry) { - if (entry.getTargetSystemId() != null && !entry.getTargetSystemId().isEmpty()) { - return entry.getTargetSystemId(); - } else { - return "Unknown System"; - } - } - - private String shortenClassName(String className) { - if (className == null) { - return "-"; - } - // Shorten package names: io.flamingock.changes -> i.f.changes - String[] parts = className.split("\\."); - StringBuilder shortened = new StringBuilder(); - for (int i = 0; i < parts.length - 1; i++) { - if (!parts[i].isEmpty()) { - shortened.append(parts[i].charAt(0)).append("."); - } - } - if (parts.length > 0) { - shortened.append(parts[parts.length - 1]); - } - return shortened.toString(); - } - - private void printResolutionProcess(String changeId, String targetSystem) { - System.out.println("🔧 Resolution Process:"); - System.out.println(); - System.out.println(" 1. Review the error details above to understand the root cause"); - System.out.println(); - System.out.println(" 2. Verify the actual state in your target system (" + targetSystem + "):"); - System.out.println(" • Check if the change was successfully applied despite the audit failure"); - System.out.println(" • Determine if the change was partially applied or not applied at all"); - System.out.println(); - System.out.println(" 3. Fix the audit state based on your findings:"); - System.out.println(); - System.out.println(" ✅ If change was successfully applied:"); - System.out.println(" " + inViolet("flamingock audit fix -c " + changeId + " -r APPLIED")); - System.out.println(); - System.out.println(" ↩️ If change was not applied or you've manually rolled it back:"); - System.out.println(" " + inViolet("flamingock audit fix -c " + changeId + " -r ROLLED_BACK")); - System.out.println(" (Flamingock will retry this change in the next execution)"); - System.out.println(); - System.out.println(" ⚠️ Important: For partially applied changes, you must either:"); - System.out.println(" • Manually complete the change, then fix it with resolution(-r) APPLIED"); - System.out.println(" • Manually revert the change, then fix it with resolution(-r) ROLLED_BACK"); - } -} \ No newline at end of file diff --git a/cli/flamingock-cli/src/main/java/io/flamingock/cli/command/issue/IssueCommand.java b/cli/flamingock-cli/src/main/java/io/flamingock/cli/command/issue/IssueCommand.java deleted file mode 100644 index 486d077f0..000000000 --- a/cli/flamingock-cli/src/main/java/io/flamingock/cli/command/issue/IssueCommand.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.command.issue; - -import io.flamingock.cli.FlamingockCli; -import picocli.CommandLine.Command; -import picocli.CommandLine.ParentCommand; - -@Command( - name = "issue", - description = "Issue operations for problematic changes", - subcommands = { - ListIssueCommand.class, - GetIssueCommand.class - } -) -public class IssueCommand { - - @ParentCommand - private FlamingockCli flamingockCli; - - public String getConfigFile() { - return flamingockCli.getConfigFile(); - } -} \ No newline at end of file diff --git a/cli/flamingock-cli/src/main/java/io/flamingock/cli/command/issue/ListIssueCommand.java b/cli/flamingock-cli/src/main/java/io/flamingock/cli/command/issue/ListIssueCommand.java deleted file mode 100644 index e622281e2..000000000 --- a/cli/flamingock-cli/src/main/java/io/flamingock/cli/command/issue/ListIssueCommand.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.command.issue; - -import io.flamingock.cli.config.ConfigLoader; -import io.flamingock.cli.config.FlamingockConfig; -import io.flamingock.cli.service.AuditService; -import io.flamingock.cli.service.JsonFormatter; -import io.flamingock.cli.service.TableFormatter; -import io.flamingock.internal.common.core.audit.issue.AuditEntryIssue; -import io.flamingock.internal.util.log.FlamingockLoggerFactory; -import org.slf4j.Logger; -import picocli.CommandLine.Command; -import picocli.CommandLine.Option; -import picocli.CommandLine.ParentCommand; - -import java.util.List; -import java.util.stream.Collectors; - -import static io.flamingock.cli.util.ASCIIColors.inViolet; - -@Command( - name = "list", - aliases = {"ls"}, - description = "List all current issues (changes with inconsistent audits)" -) -public class ListIssueCommand implements Runnable { - - private static final Logger logger = FlamingockLoggerFactory.getLogger("ListIssueCommand"); - - @ParentCommand - private IssueCommand parent; - - @Option(names = {"-j", "--json"}, description = "Output results in JSON format") - private boolean json; - - @Override - public void run() { - try { - logger.info("Starting issue list command execution"); - logger.debug("Configuration file: {}", parent.getConfigFile()); - - // Load configuration - FlamingockConfig config = ConfigLoader.loadConfig(parent.getConfigFile()); - - // Create audit service - AuditService auditService = new AuditService(config); - - // Get audit entries with issues - logger.debug("Fetching audit entries with issues"); - List issues = auditService.listAuditEntriesWithIssues(); - - // Display results - if (issues.isEmpty()) { - System.out.println("\n✅ No issues found! All changes are in consistent state.\n"); - } else { - if (json) { - // Output in JSON format - JsonFormatter formatter = new JsonFormatter(); - formatter.printIssueList(issues); - } else { - // Output as table - System.out.println("\nCurrent Issues (Changes with Inconsistent Audits):"); - System.out.println("========================================================"); - System.out.println(); - - TableFormatter formatter = new TableFormatter(); - formatter.printBasicTable(issues.stream().map(AuditEntryIssue::getAuditEntry).collect(Collectors.toList())); - - System.out.println(); - System.out.println("Total issues: " + issues.size()); - System.out.println(); - System.out.println("🔧 Use '" + inViolet("flamingock issue get -c --guidance") + "' for detailed analysis and resolution guidance.\n"); - } - } - - } catch (Exception e) { - throw new RuntimeException("Failed to list issues: " + e.getMessage(), e); - } - } - -} \ No newline at end of file diff --git a/cli/flamingock-cli/src/main/java/io/flamingock/cli/config/ConfigLoader.java b/cli/flamingock-cli/src/main/java/io/flamingock/cli/config/ConfigLoader.java deleted file mode 100644 index 2387e678a..000000000 --- a/cli/flamingock-cli/src/main/java/io/flamingock/cli/config/ConfigLoader.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.config; - -import org.yaml.snakeyaml.Yaml; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.Map; -import java.util.stream.Stream; - -public class ConfigLoader { - - public static FlamingockConfig loadConfig(String configFilePath) throws IOException { - File configFile = new File(configFilePath); - if (!configFile.exists()) { - throw new IOException("Configuration file not found: " + configFilePath); - } - - Yaml yaml = new Yaml(); - try (InputStream input = new FileInputStream(configFile)) { - Map yamlData = yaml.load(input); - return parseConfig(yamlData); - } - } - - @SuppressWarnings("unchecked") - private static FlamingockConfig parseConfig(Map yamlData) { - FlamingockConfig config = new FlamingockConfig(); - - Map flamingockData = (Map) yamlData.get("flamingock"); - if (flamingockData == null) { - throw new IllegalArgumentException("Missing 'flamingock' section in configuration file"); - } - - // Parse service identifier - if (flamingockData.containsKey("service-identifier")) { - config.setServiceIdentifier((String) flamingockData.get("service-identifier")); - } - - // Parse audit configuration - Map auditData = (Map) flamingockData.get("audit"); - if (auditData != null) { - DatabaseConfig databaseConfig = new DatabaseConfig(); - - // Parse MongoDB config - Map mongoData = (Map) auditData.get("mongodb"); - if (mongoData != null) { - DatabaseConfig.MongoDBConfig mongoConfig = new DatabaseConfig.MongoDBConfig(); - mongoConfig.setConnectionString((String) mongoData.get("connection-string")); - mongoConfig.setDatabase((String) mongoData.get("database")); - mongoConfig.setHost((String) mongoData.get("host")); - mongoConfig.setUsername((String) mongoData.get("username")); - mongoConfig.setPassword((String) mongoData.get("password")); - mongoConfig.setCollection((String) mongoData.get("collection")); - if (mongoData.get("port") != null) { - mongoConfig.setPort((Integer) mongoData.get("port")); - } - databaseConfig.setMongodb(mongoConfig); - } - - // Parse DynamoDB config - Map dynamoData = (Map) auditData.get("dynamodb"); - if (dynamoData != null) { - DatabaseConfig.DynamoDBConfig dynamoConfig = new DatabaseConfig.DynamoDBConfig(); - dynamoConfig.setRegion((String) dynamoData.get("region")); - dynamoConfig.setEndpoint((String) dynamoData.get("endpoint")); - dynamoConfig.setAccessKey((String) dynamoData.get("access-key")); - dynamoConfig.setSecretKey((String) dynamoData.get("secret-key")); - dynamoConfig.setTable((String) dynamoData.get("table")); - if (dynamoData.get("properties") != null) { - dynamoConfig.setProperties((Map) dynamoData.get("properties")); - } - databaseConfig.setDynamodb(dynamoConfig); - } - - // Parse Couchbase config - Map couchbaseData = (Map) auditData.get("couchbase"); - if (couchbaseData != null) { - DatabaseConfig.CouchbaseConfig couchbaseConfig = new DatabaseConfig.CouchbaseConfig(); - couchbaseConfig.setBucketName((String) couchbaseData.get("bucket-name")); - couchbaseConfig.setEndpoint((String) couchbaseData.get("endpoint")); - couchbaseConfig.setUsername((String) couchbaseData.get("username")); - couchbaseConfig.setPassword((String) couchbaseData.get("password")); - couchbaseConfig.setTable((String) couchbaseData.get("table")); - if (couchbaseData.get("properties") != null) { - couchbaseConfig.setProperties((Map) couchbaseData.get("properties")); - } - databaseConfig.setCouchbase(couchbaseConfig); - } - - Map sqlData = (Map) auditData.get("sql"); - if (sqlData != null) { - DatabaseConfig.SqlConfig sqlConfig = new DatabaseConfig.SqlConfig(); - sqlConfig.setEndpoint((String) sqlData.get("endpoint")); - sqlConfig.setUsername((String) sqlData.get("username")); - sqlConfig.setPassword((String) sqlData.get("password")); - sqlConfig.setSqlDialect((String) sqlData.get("sql-dialect")); - sqlConfig.setTable((String) sqlData.get("table")); - if (sqlData.get("properties") != null) { - sqlConfig.setProperties((Map) sqlData.get("properties")); - } - databaseConfig.setSql(sqlConfig); - } - - config.setAudit(databaseConfig); - } - - return config; - } - - public static DatabaseType detectDatabaseType(FlamingockConfig config) { - if (config.getAudit() == null) { - throw new IllegalArgumentException("No audit configuration found"); - } - - boolean hasMongoDB = config.getAudit().getMongodb() != null; - boolean hasDynamoDB = config.getAudit().getDynamodb() != null; - boolean hasCouchbase = config.getAudit().getCouchbase() != null; - boolean hasSql = config.getAudit().getSql() != null; - - if (Stream.of(hasMongoDB, hasDynamoDB, hasCouchbase, hasSql) - .filter(b -> b) - .count()>1) { - throw new IllegalArgumentException("Multiple database configurations found. Please configure only one database type."); - } - - if (hasMongoDB) { - return DatabaseType.MONGODB; - } else if (hasDynamoDB) { - return DatabaseType.DYNAMODB; - } else if (hasCouchbase) { - return DatabaseType.COUCHBASE; - } else if (hasSql) { - return DatabaseType.SQL; - } else { - throw new IllegalArgumentException("No supported database configuration found. Please configure MongoDB, DynamoDB, Couchbase or SQL."); - } - } - - public enum DatabaseType { - MONGODB, DYNAMODB, COUCHBASE, SQL - } -} diff --git a/cli/flamingock-cli/src/main/java/io/flamingock/cli/config/DatabaseConfig.java b/cli/flamingock-cli/src/main/java/io/flamingock/cli/config/DatabaseConfig.java deleted file mode 100644 index 247008070..000000000 --- a/cli/flamingock-cli/src/main/java/io/flamingock/cli/config/DatabaseConfig.java +++ /dev/null @@ -1,337 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.config; - -import io.flamingock.internal.common.sql.SqlDialect; - -import java.util.Map; -import java.util.Optional; - -public class DatabaseConfig { - private MongoDBConfig mongodb; - private DynamoDBConfig dynamodb; - private CouchbaseConfig couchbase; - private SqlConfig sql; - - public MongoDBConfig getMongodb() { - return mongodb; - } - - public void setMongodb(MongoDBConfig mongodb) { - this.mongodb = mongodb; - } - - public DynamoDBConfig getDynamodb() { - return dynamodb; - } - - public void setDynamodb(DynamoDBConfig dynamodb) { - this.dynamodb = dynamodb; - } - - public CouchbaseConfig getCouchbase() { - return couchbase; - } - - public void setCouchbase(CouchbaseConfig couchbase) { - this.couchbase = couchbase; - } - - public SqlConfig getSql() { - return sql; - } - - public void setSql(SqlConfig sql) { - this.sql = sql; - } - - public static class MongoDBConfig { - private String connectionString; - private String database; - private String host; - private Integer port; - private String username; - private String password; - private String collection; - - public String getConnectionString() { - return connectionString; - } - - public void setConnectionString(String connectionString) { - this.connectionString = connectionString; - } - - public String getDatabase() { - return database; - } - - public void setDatabase(String database) { - this.database = database; - } - - public String getHost() { - return host; - } - - public void setHost(String host) { - this.host = host; - } - - public Integer getPort() { - return port; - } - - public void setPort(Integer port) { - this.port = port; - } - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public String getCollection() { - return collection; - } - - public void setCollection(String collection) { - this.collection = collection; - } - } - - public static class DynamoDBConfig { - private String region; - private String endpoint; - private String accessKey; - private String secretKey; - private String table; - private Map properties; - - public String getRegion() { - return region; - } - - public void setRegion(String region) { - this.region = region; - } - - public String getEndpoint() { - return endpoint; - } - - public void setEndpoint(String endpoint) { - this.endpoint = endpoint; - } - - public String getAccessKey() { - return accessKey; - } - - public void setAccessKey(String accessKey) { - this.accessKey = accessKey; - } - - public String getSecretKey() { - return secretKey; - } - - public void setSecretKey(String secretKey) { - this.secretKey = secretKey; - } - - public String getTable() { - return table; - } - - public void setTable(String table) { - this.table = table; - } - - public Map getProperties() { - return properties; - } - - public void setProperties(Map properties) { - this.properties = properties; - } - } - - public static class CouchbaseConfig { - private String bucketName; - private String endpoint; - private String username; - private String password; - private String table; - private Map properties; - - public String getBucketName() { - return bucketName; - } - - public void setBucketName(String bucketName) { - this.bucketName = bucketName; - } - - public String getEndpoint() { - return endpoint; - } - - public void setEndpoint(String endpoint) { - this.endpoint = endpoint; - } - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public String getTable() { - return table; - } - - public void setTable(String table) { - this.table = table; - } - - public Map getProperties() { - return properties; - } - - public void setProperties(Map properties) { - this.properties = properties; - } - } - - public static class SqlConfig { - private String endpoint; - private String username; - private String password; - private SqlDialect sqlDialect; - private String table; - private Map properties; - - public String getEndpoint() { - return endpoint; - } - - public void setEndpoint(String endpoint) { - this.endpoint = endpoint; - } - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public Optional getSqlDialect() { - return Optional.ofNullable(sqlDialect); - } - - public void setSqlDialect(String sqlDialect) { - this.sqlDialect = SqlDialect.valueOf(sqlDialect.toUpperCase()); - } - - public SqlDialect getEffectiveSqlDialect() { - if (this.sqlDialect != null) { - return this.sqlDialect; - } - String[] parts = this.endpoint.split(":", 3); - if (parts.length < 2 || parts[1].isEmpty()) { - throw new IllegalStateException("Cannot determine SQL dialect from endpoint: " + this.endpoint); - } - return SqlDialect.fromString(parts[1].toLowerCase()); - } - - public String getDriverClassName() { - switch (this.getEffectiveSqlDialect()) { - case MYSQL: - return "com.mysql.cj.jdbc.Driver"; - case MARIADB: - return "org.mariadb.jdbc.Driver"; - case POSTGRESQL: - return "org.postgresql.Driver"; - case SQLITE: - return "org.sqlite.JDBC"; - case H2: - return "org.h2.Driver"; - case SQLSERVER: - return "com.microsoft.sqlserver.jdbc.SQLServerDriver"; - case SYBASE: - return "com.sybase.jdbc4.jdbc.SybDriver"; - case FIREBIRD: - return "org.firebirdsql.jdbc.FBDriver"; - case INFORMIX: - return "com.informix.jdbc.IfxDriver"; - case ORACLE: - return "oracle.jdbc.OracleDriver"; - case DB2: - return "com.ibm.db2.jcc.DB2Driver"; - default: - throw new IllegalArgumentException("Unsupported SQL Dialect: " + this.getEffectiveSqlDialect()); - } - } - - public String getTable() { - return table; - } - - public void setTable(String table) { - this.table = table; - } - - public Map getProperties() { - return properties; - } - - public void setProperties(Map properties) { - this.properties = properties; - } - } -} diff --git a/cli/flamingock-cli/src/main/java/io/flamingock/cli/config/FlamingockConfig.java b/cli/flamingock-cli/src/main/java/io/flamingock/cli/config/FlamingockConfig.java deleted file mode 100644 index 894230d52..000000000 --- a/cli/flamingock-cli/src/main/java/io/flamingock/cli/config/FlamingockConfig.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.config; - -public class FlamingockConfig { - private String serviceIdentifier = "flamingock-cli"; - private DatabaseConfig audit; - - public String getServiceIdentifier() { - return serviceIdentifier; - } - - public void setServiceIdentifier(String serviceIdentifier) { - this.serviceIdentifier = serviceIdentifier; - } - - public DatabaseConfig getAudit() { - return audit; - } - - public void setAudit(DatabaseConfig audit) { - this.audit = audit; - } -} \ No newline at end of file diff --git a/cli/flamingock-cli/src/main/java/io/flamingock/cli/factory/CouchbaseClusterFactory.java b/cli/flamingock-cli/src/main/java/io/flamingock/cli/factory/CouchbaseClusterFactory.java deleted file mode 100644 index ad0e24143..000000000 --- a/cli/flamingock-cli/src/main/java/io/flamingock/cli/factory/CouchbaseClusterFactory.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.factory; - -import com.couchbase.client.java.Cluster; -import io.flamingock.cli.config.DatabaseConfig; - -public class CouchbaseClusterFactory { - - public static Cluster createCouchbaseCluster(DatabaseConfig.CouchbaseConfig config) { - if (config == null) { - throw new IllegalArgumentException("Couchbase configuration is required"); - } - - if (config.getBucketName() == null) { - throw new IllegalArgumentException("Bucket name is required"); - } - - if (config.getEndpoint() == null) { - throw new IllegalArgumentException("Couchbase endpoint is required"); - } - - if (config.getUsername() == null) { - throw new IllegalArgumentException("Couchbase username is required"); - } - - if (config.getPassword() == null) { - throw new IllegalArgumentException("Couchbase password is required"); - } - - try { - Cluster couchbaseCluster = Cluster.connect(config.getEndpoint(), config.getUsername(), config.getPassword()); - - // Test the connection by listing collections in bucket - couchbaseCluster.bucket(config.getBucketName()).collections(); - - return couchbaseCluster; - } catch (Exception e) { - throw new RuntimeException("Failed to create Couchbase cluster: " + e.getMessage(), e); - } - } -} diff --git a/cli/flamingock-cli/src/main/java/io/flamingock/cli/factory/DynamoDBClientFactory.java b/cli/flamingock-cli/src/main/java/io/flamingock/cli/factory/DynamoDBClientFactory.java deleted file mode 100644 index 11adf50ec..000000000 --- a/cli/flamingock-cli/src/main/java/io/flamingock/cli/factory/DynamoDBClientFactory.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.factory; - -import io.flamingock.cli.config.DatabaseConfig; -import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; -import software.amazon.awssdk.auth.credentials.AwsCredentials; -import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider; -import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; -import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.DynamoDbClientBuilder; - -import java.net.URI; - -public class DynamoDBClientFactory { - - public static DynamoDbClient createDynamoDBClient(DatabaseConfig.DynamoDBConfig config) { - if (config == null) { - throw new IllegalArgumentException("DynamoDB configuration is required"); - } - - if (config.getRegion() == null) { - throw new IllegalArgumentException("DynamoDB region is required"); - } - - try { - DynamoDbClientBuilder builder = DynamoDbClient.builder() - .region(Region.of(config.getRegion())); - - // Set endpoint if provided (for local DynamoDB) - if (config.getEndpoint() != null) { - builder.endpointOverride(URI.create(config.getEndpoint())); - } - - // Set credentials if provided, otherwise use default provider chain - if (config.getAccessKey() != null && config.getSecretKey() != null) { - AwsCredentials credentials = AwsBasicCredentials.create( - config.getAccessKey(), - config.getSecretKey() - ); - builder.credentialsProvider(StaticCredentialsProvider.create(credentials)); - } else { - builder.credentialsProvider(DefaultCredentialsProvider.create()); - } - - DynamoDbClient client = builder.build(); - - // Test the connection by listing tables - client.listTables(); - - return client; - } catch (Exception e) { - throw new RuntimeException("Failed to create DynamoDB client: " + e.getMessage(), e); - } - } -} diff --git a/cli/flamingock-cli/src/main/java/io/flamingock/cli/factory/MongoClientFactory.java b/cli/flamingock-cli/src/main/java/io/flamingock/cli/factory/MongoClientFactory.java deleted file mode 100644 index 691d70295..000000000 --- a/cli/flamingock-cli/src/main/java/io/flamingock/cli/factory/MongoClientFactory.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.factory; - -import com.mongodb.ConnectionString; -import com.mongodb.MongoClientSettings; -import com.mongodb.client.MongoClient; -import com.mongodb.client.MongoClients; -import com.mongodb.client.MongoDatabase; -import io.flamingock.cli.config.DatabaseConfig; - -public class MongoClientFactory { - - public static MongoClient createMongoClient(DatabaseConfig.MongoDBConfig config) { - if (config == null) { - throw new IllegalArgumentException("MongoDB configuration is required"); - } - - String connectionString = buildConnectionString(config); - if (connectionString == null) { - throw new IllegalArgumentException("MongoDB connection string or host/database must be provided"); - } - - try { - MongoClientSettings settings = MongoClientSettings.builder() - .applyConnectionString(new ConnectionString(connectionString)) - .build(); - - MongoClient client = MongoClients.create(settings); - - // Test the connection - client.listDatabaseNames().first(); - - return client; - } catch (Exception e) { - throw new RuntimeException("Failed to create MongoDB client: " + e.getMessage(), e); - } - } - - public static MongoDatabase createMongoDatabase(MongoClient mongoClient, DatabaseConfig.MongoDBConfig config) { - if (config.getDatabase() == null) { - throw new IllegalArgumentException("MongoDB database name is required"); - } - - try { - MongoDatabase database = mongoClient.getDatabase(config.getDatabase()); - - // Test database access - database.listCollectionNames().first(); - - return database; - } catch (Exception e) { - throw new RuntimeException("Failed to access MongoDB database '" + config.getDatabase() + "': " + e.getMessage(), e); - } - } - - private static String buildConnectionString(DatabaseConfig.MongoDBConfig config) { - if (config.getConnectionString() != null) { - return config.getConnectionString(); - } - - if (config.getHost() != null && config.getDatabase() != null) { - StringBuilder sb = new StringBuilder("mongodb://"); - - if (config.getUsername() != null && config.getPassword() != null) { - sb.append(config.getUsername()).append(":").append(config.getPassword()).append("@"); - } - - sb.append(config.getHost()); - - if (config.getPort() != null) { - sb.append(":").append(config.getPort()); - } - - sb.append("/").append(config.getDatabase()); - - return sb.toString(); - } - - return null; - } -} \ No newline at end of file diff --git a/cli/flamingock-cli/src/main/java/io/flamingock/cli/factory/SqlDataSourceFactory.java b/cli/flamingock-cli/src/main/java/io/flamingock/cli/factory/SqlDataSourceFactory.java deleted file mode 100644 index 7febe33ee..000000000 --- a/cli/flamingock-cli/src/main/java/io/flamingock/cli/factory/SqlDataSourceFactory.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.factory; - -import com.zaxxer.hikari.HikariConfig; -import com.zaxxer.hikari.HikariDataSource; -import io.flamingock.cli.config.DatabaseConfig; -import io.flamingock.internal.common.sql.SqlDialect; -import org.sqlite.SQLiteDataSource; - -import javax.sql.DataSource; -import java.sql.Connection; -import java.sql.DatabaseMetaData; -import java.sql.SQLException; -import java.sql.Statement; - -public class SqlDataSourceFactory { - - public static DataSource createSqlDataSource(DatabaseConfig.SqlConfig config) { - if (config == null) { - throw new IllegalArgumentException("SQL configuration is required"); - } - - if (config.getEndpoint() == null) { - throw new IllegalArgumentException("Database endpoint is required"); - } - - if (config.getEffectiveSqlDialect() != SqlDialect.SQLITE) { - if (config.getUsername() == null) { - throw new IllegalArgumentException("Database username is required"); - } - if (config.getPassword() == null) { - throw new IllegalArgumentException("Database password is required"); - } - } - - try { - DataSource sqlDatasource; - - if (config.getEffectiveSqlDialect() == (SqlDialect.SQLITE)) { - SQLiteDataSource sqliteDatasource = new SQLiteDataSource(); - sqliteDatasource.setUrl(config.getEndpoint()); - - sqlDatasource = sqliteDatasource; - } else { - HikariConfig datasourceConfig = new HikariConfig(); - datasourceConfig.setJdbcUrl(config.getEndpoint()); - datasourceConfig.setUsername(config.getUsername()); - datasourceConfig.setPassword(config.getPassword()); - datasourceConfig.setDriverClassName(config.getDriverClassName()); - - sqlDatasource = new HikariDataSource(datasourceConfig); - } - - // Test the connection by listing tables - try (Connection conn = sqlDatasource.getConnection()) { - DatabaseMetaData metaData = conn.getMetaData(); - metaData.getTables(null, null, "%", null); - } catch (SQLException e) { - throw new RuntimeException("Failed to validate SQL DataSource connection: " + e.getMessage(), e); - } - - return sqlDatasource; - } catch (Exception e) { - throw new RuntimeException("Failed to create SQL DataSource: " + e.getMessage(), e); - } - } -} diff --git a/cli/flamingock-cli/src/main/java/io/flamingock/cli/handler/CliExceptionHandler.java b/cli/flamingock-cli/src/main/java/io/flamingock/cli/handler/CliExceptionHandler.java deleted file mode 100644 index 874cc6f4c..000000000 --- a/cli/flamingock-cli/src/main/java/io/flamingock/cli/handler/CliExceptionHandler.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.handler; - -import picocli.CommandLine; -import picocli.CommandLine.IExecutionExceptionHandler; -import picocli.CommandLine.ParseResult; -import picocli.CommandLine.UnmatchedArgumentException; - -import java.io.PrintWriter; -import java.util.Arrays; -import java.util.List; - -/** - * CLI exception handler that provides helpful guidance for common CLI errors. - * - *

This handler improves user experience by: - *

    - *
  • Providing clear error messages
  • - *
  • Suggesting corrections for common mistakes
  • - *
  • Guiding users to proper usage patterns
  • - *
- * - * @since 6.0.0 - */ -public class CliExceptionHandler implements IExecutionExceptionHandler { - - private static final List GLOBAL_FLAGS = Arrays.asList( - "--verbose", "--debug", "--trace", "--quiet" - ); - - private static final List COMMANDS = Arrays.asList( - "audit", "issue" - ); - - @Override - public int handleExecutionException(Exception ex, CommandLine commandLine, ParseResult parseResult) { - PrintWriter err = commandLine.getErr(); - - if (ex instanceof UnmatchedArgumentException) { - return handleUnmatchedArgument((UnmatchedArgumentException) ex, commandLine, err); - } - - // Professional error format - err.println(); - err.println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"); - err.println("⚠ Error: " + ex.getMessage()); - err.println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"); - - // Only show stack trace in debug mode - if (System.getProperty("flamingock.debug") != null || - System.getenv("FLAMINGOCK_DEBUG") != null) { - err.println(); - err.println("Debug information:"); - ex.printStackTrace(err); - } else { - err.println(); - err.println("For detailed error information, run with --debug flag"); - } - - return 1; - } - - private int handleUnmatchedArgument(UnmatchedArgumentException ex, CommandLine commandLine, PrintWriter err) { - String unmatchedArg = ex.getUnmatched().isEmpty() ? "" : ex.getUnmatched().get(0); - - // Check if user tried to use a global flag after a command - if (isGlobalFlag(unmatchedArg)) { - String currentCommand = getCurrentCommand(parseResult(commandLine)); - - err.println(); - err.println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"); - err.println("⚠ Error: Unknown option '" + unmatchedArg + "'"); - err.println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"); - err.println(); - err.println("💡 Hint: Global options must be placed BEFORE commands."); - err.println(); - err.println("✗ Incorrect: flamingock " + currentCommand + " " + unmatchedArg); - err.println("✓ Correct: flamingock " + unmatchedArg + " " + currentCommand); - err.println(); - err.println("Examples:"); - err.println(" flamingock --verbose audit list"); - err.println(" flamingock --debug -c config.yml issue list"); - err.println(); - err.println("For help: flamingock --help"); - - return 1; - } - - // Check for common typos - String suggestion = findSuggestion(unmatchedArg); - - err.println(); - err.println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"); - err.println("⚠ Error: " + ex.getMessage()); - err.println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"); - - if (suggestion != null) { - err.println(); - err.println("💡 Did you mean: " + suggestion + "?"); - } - - err.println(); - err.println("For available options, use: flamingock --help"); - - return 1; - } - - private boolean isGlobalFlag(String arg) { - return GLOBAL_FLAGS.stream().anyMatch(arg::equals); - } - - private String getCurrentCommand(ParseResult parseResult) { - if (parseResult == null) return ""; - - StringBuilder cmd = new StringBuilder(); - ParseResult current = parseResult; - - while (current != null) { - if (current.commandSpec().name() != null && !current.commandSpec().name().equals("flamingock")) { - if (cmd.length() > 0) cmd.append(" "); - cmd.append(current.commandSpec().name()); - } - current = current.hasSubcommand() ? current.subcommand() : null; - } - - return cmd.toString(); - } - - private ParseResult parseResult(CommandLine commandLine) { - try { - // Use reflection to get the parse result if available - java.lang.reflect.Field field = CommandLine.class.getDeclaredField("parseResult"); - field.setAccessible(true); - return (ParseResult) field.get(commandLine); - } catch (Exception e) { - return null; - } - } - - private String findSuggestion(String input) { - // Common typos and suggestions - if (input.equals("-verbose") || input.equals("verbose")) return "--verbose"; - if (input.equals("-debug") || input.equals("debug")) return "--debug"; - if (input.equals("-quiet") || input.equals("quiet")) return "--quiet"; - if (input.equals("-trace") || input.equals("trace")) return "--trace"; - if (input.equals("--v")) return "--verbose"; - if (input.equals("--d")) return "--debug"; - if (input.equals("--q")) return "--quiet"; - if (input.equals("--t")) return "--trace"; - - // Command typos - if (input.equals("audits")) return "audit"; - if (input.equals("issues")) return "issue"; - if (input.equals("ls")) return "list"; - - return null; - } -} \ No newline at end of file diff --git a/cli/flamingock-cli/src/main/java/io/flamingock/cli/logging/CliLogLevel.java b/cli/flamingock-cli/src/main/java/io/flamingock/cli/logging/CliLogLevel.java deleted file mode 100644 index a1d335a01..000000000 --- a/cli/flamingock-cli/src/main/java/io/flamingock/cli/logging/CliLogLevel.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.logging; - -/** - * Log levels for CLI output. - * - *

Each level has a priority value. A message is logged if its level's - * priority is less than or equal to the configured global log level. - * - * @since 6.0.0 - */ -public enum CliLogLevel { - ERROR(1), - WARN(2), - INFO(3), - DEBUG(4), - TRACE(5); - - private final int priority; - - CliLogLevel(int priority) { - this.priority = priority; - } - - /** - * Check if this level would enable the given level. - * - *

For example, if this level is INFO, it enables ERROR, WARN, and INFO, - * but not DEBUG or TRACE. - * - * @param other the level to check - * @return true if messages at the other level should be logged - */ - public boolean isEnabled(CliLogLevel other) { - return other.priority <= this.priority; - } - - /** - * Get the priority value. - * - * @return priority (lower values are more important) - */ - public int getPriority() { - return priority; - } -} \ No newline at end of file diff --git a/cli/flamingock-cli/src/main/java/io/flamingock/cli/logging/CliLoggerFactory.java b/cli/flamingock-cli/src/main/java/io/flamingock/cli/logging/CliLoggerFactory.java deleted file mode 100644 index f028ca91c..000000000 --- a/cli/flamingock-cli/src/main/java/io/flamingock/cli/logging/CliLoggerFactory.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.logging; - -/** - * Factory for creating CLI logger instances. - * - *

This factory manages the global log level for all CLI loggers - * and creates logger instances for specific components. - * - * @since 6.0.0 - */ -public class CliLoggerFactory { - - // Global log level for all CLI loggers - private static CliLogLevel globalLevel = CliLogLevel.WARN; - - private CliLoggerFactory() { - // Utility class - prevent instantiation - } - - /** - * Set the global log level for all CLI loggers. - * - *

This affects all existing and future logger instances. - * - * @param level the new global log level - */ - public static void setGlobalLevel(CliLogLevel level) { - globalLevel = level; - } - - /** - * Get the current global log level. - * - * @return the current global log level - */ - public static CliLogLevel getGlobalLevel() { - return globalLevel; - } - - /** - * Create a new CLI logger instance with the given name. - * - * @param name the logger name (typically the class or component name) - * @return a new CliLoggerImpl instance - */ - public static CliLoggerImpl getLogger(String name) { - return new CliLoggerImpl(name); - } - - /** - * Create a new CLI logger instance for the given class. - * - * @param clazz the class to create a logger for - * @return a new CliLoggerImpl instance - */ - public static CliLoggerImpl getLogger(Class clazz) { - return new CliLoggerImpl(clazz.getSimpleName()); - } -} \ No newline at end of file diff --git a/cli/flamingock-cli/src/main/java/io/flamingock/cli/logging/CliLoggerImpl.java b/cli/flamingock-cli/src/main/java/io/flamingock/cli/logging/CliLoggerImpl.java deleted file mode 100644 index cc2c57f89..000000000 --- a/cli/flamingock-cli/src/main/java/io/flamingock/cli/logging/CliLoggerImpl.java +++ /dev/null @@ -1,447 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.logging; - -import org.slf4j.Logger; -import org.slf4j.Marker; - -/** - * CLI-specific implementation of SLF4J Logger that outputs to System.out/System.err. - * - *

This logger implementation is designed for command-line interface usage, - * providing simple, clean output without the overhead of traditional logging frameworks. - * - *

Output routing: - *

    - *
  • ERROR, WARN → System.err
  • - *
  • INFO, DEBUG, TRACE → System.out
  • - *
- * - * @since 6.0.0 - */ -public class CliLoggerImpl implements Logger { - - private final String name; - - /** - * Create a new CLI logger with the given name. - * - * @param name the logger name - */ - public CliLoggerImpl(String name) { - this.name = name; - } - - // Helper method to format messages - private String formatMessage(String level, String format, Object... arguments) { - String message = format; - if (arguments != null && arguments.length > 0) { - // Simple {} replacement (not as sophisticated as SLF4J but adequate for CLI) - for (Object arg : arguments) { - int index = message.indexOf("{}"); - if (index >= 0) { - String argStr = arg != null ? arg.toString() : "null"; - message = message.substring(0, index) + argStr + message.substring(index + 2); - } - } - } - return String.format("[%-5s] %s: %s", level, name, message); - } - - private void logToErr(String level, String format, Object... arguments) { - System.err.println(formatMessage(level, format, arguments)); - } - - private void logToOut(String level, String format, Object... arguments) { - System.out.println(formatMessage(level, format, arguments)); - } - - // ERROR level methods - @Override - public boolean isErrorEnabled() { - return CliLoggerFactory.getGlobalLevel().isEnabled(CliLogLevel.ERROR); - } - - @Override - public void error(String msg) { - if (isErrorEnabled()) { - logToErr("ERROR", msg); - } - } - - @Override - public void error(String format, Object arg) { - if (isErrorEnabled()) { - logToErr("ERROR", format, arg); - } - } - - @Override - public void error(String format, Object arg1, Object arg2) { - if (isErrorEnabled()) { - logToErr("ERROR", format, arg1, arg2); - } - } - - @Override - public void error(String format, Object... arguments) { - if (isErrorEnabled()) { - logToErr("ERROR", format, arguments); - } - } - - @Override - public void error(String msg, Throwable t) { - if (isErrorEnabled()) { - logToErr("ERROR", msg); - if (t != null) { - t.printStackTrace(System.err); - } - } - } - - // WARN level methods - @Override - public boolean isWarnEnabled() { - return CliLoggerFactory.getGlobalLevel().isEnabled(CliLogLevel.WARN); - } - - @Override - public void warn(String msg) { - if (isWarnEnabled()) { - logToErr("WARN", msg); - } - } - - @Override - public void warn(String format, Object arg) { - if (isWarnEnabled()) { - logToErr("WARN", format, arg); - } - } - - @Override - public void warn(String format, Object arg1, Object arg2) { - if (isWarnEnabled()) { - logToErr("WARN", format, arg1, arg2); - } - } - - @Override - public void warn(String format, Object... arguments) { - if (isWarnEnabled()) { - logToErr("WARN", format, arguments); - } - } - - @Override - public void warn(String msg, Throwable t) { - if (isWarnEnabled()) { - logToErr("WARN", msg); - if (t != null) { - t.printStackTrace(System.err); - } - } - } - - // INFO level methods - @Override - public boolean isInfoEnabled() { - return CliLoggerFactory.getGlobalLevel().isEnabled(CliLogLevel.INFO); - } - - @Override - public void info(String msg) { - if (isInfoEnabled()) { - logToOut("INFO", msg); - } - } - - @Override - public void info(String format, Object arg) { - if (isInfoEnabled()) { - logToOut("INFO", format, arg); - } - } - - @Override - public void info(String format, Object arg1, Object arg2) { - if (isInfoEnabled()) { - logToOut("INFO", format, arg1, arg2); - } - } - - @Override - public void info(String format, Object... arguments) { - if (isInfoEnabled()) { - logToOut("INFO", format, arguments); - } - } - - @Override - public void info(String msg, Throwable t) { - if (isInfoEnabled()) { - logToOut("INFO", msg); - if (t != null) { - t.printStackTrace(System.out); - } - } - } - - // DEBUG level methods - @Override - public boolean isDebugEnabled() { - return CliLoggerFactory.getGlobalLevel().isEnabled(CliLogLevel.DEBUG); - } - - @Override - public void debug(String msg) { - if (isDebugEnabled()) { - logToOut("DEBUG", msg); - } - } - - @Override - public void debug(String format, Object arg) { - if (isDebugEnabled()) { - logToOut("DEBUG", format, arg); - } - } - - @Override - public void debug(String format, Object arg1, Object arg2) { - if (isDebugEnabled()) { - logToOut("DEBUG", format, arg1, arg2); - } - } - - @Override - public void debug(String format, Object... arguments) { - if (isDebugEnabled()) { - logToOut("DEBUG", format, arguments); - } - } - - @Override - public void debug(String msg, Throwable t) { - if (isDebugEnabled()) { - logToOut("DEBUG", msg); - if (t != null) { - t.printStackTrace(System.out); - } - } - } - - // TRACE level methods - @Override - public boolean isTraceEnabled() { - return CliLoggerFactory.getGlobalLevel().isEnabled(CliLogLevel.TRACE); - } - - @Override - public void trace(String msg) { - if (isTraceEnabled()) { - logToOut("TRACE", msg); - } - } - - @Override - public void trace(String format, Object arg) { - if (isTraceEnabled()) { - logToOut("TRACE", format, arg); - } - } - - @Override - public void trace(String format, Object arg1, Object arg2) { - if (isTraceEnabled()) { - logToOut("TRACE", format, arg1, arg2); - } - } - - @Override - public void trace(String format, Object... arguments) { - if (isTraceEnabled()) { - logToOut("TRACE", format, arguments); - } - } - - @Override - public void trace(String msg, Throwable t) { - if (isTraceEnabled()) { - logToOut("TRACE", msg); - if (t != null) { - t.printStackTrace(System.out); - } - } - } - - // Marker methods - not used in CLI, but required by interface - @Override - public boolean isErrorEnabled(Marker marker) { - return isErrorEnabled(); - } - - @Override - public void error(Marker marker, String msg) { - error(msg); - } - - @Override - public void error(Marker marker, String format, Object arg) { - error(format, arg); - } - - @Override - public void error(Marker marker, String format, Object arg1, Object arg2) { - error(format, arg1, arg2); - } - - @Override - public void error(Marker marker, String format, Object... arguments) { - error(format, arguments); - } - - @Override - public void error(Marker marker, String msg, Throwable t) { - error(msg, t); - } - - @Override - public boolean isWarnEnabled(Marker marker) { - return isWarnEnabled(); - } - - @Override - public void warn(Marker marker, String msg) { - warn(msg); - } - - @Override - public void warn(Marker marker, String format, Object arg) { - warn(format, arg); - } - - @Override - public void warn(Marker marker, String format, Object arg1, Object arg2) { - warn(format, arg1, arg2); - } - - @Override - public void warn(Marker marker, String format, Object... arguments) { - warn(format, arguments); - } - - @Override - public void warn(Marker marker, String msg, Throwable t) { - warn(msg, t); - } - - @Override - public boolean isInfoEnabled(Marker marker) { - return isInfoEnabled(); - } - - @Override - public void info(Marker marker, String msg) { - info(msg); - } - - @Override - public void info(Marker marker, String format, Object arg) { - info(format, arg); - } - - @Override - public void info(Marker marker, String format, Object arg1, Object arg2) { - info(format, arg1, arg2); - } - - @Override - public void info(Marker marker, String format, Object... arguments) { - info(format, arguments); - } - - @Override - public void info(Marker marker, String msg, Throwable t) { - info(msg, t); - } - - @Override - public boolean isDebugEnabled(Marker marker) { - return isDebugEnabled(); - } - - @Override - public void debug(Marker marker, String msg) { - debug(msg); - } - - @Override - public void debug(Marker marker, String format, Object arg) { - debug(format, arg); - } - - @Override - public void debug(Marker marker, String format, Object arg1, Object arg2) { - debug(format, arg1, arg2); - } - - @Override - public void debug(Marker marker, String format, Object... arguments) { - debug(format, arguments); - } - - @Override - public void debug(Marker marker, String msg, Throwable t) { - debug(msg, t); - } - - @Override - public boolean isTraceEnabled(Marker marker) { - return isTraceEnabled(); - } - - @Override - public void trace(Marker marker, String msg) { - trace(msg); - } - - @Override - public void trace(Marker marker, String format, Object arg) { - trace(format, arg); - } - - @Override - public void trace(Marker marker, String format, Object arg1, Object arg2) { - trace(format, arg1, arg2); - } - - @Override - public void trace(Marker marker, String format, Object... arguments) { - trace(format, arguments); - } - - @Override - public void trace(Marker marker, String msg, Throwable t) { - trace(msg, t); - } - - @Override - public String getName() { - return name; - } -} \ No newline at end of file diff --git a/cli/flamingock-cli/src/main/java/io/flamingock/cli/logging/CliSLF4JLoggerFactory.java b/cli/flamingock-cli/src/main/java/io/flamingock/cli/logging/CliSLF4JLoggerFactory.java deleted file mode 100644 index 4d58d97c9..000000000 --- a/cli/flamingock-cli/src/main/java/io/flamingock/cli/logging/CliSLF4JLoggerFactory.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.logging; - -import org.slf4j.ILoggerFactory; -import org.slf4j.Logger; - -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -/** - * SLF4J LoggerFactory implementation for Flamingock CLI. - * - *

This factory creates CLI-appropriate loggers that output to System.out/System.err - * for Flamingock components (those with "FK-" prefix) and creates silent NoOp loggers - * for all other components to keep CLI output clean. - * - *

Logger instances are cached to ensure consistent behavior and avoid duplicate - * logger creation. - * - * @since 6.0.0 - */ -public class CliSLF4JLoggerFactory implements ILoggerFactory { - - private final ConcurrentMap loggerMap = new ConcurrentHashMap<>(); - - @Override - public Logger getLogger(String name) { - Logger logger = loggerMap.get(name); - if (logger == null) { - Logger newLogger; - if (name.startsWith("FK-")) { - // Use CLI logger for Flamingock components - newLogger = new CliLoggerImpl(name); - } else { - // Use NoOp logger for non-Flamingock components to keep output clean - newLogger = new NoOpLogger(name); - } - Logger existingLogger = loggerMap.putIfAbsent(name, newLogger); - logger = existingLogger == null ? newLogger : existingLogger; - } - return logger; - } -} \ No newline at end of file diff --git a/cli/flamingock-cli/src/main/java/io/flamingock/cli/logging/CliSLF4JServiceProvider.java b/cli/flamingock-cli/src/main/java/io/flamingock/cli/logging/CliSLF4JServiceProvider.java deleted file mode 100644 index 3dbbed2bc..000000000 --- a/cli/flamingock-cli/src/main/java/io/flamingock/cli/logging/CliSLF4JServiceProvider.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.logging; - -import org.slf4j.ILoggerFactory; -import org.slf4j.IMarkerFactory; -import org.slf4j.helpers.BasicMarkerFactory; -import org.slf4j.helpers.NOPMDCAdapter; -import org.slf4j.spi.MDCAdapter; -import org.slf4j.spi.SLF4JServiceProvider; - -/** - * SLF4J 2.x ServiceProvider for Flamingock CLI. - * - *

This service provider is discovered via Java's ServiceLoader mechanism - * and provides CLI-appropriate logging implementations. - * - * @since 6.0.0 - */ -public class CliSLF4JServiceProvider implements SLF4JServiceProvider { - - /** - * Declare the version of the SLF4J API this implementation is compiled against. - */ - public static final String REQUESTED_API_VERSION = "2.0.99"; - - private ILoggerFactory loggerFactory; - private IMarkerFactory markerFactory; - private MDCAdapter mdcAdapter; - - @Override - public ILoggerFactory getLoggerFactory() { - return loggerFactory; - } - - @Override - public IMarkerFactory getMarkerFactory() { - return markerFactory; - } - - @Override - public MDCAdapter getMDCAdapter() { - return mdcAdapter; - } - - @Override - public String getRequestedApiVersion() { - return REQUESTED_API_VERSION; - } - - @Override - public void initialize() { - loggerFactory = new CliSLF4JLoggerFactory(); - markerFactory = new BasicMarkerFactory(); - mdcAdapter = new NOPMDCAdapter(); - } -} \ No newline at end of file diff --git a/cli/flamingock-cli/src/main/java/io/flamingock/cli/logging/LoggingMixin.java b/cli/flamingock-cli/src/main/java/io/flamingock/cli/logging/LoggingMixin.java deleted file mode 100644 index f9c08c874..000000000 --- a/cli/flamingock-cli/src/main/java/io/flamingock/cli/logging/LoggingMixin.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.logging; - -import picocli.CommandLine.Option; - -/** - * Mixin class for CLI logging options. - * Provides global logging level control for all Flamingock operations. - * - *

Note: These options must be specified before the command name. - * Example: {@code flamingock --verbose audit list} - * - * @since 6.0.0 - */ -public class LoggingMixin { - - @Option(names = {"--quiet"}, - description = "Suppress all output except errors. Global option - must be placed before commands.", - order = 1) - private boolean quiet; - - @Option(names = {"--verbose"}, - description = "Enable informational output. Global option - must be placed before commands.", - order = 2) - private boolean verbose; - - @Option(names = {"--debug"}, - description = "Enable debug output for troubleshooting. Global option - must be placed before commands.", - order = 3) - private boolean debug; - - @Option(names = {"--trace"}, - description = "Enable trace output (most detailed). Global option - must be placed before commands.", - order = 4) - private boolean trace; - - /** - * Initialize logging level based on CLI flags. - * Should be called before any logging operations. - */ - public void initializeLogging() { - CliLogLevel level; - - if (quiet) { - level = CliLogLevel.ERROR; - } else if (trace) { - level = CliLogLevel.TRACE; - } else if (debug) { - level = CliLogLevel.DEBUG; - } else if (verbose) { - level = CliLogLevel.INFO; - } else { - // Default: WARN + ERROR - level = CliLogLevel.WARN; - } - - CliLoggerFactory.setGlobalLevel(level); - } -} \ No newline at end of file diff --git a/cli/flamingock-cli/src/main/java/io/flamingock/cli/logging/NoOpLogger.java b/cli/flamingock-cli/src/main/java/io/flamingock/cli/logging/NoOpLogger.java deleted file mode 100644 index 74c144033..000000000 --- a/cli/flamingock-cli/src/main/java/io/flamingock/cli/logging/NoOpLogger.java +++ /dev/null @@ -1,355 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.logging; - -import org.slf4j.Logger; -import org.slf4j.Marker; - -/** - * No-operation logger implementation for non-Flamingock components. - * - *

This logger silently discards all log messages to keep CLI output clean. - * It's used for third-party libraries and other components that shouldn't - * produce output in a CLI environment. - * - *

All logging methods are no-ops, and all isXxxEnabled() methods return false. - * - * @since 6.0.0 - */ -public class NoOpLogger implements Logger { - - private final String name; - - /** - * Create a new NoOp logger with the given name. - * - * @param name the logger name - */ - public NoOpLogger(String name) { - this.name = name; - } - - @Override - public String getName() { - return name; - } - - // All enabled checks return false - @Override - public boolean isTraceEnabled() { - return false; - } - - @Override - public boolean isTraceEnabled(Marker marker) { - return false; - } - - @Override - public boolean isDebugEnabled() { - return false; - } - - @Override - public boolean isDebugEnabled(Marker marker) { - return false; - } - - @Override - public boolean isInfoEnabled() { - return false; - } - - @Override - public boolean isInfoEnabled(Marker marker) { - return false; - } - - @Override - public boolean isWarnEnabled() { - return false; - } - - @Override - public boolean isWarnEnabled(Marker marker) { - return false; - } - - @Override - public boolean isErrorEnabled() { - return false; - } - - @Override - public boolean isErrorEnabled(Marker marker) { - return false; - } - - // All trace methods are no-ops - @Override - public void trace(String msg) { - // no-op - } - - @Override - public void trace(String format, Object arg) { - // no-op - } - - @Override - public void trace(String format, Object arg1, Object arg2) { - // no-op - } - - @Override - public void trace(String format, Object... arguments) { - // no-op - } - - @Override - public void trace(String msg, Throwable t) { - // no-op - } - - @Override - public void trace(Marker marker, String msg) { - // no-op - } - - @Override - public void trace(Marker marker, String format, Object arg) { - // no-op - } - - @Override - public void trace(Marker marker, String format, Object arg1, Object arg2) { - // no-op - } - - @Override - public void trace(Marker marker, String format, Object... argArray) { - // no-op - } - - @Override - public void trace(Marker marker, String msg, Throwable t) { - // no-op - } - - // All debug methods are no-ops - @Override - public void debug(String msg) { - // no-op - } - - @Override - public void debug(String format, Object arg) { - // no-op - } - - @Override - public void debug(String format, Object arg1, Object arg2) { - // no-op - } - - @Override - public void debug(String format, Object... arguments) { - // no-op - } - - @Override - public void debug(String msg, Throwable t) { - // no-op - } - - @Override - public void debug(Marker marker, String msg) { - // no-op - } - - @Override - public void debug(Marker marker, String format, Object arg) { - // no-op - } - - @Override - public void debug(Marker marker, String format, Object arg1, Object arg2) { - // no-op - } - - @Override - public void debug(Marker marker, String format, Object... arguments) { - // no-op - } - - @Override - public void debug(Marker marker, String msg, Throwable t) { - // no-op - } - - // All info methods are no-ops - @Override - public void info(String msg) { - // no-op - } - - @Override - public void info(String format, Object arg) { - // no-op - } - - @Override - public void info(String format, Object arg1, Object arg2) { - // no-op - } - - @Override - public void info(String format, Object... arguments) { - // no-op - } - - @Override - public void info(String msg, Throwable t) { - // no-op - } - - @Override - public void info(Marker marker, String msg) { - // no-op - } - - @Override - public void info(Marker marker, String format, Object arg) { - // no-op - } - - @Override - public void info(Marker marker, String format, Object arg1, Object arg2) { - // no-op - } - - @Override - public void info(Marker marker, String format, Object... arguments) { - // no-op - } - - @Override - public void info(Marker marker, String msg, Throwable t) { - // no-op - } - - // All warn methods are no-ops - @Override - public void warn(String msg) { - // no-op - } - - @Override - public void warn(String format, Object arg) { - // no-op - } - - @Override - public void warn(String format, Object... arguments) { - // no-op - } - - @Override - public void warn(String format, Object arg1, Object arg2) { - // no-op - } - - @Override - public void warn(String msg, Throwable t) { - // no-op - } - - @Override - public void warn(Marker marker, String msg) { - // no-op - } - - @Override - public void warn(Marker marker, String format, Object arg) { - // no-op - } - - @Override - public void warn(Marker marker, String format, Object arg1, Object arg2) { - // no-op - } - - @Override - public void warn(Marker marker, String format, Object... arguments) { - // no-op - } - - @Override - public void warn(Marker marker, String msg, Throwable t) { - // no-op - } - - // All error methods are no-ops - @Override - public void error(String msg) { - // no-op - } - - @Override - public void error(String format, Object arg) { - // no-op - } - - @Override - public void error(String format, Object arg1, Object arg2) { - // no-op - } - - @Override - public void error(String format, Object... arguments) { - // no-op - } - - @Override - public void error(String msg, Throwable t) { - // no-op - } - - @Override - public void error(Marker marker, String msg) { - // no-op - } - - @Override - public void error(Marker marker, String format, Object arg) { - // no-op - } - - @Override - public void error(Marker marker, String format, Object arg1, Object arg2) { - // no-op - } - - @Override - public void error(Marker marker, String format, Object... arguments) { - // no-op - } - - @Override - public void error(Marker marker, String msg, Throwable t) { - // no-op - } -} \ No newline at end of file diff --git a/cli/flamingock-cli/src/main/java/io/flamingock/cli/service/AuditService.java b/cli/flamingock-cli/src/main/java/io/flamingock/cli/service/AuditService.java deleted file mode 100644 index 1dba7c85c..000000000 --- a/cli/flamingock-cli/src/main/java/io/flamingock/cli/service/AuditService.java +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.service; - -import com.couchbase.client.java.Cluster; -import com.mongodb.client.MongoClient; -import io.flamingock.cli.config.ConfigLoader; -import io.flamingock.cli.config.DatabaseConfig; -import io.flamingock.cli.config.FlamingockConfig; -import io.flamingock.cli.factory.CouchbaseClusterFactory; -import io.flamingock.cli.factory.DynamoDBClientFactory; -import io.flamingock.cli.factory.MongoClientFactory; -import io.flamingock.cli.factory.SqlDataSourceFactory; -import io.flamingock.store.couchbase.CouchbaseAuditStore; -import io.flamingock.store.dynamodb.DynamoDBAuditStore; -import io.flamingock.store.mongodb.sync.MongoDBSyncAuditStore; -import io.flamingock.store.sql.SqlAuditStore; -import io.flamingock.internal.common.core.audit.AuditEntry; -import io.flamingock.internal.common.core.context.Context; -import io.flamingock.internal.common.core.context.Dependency; -import io.flamingock.internal.common.core.audit.issue.AuditEntryIssue; -import io.flamingock.internal.common.core.recovery.FixResult; -import io.flamingock.internal.common.core.recovery.Resolution; -import io.flamingock.internal.core.builder.OpsClient; -import io.flamingock.internal.core.builder.OpsClientBuilder; -import io.flamingock.internal.core.configuration.core.CoreConfiguration; -import io.flamingock.internal.core.configuration.community.CommunityConfiguration; -import io.flamingock.internal.core.context.SimpleContext; -import io.flamingock.internal.core.external.store.AuditStore; -import io.flamingock.targetsystem.dynamodb.DynamoDBTargetSystem; -import io.flamingock.targetsystem.couchbase.CouchbaseTargetSystem; -import io.flamingock.targetsystem.sql.SqlTargetSystem; -import io.flamingock.targetsystem.mongodb.sync.MongoDBSyncTargetSystem; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; - -import javax.sql.DataSource; -import java.time.LocalDateTime; -import java.util.List; -import java.util.Optional; - -public class AuditService { - private final FlamingockConfig config; - private final ConfigLoader.DatabaseType databaseType; - private OpsClient opsClient; - - public AuditService(FlamingockConfig config) { - this.config = config; - this.databaseType = ConfigLoader.detectDatabaseType(config); - this.opsClient = createOpsClient(); - } - - /** - * Get snapshot view - latest state per change (DEFAULT) - * - * @return list of audit entries representing the latest state of each change - */ - public List listAuditEntriesSnapshot() { - return opsClient.getAuditSnapshot(); - } - - /** - * Get full chronological history - * - * @return list of audit entries in chronological order - */ - public List listAuditEntriesHistory() { - return opsClient.getAuditHistory(); - } - - /** - * Get entries since a specific date - * - * @param since the date from which to retrieve audit entries - * @return list of audit entries since the specified date - */ - public List listAuditEntriesSince(LocalDateTime since) { - return opsClient.getAuditSnapshotSince(since); - } - - /** - * Get only entries with issues - * - * @return list of audit entries that have issues requiring attention - */ - public List listAuditEntriesWithIssues() { - return opsClient.getAuditIssues(); - } - - - public FixResult fixAuditIssue(String changeId, Resolution resolution) { - if (changeId == null || changeId.trim().isEmpty()) { - throw new IllegalArgumentException("Change ID is required"); - } - return opsClient.fixAuditIssue(changeId.trim(), resolution); - } - /** - * Get detailed information about a specific change that has issues. - * - * @param changeId the change ID to inspect - * @return detailed issue information including all audit entries, error details, etc. - */ - public Optional getAuditEntryIssue(String changeId) { - if (changeId == null || changeId.trim().isEmpty()) { - throw new IllegalArgumentException("Change ID is required"); - } - return opsClient.getAuditIssueByChangeId(changeId.trim()); - } - - private OpsClient createOpsClient() { - try { - // Create core configuration - CoreConfiguration coreConfig = new CoreConfiguration(); - if (config.getServiceIdentifier() != null) { - coreConfig.setServiceIdentifier(config.getServiceIdentifier()); - } - - // Create context with community configuration - Context context = new SimpleContext(); - context.addDependency(new Dependency(CommunityConfiguration.class, new CommunityConfiguration())); - - // Create database-specific clients and audit store - AuditStore auditStore = createAuditStore(context); - - // Build and return OpsClient - return new OpsClientBuilder(coreConfig, context, auditStore).build(); - } catch (Exception e) { - throw new RuntimeException("Failed to create OpsClient: " + e.getMessage(), e); - } - } - - private AuditStore createAuditStore(Context context) { - switch (databaseType) { - case MONGODB: - return createMongoAuditStore(context); - case DYNAMODB: - return createDynamoAuditStore(context); - case COUCHBASE: - return createCouchbaseAuditStore(context); - case SQL: - return createSqlAuditStore(context); - default: - throw new IllegalStateException("Unsupported database type: " + databaseType); - } - } - - private AuditStore createMongoAuditStore(Context context) { - DatabaseConfig.MongoDBConfig mongoConfig = config.getAudit().getMongodb(); - - // Create MongoDB clients - MongoClient mongoClient = MongoClientFactory.createMongoClient(mongoConfig); - - return MongoDBSyncAuditStore.from(new MongoDBSyncTargetSystem("mongodb", mongoClient, mongoConfig.getDatabase())) - .withAuditRepositoryName(mongoConfig.getCollection()); - } - - private AuditStore createDynamoAuditStore(Context context) { - DatabaseConfig.DynamoDBConfig dynamoConfig = config.getAudit().getDynamodb(); - - // Create DynamoDB client - DynamoDbClient dynamoClient = DynamoDBClientFactory.createDynamoDBClient(dynamoConfig); - - return DynamoDBAuditStore.from(new DynamoDBTargetSystem("dynamo", dynamoClient)) - .withAuditRepositoryName(dynamoConfig.getTable()); - } - - private AuditStore createCouchbaseAuditStore(Context context) { - DatabaseConfig.CouchbaseConfig couchbaseConfig = config.getAudit().getCouchbase(); - - // Create Couchbase cluster - Cluster couchbaseCluster = CouchbaseClusterFactory.createCouchbaseCluster(couchbaseConfig); - - return CouchbaseAuditStore.from(new CouchbaseTargetSystem("couchbase", couchbaseCluster, couchbaseConfig.getBucketName())) - .withAuditRepositoryName(couchbaseConfig.getTable()); - } - - private AuditStore createSqlAuditStore(Context context) { - DatabaseConfig.SqlConfig sqlConfig = config.getAudit().getSql(); - - // Create Couchbase cluster - DataSource dataSource = SqlDataSourceFactory.createSqlDataSource(sqlConfig); - - return SqlAuditStore.from(new SqlTargetSystem("sql", dataSource)) - .withAuditRepositoryName(sqlConfig.getTable()); - } -} diff --git a/cli/flamingock-cli/src/main/java/io/flamingock/cli/service/JsonFormatter.java b/cli/flamingock-cli/src/main/java/io/flamingock/cli/service/JsonFormatter.java deleted file mode 100644 index d97efdb98..000000000 --- a/cli/flamingock-cli/src/main/java/io/flamingock/cli/service/JsonFormatter.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.service; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonSerializer; -import io.flamingock.internal.common.core.audit.AuditEntry; -import io.flamingock.internal.common.core.audit.issue.AuditEntryIssue; - -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -/** - * Utility class for formatting CLI output as JSON. - * Provides methods to convert various data structures to pretty-printed JSON. - */ -public class JsonFormatter { - - private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE_TIME; - - private final Gson gson; - - public JsonFormatter() { - this.gson = new GsonBuilder() - .setPrettyPrinting() - .registerTypeAdapter(LocalDateTime.class, - (JsonSerializer) (src, typeOfSrc, context) -> - context.serialize(src.format(DATE_TIME_FORMATTER))) - .create(); - } - - /** - * Print a list of audit entries with issues as JSON. - * - * @param issues List of audit entries representing issues - */ - public void printIssueList(List issues) { - List> issueList = issues.stream() - .map(AuditEntryIssue::getAuditEntry) - .map(this::auditEntryToMap) - .collect(Collectors.toList()); - - Map output = new HashMap<>(); - output.put("issues", issueList); - output.put("total", issues.size()); - - System.out.println(gson.toJson(output)); - } - - /** - * Print detailed issue information as JSON. - * - * @param details Issue details to format - */ - public void printIssueDetails(AuditEntryIssue details) { - Map output = new HashMap<>(); - output.put("changeId", details.getChangeId() != null ? details.getChangeId() : ""); - output.put("errorMessage", details.getErrorMessage() != null ? details.getErrorMessage() : ""); - output.put("auditEntry", details.getAuditEntry()); - - System.out.println(gson.toJson(output)); - } - - /** - * Print a list of audit entries as JSON. - * - * @param entries List of audit entries to format - */ - public void printAuditEntries(List entries) { - List> entryList = entries.stream() - .map(this::auditEntryToMap) - .collect(Collectors.toList()); - - Map output = new HashMap<>(); - output.put("entries", entryList); - output.put("total", entries.size()); - - System.out.println(gson.toJson(output)); - } - - /** - * Convert an AuditEntry to a Map for JSON serialization. - */ - private Map auditEntryToMap(AuditEntry entry) { - Map map = new HashMap<>(); - map.put("changeId", entry.getTaskId() != null ? entry.getTaskId() : ""); - map.put("state", entry.getState() != null ? entry.getState().toString() : ""); - map.put("author", entry.getAuthor() != null ? entry.getAuthor() : ""); - map.put("executionId", entry.getExecutionId() != null ? entry.getExecutionId() : ""); - map.put("className", entry.getClassName() != null ? entry.getClassName() : ""); - map.put("methodName", entry.getMethodName() != null ? entry.getMethodName() : ""); - map.put("createdAt", entry.getCreatedAt() != null ? entry.getCreatedAt() : ""); - map.put("executionMillis", entry.getExecutionMillis()); - map.put("executionHostname", entry.getExecutionHostname() != null ? entry.getExecutionHostname() : ""); - return map; - } -} \ No newline at end of file diff --git a/cli/flamingock-cli/src/main/java/io/flamingock/cli/service/TableColumn.java b/cli/flamingock-cli/src/main/java/io/flamingock/cli/service/TableColumn.java deleted file mode 100644 index 2f7f031ec..000000000 --- a/cli/flamingock-cli/src/main/java/io/flamingock/cli/service/TableColumn.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.service; - -/** - * Represents a table column definition with title, width and alignment - */ -public class TableColumn { - - public enum Alignment { - LEFT, CENTER, RIGHT - } - - private final String title; - private final int width; - private final Alignment alignment; - - public TableColumn(String title, int width, Alignment alignment) { - this.title = title; - this.width = width; - this.alignment = alignment; - } - - public TableColumn(String title, int width) { - this(title, width, Alignment.LEFT); - } - - public String getTitle() { - return title; - } - - public int getWidth() { - return width; - } - - public Alignment getAlignment() { - return alignment; - } - - /** - * Format text according to column width and alignment - * - * @param text the text to format - * @return formatted text that fits within the column width and alignment - */ - public String format(String text) { - String truncated = truncate(text); - return align(truncated); - } - - /** - * Truncate text if it exceeds column width - */ - private String truncate(String text) { - if (text == null) { - return "-"; - } - if (text.length() <= width) { - return text; - } - // Account for "..." when truncating - if (width <= 3) { - return "...".substring(0, width); - } - return text.substring(0, width - 3) + "..."; - } - - /** - * Align text within column width - */ - private String align(String text) { - switch (alignment) { - case RIGHT: - return String.format("%" + width + "s", text); - case CENTER: - int totalPadding = width - text.length(); - if (totalPadding <= 0) { - return text; - } - int leftPad = totalPadding / 2; - int rightPad = totalPadding - leftPad; - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < leftPad; i++) sb.append(" "); - sb.append(text); - for (int i = 0; i < rightPad; i++) sb.append(" "); - return sb.toString(); - case LEFT: - default: - return String.format("%-" + width + "s", text); - } - } -} \ No newline at end of file diff --git a/cli/flamingock-cli/src/main/java/io/flamingock/cli/service/TableFormatter.java b/cli/flamingock-cli/src/main/java/io/flamingock/cli/service/TableFormatter.java deleted file mode 100644 index 033fa6ee5..000000000 --- a/cli/flamingock-cli/src/main/java/io/flamingock/cli/service/TableFormatter.java +++ /dev/null @@ -1,286 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.service; - -import io.flamingock.internal.common.core.audit.AuditEntry; - -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.List; - - -public class TableFormatter { - - // Unicode box-drawing characters - private static final String VERTICAL = "│"; - private static final String HORIZONTAL = "─"; - private static final String TOP_LEFT = "┌"; - private static final String TOP_RIGHT = "┐"; - private static final String BOTTOM_LEFT = "└"; - private static final String BOTTOM_RIGHT = "┘"; - private static final String T_DOWN = "┬"; - private static final String T_UP = "┴"; - private static final String T_RIGHT = "├"; - private static final String T_LEFT = "┤"; - private static final String CROSS = "┼"; - - // Column widths for basic table - private static final int CHANGE_ID_WIDTH = 30; - private static final int STATE_WIDTH = 8; - private static final int EXEC_ID_WIDTH = 34; - private static final int AUTHOR_WIDTH = 18; - private static final int TIME_WIDTH = 21; - - // Additional column widths for extended table - private static final int DURATION_WIDTH = 10; - private static final int CLASS_WIDTH = 34; - private static final int METHOD_WIDTH = 13; - private static final int HOSTNAME_WIDTH = 29; - - private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); - - /** - * Print basic table with 4 columns - * - * @param entries the audit entries to display in the table - */ - public void printBasicTable(List entries) { - List columns = new ArrayList<>(); - columns.add(new TableColumn("Change ID", CHANGE_ID_WIDTH)); - columns.add(new TableColumn("State", STATE_WIDTH, TableColumn.Alignment.CENTER)); - columns.add(new TableColumn("Author", AUTHOR_WIDTH)); - columns.add(new TableColumn("Time", TIME_WIDTH)); - - printTable(entries, columns, false); - } - - /** - * Print extended table with 9 columns - * - * @param entries the audit entries to display in the extended table - */ - public void printExtendedTable(List entries) { - List columns = new ArrayList<>(); - columns.add(new TableColumn("Change ID", CHANGE_ID_WIDTH)); - columns.add(new TableColumn("State", STATE_WIDTH, TableColumn.Alignment.CENTER)); - columns.add(new TableColumn("Execution ID", EXEC_ID_WIDTH)); - columns.add(new TableColumn("Author", AUTHOR_WIDTH)); - columns.add(new TableColumn("Time", TIME_WIDTH)); - columns.add(new TableColumn("Duration", DURATION_WIDTH, TableColumn.Alignment.RIGHT)); - columns.add(new TableColumn("Class", CLASS_WIDTH)); - columns.add(new TableColumn("Method", METHOD_WIDTH)); - columns.add(new TableColumn("Hostname", HOSTNAME_WIDTH)); - - printTable(entries, columns, true); - } - - private void printTable(List entries, List columns, boolean extended) { - // Print top border - printTopBorder(columns); - - // Print header row - printHeaderRow(columns); - - // Print header separator - printMiddleBorder(columns); - - // Print data rows - for (int i = 0; i < entries.size(); i++) { - printDataRow(entries.get(i), columns); - if (extended && i < entries.size() - 1) { - // Add spacing row between entries in extended view - printSpacingRow(columns); - printMiddleBorder(columns); - } - } - - // Print bottom border - printBottomBorder(columns); - } - - private void printTopBorder(List columns) { - System.out.print(TOP_LEFT); - for (int i = 0; i < columns.size(); i++) { - for (int j = 0; j < columns.get(i).getWidth(); j++) { - System.out.print(HORIZONTAL); - } - if (i < columns.size() - 1) { - System.out.print(T_DOWN); - } - } - System.out.println(TOP_RIGHT); - } - - private void printMiddleBorder(List columns) { - System.out.print(T_RIGHT); - for (int i = 0; i < columns.size(); i++) { - for (int j = 0; j < columns.get(i).getWidth(); j++) { - System.out.print(HORIZONTAL); - } - if (i < columns.size() - 1) { - System.out.print(CROSS); - } - } - System.out.println(T_LEFT); - } - - private void printBottomBorder(List columns) { - System.out.print(BOTTOM_LEFT); - for (int i = 0; i < columns.size(); i++) { - for (int j = 0; j < columns.get(i).getWidth(); j++) { - System.out.print(HORIZONTAL); - } - if (i < columns.size() - 1) { - System.out.print(T_UP); - } - } - System.out.println(BOTTOM_RIGHT); - } - - private void printHeaderRow(List columns) { - System.out.print(VERTICAL); - for (TableColumn column : columns) { - System.out.print(column.format(column.getTitle())); - System.out.print(VERTICAL); - } - System.out.println(); - } - - private void printSpacingRow(List columns) { - System.out.print(VERTICAL); - for (TableColumn column : columns) { - System.out.print(column.format("")); - System.out.print(VERTICAL); - } - System.out.println(); - } - - private void printDataRow(AuditEntry entry, List columns) { - System.out.print(VERTICAL); - for (int i = 0; i < columns.size(); i++) { - String value = getColumnValue(entry, i, columns.size() > 5); - System.out.print(columns.get(i).format(value)); - System.out.print(VERTICAL); - } - System.out.println(); - } - - private String getColumnValue(AuditEntry entry, int columnIndex, boolean extended) { - if (extended) { - // Extended table (9 columns) - switch (columnIndex) { - case 0: // Change ID - return entry.getTaskId(); - case 1: // State (icon only) - return getStateIcon(entry.getState()); - case 2: // Execution ID - return entry.getExecutionId(); - case 3: // Author - return entry.getAuthor(); - case 4: // Time - return formatTime(entry.getCreatedAt()); - case 5: // Duration - return formatDuration(entry.getExecutionMillis()); - case 6: // Class - return shortenClassName(entry.getClassName()); - case 7: // Method - return entry.getMethodName(); - case 8: // Hostname - return entry.getExecutionHostname(); - default: - return ""; - } - } else { - // Basic table (4 columns) - switch (columnIndex) { - case 0: // Change ID - return entry.getTaskId(); - case 1: // State (icon only) - return getStateIcon(entry.getState()); - case 2: // Author - return entry.getAuthor(); - case 3: // Time - return formatTime(entry.getCreatedAt()); - default: - return ""; - } - } - } - - private String getStateIcon(AuditEntry.Status status) { - if (status == null) return "❓"; - - switch (status) { - case APPLIED: - case MANUAL_MARKED_AS_APPLIED: - return "✅"; - case ROLLED_BACK: - case MANUAL_MARKED_AS_ROLLED_BACK: - return "🔄"; - case FAILED: - case ROLLBACK_FAILED: - return "❌"; - case STARTED: - return "⚠️"; - default: - return "❓"; - } - } - - private String formatTime(LocalDateTime time) { - if (time == null) { - return "-"; - } - return time.format(TIME_FORMATTER); - } - - private String formatDuration(Long millis) { - if (millis == null) { - return "-"; - } - return millis + "ms"; - } - - private String shortenClassName(String className) { - if (className == null) { - return "-"; - } - // Shorten package names: io.flamingock.changes -> i.f.changes - String[] parts = className.split("\\."); - StringBuilder shortened = new StringBuilder(); - for (int i = 0; i < parts.length - 1; i++) { - if (parts[i].length() > 0) { - shortened.append(parts[i].charAt(0)).append("."); - } - } - if (parts.length > 0) { - shortened.append(parts[parts.length - 1]); - } - return shortened.toString(); - } - - /** - * Print the state legend - */ - public static void printStateLegend() { - System.out.println("\nState Legend:"); - System.out.println("✅ APPLIED - Successfully completed, won't be reapplied"); - System.out.println("🔄 ROLLED_BACK - Successfully reverted, needs to be applied again"); - System.out.println("❌ FAILED - Execution or rollback failed"); - System.out.println("⚠️ STARTED - Unknown/incomplete state (partial execution or audit failure)"); - } -} \ No newline at end of file diff --git a/cli/flamingock-cli/src/main/java/io/flamingock/cli/util/ASCIIColors.java b/cli/flamingock-cli/src/main/java/io/flamingock/cli/util/ASCIIColors.java deleted file mode 100644 index 11de42385..000000000 --- a/cli/flamingock-cli/src/main/java/io/flamingock/cli/util/ASCIIColors.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.util; - -public final class ASCIIColors { - private ASCIIColors(){} - - public static final String VIOLET = "\u001B[35m"; - public static final String RESET = "\u001B[0m"; - - public static String inViolet(String msg) { - return VIOLET + msg + RESET; - } -} diff --git a/cli/flamingock-cli/src/main/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider b/cli/flamingock-cli/src/main/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider deleted file mode 100644 index cd6b33e27..000000000 --- a/cli/flamingock-cli/src/main/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider +++ /dev/null @@ -1 +0,0 @@ -io.flamingock.cli.logging.CliSLF4JServiceProvider \ No newline at end of file diff --git a/cli/flamingock-cli/src/test/java/io/flamingock/cli/SimpleCLITest.java b/cli/flamingock-cli/src/test/java/io/flamingock/cli/SimpleCLITest.java deleted file mode 100644 index fab06cfe8..000000000 --- a/cli/flamingock-cli/src/test/java/io/flamingock/cli/SimpleCLITest.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli; - -import io.flamingock.cli.config.ConfigLoader; -import io.flamingock.cli.config.FlamingockConfig; -import io.flamingock.cli.test.TestUtils; -import org.junit.jupiter.api.Test; -import picocli.CommandLine; - -import static io.flamingock.internal.common.sql.SqlDialect.SQLSERVER; -import static org.assertj.core.api.Assertions.*; - -class SimpleCLITest { - - @Test - void shouldCreateCommandLine() { - // When - CommandLine cmd = new CommandLine(new FlamingockCli()); - - // Then - assertThat(cmd).isNotNull(); - assertThat(cmd.getCommandName()).isEqualTo("flamingock"); - } - - @Test - void shouldHaveAuditSubcommands() { - // When - CommandLine cmd = new CommandLine(new FlamingockCli()); - - // Then - assertThat(cmd.getSubcommands()).containsKey("audit"); - CommandLine auditCmd = cmd.getSubcommands().get("audit"); - assertThat(auditCmd.getSubcommands()).containsKeys("list", "fix"); - } - - - @Test - void shouldDetectDatabaseTypes() { - // Given - FlamingockConfig mongoConfig = TestUtils.createMongoConfig(); - FlamingockConfig dynamoConfig = TestUtils.createDynamoConfig(); - FlamingockConfig couchbaseConfig = TestUtils.createCouchbaseConfig(); - FlamingockConfig sqlConfig = TestUtils.createSqlConfig(); - - // When/Then - assertThat(ConfigLoader.detectDatabaseType(mongoConfig)) - .isEqualTo(ConfigLoader.DatabaseType.MONGODB); - - assertThat(ConfigLoader.detectDatabaseType(dynamoConfig)) - .isEqualTo(ConfigLoader.DatabaseType.DYNAMODB); - - assertThat(ConfigLoader.detectDatabaseType(couchbaseConfig)) - .isEqualTo(ConfigLoader.DatabaseType.COUCHBASE); - - assertThat(ConfigLoader.detectDatabaseType(sqlConfig)) - .isEqualTo(ConfigLoader.DatabaseType.SQL); - } - - @Test - void shouldCreateTestConfigs() { - // When - FlamingockConfig mongoConfig = TestUtils.createMongoConfig(); - FlamingockConfig dynamoConfig = TestUtils.createDynamoConfig(); - FlamingockConfig couchbaseoConfig = TestUtils.createCouchbaseConfig(); - FlamingockConfig sqlConfig = TestUtils.createSqlConfig(); - - // Then - assertThat(mongoConfig).isNotNull(); - assertThat(mongoConfig.getAudit().getMongodb()).isNotNull(); - assertThat(mongoConfig.getAudit().getMongodb().getDatabase()).isEqualTo("test-db"); - - assertThat(dynamoConfig).isNotNull(); - assertThat(dynamoConfig.getAudit().getDynamodb()).isNotNull(); - assertThat(dynamoConfig.getAudit().getDynamodb().getRegion()).isEqualTo("us-east-1"); - - assertThat(couchbaseoConfig).isNotNull(); - assertThat(couchbaseoConfig.getAudit().getCouchbase()).isNotNull(); - assertThat(couchbaseoConfig.getAudit().getCouchbase().getBucketName()).isEqualTo("test-bucket"); - - assertThat(sqlConfig).isNotNull(); - assertThat(sqlConfig.getAudit().getSql()).isNotNull(); - assertThat(sqlConfig.getAudit().getSql().getEffectiveSqlDialect()).isEqualTo(SQLSERVER); - } -} diff --git a/cli/flamingock-cli/src/test/java/io/flamingock/cli/config/SimpleConfigLoaderTest.java b/cli/flamingock-cli/src/test/java/io/flamingock/cli/config/SimpleConfigLoaderTest.java deleted file mode 100644 index 89794946e..000000000 --- a/cli/flamingock-cli/src/test/java/io/flamingock/cli/config/SimpleConfigLoaderTest.java +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.config; - -import io.flamingock.cli.test.TestUtils; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Optional; - -import static io.flamingock.internal.common.sql.SqlDialect.SQLSERVER; -import static org.assertj.core.api.Assertions.*; - -class SimpleConfigLoaderTest { - - @TempDir - Path tempDir; - - @Test - void shouldLoadValidMongoConfiguration() throws IOException { - // Given - Path configFile = tempDir.resolve("mongo-config.yml"); - Files.write(configFile, TestUtils.getValidMongoYaml().getBytes()); - - // When - FlamingockConfig config = ConfigLoader.loadConfig(configFile.toString()); - - // Then - assertThat(config.getServiceIdentifier()).isEqualTo("test-cli"); - assertThat(config.getAudit()).isNotNull(); - assertThat(config.getAudit().getMongodb()).isNotNull(); - assertThat(config.getAudit().getMongodb().getConnectionString()).isEqualTo("mongodb://localhost:27017"); - assertThat(config.getAudit().getMongodb().getDatabase()).isEqualTo("test"); - } - - @Test - void shouldLoadValidDynamoConfiguration() throws IOException { - // Given - Path configFile = tempDir.resolve("dynamo-config.yml"); - Files.write(configFile, TestUtils.getValidDynamoYaml().getBytes()); - - // When - FlamingockConfig config = ConfigLoader.loadConfig(configFile.toString()); - - // Then - assertThat(config.getServiceIdentifier()).isEqualTo("test-cli"); - assertThat(config.getAudit()).isNotNull(); - assertThat(config.getAudit().getDynamodb()).isNotNull(); - assertThat(config.getAudit().getDynamodb().getRegion()).isEqualTo("us-east-1"); - assertThat(config.getAudit().getDynamodb().getEndpoint()).isEqualTo("http://localhost:8000"); - } - - @Test - void shouldLoadValidCouchbaseConfiguration() throws IOException { - // Given - Path configFile = tempDir.resolve("couchbase-config.yml"); - Files.write(configFile, TestUtils.getValidCouchbaseYaml().getBytes()); - - // When - FlamingockConfig config = ConfigLoader.loadConfig(configFile.toString()); - - // Then - assertThat(config.getServiceIdentifier()).isEqualTo("test-cli"); - assertThat(config.getAudit()).isNotNull(); - assertThat(config.getAudit().getCouchbase()).isNotNull(); - assertThat(config.getAudit().getCouchbase().getEndpoint()).isEqualTo("couchbase://localhost:12110"); - assertThat(config.getAudit().getCouchbase().getUsername()).isEqualTo("test-user"); - assertThat(config.getAudit().getCouchbase().getPassword()).isEqualTo("test-password"); - assertThat(config.getAudit().getCouchbase().getBucketName()).isEqualTo("test-bucket"); - } - - @Test - void shouldLoadValidSqlConfiguration() throws IOException { - // Given - Path configFile = tempDir.resolve("sql-config.yml"); - Files.write(configFile, TestUtils.getValidSqlYaml().getBytes()); - - // When - FlamingockConfig config = ConfigLoader.loadConfig(configFile.toString()); - - // Then - assertThat(config.getServiceIdentifier()).isEqualTo("test-cli"); - assertThat(config.getAudit()).isNotNull(); - assertThat(config.getAudit().getSql()).isNotNull(); - assertThat(config.getAudit().getSql().getEndpoint()).isEqualTo("jdbc:sqlserver://localhost:1433"); - assertThat(config.getAudit().getSql().getUsername()).isEqualTo("test-user"); - assertThat(config.getAudit().getSql().getPassword()).isEqualTo("test-password"); - assertThat(config.getAudit().getSql().getSqlDialect()).isEqualTo(Optional.of(SQLSERVER)); - } - - @Test - void shouldDetectMongoDBType() throws IOException { - // Given - FlamingockConfig config = TestUtils.createMongoConfig(); - - // When - ConfigLoader.DatabaseType type = ConfigLoader.detectDatabaseType(config); - - // Then - assertThat(type).isEqualTo(ConfigLoader.DatabaseType.MONGODB); - } - - @Test - void shouldDetectDynamoDBType() throws IOException { - // Given - FlamingockConfig config = TestUtils.createDynamoConfig(); - - // When - ConfigLoader.DatabaseType type = ConfigLoader.detectDatabaseType(config); - - // Then - assertThat(type).isEqualTo(ConfigLoader.DatabaseType.DYNAMODB); - } - - @Test - void shouldDetectCouchbaseType() throws IOException { - // Given - FlamingockConfig config = TestUtils.createCouchbaseConfig(); - - // When - ConfigLoader.DatabaseType type = ConfigLoader.detectDatabaseType(config); - - // Then - assertThat(type).isEqualTo(ConfigLoader.DatabaseType.COUCHBASE); - } - - @Test - void shouldDetectSqlType() throws IOException { - // Given - FlamingockConfig config = TestUtils.createSqlConfig(); - - // When - ConfigLoader.DatabaseType type = ConfigLoader.detectDatabaseType(config); - - // Then - assertThat(type).isEqualTo(ConfigLoader.DatabaseType.SQL); - } - - @Test - void shouldThrowExceptionForMissingFile() { - // When/Then - assertThatThrownBy(() -> ConfigLoader.loadConfig("non-existent-file.yml")) - .isInstanceOf(IOException.class) - .hasMessageContaining("Configuration file not found"); - } - - @Test - void shouldThrowExceptionForMultipleDatabases() throws IOException { - // Given - String yamlContent = "flamingock:\n" + - " audit:\n" + - " mongodb:\n" + - " connection-string: \"mongodb://localhost:27017\"\n" + - " database: \"test\"\n" + - " dynamodb:\n" + - " region: \"us-east-1\"\n"; - - Path configFile = tempDir.resolve("multi-db.yml"); - Files.write(configFile, yamlContent.getBytes()); - - // When - FlamingockConfig config = ConfigLoader.loadConfig(configFile.toString()); - - // Then - assertThatThrownBy(() -> ConfigLoader.detectDatabaseType(config)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("Multiple database configurations found"); - } - - @Test - void shouldThrowExceptionForNoDatabases() throws IOException { - // Given - String yamlContent = "flamingock:\n" + - " service-identifier: \"no-db-service\"\n"; - - Path configFile = tempDir.resolve("no-db.yml"); - Files.write(configFile, yamlContent.getBytes()); - - // When - FlamingockConfig config = ConfigLoader.loadConfig(configFile.toString()); - - // Then - assertThatThrownBy(() -> ConfigLoader.detectDatabaseType(config)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("No audit configuration found"); - } -} diff --git a/cli/flamingock-cli/src/test/java/io/flamingock/cli/integration/CLICommandExecutionTest.java b/cli/flamingock-cli/src/test/java/io/flamingock/cli/integration/CLICommandExecutionTest.java deleted file mode 100644 index c14c4ec19..000000000 --- a/cli/flamingock-cli/src/test/java/io/flamingock/cli/integration/CLICommandExecutionTest.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.integration; - -import com.github.stefanbirkner.systemlambda.SystemLambda; -import io.flamingock.cli.FlamingockCli; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; -import org.testcontainers.containers.MongoDBContainer; -import org.testcontainers.junit.jupiter.Container; -import org.testcontainers.junit.jupiter.Testcontainers; -import picocli.CommandLine; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; - -import static org.assertj.core.api.Assertions.*; - -@Testcontainers -class CLICommandExecutionTest { - - @Container - static MongoDBContainer mongoContainer = new MongoDBContainer("mongo:5.0") - .withExposedPorts(27017); - - @Test - void shouldExecuteAllBasicCLICommands() throws Exception { - // Given - Create config file - Path configFile = createMongoConfigFile(); - - try { - // Test help command - testHelpCommand(); - - // Test audit list command - testAuditListCommand(configFile); - - // Test audit list verbose - testAuditListVerboseCommand(configFile); - - } finally { - Files.deleteIfExists(configFile); - } - } - - @Test - void shouldVerifyOpsClientCreation() throws Exception { - // Given - Path configFile = createMongoConfigFile(); - - try { - // When - Execute command that requires OpsClient creation - String output = SystemLambda.tapSystemOut(() -> { - String[] args = {"--debug", "--config", configFile.toString(), "audit", "list"}; - CommandLine cmd = new CommandLine(new FlamingockCli()); - int exitCode = cmd.execute(args); - - // Then - Should successfully create OpsClient and connect - assertThat(exitCode).isIn(0, 1, 2); // Accept success, runtime errors, and CLI syntax/config errors - }); - - // Verify OpsClient was created successfully (no connection errors) - System.out.println("OpsClient creation test output:"); - System.out.println(output); - - // The fact that we get exit code 0 means: - // 1. Configuration was loaded successfully - // 2. Database type was detected - // 3. OpsClient was created - // 4. Connection to database was established - - } finally { - Files.deleteIfExists(configFile); - } - } - - @Test - void shouldHandleMarkCommandValidation() throws Exception { - // Given - Path configFile = createMongoConfigFile(); - - try { - // When - Try to run mark command without required parameters - String output = SystemLambda.tapSystemErr(() -> { - String[] args = {"--config", configFile.toString(), "audit", "mark"}; - CommandLine cmd = new CommandLine(new FlamingockCli()); - int exitCode = cmd.execute(args); - - // Then - Should fail with validation error - assertThat(exitCode).isNotEqualTo(0); - }); - - System.out.println("Mark command validation output:"); - System.out.println(output); - - } finally { - Files.deleteIfExists(configFile); - } - } - - private void testHelpCommand() throws Exception { - String output = SystemLambda.tapSystemOut(() -> { - String[] args = {"--help"}; - CommandLine cmd = new CommandLine(new FlamingockCli()); - int exitCode = cmd.execute(args); - assertThat(exitCode).isEqualTo(0); - }); - - System.out.println("Help command output:"); - System.out.println(output); - assertThat(output).contains("flamingock"); - } - - private void testAuditListCommand(Path configFile) throws Exception { - String output = SystemLambda.tapSystemOut(() -> { - String[] args = {"--config", configFile.toString(), "audit", "list"}; - CommandLine cmd = new CommandLine(new FlamingockCli()); - int exitCode = cmd.execute(args); - assertThat(exitCode).isIn(0, 1, 2); // Accept success, runtime errors, and CLI syntax/config errors - }); - - System.out.println("Audit list command output:"); - System.out.println(output); - } - - private void testAuditListVerboseCommand(Path configFile) throws Exception { - String output = SystemLambda.tapSystemOut(() -> { - String[] args = {"--debug", "--config", configFile.toString(), "audit", "list"}; - CommandLine cmd = new CommandLine(new FlamingockCli()); - int exitCode = cmd.execute(args); - assertThat(exitCode).isIn(0, 1, 2); // Accept success, runtime errors, and CLI syntax/config errors - }); - - System.out.println("Audit list verbose command output:"); - System.out.println(output); - } - - private Path createMongoConfigFile() throws IOException { - Path configFile = Files.createTempFile("cli-test", ".yml"); - String mongoConfig = "flamingock:\n" + - " service-identifier: \"cli-execution-test\"\n" + - " audit:\n" + - " mongodb:\n" + - " connection-string: \"" + mongoContainer.getConnectionString() + "\"\n" + - " database: \"cli_test\"\n"; - - Files.write(configFile, mongoConfig.getBytes()); - return configFile; - } -} \ No newline at end of file diff --git a/cli/flamingock-cli/src/test/java/io/flamingock/cli/integration/CLICouchbaseIntegrationTest.java b/cli/flamingock-cli/src/test/java/io/flamingock/cli/integration/CLICouchbaseIntegrationTest.java deleted file mode 100644 index 606e68e19..000000000 --- a/cli/flamingock-cli/src/test/java/io/flamingock/cli/integration/CLICouchbaseIntegrationTest.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.integration; - -import com.github.stefanbirkner.systemlambda.SystemLambda; -import io.flamingock.cli.FlamingockCli; -import org.junit.jupiter.api.Test; -import org.testcontainers.couchbase.BucketDefinition; -import org.testcontainers.couchbase.CouchbaseContainer; -import org.testcontainers.junit.jupiter.Container; -import org.testcontainers.junit.jupiter.Testcontainers; -import picocli.CommandLine; - -import java.nio.file.Files; -import java.nio.file.Path; - -import static org.assertj.core.api.Assertions.assertThat; - -@Testcontainers -class CLICouchbaseIntegrationTest { - - private static final String BUCKET_NAME = "test"; - - @Container - public static final CouchbaseContainer couchbaseContainer = new CouchbaseContainer("couchbase/server:7.2.4") - .withBucket(new BucketDefinition(BUCKET_NAME)); - - @Test - void shouldRunAuditListCommandWithCouchbase() throws Exception { - // Given - Create temporary config file with TestContainers Couchbase connection - Path configFile = Files.createTempFile("couchbase-integration", ".yml"); - String couchbaseConfig = "flamingock:\n" + - " service-identifier: \"integration-test-cli\"\n" + - " audit:\n" + - " couchbase:\n" + - " endpoint: \"" + couchbaseContainer.getConnectionString() + "\"\n" + - " username: \"" + couchbaseContainer.getUsername() + "\"\n" + - " password: \"" + couchbaseContainer.getPassword() + "\"\n" + - " bucket-name: \"" + BUCKET_NAME + "\"\n"; - - Files.write(configFile, couchbaseConfig.getBytes()); - - try { - // When - Execute CLI audit list command - String output = SystemLambda.tapSystemOut(() -> { - String errorOutput = SystemLambda.tapSystemErr(() -> { - String[] args = {"--config", configFile.toString(), "audit", "list"}; - CommandLine cmd = new CommandLine(new FlamingockCli()); - int exitCode = cmd.execute(args); - - // Then - Should be able to start CLI and attempt database connection - // Exit code might be 1 if no audit entries exist or OpsClient fails to initialize - // The important thing is that TestContainers Couchbase is running and CLI can connect - assertThat(exitCode).isIn(0, 1); // Accept both success and expected failures - }); - System.out.println("Error output captured: " + errorOutput); - }); - - // Verify TestContainers setup is working and CLI attempted database connection - System.out.println("CLI output: " + output); - System.out.println("Couchbase container is running: " + couchbaseContainer.isRunning()); - System.out.println("Couchbase connection string: " + couchbaseContainer.getConnectionString()); - - // The fact that we got here means TestContainers worked and CLI applied - assertThat(couchbaseContainer.isRunning()).isTrue(); - - } finally { - // Cleanup - Files.deleteIfExists(configFile); - } - } - - @Test - void shouldRunAuditListCommandVerboseWithCouchbase() throws Exception { - // Given - Path configFile = Files.createTempFile("couchbase-integration-verbose", ".yml"); - String couchbaseConfig = "flamingock:\n" + - " service-identifier: \"integration-test-verbose-cli\"\n" + - " audit:\n" + - " couchbase:\n" + - " endpoint: \"" + couchbaseContainer.getConnectionString() + "\"\n" + - " username: \"" + couchbaseContainer.getUsername() + "\"\n" + - " password: \"" + couchbaseContainer.getPassword() + "\"\n" + - " bucket-name: \"" + BUCKET_NAME + "\"\n"; - - Files.write(configFile, couchbaseConfig.getBytes()); - - try { - // When - Execute CLI audit list command with verbose flag - String output = SystemLambda.tapSystemOut(() -> { - String[] args = {"--debug", "--config", configFile.toString(), "audit", "list"}; - CommandLine cmd = new CommandLine(new FlamingockCli()); - int exitCode = cmd.execute(args); - - // Then - Accept multiple exit codes: 0=success, 1=runtime error, 2=CLI syntax/config error - assertThat(exitCode).isIn(0, 1, 2); - }); - - System.out.println("Verbose CLI output: " + output); - - } finally { - Files.deleteIfExists(configFile); - } - } - - @Test - void shouldHandleInvalidCouchbaseConnectionGracefully() throws Exception { - // Given - Invalid Couchbase connection - Path configFile = Files.createTempFile("couchbase-invalid", ".yml"); - String invalidConfig = "flamingock:\n" + - " service-identifier: \"invalid-test-cli\"\n" + - " audit:\n" + - " couchbase:\n" + - " endpoint: \"couchbase://invalid-host:42867\"\n" + - " username: \"" + couchbaseContainer.getUsername() + "\"\n" + - " password: \"" + couchbaseContainer.getPassword() + "\"\n" + - " bucket-name: \"" + BUCKET_NAME + "\"\n"; - - Files.write(configFile, invalidConfig.getBytes()); - - try { - // When - Execute CLI with invalid connection - String output = SystemLambda.tapSystemOut(() -> { - String[] args = {"--config", configFile.toString(), "audit", "list"}; - CommandLine cmd = new CommandLine(new FlamingockCli()); - int exitCode = cmd.execute(args); - - // Then - Should handle connection error gracefully (non-zero exit code expected) - assertThat(exitCode).isNotEqualTo(0); - }); - - System.out.println("Error output: " + output); - - } finally { - Files.deleteIfExists(configFile); - } - } -} diff --git a/cli/flamingock-cli/src/test/java/io/flamingock/cli/integration/CLIDynamoIntegrationTest.java b/cli/flamingock-cli/src/test/java/io/flamingock/cli/integration/CLIDynamoIntegrationTest.java deleted file mode 100644 index 4a8c2767f..000000000 --- a/cli/flamingock-cli/src/test/java/io/flamingock/cli/integration/CLIDynamoIntegrationTest.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.integration; - -import com.github.stefanbirkner.systemlambda.SystemLambda; -import io.flamingock.cli.FlamingockCli; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; -import org.testcontainers.containers.GenericContainer; -import org.testcontainers.junit.jupiter.Container; -import org.testcontainers.junit.jupiter.Testcontainers; -import org.testcontainers.utility.DockerImageName; -import picocli.CommandLine; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; - -import static org.assertj.core.api.Assertions.*; - -@Testcontainers -class CLIDynamoIntegrationTest { - - @Container - static GenericContainer dynamoContainer = new GenericContainer<>(DockerImageName.parse("amazon/dynamodb-local:latest")) - .withExposedPorts(8000) - .withCommand("-jar", "DynamoDBLocal.jar", "-sharedDb", "-inMemory"); - - @Test - void shouldRunAuditListCommandWithDynamoDB() throws Exception { - // Given - Create temporary config file with TestContainers DynamoDB connection - Path configFile = Files.createTempFile("dynamo-integration", ".yml"); - String dynamoConfig = "flamingock:\n" + - " service-identifier: \"dynamo-integration-test-cli\"\n" + - " audit:\n" + - " dynamodb:\n" + - " region: \"us-east-1\"\n" + - " endpoint: \"http://localhost:" + dynamoContainer.getMappedPort(8000) + "\"\n" + - " access-key: \"test-key\"\n" + - " secret-key: \"test-secret\"\n"; - - Files.write(configFile, dynamoConfig.getBytes()); - - try { - // When - Execute CLI audit list command - String output = SystemLambda.tapSystemOut(() -> { - String[] args = {"--config", configFile.toString(), "audit", "list"}; - CommandLine cmd = new CommandLine(new FlamingockCli()); - int exitCode = cmd.execute(args); - - // Then - Should be able to start CLI and attempt database connection - assertThat(exitCode).isIn(0, 1, 2); // Accept success, runtime errors, and CLI syntax/config errors - }); - - // Should be able to connect and create OpsClient - System.out.println("DynamoDB CLI output: " + output); - - } finally { - // Cleanup - Files.deleteIfExists(configFile); - } - } - - @Test - void shouldRunAuditListCommandVerboseWithDynamoDB() throws Exception { - // Given - Path configFile = Files.createTempFile("dynamo-integration-verbose", ".yml"); - String dynamoConfig = "flamingock:\n" + - " service-identifier: \"dynamo-integration-verbose-cli\"\n" + - " audit:\n" + - " dynamodb:\n" + - " region: \"us-east-1\"\n" + - " endpoint: \"http://localhost:" + dynamoContainer.getMappedPort(8000) + "\"\n" + - " access-key: \"test-key\"\n" + - " secret-key: \"test-secret\"\n"; - - Files.write(configFile, dynamoConfig.getBytes()); - - try { - // When - Execute CLI audit list command with verbose flag - String output = SystemLambda.tapSystemOut(() -> { - String[] args = {"--debug", "--config", configFile.toString(), "audit", "list"}; - CommandLine cmd = new CommandLine(new FlamingockCli()); - int exitCode = cmd.execute(args); - - // Then - Accept multiple exit codes: 0=success, 1=runtime error, 2=CLI syntax/config error - assertThat(exitCode).isIn(0, 1, 2); - }); - - System.out.println("Verbose DynamoDB CLI output: " + output); - - } finally { - Files.deleteIfExists(configFile); - } - } - - @Test - void shouldHandleInvalidDynamoConnectionGracefully() throws Exception { - // Given - Invalid DynamoDB connection - Path configFile = Files.createTempFile("dynamo-invalid", ".yml"); - String invalidConfig = "flamingock:\n" + - " service-identifier: \"dynamo-invalid-test-cli\"\n" + - " audit:\n" + - " dynamodb:\n" + - " region: \"us-east-1\"\n" + - " endpoint: \"http://invalid-host:8000\"\n" + - " access-key: \"test-key\"\n" + - " secret-key: \"test-secret\"\n"; - - Files.write(configFile, invalidConfig.getBytes()); - - try { - // When - Execute CLI with invalid connection - String output = SystemLambda.tapSystemOut(() -> { - String[] args = {"--config", configFile.toString(), "audit", "list"}; - CommandLine cmd = new CommandLine(new FlamingockCli()); - int exitCode = cmd.execute(args); - - // Then - Should handle connection error gracefully (non-zero exit code expected) - assertThat(exitCode).isNotEqualTo(0); - }); - - System.out.println("DynamoDB Error output: " + output); - - } finally { - Files.deleteIfExists(configFile); - } - } -} \ No newline at end of file diff --git a/cli/flamingock-cli/src/test/java/io/flamingock/cli/integration/CLIMongoIntegrationTest.java b/cli/flamingock-cli/src/test/java/io/flamingock/cli/integration/CLIMongoIntegrationTest.java deleted file mode 100644 index d7cc6a472..000000000 --- a/cli/flamingock-cli/src/test/java/io/flamingock/cli/integration/CLIMongoIntegrationTest.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.integration; - -import com.github.stefanbirkner.systemlambda.SystemLambda; -import io.flamingock.cli.FlamingockCli; -import io.flamingock.cli.test.TestUtils; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; -import org.testcontainers.containers.MongoDBContainer; -import org.testcontainers.junit.jupiter.Container; -import org.testcontainers.junit.jupiter.Testcontainers; -import picocli.CommandLine; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; - -import static org.assertj.core.api.Assertions.*; - -@Testcontainers -class CLIMongoIntegrationTest { - - @Container - static MongoDBContainer mongoContainer = new MongoDBContainer("mongo:5.0") - .withExposedPorts(27017); - - @Test - void shouldRunAuditListCommandWithMongoDB() throws Exception { - // Given - Create temporary config file with TestContainers MongoDB connection - Path configFile = Files.createTempFile("mongo-integration", ".yml"); - String mongoConfig = "flamingock:\n" + - " service-identifier: \"integration-test-cli\"\n" + - " audit:\n" + - " mongodb:\n" + - " connection-string: \"" + mongoContainer.getConnectionString() + "\"\n" + - " database: \"integration_test\"\n"; - - Files.write(configFile, mongoConfig.getBytes()); - - try { - // When - Execute CLI audit list command - String output = SystemLambda.tapSystemOut(() -> { - String errorOutput = SystemLambda.tapSystemErr(() -> { - String[] args = {"--config", configFile.toString(), "audit", "list"}; - CommandLine cmd = new CommandLine(new FlamingockCli()); - int exitCode = cmd.execute(args); - - // Then - Should be able to start CLI and attempt database connection - // Exit code might be 1 if no audit entries exist or OpsClient fails to initialize - // The important thing is that TestContainers MongoDB is running and CLI can connect - assertThat(exitCode).isIn(0, 1); // Accept both success and expected failures - }); - System.out.println("Error output captured: " + errorOutput); - }); - - // Verify TestContainers setup is working and CLI attempted database connection - System.out.println("CLI output: " + output); - System.out.println("MongoDB container is running: " + mongoContainer.isRunning()); - System.out.println("MongoDB connection string: " + mongoContainer.getConnectionString()); - - // The fact that we got here means TestContainers worked and CLI applied - assertThat(mongoContainer.isRunning()).isTrue(); - - } finally { - // Cleanup - Files.deleteIfExists(configFile); - } - } - - @Test - void shouldRunAuditListCommandVerboseWithMongoDB() throws Exception { - // Given - Path configFile = Files.createTempFile("mongo-integration-verbose", ".yml"); - String mongoConfig = "flamingock:\n" + - " service-identifier: \"integration-test-verbose-cli\"\n" + - " audit:\n" + - " mongodb:\n" + - " connection-string: \"" + mongoContainer.getConnectionString() + "\"\n" + - " database: \"integration_test_verbose\"\n"; - - Files.write(configFile, mongoConfig.getBytes()); - - try { - // When - Execute CLI audit list command with verbose flag - String output = SystemLambda.tapSystemOut(() -> { - String[] args = {"--debug", "--config", configFile.toString(), "audit", "list"}; - CommandLine cmd = new CommandLine(new FlamingockCli()); - int exitCode = cmd.execute(args); - - // Then - Accept multiple exit codes: 0=success, 1=runtime error, 2=CLI syntax/config error - assertThat(exitCode).isIn(0, 1, 2); - }); - - System.out.println("Verbose CLI output: " + output); - - } finally { - Files.deleteIfExists(configFile); - } - } - - @Test - void shouldHandleInvalidMongoConnectionGracefully() throws Exception { - // Given - Invalid MongoDB connection - Path configFile = Files.createTempFile("mongo-invalid", ".yml"); - String invalidConfig = "flamingock:\n" + - " service-identifier: \"invalid-test-cli\"\n" + - " audit:\n" + - " mongodb:\n" + - " connection-string: \"mongodb://invalid-host:27017\"\n" + - " database: \"test\"\n"; - - Files.write(configFile, invalidConfig.getBytes()); - - try { - // When - Execute CLI with invalid connection - String output = SystemLambda.tapSystemOut(() -> { - String[] args = {"--config", configFile.toString(), "audit", "list"}; - CommandLine cmd = new CommandLine(new FlamingockCli()); - int exitCode = cmd.execute(args); - - // Then - Should handle connection error gracefully (non-zero exit code expected) - assertThat(exitCode).isNotEqualTo(0); - }); - - System.out.println("Error output: " + output); - - } finally { - Files.deleteIfExists(configFile); - } - } -} \ No newline at end of file diff --git a/cli/flamingock-cli/src/test/java/io/flamingock/cli/integration/CLISqlIntegrationTest.java b/cli/flamingock-cli/src/test/java/io/flamingock/cli/integration/CLISqlIntegrationTest.java deleted file mode 100644 index e36e4d7cc..000000000 --- a/cli/flamingock-cli/src/test/java/io/flamingock/cli/integration/CLISqlIntegrationTest.java +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.integration; - -import com.github.stefanbirkner.systemlambda.SystemLambda; -import io.flamingock.cli.FlamingockCli; -import io.flamingock.internal.common.sql.SqlDialect; -import io.flamingock.internal.common.sql.testContainers.SharedSqlContainers; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.TestInstance; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; -import org.testcontainers.containers.JdbcDatabaseContainer; -import org.testcontainers.junit.jupiter.Testcontainers; -import picocli.CommandLine; - -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static org.assertj.core.api.Assertions.assertThat; - -@TestInstance(TestInstance.Lifecycle.PER_CLASS) -@Testcontainers -class CLISqlIntegrationTest { - - private static final Map> containers = new HashMap<>(); - - static Stream dialectProvider() { - String enabledDialects = System.getProperty("sql.test.dialects", "mysql"); - Set enabled = Arrays.stream(enabledDialects.split(",")) - .map(String::trim) - .collect(Collectors.toSet()); - - Stream allDialects = Stream.of( - Arguments.of(SqlDialect.MYSQL, "mysql"), - Arguments.of(SqlDialect.SQLSERVER, "sqlserver"), - Arguments.of(SqlDialect.ORACLE, "oracle"), - Arguments.of(SqlDialect.POSTGRESQL, "postgresql"), - Arguments.of(SqlDialect.MARIADB, "mariadb"), - Arguments.of(SqlDialect.H2, "h2"), - Arguments.of(SqlDialect.SQLITE, "sqlite"), - Arguments.of(SqlDialect.INFORMIX, "informix"), - Arguments.of(SqlDialect.FIREBIRD, "firebird") - ); - - return allDialects.filter(args -> { - String dialectName = (String) args.get()[1]; - return enabled.contains(dialectName); - }); - } - - @BeforeAll - static void startContainers() { - for (Arguments arg : dialectProvider().toArray(Arguments[]::new)) { - SqlDialect dialect = (SqlDialect) arg.get()[0]; - String dialectName = (String) arg.get()[1]; - if (!"h2".equals(dialectName) && !"sqlite".equals(dialectName)) { - JdbcDatabaseContainer container = SharedSqlContainers.getContainer(dialectName); - container.start(); - containers.put(dialectName, container); - } - } - } - - @AfterAll - static void stopContainers() { - containers.values().forEach(JdbcDatabaseContainer::stop); - } - - @ParameterizedTest - @MethodSource("dialectProvider") - void shouldRunAuditListCommandWithSql(SqlDialect sqlDialect, String dialectName) throws Exception { - JdbcDatabaseContainer sqlContainer = SharedSqlContainers.getContainer(dialectName); - // Given - Create temporary config file with TestContainers SQL connection - Path configFile = Files.createTempFile("sql-integration", ".yml"); - String sqlConfig = "flamingock:\n" + - " service-identifier: \"integration-test-cli\"\n" + - " audit:\n" + - " sql:\n" + - " endpoint: \"" + sqlContainer.getJdbcUrl() + "\"\n" + - " username: \"" + sqlContainer.getUsername() + "\"\n" + - " password: \"" + sqlContainer.getPassword() + "\"\n"; - - Files.write(configFile, sqlConfig.getBytes()); - - try { - // When - Execute CLI audit list command - String output = SystemLambda.tapSystemOut(() -> { - String errorOutput = SystemLambda.tapSystemErr(() -> { - String[] args = {"--config", configFile.toString(), "audit", "list"}; - CommandLine cmd = new CommandLine(new FlamingockCli()); - int exitCode = cmd.execute(args); - - // Then - Should be able to start CLI and attempt database connection - // Exit code might be 1 if no audit entries exist or OpsClient fails to initialize - // The important thing is that TestContainers SQL is running and CLI can connect - assertThat(exitCode).isIn(0, 1); // Accept both success and expected failures - }); - System.out.println("Error output captured: " + errorOutput); - }); - - // Verify TestContainers setup is working and CLI attempted database connection - System.out.println("CLI output: " + output); - System.out.println("SQL container is running: " + sqlContainer.isRunning()); - System.out.println("SQL connection string: " + sqlContainer.getJdbcUrl()); - - // The fact that we got here means TestContainers worked and CLI applied - assertThat(sqlContainer.isRunning()).isTrue(); - - } finally { - // Cleanup - Files.deleteIfExists(configFile); - } - } - - @ParameterizedTest - @MethodSource("dialectProvider") - void shouldRunAuditListCommandVerboseWithSql(SqlDialect sqlDialect, String dialectName) throws Exception { - JdbcDatabaseContainer sqlContainer = SharedSqlContainers.getContainer(dialectName); - // Given - Path configFile = Files.createTempFile("sql-integration-verbose", ".yml"); - String sqlConfig = "flamingock:\n" + - " service-identifier: \"integration-test-verbose-cli\"\n" + - " audit:\n" + - " sql:\n" + - " endpoint: \"" + sqlContainer.getJdbcUrl() + "\"\n" + - " username: \"" + sqlContainer.getUsername() + "\"\n" + - " password: \"" + sqlContainer.getPassword() + "\"\n" + - " sql-dialect: \"" + dialectName + "\"\n"; - - Files.write(configFile, sqlConfig.getBytes()); - - try { - // When - Execute CLI audit list command with verbose flag - String output = SystemLambda.tapSystemOut(() -> { - String[] args = {"--debug", "--config", configFile.toString(), "audit", "list"}; - CommandLine cmd = new CommandLine(new FlamingockCli()); - int exitCode = cmd.execute(args); - - // Then - Accept multiple exit codes: 0=success, 1=runtime error, 2=CLI syntax/config error - assertThat(exitCode).isIn(0, 1, 2); - }); - - System.out.println("Verbose CLI output: " + output); - - } finally { - Files.deleteIfExists(configFile); - } - } - - @ParameterizedTest - @MethodSource("dialectProvider") - void shouldHandleInvalidSqlConnectionGracefully(SqlDialect sqlDialect, String dialectName) throws Exception { - JdbcDatabaseContainer sqlContainer = SharedSqlContainers.getContainer(dialectName); - // Given - Invalid SQL connection - Path configFile = Files.createTempFile("sql-invalid", ".yml"); - String invalidConfig = "flamingock:\n" + - " service-identifier: \"invalid-test-cli\"\n" + - " audit:\n" + - " sql:\n" + - " endpoint: \"jdbc:sqlserver://invalid-host:1433\"\n" + - " username: \"" + sqlContainer.getUsername() + "\"\n" + - " password: \"" + sqlContainer.getPassword() + "\"\n"; - - Files.write(configFile, invalidConfig.getBytes()); - - try { - // When - Execute CLI with invalid connection - String output = SystemLambda.tapSystemOut(() -> { - String[] args = {"--config", configFile.toString(), "audit", "list"}; - CommandLine cmd = new CommandLine(new FlamingockCli()); - int exitCode = cmd.execute(args); - - // Then - Should handle connection error gracefully (non-zero exit code expected) - assertThat(exitCode).isNotEqualTo(0); - }); - - System.out.println("Error output: " + output); - - } finally { - Files.deleteIfExists(configFile); - } - } -} diff --git a/cli/flamingock-cli/src/test/java/io/flamingock/cli/test/TestUtils.java b/cli/flamingock-cli/src/test/java/io/flamingock/cli/test/TestUtils.java deleted file mode 100644 index dc0ffb60e..000000000 --- a/cli/flamingock-cli/src/test/java/io/flamingock/cli/test/TestUtils.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.cli.test; - -import io.flamingock.cli.config.DatabaseConfig; -import io.flamingock.cli.config.FlamingockConfig; -import io.flamingock.internal.common.core.audit.AuditEntry; - -import java.time.LocalDateTime; -import java.util.Arrays; -import java.util.List; - -public class TestUtils { - - public static FlamingockConfig createMongoConfig() { - FlamingockConfig config = new FlamingockConfig(); - config.setServiceIdentifier("test-service"); - - DatabaseConfig databaseConfig = new DatabaseConfig(); - DatabaseConfig.MongoDBConfig mongoConfig = new DatabaseConfig.MongoDBConfig(); - mongoConfig.setConnectionString("mongodb://localhost:27017"); - mongoConfig.setDatabase("test-db"); - databaseConfig.setMongodb(mongoConfig); - - config.setAudit(databaseConfig); - return config; - } - - public static FlamingockConfig createDynamoConfig() { - FlamingockConfig config = new FlamingockConfig(); - config.setServiceIdentifier("test-service"); - - DatabaseConfig databaseConfig = new DatabaseConfig(); - DatabaseConfig.DynamoDBConfig dynamoConfig = new DatabaseConfig.DynamoDBConfig(); - dynamoConfig.setRegion("us-east-1"); - dynamoConfig.setEndpoint("http://localhost:8000"); - dynamoConfig.setAccessKey("testKey"); - dynamoConfig.setSecretKey("testSecret"); - databaseConfig.setDynamodb(dynamoConfig); - - config.setAudit(databaseConfig); - return config; - } - - public static FlamingockConfig createCouchbaseConfig() { - FlamingockConfig config = new FlamingockConfig(); - config.setServiceIdentifier("test-service"); - - DatabaseConfig databaseConfig = new DatabaseConfig(); - DatabaseConfig.CouchbaseConfig couchbaseConfig = new DatabaseConfig.CouchbaseConfig(); - couchbaseConfig.setEndpoint("couchbase://localhost:12110"); - couchbaseConfig.setUsername("test-user"); - couchbaseConfig.setPassword("test-password"); - couchbaseConfig.setBucketName("test-bucket"); - databaseConfig.setCouchbase(couchbaseConfig); - - config.setAudit(databaseConfig); - return config; - } - - public static FlamingockConfig createSqlConfig() { - FlamingockConfig config = new FlamingockConfig(); - config.setServiceIdentifier("test-service"); - - DatabaseConfig databaseConfig = new DatabaseConfig(); - DatabaseConfig.SqlConfig sqlConfig = new DatabaseConfig.SqlConfig(); - sqlConfig.setEndpoint("jdbc:sqlserver://localhost:1433"); - sqlConfig.setUsername("test-user"); - sqlConfig.setPassword("test-password"); - databaseConfig.setSql(sqlConfig); - - config.setAudit(databaseConfig); - return config; - } - - public static List createSampleAuditEntries() { - AuditEntry entry1 = new AuditEntry( - "exec-001", - "stage-001", - "change-001", - "developer1", - LocalDateTime.now().minusHours(1), - AuditEntry.Status.APPLIED, - AuditEntry.ChangeType.STANDARD_CODE, - "com.example.Change001", - "migrate", - "sourceFile", - 1500, - "server1.example.com", - null, - false, - null - ); - - AuditEntry entry2 = new AuditEntry( - "exec-002", - "stage-002", - "change-002", - "developer2", - LocalDateTime.now().minusMinutes(30), - AuditEntry.Status.FAILED, - AuditEntry.ChangeType.STANDARD_CODE, - "com.example.Change002", - "rollback", - "sourceFile", - 800, - "server2.example.com", - null, - false, - "Connection timeout" - ); - - return Arrays.asList(entry1, entry2); - } - - public static String createTempConfigFile(String content) { - try { - java.nio.file.Path tempFile = java.nio.file.Files.createTempFile("test-config", ".yml"); - java.nio.file.Files.write(tempFile, content.getBytes()); - return tempFile.toString(); - } catch (Exception e) { - throw new RuntimeException("Failed to create temp config file", e); - } - } - - public static String getValidMongoYaml() { - return "flamingock:\n" + - " service-identifier: \"test-cli\"\n" + - " audit:\n" + - " mongodb:\n" + - " connection-string: \"mongodb://localhost:27017\"\n" + - " database: \"test\"\n"; - } - - public static String getValidDynamoYaml() { - return "flamingock:\n" + - " service-identifier: \"test-cli\"\n" + - " audit:\n" + - " dynamodb:\n" + - " region: \"us-east-1\"\n" + - " endpoint: \"http://localhost:8000\"\n" + - " access-key: \"testKey\"\n" + - " secret-key: \"testSecret\"\n"; - } - - public static String getValidCouchbaseYaml() { - return "flamingock:\n" + - " service-identifier: \"test-cli\"\n" + - " audit:\n" + - " couchbase:\n" + - " endpoint: \"couchbase://localhost:12110\"\n" + - " username: \"test-user\"\n" + - " password: \"test-password\"\n" + - " bucket-name: \"test-bucket\"\n"; - } - - public static String getValidSqlYaml() { - return "flamingock:\n" + - " service-identifier: \"test-cli\"\n" + - " audit:\n" + - " sql:\n" + - " endpoint: \"jdbc:sqlserver://localhost:1433\"\n" + - " username: \"test-user\"\n" + - " password: \"test-password\"\n" + - " sql-dialect: \"SqlServer\"\n"; - } -} diff --git a/core/flamingock-core-api/src/main/java/io/flamingock/api/annotations/Categories.java b/core/flamingock-core-api/src/main/java/io/flamingock/api/annotations/Categories.java deleted file mode 100644 index 12b5af8e7..000000000 --- a/core/flamingock-core-api/src/main/java/io/flamingock/api/annotations/Categories.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2023 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.api.annotations; - -import io.flamingock.api.task.ChangeCategory; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - - -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -public @interface Categories { - - ChangeCategory[] value(); - -} diff --git a/core/flamingock-core-api/src/main/java/io/flamingock/api/annotations/ChangeTemplate.java b/core/flamingock-core-api/src/main/java/io/flamingock/api/annotations/ChangeTemplate.java deleted file mode 100644 index 4b978fa89..000000000 --- a/core/flamingock-core-api/src/main/java/io/flamingock/api/annotations/ChangeTemplate.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2023 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.api.annotations; - - -import java.lang.annotation.ElementType; -import java.lang.annotation.Inherited; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Inherited -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -public @interface ChangeTemplate { - - - String name(); - - -} diff --git a/core/flamingock-core-api/src/main/java/io/flamingock/api/annotations/Stage.java b/core/flamingock-core-api/src/main/java/io/flamingock/api/annotations/Stage.java index 5ce800998..b2cdc47eb 100644 --- a/core/flamingock-core-api/src/main/java/io/flamingock/api/annotations/Stage.java +++ b/core/flamingock-core-api/src/main/java/io/flamingock/api/annotations/Stage.java @@ -15,8 +15,6 @@ */ package io.flamingock.api.annotations; -import io.flamingock.api.StageType; - public @interface Stage { /** * Specifies the location where changes are found. This field is mandatory. diff --git a/core/flamingock-core-api/src/main/java/io/flamingock/api/external/targets/TargetSystem.java b/core/flamingock-core-api/src/main/java/io/flamingock/api/external/TargetSystem.java similarity index 87% rename from core/flamingock-core-api/src/main/java/io/flamingock/api/external/targets/TargetSystem.java rename to core/flamingock-core-api/src/main/java/io/flamingock/api/external/TargetSystem.java index efd00c701..85c9fa771 100644 --- a/core/flamingock-core-api/src/main/java/io/flamingock/api/external/targets/TargetSystem.java +++ b/core/flamingock-core-api/src/main/java/io/flamingock/api/external/TargetSystem.java @@ -13,10 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.flamingock.api.external.targets; +package io.flamingock.api.external; -import io.flamingock.api.external.ExternalSystem; - public interface TargetSystem extends ExternalSystem { } diff --git a/core/flamingock-core-api/src/main/java/io/flamingock/api/task/ChangeCategory.java b/core/flamingock-core-api/src/main/java/io/flamingock/api/task/ChangeCategory.java deleted file mode 100644 index 3de9c4bdf..000000000 --- a/core/flamingock-core-api/src/main/java/io/flamingock/api/task/ChangeCategory.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.api.task; - -public enum ChangeCategory { - IMPORT, SYSTEM, UNSORTABLE -} diff --git a/core/flamingock-core-api/src/main/java/io/flamingock/api/task/ChangeCategoryAware.java b/core/flamingock-core-api/src/main/java/io/flamingock/api/task/ChangeCategoryAware.java deleted file mode 100644 index ffd63a259..000000000 --- a/core/flamingock-core-api/src/main/java/io/flamingock/api/task/ChangeCategoryAware.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed 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 io.flamingock.api.task; - -public interface ChangeCategoryAware { - -} diff --git a/core/flamingock-core-commons/build.gradle.kts b/core/flamingock-core-commons/build.gradle.kts index c0b04e495..e40cd4947 100644 --- a/core/flamingock-core-commons/build.gradle.kts +++ b/core/flamingock-core-commons/build.gradle.kts @@ -1,7 +1,7 @@ val jacksonVersion = "2.16.0" dependencies { - api(project(":utils:general-util")) api(project(":core:flamingock-core-api")) + api(project(":utils:general-util"))//todo implementation api("jakarta.annotation:jakarta.annotation-api:2.1.1")//todo can this be implementation? implementation("com.fasterxml.jackson.core:jackson-databind:$jacksonVersion") diff --git a/core/flamingock-core/build.gradle.kts b/core/flamingock-core/build.gradle.kts index c775aa5f8..6914d3093 100644 --- a/core/flamingock-core/build.gradle.kts +++ b/core/flamingock-core/build.gradle.kts @@ -2,7 +2,7 @@ val jacksonVersion = "2.16.0" dependencies { api(project(":core:flamingock-core-commons")) - api(project(":utils:general-util")) + api(project(":utils:general-util"))//todo implementation api("javax.inject:javax.inject:1") api("org.reflections:reflections:0.10.1")//TODO remove diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/builder/AbstractBuilder.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/builder/AbstractBuilder.java index 17bc3d69e..5dce1754a 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/builder/AbstractBuilder.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/builder/AbstractBuilder.java @@ -15,7 +15,7 @@ */ package io.flamingock.internal.core.builder; -import io.flamingock.api.external.targets.TargetSystem; +import io.flamingock.api.external.TargetSystem; import io.flamingock.internal.common.core.context.Context; import io.flamingock.internal.common.core.context.ContextConfigurable; import io.flamingock.internal.common.core.context.Dependency; diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/builder/AbstractChangeRunnerBuilder.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/builder/AbstractChangeRunnerBuilder.java index 4b0f0aac3..3bb757e18 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/builder/AbstractChangeRunnerBuilder.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/builder/AbstractChangeRunnerBuilder.java @@ -15,7 +15,7 @@ */ package io.flamingock.internal.core.builder; -import io.flamingock.api.external.targets.TargetSystem; +import io.flamingock.api.external.TargetSystem; import io.flamingock.internal.common.core.context.Context; import io.flamingock.internal.common.core.context.ContextInjectable; import io.flamingock.internal.common.core.context.ContextResolver; diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/configuration/core/CoreConfigurator.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/configuration/core/CoreConfigurator.java index 3628e4714..5389bf091 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/configuration/core/CoreConfigurator.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/configuration/core/CoreConfigurator.java @@ -16,7 +16,7 @@ package io.flamingock.internal.core.configuration.core; -import io.flamingock.api.external.targets.TargetSystem; +import io.flamingock.api.external.TargetSystem; import java.util.Map; diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/external/targets/AbstractTargetSystem.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/external/targets/AbstractTargetSystem.java index 50e0a3911..7865edbcf 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/external/targets/AbstractTargetSystem.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/external/targets/AbstractTargetSystem.java @@ -15,7 +15,7 @@ */ package io.flamingock.internal.core.external.targets; -import io.flamingock.api.external.targets.TargetSystem; +import io.flamingock.api.external.TargetSystem; import io.flamingock.internal.common.core.context.Context; import io.flamingock.internal.common.core.context.ContextConfigurable; import io.flamingock.internal.common.core.context.ContextProvider; diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/external/targets/TargetSystemManager.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/external/targets/TargetSystemManager.java index 3ca75c546..35fddf19d 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/external/targets/TargetSystemManager.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/external/targets/TargetSystemManager.java @@ -15,7 +15,7 @@ */ package io.flamingock.internal.core.external.targets; -import io.flamingock.api.external.targets.TargetSystem; +import io.flamingock.api.external.TargetSystem; import io.flamingock.internal.common.core.context.ContextInitializable; import io.flamingock.internal.common.core.context.ContextResolver; import io.flamingock.internal.common.core.error.FlamingockException; diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/external/targets/operations/TargetSystemOps.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/external/targets/operations/TargetSystemOps.java index 6e8112d04..f2b7e4d6c 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/external/targets/operations/TargetSystemOps.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/external/targets/operations/TargetSystemOps.java @@ -15,7 +15,7 @@ */ package io.flamingock.internal.core.external.targets.operations; -import io.flamingock.api.external.targets.TargetSystem; +import io.flamingock.api.external.TargetSystem; import io.flamingock.internal.common.core.targets.OperationType; import io.flamingock.internal.core.external.targets.AbstractTargetSystem; import io.flamingock.internal.core.runtime.ExecutionRuntime; diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/AbstractLoadedTask.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/AbstractLoadedTask.java index 6209ec68f..37f697720 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/AbstractLoadedTask.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/AbstractLoadedTask.java @@ -15,8 +15,6 @@ */ package io.flamingock.internal.core.task.loaded; -import io.flamingock.api.task.ChangeCategory; -import io.flamingock.api.task.ChangeCategoryAware; import io.flamingock.internal.common.core.error.validation.Validatable; import io.flamingock.internal.common.core.task.AbstractTaskDescriptor; diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/CodeLoadedChange.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/CodeLoadedChange.java index ca7ff747f..1c53f7175 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/CodeLoadedChange.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/CodeLoadedChange.java @@ -15,7 +15,6 @@ */ package io.flamingock.internal.core.task.loaded; -import io.flamingock.api.task.ChangeCategory; import io.flamingock.internal.common.core.task.RecoveryDescriptor; import io.flamingock.internal.common.core.task.TargetSystemDescriptor; diff --git a/core/flamingock-graalvm/build.gradle.kts b/core/flamingock-graalvm/build.gradle.kts index 498abd126..9f4bb60d9 100644 --- a/core/flamingock-graalvm/build.gradle.kts +++ b/core/flamingock-graalvm/build.gradle.kts @@ -2,7 +2,6 @@ val jacksonVersion = "2.15.2" dependencies { //this way the user doesn't need to import the core api(project(":core:flamingock-core")) - implementation(project(":core:flamingock-core-commons")) implementation("com.fasterxml.jackson.core:jackson-databind:$jacksonVersion") diff --git a/core/flamingock-processor/build.gradle.kts b/core/flamingock-processor/build.gradle.kts index 1d8a93afa..e79ba8fcd 100644 --- a/core/flamingock-processor/build.gradle.kts +++ b/core/flamingock-processor/build.gradle.kts @@ -1,9 +1,9 @@ val jacksonVersion = "2.16.0" dependencies { api(project(":core:flamingock-core-commons")) - api(project(":utils:general-util")) - api("org.yaml:snakeyaml:2.2") - api("com.fasterxml.jackson.core:jackson-databind:$jacksonVersion") + api(project(":utils:general-util"))//todo implementation + api("org.yaml:snakeyaml:2.2")//todo implementation + api("com.fasterxml.jackson.core:jackson-databind:$jacksonVersion")//todo implementation } description = "Annotation processor for generating change metadata and pipeline configurations from @Change annotations and YAML templates" diff --git a/core/target-systems/couchbase-target-system/src/test/java/io/flamingock/targetsystem/couchbase/CouchbaseTargetSystemTest.java b/core/target-systems/couchbase-target-system/src/test/java/io/flamingock/targetsystem/couchbase/CouchbaseTargetSystemTest.java index 1a16167e3..5e4c18ab4 100644 --- a/core/target-systems/couchbase-target-system/src/test/java/io/flamingock/targetsystem/couchbase/CouchbaseTargetSystemTest.java +++ b/core/target-systems/couchbase-target-system/src/test/java/io/flamingock/targetsystem/couchbase/CouchbaseTargetSystemTest.java @@ -18,7 +18,7 @@ import com.couchbase.client.core.io.CollectionIdentifier; import com.couchbase.client.java.Bucket; import com.couchbase.client.java.Cluster; -import io.flamingock.api.external.targets.TargetSystem; +import io.flamingock.api.external.TargetSystem; import io.flamingock.common.test.cloud.AuditRequestExpectation; import io.flamingock.common.test.cloud.MockRunnerServer; import io.flamingock.common.test.cloud.execution.ExecutionContinueRequestResponseMock; diff --git a/core/target-systems/mongodb-springdata-target-system/src/test/java/io/flamingock/targetsystem/mongodb/springdata/MongoDBSpringDataTargetSystemTest.java b/core/target-systems/mongodb-springdata-target-system/src/test/java/io/flamingock/targetsystem/mongodb/springdata/MongoDBSpringDataTargetSystemTest.java index 40678de09..bd8bd9f9d 100644 --- a/core/target-systems/mongodb-springdata-target-system/src/test/java/io/flamingock/targetsystem/mongodb/springdata/MongoDBSpringDataTargetSystemTest.java +++ b/core/target-systems/mongodb-springdata-target-system/src/test/java/io/flamingock/targetsystem/mongodb/springdata/MongoDBSpringDataTargetSystemTest.java @@ -20,7 +20,7 @@ import com.mongodb.client.MongoClient; import com.mongodb.client.MongoClients; import com.mongodb.client.MongoDatabase; -import io.flamingock.api.external.targets.TargetSystem; +import io.flamingock.api.external.TargetSystem; import io.flamingock.targetsystem.mongodb.springdata.changes.happypath._001__HappyCreateClientsCollectionChange; import io.flamingock.targetsystem.mongodb.springdata.changes.happypath._002__HappyInsertClientsChange; import io.flamingock.targetsystem.mongodb.springdata.changes.unhappypath._001__UnhappyCreateClientsCollectionChange; diff --git a/core/target-systems/mongodb-sync-target-system/src/test/java/io/flamingock/targetsystem/mongodb/sync/MongoDBSyncTargetSystemTest.java b/core/target-systems/mongodb-sync-target-system/src/test/java/io/flamingock/targetsystem/mongodb/sync/MongoDBSyncTargetSystemTest.java index 12b004941..17abb1ca3 100644 --- a/core/target-systems/mongodb-sync-target-system/src/test/java/io/flamingock/targetsystem/mongodb/sync/MongoDBSyncTargetSystemTest.java +++ b/core/target-systems/mongodb-sync-target-system/src/test/java/io/flamingock/targetsystem/mongodb/sync/MongoDBSyncTargetSystemTest.java @@ -20,7 +20,7 @@ import com.mongodb.client.MongoClient; import com.mongodb.client.MongoClients; import com.mongodb.client.MongoDatabase; -import io.flamingock.api.external.targets.TargetSystem; +import io.flamingock.api.external.TargetSystem; import io.flamingock.targetsystem.mongodb.sync.changes.happypath._001__HappyCreateClientsCollectionChange; import io.flamingock.targetsystem.mongodb.sync.changes.happypath._002__HappyInsertClientsChange; import io.flamingock.targetsystem.mongodb.sync.changes.unhappypath._001__UnhappyCreateClientsCollectionChange; diff --git a/legacy/mongock-support/build.gradle.kts b/legacy/mongock-support/build.gradle.kts index a1132a2b6..db20e408c 100644 --- a/legacy/mongock-support/build.gradle.kts +++ b/legacy/mongock-support/build.gradle.kts @@ -1,6 +1,6 @@ dependencies { - api(project(":core:flamingock-core-api")) - api(project(":core:flamingock-core")) + api(project(":core:flamingock-core"))//todo implementation + api(project(":core:flamingock-core-api"))//todo remove. This should be imported by core } description = "Provides a compatibility layer for Mongock-based applications, including drop-in annotations and audit store migration utilities to Flamingock." diff --git a/platform-plugins/flamingock-springboot-integration/build.gradle.kts b/platform-plugins/flamingock-springboot-integration/build.gradle.kts index 3aba097a4..65a16142a 100644 --- a/platform-plugins/flamingock-springboot-integration/build.gradle.kts +++ b/platform-plugins/flamingock-springboot-integration/build.gradle.kts @@ -3,7 +3,6 @@ val springBootVersion = "2.0.0.RELEASE" val springFrameworkVersion = "5.0.4.RELEASE" dependencies { api(project(":core:flamingock-core")) - implementation(project(":core:flamingock-core-commons")) compileOnly("org.springframework:spring-context:${springFrameworkVersion}") compileOnly("org.springframework.boot:spring-boot:${springBootVersion}") compileOnly("org.springframework.boot:spring-boot-autoconfigure:${springBootVersion}") diff --git a/platform-plugins/flamingock-springboot-integration/src/main/java/io/flamingock/springboot/FlamingockAutoConfiguration.java b/platform-plugins/flamingock-springboot-integration/src/main/java/io/flamingock/springboot/FlamingockAutoConfiguration.java index f06379d55..ce47acf23 100644 --- a/platform-plugins/flamingock-springboot-integration/src/main/java/io/flamingock/springboot/FlamingockAutoConfiguration.java +++ b/platform-plugins/flamingock-springboot-integration/src/main/java/io/flamingock/springboot/FlamingockAutoConfiguration.java @@ -15,7 +15,7 @@ */ package io.flamingock.springboot; -import io.flamingock.api.external.targets.TargetSystem; +import io.flamingock.api.external.TargetSystem; import io.flamingock.internal.core.builder.FlamingockFactory; import io.flamingock.internal.core.builder.AbstractChangeRunnerBuilder; import io.flamingock.internal.core.external.store.CommunityAuditStore; diff --git a/platform-plugins/flamingock-springboot-integration/src/test/java/io/flamingock/springboot/FlamingockAutoConfigurationTests.java b/platform-plugins/flamingock-springboot-integration/src/test/java/io/flamingock/springboot/FlamingockAutoConfigurationTests.java index d27475dba..d1ee778b0 100644 --- a/platform-plugins/flamingock-springboot-integration/src/test/java/io/flamingock/springboot/FlamingockAutoConfigurationTests.java +++ b/platform-plugins/flamingock-springboot-integration/src/test/java/io/flamingock/springboot/FlamingockAutoConfigurationTests.java @@ -15,7 +15,7 @@ */ package io.flamingock.springboot; -import io.flamingock.api.external.targets.TargetSystem; +import io.flamingock.api.external.TargetSystem; import io.flamingock.internal.core.external.store.CommunityAuditStore; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; diff --git a/platform-plugins/flamingock-springboot-test-support/build.gradle.kts b/platform-plugins/flamingock-springboot-test-support/build.gradle.kts index cf1e62aae..d5a49097d 100644 --- a/platform-plugins/flamingock-springboot-test-support/build.gradle.kts +++ b/platform-plugins/flamingock-springboot-test-support/build.gradle.kts @@ -4,7 +4,6 @@ dependencies { api(project(":core:flamingock-test-support")) implementation(project(":core:flamingock-core")) - implementation(project(":core:flamingock-core-commons")) compileOnly("org.springframework:spring-context:${springFrameworkVersion}") compileOnly("org.springframework.boot:spring-boot:${springBootVersion}") compileOnly("org.springframework.boot:spring-boot-autoconfigure:${springBootVersion}") diff --git a/platform-plugins/flamingock-springboot-test-support/src/test/java/io/flamingock/springboot/testsupport/FlamingockTestConfiguration.java b/platform-plugins/flamingock-springboot-test-support/src/test/java/io/flamingock/springboot/testsupport/FlamingockTestConfiguration.java index 6ab97da46..44fe53bcc 100644 --- a/platform-plugins/flamingock-springboot-test-support/src/test/java/io/flamingock/springboot/testsupport/FlamingockTestConfiguration.java +++ b/platform-plugins/flamingock-springboot-test-support/src/test/java/io/flamingock/springboot/testsupport/FlamingockTestConfiguration.java @@ -15,7 +15,7 @@ */ package io.flamingock.springboot.testsupport; -import io.flamingock.api.external.targets.TargetSystem; +import io.flamingock.api.external.TargetSystem; import io.flamingock.core.kit.inmemory.InternalInMemoryAuditStorage; import io.flamingock.core.kit.inmemory.InternalInMemoryLockStorage; import io.flamingock.core.kit.inmemory.InternalInMemoryTestAuditStore; diff --git a/settings.gradle.kts b/settings.gradle.kts index 320388399..7942c85f2 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -188,16 +188,6 @@ include("legacy:mongock-importer-couchbase") project(":legacy:mongock-importer-couchbase").name = "mongock-importer-couchbase" project(":legacy:mongock-importer-couchbase").projectDir = file("legacy/mongock-importer-couchbase") -////////////////////////////////////// -// CLI -////////////////////////////////////// -include("cli:flamingock-cli") -project(":cli:flamingock-cli").name = "flamingock-cli" -project(":cli:flamingock-cli").projectDir = file("cli/flamingock-cli") - -include("cli:flamingock-cli-executor") -project(":cli:flamingock-cli-executor").name = "flamingock-cli-executor" -project(":cli:flamingock-cli-executor").projectDir = file("cli/flamingock-cli-executor") ////////////////////////////////////// // E2E TESTS diff --git a/utils/general-util/build.gradle.kts b/utils/general-util/build.gradle.kts index 08a67f812..af750e383 100644 --- a/utils/general-util/build.gradle.kts +++ b/utils/general-util/build.gradle.kts @@ -6,7 +6,7 @@ plugins { val jacksonVersion = "2.16.0" dependencies { - implementation("org.reflections:reflections:0.10.1") + implementation("org.reflections:reflections:0.10.1")//todo remove // api("org.objenesis:objenesis:3.2") api("org.yaml:snakeyaml:2.2") //