diff --git a/bpmn-process/build.gradle.kts b/bpmn-process/build.gradle.kts index 74373b74..7ce31334 100644 --- a/bpmn-process/build.gradle.kts +++ b/bpmn-process/build.gradle.kts @@ -2,7 +2,12 @@ micronaut { testRuntime("junit5") } +tasks.compileJava { + options.compilerArgs.add("-Amicronaut.jsonschema.baseUri=https://www.pledger.io/schemas") // (1) +} + dependencies { + annotationProcessor(mn.micronaut.json.schema.processor) annotationProcessor(mn.lombok) implementation(libs.lang) @@ -19,6 +24,7 @@ dependencies { implementation(mn.micronaut.jackson.databind) implementation(mn.micronaut.serde.jackson) implementation(mn.validation) + implementation(mn.micronaut.json.schema.annotations) implementation(project(":core")) implementation(project(":domain")) diff --git a/bpmn-process/src/main/java/com/jongsoft/finance/serialized/AccountJson.java b/bpmn-process/src/main/java/com/jongsoft/finance/serialized/AccountJson.java index 90fc4856..18462f3c 100644 --- a/bpmn-process/src/main/java/com/jongsoft/finance/serialized/AccountJson.java +++ b/bpmn-process/src/main/java/com/jongsoft/finance/serialized/AccountJson.java @@ -2,8 +2,12 @@ import com.jongsoft.finance.domain.account.Account; import com.jongsoft.finance.schedule.Periodicity; +import io.micronaut.core.annotation.NonNull; +import io.micronaut.core.annotation.Nullable; +import io.micronaut.jsonschema.JsonSchema; import io.micronaut.serde.annotation.Serdeable; import lombok.Builder; +import lombok.Data; import lombok.Getter; import lombok.Setter; import org.bouncycastle.util.encoders.Hex; @@ -11,16 +15,30 @@ import java.io.Serializable; import java.util.function.Supplier; -@Getter -@Setter +@Data @Builder @Serdeable +@JsonSchema(uri = "/account", title = "Account", description = "A account is a financial account that can be used to record transactions.") public class AccountJson implements Serializable { + /** + * The name of the account. + */ + @NonNull private String name; + /** + * The description of the account. + */ private String description; + /** + * The currency of the account, in a 3-letter ISO currency code. + */ + @NonNull private String currency; + /** + * The icon of the account, in a base64 encoded string. + */ private String icon; private double interest; @@ -29,6 +47,10 @@ public class AccountJson implements Serializable { private String iban; private String bic; private String number; + /** + * The type of the account. + */ + @NonNull private String type; public static AccountJson fromDomain(Account account, Supplier iconSupplier) { diff --git a/bpmn-process/src/main/java/com/jongsoft/finance/serialized/BudgetJson.java b/bpmn-process/src/main/java/com/jongsoft/finance/serialized/BudgetJson.java index 6a83e720..3aaf0cd8 100644 --- a/bpmn-process/src/main/java/com/jongsoft/finance/serialized/BudgetJson.java +++ b/bpmn-process/src/main/java/com/jongsoft/finance/serialized/BudgetJson.java @@ -1,36 +1,49 @@ package com.jongsoft.finance.serialized; import com.jongsoft.finance.domain.user.Budget; +import io.micronaut.core.annotation.NonNull; +import io.micronaut.jsonschema.JsonSchema; import io.micronaut.serde.annotation.Serdeable; import lombok.Builder; -import lombok.Getter; -import lombok.Setter; +import lombok.Data; import java.io.Serializable; import java.time.LocalDate; import java.util.List; import java.util.stream.Collectors; -@Getter -@Setter +@Data @Builder @Serdeable +@JsonSchema(title = "Budget", description = "Budget for a user", uri = "/budget") public class BudgetJson implements Serializable { - @Getter - @Setter + @Data @Builder @Serdeable + @JsonSchema(title = "Expense", description = "Expense for a budget", uri = "/budget-expense") public static class ExpenseJson implements Serializable { + @NonNull private String name; private double lowerBound; private double upperBound; } + /** + * Start date of the budget, in ISO 8601 format. + */ + @NonNull private LocalDate start; private LocalDate end; + /** + * Expected income for the budget. + */ private double expectedIncome; + /** + * List of expenses for the budget. + */ + @NonNull private List expenses; public static BudgetJson fromDomain(Budget budget) { diff --git a/bpmn-process/src/main/java/com/jongsoft/finance/serialized/CategoryJson.java b/bpmn-process/src/main/java/com/jongsoft/finance/serialized/CategoryJson.java index ce1aac96..2e122eb5 100644 --- a/bpmn-process/src/main/java/com/jongsoft/finance/serialized/CategoryJson.java +++ b/bpmn-process/src/main/java/com/jongsoft/finance/serialized/CategoryJson.java @@ -1,20 +1,25 @@ package com.jongsoft.finance.serialized; import com.jongsoft.finance.domain.user.Category; +import io.micronaut.core.annotation.NonNull; +import io.micronaut.jsonschema.JsonSchema; import io.micronaut.serde.annotation.Serdeable; import lombok.Builder; import lombok.Data; -import lombok.Getter; -import lombok.Setter; import java.io.Serializable; -@Getter -@Setter +@Data @Builder @Serdeable +@JsonSchema( + title = "Category", + description = "Category of a transaction", + uri = "/category" +) public class CategoryJson implements Serializable { + @NonNull private String label; private String description; diff --git a/bpmn-process/src/main/java/com/jongsoft/finance/serialized/ContractJson.java b/bpmn-process/src/main/java/com/jongsoft/finance/serialized/ContractJson.java index 6676b193..4382d1c4 100644 --- a/bpmn-process/src/main/java/com/jongsoft/finance/serialized/ContractJson.java +++ b/bpmn-process/src/main/java/com/jongsoft/finance/serialized/ContractJson.java @@ -1,6 +1,8 @@ package com.jongsoft.finance.serialized; import com.jongsoft.finance.domain.account.Contract; +import io.micronaut.core.annotation.NonNull; +import io.micronaut.jsonschema.JsonSchema; import io.micronaut.serde.annotation.Serdeable; import lombok.Builder; import lombok.Data; @@ -12,18 +14,25 @@ import java.time.LocalDate; import java.util.function.Supplier; -@Getter -@Setter +@Data @Builder @Serdeable +@JsonSchema(title = "Contract", description = "Contract details", uri = "/contract") public class ContractJson implements Serializable { + @NonNull private String name; private String description; + @NonNull private String company; + /** + * The contract attachment as a hex string. + */ private String contract; private boolean terminated; + @NonNull private LocalDate start; + @NonNull private LocalDate end; public static ContractJson fromDomain(Contract contract, Supplier attachmentSupplier) { diff --git a/bpmn-process/src/main/java/com/jongsoft/finance/serialized/ExportJson.java b/bpmn-process/src/main/java/com/jongsoft/finance/serialized/ExportJson.java index 2de3ba7c..e5a2afb1 100644 --- a/bpmn-process/src/main/java/com/jongsoft/finance/serialized/ExportJson.java +++ b/bpmn-process/src/main/java/com/jongsoft/finance/serialized/ExportJson.java @@ -1,17 +1,19 @@ package com.jongsoft.finance.serialized; +import io.micronaut.jsonschema.JsonSchema; import io.micronaut.serde.annotation.Serdeable; import lombok.Builder; +import lombok.Data; import lombok.Getter; import lombok.Setter; import java.io.Serializable; import java.util.List; -@Getter -@Setter +@Data @Builder @Serdeable +@JsonSchema(title = "Profile", description = "A user profile", uri = "/profile") public class ExportJson implements Serializable { private List accounts; diff --git a/bpmn-process/src/main/java/com/jongsoft/finance/serialized/RuleConfigJson.java b/bpmn-process/src/main/java/com/jongsoft/finance/serialized/RuleConfigJson.java index f2f19077..ac791cfc 100644 --- a/bpmn-process/src/main/java/com/jongsoft/finance/serialized/RuleConfigJson.java +++ b/bpmn-process/src/main/java/com/jongsoft/finance/serialized/RuleConfigJson.java @@ -3,8 +3,10 @@ import com.jongsoft.finance.core.RuleColumn; import com.jongsoft.finance.core.RuleOperation; import com.jongsoft.finance.domain.transaction.TransactionRule; +import io.micronaut.jsonschema.JsonSchema; import io.micronaut.serde.annotation.Serdeable; import lombok.Builder; +import lombok.Data; import lombok.Getter; import lombok.Setter; @@ -13,16 +15,16 @@ import java.util.function.BiFunction; import java.util.stream.Collectors; -@Getter -@Setter +@Data @Builder @Serdeable +@JsonSchema(title = "Transaction Rules", description = "Configuration for transaction rules", uri = "/rules") public class RuleConfigJson implements Serializable { - @Getter - @Setter + @Data @Builder @Serdeable + @JsonSchema(title = "Transaction Rule", description = "A single transaction rule", uri = "/rule") public static class RuleJson implements Serializable { private String name; private String description; @@ -57,8 +59,7 @@ public static RuleJson fromDomain(TransactionRule rule, BiFunction handle(HttpRequest request, AuthorizationExceptio log.info("{}: {} - User {} is not authenticated.", request.getMethod(), request.getPath(), - exception.getAuthentication().getName()); + Control.Option(exception.getAuthentication()) + .map(Authentication::getName) + .getOrSupply(() -> "Unknown")); return HttpResponse.unauthorized(); } diff --git a/fintrack-api/src/main/java/com/jongsoft/finance/filter/AuthenticationFilter.java b/fintrack-api/src/main/java/com/jongsoft/finance/filter/AuthenticationFilter.java index 2e46c7e5..25a8ff67 100644 --- a/fintrack-api/src/main/java/com/jongsoft/finance/filter/AuthenticationFilter.java +++ b/fintrack-api/src/main/java/com/jongsoft/finance/filter/AuthenticationFilter.java @@ -49,12 +49,13 @@ public Publisher> doFilter(final HttpRequest request, } else { if (log.isTraceEnabled() && request.getBody().isPresent()) { Object body = request.getBody().get(); - log.trace("{}: {} in {} ms, with request body {}.", - request.getMethod(), - request.getPath(), - Duration.between(startTime, Instant.now()).toMillis(), - Control.Try(() -> objectMapper.writeValueAsString(body)) - .recover(Throwable::getMessage).get()); + log.atTrace() + .addArgument(request::getMethod) + .addArgument(request::getPath) + .addArgument(() -> Duration.between(startTime, Instant.now()).toMillis()) + .addArgument(() -> Control.Try(() -> objectMapper.writeValueAsString(body)) + .recover(Throwable::getMessage).get()) + .log("{}: {} in {} ms, with request body {}."); } else { log.info("{}: {} in {} ms", request.getMethod(), request.getPath(), Duration.between(startTime, Instant.now()).toMillis()); } diff --git a/fintrack-api/src/main/java/com/jongsoft/finance/rest/transaction/TransactionResource.java b/fintrack-api/src/main/java/com/jongsoft/finance/rest/transaction/TransactionResource.java index 35389ed4..5170a486 100644 --- a/fintrack-api/src/main/java/com/jongsoft/finance/rest/transaction/TransactionResource.java +++ b/fintrack-api/src/main/java/com/jongsoft/finance/rest/transaction/TransactionResource.java @@ -186,7 +186,7 @@ LocalDate firstTransaction(@Body TransactionSearchRequest request) { summary = "Export transactions", description = "Creates a CSV export of all transactions in the system." ) - OutputStream export() throws IOException { + String export() throws IOException { var outputStream = new ByteArrayOutputStream(); outputStream.write(("Date,Booking Date,Interest Date,From name,From IBAN," + "To name,To IBAN,Description,Category,Budget,Contract,Amount\n").getBytes(StandardCharsets.UTF_8)); @@ -210,7 +210,7 @@ OutputStream export() throws IOException { page = transactionProvider.lookup(filterCommand); } while (page.hasNext()); - return outputStream; + return outputStream.toString(); } @Get("/apply-all-rules") diff --git a/gradle.properties b/gradle.properties index 11ea5f49..65c5fd58 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,2 +1,2 @@ -micronautVersion=4.4.3 +micronautVersion=4.5.0 version=3.3.0-SNAPSHOT diff --git a/jpa-repository/src/main/java/db/migration/V20200429151821__MigrateEncryptedStorage.java b/jpa-repository/src/main/java/db/migration/V20200429151821__MigrateEncryptedStorage.java index 96b88758..08d576e8 100644 --- a/jpa-repository/src/main/java/db/migration/V20200429151821__MigrateEncryptedStorage.java +++ b/jpa-repository/src/main/java/db/migration/V20200429151821__MigrateEncryptedStorage.java @@ -22,7 +22,7 @@ public V20200429151821__MigrateEncryptedStorage() { @Override public void migrate(Context context) throws Exception { - jdbcTemplate = new JdbcTemplate(context.getConnection()); +// jdbcTemplate = new JdbcTemplate(context.getConnection()); // jdbcTemplate.query("select id, username, two_factor_secret from user_account", this::processUser); } diff --git a/jpa-repository/src/main/java/db/migration/V20200430171321__MigrateToEncryptedDatabase.java b/jpa-repository/src/main/java/db/migration/V20200430171321__MigrateToEncryptedDatabase.java index 98ea1b90..2e14f137 100644 --- a/jpa-repository/src/main/java/db/migration/V20200430171321__MigrateToEncryptedDatabase.java +++ b/jpa-repository/src/main/java/db/migration/V20200430171321__MigrateToEncryptedDatabase.java @@ -19,7 +19,7 @@ public V20200430171321__MigrateToEncryptedDatabase() { @Override public void migrate(Context context) throws Exception { - jdbcTemplate = new JdbcTemplate(context.getConnection()); +// jdbcTemplate = new JdbcTemplate(context.getConnection()); // jdbcTemplate.query("select id, username from user_account", this::processUser); } diff --git a/jpa-repository/src/main/java/db/migration/V20200503171321__MigrateToDecryptDatabase.java b/jpa-repository/src/main/java/db/migration/V20200503171321__MigrateToDecryptDatabase.java index 5e3ba412..a67eaa2a 100644 --- a/jpa-repository/src/main/java/db/migration/V20200503171321__MigrateToDecryptDatabase.java +++ b/jpa-repository/src/main/java/db/migration/V20200503171321__MigrateToDecryptDatabase.java @@ -19,7 +19,7 @@ public V20200503171321__MigrateToDecryptDatabase() { @Override public void migrate(Context context) throws Exception { - jdbcTemplate = new JdbcTemplate(context.getConnection()); +// jdbcTemplate = new JdbcTemplate(context.getConnection()); // jdbcTemplate.query("select id, username from user_account", this::processUser); } diff --git a/renovate.json b/renovate.json index 5db72dd6..4f8425a1 100644 --- a/renovate.json +++ b/renovate.json @@ -2,5 +2,7 @@ "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ "config:recommended" - ] + ], + "addLabels": ["dependency", "triage"], + "assignees": ["@gjong"] }