From 50f55a217fb832bbe56f6ed77249ee1a0f62a0a3 Mon Sep 17 00:00:00 2001 From: Martin Vogel <martin.vogel@sinc.de> Date: Wed, 12 Mar 2025 12:46:18 +0100 Subject: [PATCH] refactor: add runnable picocli commands (planning#2217) --- .../java/dev/fitko/fitconnect/cli/CLI.java | 11 +++- .../cli/{ => commands}/ConfigCommand.java | 2 +- .../cli/commands/CreateTestKeysCommand.java | 20 ------ .../commands/GetAllSubmissionsCommand.java | 51 +++++++++----- .../cli/commands/GetOneSubmissionCommand.java | 28 -------- .../cli/commands/GetSubmissionCommand.java | 60 +++++++++++++++++ .../cli/{ => commands}/KeyGenCommand.java | 9 +-- .../commands/ListAllSubmissionsCommand.java | 24 ------- .../cli/{ => commands}/ListCommand.java | 4 +- .../fitconnect/cli/util/SubmissionWriter.java | 66 +++++++++++++++++++ 10 files changed, 176 insertions(+), 99 deletions(-) rename java/cli/src/main/java/dev/fitko/fitconnect/cli/{ => commands}/ConfigCommand.java (93%) delete mode 100644 java/cli/src/main/java/dev/fitko/fitconnect/cli/commands/CreateTestKeysCommand.java delete mode 100644 java/cli/src/main/java/dev/fitko/fitconnect/cli/commands/GetOneSubmissionCommand.java create mode 100644 java/cli/src/main/java/dev/fitko/fitconnect/cli/commands/GetSubmissionCommand.java rename java/cli/src/main/java/dev/fitko/fitconnect/cli/{ => commands}/KeyGenCommand.java (90%) delete mode 100644 java/cli/src/main/java/dev/fitko/fitconnect/cli/commands/ListAllSubmissionsCommand.java rename java/cli/src/main/java/dev/fitko/fitconnect/cli/{ => commands}/ListCommand.java (94%) create mode 100644 java/cli/src/main/java/dev/fitko/fitconnect/cli/util/SubmissionWriter.java diff --git a/java/cli/src/main/java/dev/fitko/fitconnect/cli/CLI.java b/java/cli/src/main/java/dev/fitko/fitconnect/cli/CLI.java index b898858..51527db 100644 --- a/java/cli/src/main/java/dev/fitko/fitconnect/cli/CLI.java +++ b/java/cli/src/main/java/dev/fitko/fitconnect/cli/CLI.java @@ -1,5 +1,9 @@ package dev.fitko.fitconnect.cli; +import dev.fitko.fitconnect.cli.commands.GetAllSubmissionsCommand; +import dev.fitko.fitconnect.cli.commands.GetSubmissionCommand; +import dev.fitko.fitconnect.cli.commands.KeyGenCommand; +import dev.fitko.fitconnect.cli.commands.ListCommand; import org.fusesource.jansi.AnsiConsole; import picocli.CommandLine; @@ -12,7 +16,11 @@ import static java.lang.System.exit; import static java.lang.System.out; @CommandLine.Command(version = "2.0.0", - subcommands = {KeyGenCommand.class, ListCommand.class}, + subcommands = { + KeyGenCommand.class, + ListCommand.class, + GetSubmissionCommand.class, + GetAllSubmissionsCommand.class}, mixinStandardHelpOptions = true) public class CLI implements Runnable { private static final String LOGO = "/splash_screen_banner.txt"; @@ -24,6 +32,7 @@ public class CLI implements Runnable { int exitCode = commandLine.execute(args); exit(exitCode); } + @Override public void run() { final CommandLine.Help.Ansi ansi = new CommandLine.Help.ColorScheme.Builder().ansi(); diff --git a/java/cli/src/main/java/dev/fitko/fitconnect/cli/ConfigCommand.java b/java/cli/src/main/java/dev/fitko/fitconnect/cli/commands/ConfigCommand.java similarity index 93% rename from java/cli/src/main/java/dev/fitko/fitconnect/cli/ConfigCommand.java rename to java/cli/src/main/java/dev/fitko/fitconnect/cli/commands/ConfigCommand.java index 0b0bbd1..920a798 100644 --- a/java/cli/src/main/java/dev/fitko/fitconnect/cli/ConfigCommand.java +++ b/java/cli/src/main/java/dev/fitko/fitconnect/cli/commands/ConfigCommand.java @@ -1,4 +1,4 @@ -package dev.fitko.fitconnect.cli; +package dev.fitko.fitconnect.cli.commands; import dev.fitko.fitconnect.api.config.ApplicationConfig; import dev.fitko.fitconnect.client.bootstrap.ApplicationConfigLoader; diff --git a/java/cli/src/main/java/dev/fitko/fitconnect/cli/commands/CreateTestKeysCommand.java b/java/cli/src/main/java/dev/fitko/fitconnect/cli/commands/CreateTestKeysCommand.java deleted file mode 100644 index 4901e7e..0000000 --- a/java/cli/src/main/java/dev/fitko/fitconnect/cli/commands/CreateTestKeysCommand.java +++ /dev/null @@ -1,20 +0,0 @@ -package dev.fitko.fitconnect.cli.commands; - -import com.beust.jcommander.Parameter; -import com.beust.jcommander.Parameters; - -@Parameters( - commandNames = {CreateTestKeysCommand.CREATE_TEST_KEYS_COMMAND_NAME}, - commandDescription = "Generates JWK test keys for encryption, decryption, signing and signature validation", - separators = "=" -) -public class CreateTestKeysCommand { - - public static final String CREATE_TEST_KEYS_COMMAND_NAME = "keygen"; - - @Parameter(names = {"--outDir"}, description = "Output directory folder where the generated test keys are written to", arity = 1) - public String outputDir; - - @Parameter(names = {"--withConfig"}, description = "Generates config.yaml with paths of the generated keys", arity = 1) - public boolean generateConfig; -} diff --git a/java/cli/src/main/java/dev/fitko/fitconnect/cli/commands/GetAllSubmissionsCommand.java b/java/cli/src/main/java/dev/fitko/fitconnect/cli/commands/GetAllSubmissionsCommand.java index 9ad74e0..551105c 100644 --- a/java/cli/src/main/java/dev/fitko/fitconnect/cli/commands/GetAllSubmissionsCommand.java +++ b/java/cli/src/main/java/dev/fitko/fitconnect/cli/commands/GetAllSubmissionsCommand.java @@ -1,27 +1,44 @@ package dev.fitko.fitconnect.cli.commands; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.Parameters; -import dev.fitko.fitconnect.cli.util.UUIDConverter; -import dev.fitko.fitconnect.cli.util.UUIDValidator; +import dev.fitko.fitconnect.api.config.ApplicationConfig; +import dev.fitko.fitconnect.api.domain.model.submission.SubmissionForPickup; +import dev.fitko.fitconnect.client.SubscriberClient; +import dev.fitko.fitconnect.client.bootstrap.ApplicationConfigLoader; +import dev.fitko.fitconnect.client.bootstrap.ClientFactory; +import lombok.SneakyThrows; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import picocli.CommandLine; +import java.nio.file.Path; +import java.util.Set; import java.util.UUID; -@Parameters( - commandNames = {GetAllSubmissionsCommand.GET_ALL_CMD_NAME}, - commandDescription = "Fetches all available submission for a destination", - separators = "=" -) -public class GetAllSubmissionsCommand { +@CommandLine.Command(name = "get", description = "Fetch a submission by id", mixinStandardHelpOptions = true) +public class GetAllSubmissionsCommand implements Runnable { - public static final String GET_ALL_CMD_NAME = "all"; + private static final Logger LOGGER = LoggerFactory.getLogger(GetAllSubmissionsCommand.class); - @Parameter(names = { "--config" }, description = "Path to the config yaml", arity = 1) - public String config; + @CommandLine.Option(names = {"--config"}, description = "Path to the config yaml", required = true) + public Path configPath; - @Parameter(names = { "--destinationId" }, description = "Unique destination identifier in UUID format", converter = UUIDConverter.class, validateWith = UUIDValidator.class, arity = 1, required = true) - public UUID destinationId; + @CommandLine.Option(names = {"-d", "--dest"}, required = true, description = "UUID of the destination") + private UUID destinationId; - @Parameter(names = { "--target" }, description = "Target folder where attachments and data is written to", arity = 1) - public String targetFolder; + @CommandLine.Option(names = {"--target"}, description = "Target folder where attachments and data is written to", required = false) + public Path targetFolder; + + @Override + @SneakyThrows + public void run() { + + LOGGER.info("Getting all available submissions for destination {}", destinationId); + + final ApplicationConfig config = ApplicationConfigLoader.loadConfigFromPath(configPath); + final SubscriberClient subscriberClient = ClientFactory.createSubscriberClient(config); + + final GetSubmissionCommand getSubmissionCommand = new GetSubmissionCommand(); + final Set<SubmissionForPickup> submissions = subscriberClient.getAvailableSubmissionsForDestination(destinationId); + submissions.forEach(submission -> getSubmissionCommand.getSubmission(subscriberClient, submission.getSubmissionId(), targetFolder)); + } } diff --git a/java/cli/src/main/java/dev/fitko/fitconnect/cli/commands/GetOneSubmissionCommand.java b/java/cli/src/main/java/dev/fitko/fitconnect/cli/commands/GetOneSubmissionCommand.java deleted file mode 100644 index fd74bd7..0000000 --- a/java/cli/src/main/java/dev/fitko/fitconnect/cli/commands/GetOneSubmissionCommand.java +++ /dev/null @@ -1,28 +0,0 @@ -package dev.fitko.fitconnect.cli.commands; - -import com.beust.jcommander.Parameter; -import com.beust.jcommander.Parameters; -import dev.fitko.fitconnect.cli.util.UUIDConverter; -import dev.fitko.fitconnect.cli.util.UUIDValidator; - -import java.util.UUID; - - -@Parameters( - commandNames = {GetOneSubmissionCommand.GET_CMD_NAME}, - commandDescription = "Fetches one submission by id", - separators = "=" -) -public class GetOneSubmissionCommand { - - public static final String GET_CMD_NAME = "get"; - - @Parameter(names = { "--config" }, description = "Path to the config yaml", arity = 1) - public String config; - - @Parameter(names = { "--submissionId" }, description = "Unique submission identifier in UUID format", converter = UUIDConverter.class, validateWith = UUIDValidator.class, arity = 1, required = true) - public UUID submissionId; - - @Parameter(names = { "--target" }, description = "Target folder where attachments and data is written to", arity = 1) - public String targetFolder; -} diff --git a/java/cli/src/main/java/dev/fitko/fitconnect/cli/commands/GetSubmissionCommand.java b/java/cli/src/main/java/dev/fitko/fitconnect/cli/commands/GetSubmissionCommand.java new file mode 100644 index 0000000..6ada861 --- /dev/null +++ b/java/cli/src/main/java/dev/fitko/fitconnect/cli/commands/GetSubmissionCommand.java @@ -0,0 +1,60 @@ +package dev.fitko.fitconnect.cli.commands; + +import dev.fitko.fitconnect.api.config.ApplicationConfig; +import dev.fitko.fitconnect.cli.util.SubmissionWriter; +import dev.fitko.fitconnect.client.SubscriberClient; +import dev.fitko.fitconnect.client.bootstrap.ApplicationConfigLoader; +import dev.fitko.fitconnect.client.bootstrap.ClientFactory; +import dev.fitko.fitconnect.core.utils.StopWatch; +import lombok.SneakyThrows; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import picocli.CommandLine; + +import java.nio.file.Path; +import java.util.UUID; + +@CommandLine.Command(name = "get", description = "Fetch a submission by id", mixinStandardHelpOptions = true) +public class GetSubmissionCommand implements Runnable { + + private static final Logger LOGGER = LoggerFactory.getLogger(GetSubmissionCommand.class); + + @CommandLine.Option(names = {"-c", "--config"}, required = true, description = "Path to the config yaml with subscriber clientId and clientSecret") + public Path configPath; + + @CommandLine.Option(names = {"-id", "--submissionId"}, description = "Unique submission identifier in UUID format", required = true) + public UUID submissionId; + + @CommandLine.Option(names = {"--target"}, description = "Target folder where attachments and data is written to", required = false) + public Path targetFolder; + + @Override + public void run() { + + LOGGER.info("Getting submission for id {}", submissionId); + + final ApplicationConfig config = ApplicationConfigLoader.loadConfigFromPath(configPath); + final SubscriberClient subscriberClient = ClientFactory.createSubscriberClient(config); + + getSubmission(subscriberClient, submissionId, targetFolder); + } + + @SneakyThrows + public void getSubmission(SubscriberClient subscriberClient, UUID submissionId, Path targetFolder) { + LOGGER.info("Getting submission for id {}", submissionId); + + final var startTime = StopWatch.start(); + final var submission = subscriberClient.requestSubmission(submissionId); + LOGGER.info("Submission download took {}", StopWatch.stop(startTime)); + if (submission == null) { + LOGGER.info("No submission found for submission id {}", submissionId); + } else { + submission.acceptSubmission(); + SubmissionWriter.writeSubmissionData(submission, getTargetFolderPath(targetFolder, submissionId)); + } + } + + private String getTargetFolderPath(Path targetFolder, UUID submissionId) { + return targetFolder != null ? targetFolder + "/" + submissionId : submissionId.toString(); + } +} diff --git a/java/cli/src/main/java/dev/fitko/fitconnect/cli/KeyGenCommand.java b/java/cli/src/main/java/dev/fitko/fitconnect/cli/commands/KeyGenCommand.java similarity index 90% rename from java/cli/src/main/java/dev/fitko/fitconnect/cli/KeyGenCommand.java rename to java/cli/src/main/java/dev/fitko/fitconnect/cli/commands/KeyGenCommand.java index ecd9fce..77bb043 100644 --- a/java/cli/src/main/java/dev/fitko/fitconnect/cli/KeyGenCommand.java +++ b/java/cli/src/main/java/dev/fitko/fitconnect/cli/commands/KeyGenCommand.java @@ -1,4 +1,4 @@ -package dev.fitko.fitconnect.cli; +package dev.fitko.fitconnect.cli.commands; import dev.fitko.fitconnect.api.domain.crypto.JWKPair; import dev.fitko.fitconnect.tools.keygen.KeyWriter; @@ -10,12 +10,11 @@ import picocli.CommandLine; import picocli.CommandLine.Option; import java.nio.file.Path; -import java.util.concurrent.Callable; import static dev.fitko.fitconnect.tools.keygen.TestKeyBuilder.DEFAULT_KEY_SIZE; @CommandLine.Command(name = "keygen", description = "Generate JWKs for TEST usage", mixinStandardHelpOptions = true) -public class KeyGenCommand implements Callable<Integer> { +public class KeyGenCommand implements Runnable { private static final Logger LOGGER = LoggerFactory.getLogger(KeyGenCommand.class); @@ -26,8 +25,7 @@ public class KeyGenCommand implements Callable<Integer> { private boolean generateConfig; @Override - public Integer call() { - + public void run() { LOGGER.info("Generating Test JWKs ..."); final JWKPair encryptionKeyPair = TestKeyBuilder.generateEncryptionKeyPair(DEFAULT_KEY_SIZE); @@ -43,6 +41,5 @@ public class KeyGenCommand implements Callable<Integer> { } KeyWriter.writeKeys(settingsBuilder.build()); - return null; } } diff --git a/java/cli/src/main/java/dev/fitko/fitconnect/cli/commands/ListAllSubmissionsCommand.java b/java/cli/src/main/java/dev/fitko/fitconnect/cli/commands/ListAllSubmissionsCommand.java deleted file mode 100644 index 0099467..0000000 --- a/java/cli/src/main/java/dev/fitko/fitconnect/cli/commands/ListAllSubmissionsCommand.java +++ /dev/null @@ -1,24 +0,0 @@ -package dev.fitko.fitconnect.cli.commands; - -import com.beust.jcommander.Parameter; -import com.beust.jcommander.Parameters; -import dev.fitko.fitconnect.cli.util.UUIDConverter; -import dev.fitko.fitconnect.cli.util.UUIDValidator; - -import java.util.UUID; - -@Parameters( - commandNames = {ListAllSubmissionsCommand.LIST_CMD_NAME}, - commandDescription = "Lists available submissions", - separators = "=" -) -public class ListAllSubmissionsCommand { - - public static final String LIST_CMD_NAME = "list"; - - @Parameter(names = { "--config" }, description = "Path to the config yaml", arity = 1) - public String config; - - @Parameter(names = { "--destinationId" }, description = "Unique destination identifier in UUID format", converter = UUIDConverter.class, validateWith = UUIDValidator.class, arity = 1, required = true) - public UUID destinationId; -} diff --git a/java/cli/src/main/java/dev/fitko/fitconnect/cli/ListCommand.java b/java/cli/src/main/java/dev/fitko/fitconnect/cli/commands/ListCommand.java similarity index 94% rename from java/cli/src/main/java/dev/fitko/fitconnect/cli/ListCommand.java rename to java/cli/src/main/java/dev/fitko/fitconnect/cli/commands/ListCommand.java index 51e5942..0013522 100644 --- a/java/cli/src/main/java/dev/fitko/fitconnect/cli/ListCommand.java +++ b/java/cli/src/main/java/dev/fitko/fitconnect/cli/commands/ListCommand.java @@ -1,4 +1,4 @@ -package dev.fitko.fitconnect.cli; +package dev.fitko.fitconnect.cli.commands; import dev.fitko.fitconnect.api.config.ApplicationConfig; import dev.fitko.fitconnect.api.domain.model.submission.SubmissionForPickup; @@ -21,7 +21,7 @@ public class ListCommand implements Runnable { @CommandLine.Option(names = {"-c", "--config"}, required = true, description = "Path to the config yaml with subscriber clientId and clientSecret") private Path configPath; - @CommandLine.Option(names = {"-d", "--dest"}, required = true, description = "UUID of the destinationId to poll") + @CommandLine.Option(names = {"-d", "--dest"}, required = true, description = "UUID of the destination to poll") private UUID destinationId; @Override diff --git a/java/cli/src/main/java/dev/fitko/fitconnect/cli/util/SubmissionWriter.java b/java/cli/src/main/java/dev/fitko/fitconnect/cli/util/SubmissionWriter.java new file mode 100644 index 0000000..2e3a446 --- /dev/null +++ b/java/cli/src/main/java/dev/fitko/fitconnect/cli/util/SubmissionWriter.java @@ -0,0 +1,66 @@ +package dev.fitko.fitconnect.cli.util; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; +import dev.fitko.fitconnect.api.domain.model.attachment.Attachment; +import dev.fitko.fitconnect.api.domain.model.metadata.Metadata; +import dev.fitko.fitconnect.api.domain.subscriber.ReceivedSubmission; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class SubmissionWriter { + + private static final Logger LOGGER = LoggerFactory.getLogger(SubmissionWriter.class); + + private static final ObjectWriter JSON_WRITER = new ObjectMapper().writerWithDefaultPrettyPrinter(); + + public static void writeSubmissionData(final ReceivedSubmission receivedSubmission, final String dataDirPath) throws IOException { + + LOGGER.info("Creating data directory for submission in {}", dataDirPath); + Files.createDirectories(Path.of(dataDirPath)); + + final var fileEnding = AttachmentDataType.getFileTypeFromMimeType(receivedSubmission.getDataMimeType()); + final var filePath = Path.of(dataDirPath + "/data." + fileEnding); + LOGGER.info("Writing data.{}", fileEnding); + Files.write(filePath, receivedSubmission.getDataAsString().getBytes(StandardCharsets.UTF_8)); + + LOGGER.info("Writing metadata.json"); + final Metadata metadata = receivedSubmission.getMetadata(); + Files.write(Path.of(dataDirPath, "metadata.json"), JSON_WRITER.writeValueAsString(metadata).getBytes()); + + final List<Attachment> attachments = receivedSubmission.getAttachments(); + for (int i = 0; i < attachments.size(); i++) { + final Attachment attachment = attachments.get(i); + final String filename = getAttachmentFilename(attachment, i); + LOGGER.info("Writing attachment {}", filename); + final Path tragetPath = Path.of(dataDirPath, filename); + if (attachment.isInMemoryAttachment()) { + Files.write(tragetPath, attachment.getDataAsBytes()); + } else { + try (OutputStream os = Files.newOutputStream(tragetPath)) { + attachment.getDataAsInputStream().transferTo(os); + } + } + } + } + + private static String getAttachmentFilename(Attachment attachment, int i) { + if (attachment.getFileName() == null) { + if (attachment.getMimeType() != null) { + return "attachment_" + i + "." + attachment.getMimeType().split("/")[1]; + } + return attachment + "_" + i; + } + return attachment.getFileName(); + } +} -- GitLab