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 b89885844020b1314a8a55b64c107de5c19fafbc..51527dbfe75a76d0819de8f3bacb11111c5200a4 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 0b0bbd1faac844119b015c164785e1f795c3714e..920a7987b7fe56d5d627a84e1c9786a32958ac2a 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 4901e7e49d619e475801f389febfc73366710651..0000000000000000000000000000000000000000 --- 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 9ad74e0e6fad6e344e540b41578c03fda8f620be..551105c5e10b31f7723afe04920082ad6d344f31 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 fd74bd7f7d4cbf063424edd4f5c0d327357487e5..0000000000000000000000000000000000000000 --- 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 0000000000000000000000000000000000000000..6ada8619f463265b9a5b787f68108a27baa65347 --- /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 ecd9fce65e5392c5058bc2f81301a5629d1be727..77bb043b8fcbda264da5bf39547c668e8f4ef2fd 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 00994674ac2d8951348b574250a501899ae1d680..0000000000000000000000000000000000000000 --- 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 51e5942e4570d0587b9db0376ba46238f1d40685..001352246a11e178351992af59614b7e8060c4c8 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 0000000000000000000000000000000000000000..2e3a44601ad325bd2e57b22cd36dfc1c9c472cdf --- /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(); + } +}