diff --git a/.docs/install.md b/.docs/install.md index 91f9a28..83565aa 100644 --- a/.docs/install.md +++ b/.docs/install.md @@ -36,7 +36,7 @@ repositories { } dependencies { - implementation("cz.insuit:moneta-api-spring-boot-starter:0.1.0") // or -play, -micronaut, -quarkus, or moneta-api alone + implementation("cz.insuit:moneta-api-spring-boot-starter:0.2.0") // or -play, -micronaut, -quarkus, or moneta-api alone } // gh's token, or nothing when gh is missing or signed out: the build then gets GitHub's 401, not a crash. @@ -71,7 +71,7 @@ fun gh(vararg args: String): Provider = cz.insuit moneta-api-spring-boot-starter - 0.1.0 + 0.2.0 ``` @@ -80,4 +80,4 @@ and in `~/.m2/settings.xml` a `` with the same `id`, your GitHub user an ## Snapshots None are published: only a tagged release is. To try what is not released yet, `make publish-local` in a checkout -installs every jar into `~/.m2` as `0.1.0-SNAPSHOT`, where `mavenLocal()` finds them. +installs every jar into `~/.m2` as `0.3.0-SNAPSHOT`, where `mavenLocal()` finds them. diff --git a/README.md b/README.md index 7339d7a..eba1372 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ Maven Central. | [Roadmap](.docs/plan/roadmap.md) | the working plan: what is still unknown, what comes next, what is not covered | | [Development](.docs/development.md) | build, tests, `make dev` against a real account | | [Example café](examples/cafe/README.md) | bills as QR payments, found paid; try it at http://localhost:8080/scalar after `make dev` | +| [llms.txt](llms.txt) | the setup and the mistakes to avoid, for a coding assistant adding the library to a project | ## What it covers diff --git a/build.gradle.kts b/build.gradle.kts index cf2d489..391856e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -15,7 +15,7 @@ plugins { val testsJdk = providers.gradleProperty("testsJdk") // A tag vX.Y.Z releases X.Y.Z (-PreleaseVersion, set by `make publish`); anything else is the next snapshot. -val releaseVersion = providers.gradleProperty("releaseVersion").orElse("0.1.0-SNAPSHOT") +val releaseVersion = providers.gradleProperty("releaseVersion").orElse("0.3.0-SNAPSHOT") allprojects { group = "cz.insuit" diff --git a/llms.txt b/llms.txt new file mode 100644 index 0000000..7854eec --- /dev/null +++ b/llms.txt @@ -0,0 +1,48 @@ +# moneta-api-java + +> A Java 21+ and Kotlin client for MONETA API, the free API of the Czech bank MONETA Money Bank for business +> accounts: it reads accounts, balances and transactions with a token the account holder issues in Internet Banka. +> Modules for Spring Boot, Play, Micronaut and Quarkus. Published on GitHub Packages (group `cz.insuit`), not Maven +> Central. Pre-1.0: read records by accessor, never by record pattern. + +Setting it up in a project: + +- Add the module for the project's framework, or `cz.insuit:moneta-api` without one; each module's README has + the Gradle and Maven lines, sbt for Play, and a prompt for a coding assistant. The framework modules bring + `moneta-api` with them; do not add both. +- Add the GitHub Packages repository `https://maven.pkg.github.com/landsman/moneta-api-java`. It asks for a token + even for a public package (`read:packages`). Never write a token into the build file or the repository. +- The MONETA token comes from the environment: `moneta.token` (Spring Boot, Play, Micronaut) or + `quarkus.moneta.token` (Quarkus), which also provides an `AccessToken` bean. +- One `MonetaClient` per application (framework modules provide it as a bean). Bind a token with + `moneta.with(token)`, then `transactions().between(from, to)`, optionally `.incoming()`, `.outgoing()`, + `.transfers()`, `.cards()`, `.booked()`, `.pending()`, and `.list()`. + +Mistakes to avoid: + +- `entryReference` changes when a pending movement is booked: deduplicating by it counts a payment twice. +- Movements have dates only, no time of day. +- `amount()` is signed: negative is money out. +- `Transaction.Method` gains constants: keep a `default` branch. +- A read is all or nothing across pages; read long ranges month by month. +- The library filters by method, direction and status in memory: only a shorter date range makes a read cheaper. +- Do not return a `Transaction` from a controller as JSON: `raw()` carries the counterparty's name and account. + +## Docs + +- [Installing](https://github.com/landsman/moneta-api-java/blob/main/.docs/install.md): the repository block and the token, for Gradle and Maven +- [Using the client](https://github.com/landsman/moneta-api-java/blob/main/.docs/usage.md): calls, filtering, more than one account, failures +- [How MONETA API behaves](https://github.com/landsman/moneta-api-java/blob/main/.docs/moneta-api.md): pending and booked movements, dates, ids, errors + +## Modules + +- [moneta-api](https://github.com/landsman/moneta-api-java/blob/main/modules/moneta-api/README.md): the client alone +- [Spring Boot starter](https://github.com/landsman/moneta-api-java/blob/main/modules/moneta-api-spring-boot-starter/README.md): `moneta.*` in application.yml, a `MonetaClient` bean +- [Play module](https://github.com/landsman/moneta-api-java/blob/main/modules/moneta-api-play/README.md): `moneta.*` in application.conf; calls block, run them on their own pool +- [Micronaut module](https://github.com/landsman/moneta-api-java/blob/main/modules/moneta-api-micronaut/README.md): `moneta.*`, a `MonetaClient` bean +- [Quarkus extension](https://github.com/landsman/moneta-api-java/blob/main/modules/moneta-api-quarkus/README.md): `quarkus.moneta.*`, JVM and native + +## Optional + +- [Kotlin and Spring Boot](https://github.com/landsman/moneta-api-java/blob/main/.docs/compatibility.md): Spring Boot 3 before 3.5.13 needs one dependency pinned; native images +- [Roadmap](https://github.com/landsman/moneta-api-java/blob/main/.docs/plan/roadmap.md): what is still unknown diff --git a/modules/moneta-api-micronaut/README.md b/modules/moneta-api-micronaut/README.md index ac8636d..75f5ca5 100644 --- a/modules/moneta-api-micronaut/README.md +++ b/modules/moneta-api-micronaut/README.md @@ -3,15 +3,113 @@ Micronaut wiring for the client: add the dependency, set `moneta.*`, inject `MonetaClient`. One jar for Micronaut 4.10 and 5; [testing/micronaut](../../testing/README.md) checks each. +## Install + +```kotlin +// build.gradle.kts +dependencies { + implementation("cz.insuit:moneta-api-micronaut:0.2.0") +} +``` + +
Maven + +```xml + + + cz.insuit + moneta-api-micronaut + 0.2.0 + +``` + +
+ +GitHub Packages asks for a token even for a public package: the repository block and the token are in +[Installing](../../.docs/install.md). + +## With a coding assistant + +Paste this into it: + +```text +Add the MONETA API client to this Micronaut project. + +1. Read https://github.com/landsman/moneta-api-java/blob/main/llms.txt and https://github.com/landsman/moneta-api-java/blob/main/modules/moneta-api-micronaut/README.md first. Do not guess the API. +2. Dependency: cz.insuit:moneta-api-micronaut:0.2.0 from GitHub Packages, with Gradle or Maven, as + https://github.com/landsman/moneta-api-java/blob/main/.docs/install.md shows. Never write a token into the project. +3. Configuration: moneta.token: ${MONETA_TOKEN} in application.yml. +4. Code: inject MonetaClient and AccessToken. Bind the token with moneta.with(token), read with + transactions().between(from, to), narrowed by incoming(), outgoing(), transfers(), + cards(), booked() or pending(), then list(). +5. Act on booked movements only, unless I ask for pending ones. +``` + +## Configure + ```yaml moneta: token: ${MONETA_TOKEN} ``` That is the whole setup for an application reading one account: inject `MonetaClient` and `AccessToken`. One -serving many merchants leaves `moneta.token` unset and passes each one's token with the call. Keep the token in +serving many account holders leaves `moneta.token` unset and passes each one's token with the call. Keep the token in the environment, never in a committed file. +```java +import cz.insuit.moneta.AccessToken; +import cz.insuit.moneta.AccountHolder; +import cz.insuit.moneta.MonetaClient; +import cz.insuit.moneta.Transaction; +import jakarta.inject.Singleton; +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.List; + +@Singleton +public class Payments { + private final AccountHolder me; + + public Payments(MonetaClient moneta, AccessToken token) { + this.me = moneta.with(token); + } + + /** Money received over the last week, booked. */ + public List receivedThisWeek() { + var today = LocalDate.now(ZoneId.of("Europe/Prague")); + return me.transactions().between(today.minusDays(7), today).incoming().booked().list(); + } +} +``` + +
Kotlin + +```kotlin +import cz.insuit.moneta.AccessToken +import cz.insuit.moneta.MonetaClient +import cz.insuit.moneta.Transaction +import jakarta.inject.Singleton +import java.time.LocalDate +import java.time.ZoneId + +@Singleton +class Payments(moneta: MonetaClient, token: AccessToken) { + private val me = moneta.with(token) + + /** Money received over the last week, booked. */ + fun receivedThisWeek(): List { + val today = LocalDate.now(ZoneId.of("Europe/Prague")) + return me.transactions().between(today.minusDays(7), today).incoming().booked().list() + } +} +``` + +
+ +It reads the only account the token opens and throws `IllegalStateException` when there are more; then, or with +one token per account holder, see [More than one account](../../.docs/usage.md#more-than-one-account). +Without `moneta.token` there is no `AccessToken` bean to inject. + A `MonetaClient` bean of the application's own replaces the one built here. Like every Micronaut bean, the client is built when first injected, not at startup, and closed with the context. A token or partner value it would refuse fails when `moneta.*` is bound; to have that stop the start, without building the client, opt in as Micronaut @@ -98,7 +196,7 @@ None is needed against production. The names are the Spring Boot starter's. |----------|---------|---| | `moneta.base-uri` | production, `MonetaClient.PRODUCTION` | the sandbox's host once the portal gives it | | `moneta.request-timeout` | the client's, 30 s | one call to MONETA: `20s` | -| `moneta.partner.identification-number`, `.business-name`, `.application-name` | — | the registered application's headers, all three or none, ASCII only | +| `moneta.partner.identification-number`, `.business-name`, `.application-name` | none | the registered application's headers, all three or none, ASCII only | MONETA does not ask for the partner headers: every call so far went through without ([how it behaves](../../.docs/moneta-api.md)). Likely for an application approved on the developer portal; set them once the diff --git a/modules/moneta-api-play/README.md b/modules/moneta-api-play/README.md index 7ce178e..831c85b 100644 --- a/modules/moneta-api-play/README.md +++ b/modules/moneta-api-play/README.md @@ -4,15 +4,102 @@ A Play Framework module for the client: add the dependency, set `moneta.*`, inje `reference.conf` enables it; nothing else to register. Works on Play 3.0 (Scala 2.13 and 3) and 2.9; [testing/play](../../testing/README.md) checks each. +## Install + +```scala +// build.sbt +resolvers += "GitHub Packages" at "https://maven.pkg.github.com/landsman/moneta-api-java" +credentials += Credentials("GitHub Package Registry", "maven.pkg.github.com", "gh", sys.env.getOrElse("GITHUB_TOKEN", "")) +libraryDependencies += "cz.insuit" % "moneta-api-play" % "0.2.0" +``` + +`GITHUB_TOKEN` holds a token with `read:packages`; Gradle and Maven below. + +```kotlin +// build.gradle.kts +dependencies { + implementation("cz.insuit:moneta-api-play:0.2.0") +} +``` + +
Maven + +```xml + + + cz.insuit + moneta-api-play + 0.2.0 + +``` + +
+ +GitHub Packages asks for a token even for a public package: the repository block and the token are in +[Installing](../../.docs/install.md). + +## With a coding assistant + +Paste this into it: + +```text +Add the MONETA API client to this Play Framework project. + +1. Read https://github.com/landsman/moneta-api-java/blob/main/llms.txt and https://github.com/landsman/moneta-api-java/blob/main/modules/moneta-api-play/README.md first. Do not guess the API. +2. Dependency: cz.insuit:moneta-api-play:0.2.0 from GitHub Packages, with sbt, Gradle or Maven, as + https://github.com/landsman/moneta-api-java/blob/main/.docs/install.md shows. Never write a token into the project. +3. Configuration: moneta.token = ${?MONETA_TOKEN} in application.conf. +4. Code: inject MonetaClient and AccessToken (javax.inject), and run the calls on a pool of their own, not Play's default one. Bind the token with moneta.with(token), read with + transactions().between(from, to), narrowed by incoming(), outgoing(), transfers(), + cards(), booked() or pending(), then list(). +5. Act on booked movements only, unless I ask for pending ones. +``` + +## Configure + ```hocon # application.conf moneta.token = ${?MONETA_TOKEN} ``` That is the whole setup for an application reading one account: inject `MonetaClient` and `AccessToken`. One -serving many merchants leaves `moneta.token` unset and passes each one's token with the call. Keep the token in +serving many account holders leaves `moneta.token` unset and passes each one's token with the call. Keep the token in the environment, never in a committed file. +```java +import cz.insuit.moneta.AccessToken; +import cz.insuit.moneta.AccountHolder; +import cz.insuit.moneta.MonetaClient; +import cz.insuit.moneta.Transaction; +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.List; +import javax.inject.Inject; +import javax.inject.Singleton; + +@Singleton +public class Payments { + private final AccountHolder me; + + @Inject + public Payments(MonetaClient moneta, AccessToken token) { + this.me = moneta.with(token); + } + + /** Money received over the last week, booked. */ + public List receivedThisWeek() { + var today = LocalDate.now(ZoneId.of("Europe/Prague")); + return me.transactions().between(today.minusDays(7), today).incoming().booked().list(); + } +} +``` + +It reads the only account the token opens and throws `IllegalStateException` when there are more; then, or with +one token per account holder, see [More than one account](../../.docs/usage.md#more-than-one-account). +Without `moneta.token` there is no `AccessToken` bean to inject. + +The call blocks: run it on [a pool of its own](#calls-block-give-them-a-pool-of-their-own), not Play's default one. + The client is a singleton built when the application starts, so a token it would refuse stops the start, and it is closed when the application stops, a dev-mode reload included. @@ -44,18 +131,16 @@ public class BlockingExecutor implements Executor { ```java public class PaymentsController extends Controller { - private final MonetaClient moneta; - private final AccessToken token; + private final Payments payments; private final BlockingExecutor blocking; private final ClassLoaderExecutionContext http; @Inject - public PaymentsController( - MonetaClient moneta, AccessToken token, BlockingExecutor blocking, ClassLoaderExecutionContext http) { … } + public PaymentsController(Payments payments, BlockingExecutor blocking, ClassLoaderExecutionContext http) { … } - public CompletionStage incoming() { - return CompletableFuture.supplyAsync(() -> moneta.incoming(token, accountId, from, to), blocking) - .thenApplyAsync(ts -> ok(views.html.incoming.render(ts)), http.current()); // back off the blocking pool + public CompletionStage received() { + return CompletableFuture.supplyAsync(payments::receivedThisWeek, blocking) + .thenApplyAsync(ts -> ok(views.html.received.render(ts)), http.current()); // back off the blocking pool } } ``` @@ -126,7 +211,7 @@ None is needed against production. The keys are the Spring Boot starter's. |-----|---------|---| | `moneta.base-uri` | production, `MonetaClient.PRODUCTION` | the sandbox's host once the portal gives it | | `moneta.request-timeout` | the client's, 30 s | one call to MONETA, as a HOCON duration: `20s` | -| `moneta.partner.identification-number`, `.business-name`, `.application-name` | — | the registered application's headers, all three or none, ASCII only | +| `moneta.partner.identification-number`, `.business-name`, `.application-name` | none | the registered application's headers, all three or none, ASCII only | MONETA does not ask for the partner headers: every call so far went through without ([how it behaves](../../.docs/moneta-api.md)). Likely for an application approved on the developer portal; set them once the diff --git a/modules/moneta-api-quarkus/README.md b/modules/moneta-api-quarkus/README.md index be90e69..1e8ef2d 100644 --- a/modules/moneta-api-quarkus/README.md +++ b/modules/moneta-api-quarkus/README.md @@ -4,14 +4,112 @@ A Quarkus extension for the client: add the dependency, set `quarkus.moneta.*`, mode and as a native image. Compiled against Quarkus 3.33 LTS; [testing/quarkus](../../testing/README.md) checks 3.33 and 3.40. +## Install + +```kotlin +// build.gradle.kts +dependencies { + implementation("cz.insuit:moneta-api-quarkus:0.2.0") +} +``` + +
Maven + +```xml + + + cz.insuit + moneta-api-quarkus + 0.2.0 + +``` + +
+ +GitHub Packages asks for a token even for a public package: the repository block and the token are in +[Installing](../../.docs/install.md). Maven needs both, the `` entry and the ``, because +Quarkus fetches `moneta-api-quarkus-deployment` from there at build time. + +## With a coding assistant + +Paste this into it: + +```text +Add the MONETA API client to this Quarkus project. + +1. Read https://github.com/landsman/moneta-api-java/blob/main/llms.txt and https://github.com/landsman/moneta-api-java/blob/main/modules/moneta-api-quarkus/README.md first. Do not guess the API. +2. Dependency: cz.insuit:moneta-api-quarkus:0.2.0 from GitHub Packages, with Gradle or Maven, as + https://github.com/landsman/moneta-api-java/blob/main/.docs/install.md shows. Never write a token into the project. +3. Configuration: quarkus.moneta.token=${MONETA_TOKEN} in application.properties. +4. Code: inject MonetaClient and AccessToken. Bind the token with moneta.with(token), read with + transactions().between(from, to), narrowed by incoming(), outgoing(), transfers(), + cards(), booked() or pending(), then list(). +5. Act on booked movements only, unless I ask for pending ones. +``` + +## Configure + ```properties quarkus.moneta.token=${MONETA_TOKEN} ``` That is the whole setup for an application reading one account: inject `MonetaClient` and `AccessToken`. One -serving many merchants leaves `quarkus.moneta.token` unset and passes each one's token with the call; injecting +serving many account holders leaves `quarkus.moneta.token` unset and passes each one's token with the call; injecting `AccessToken` then fails, naming the property. Keep the token in the environment, never in a committed file. +```java +import cz.insuit.moneta.AccessToken; +import cz.insuit.moneta.AccountHolder; +import cz.insuit.moneta.MonetaClient; +import cz.insuit.moneta.Transaction; +import jakarta.enterprise.context.ApplicationScoped; +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.List; + +@ApplicationScoped +public class Payments { + private final AccountHolder me; + + public Payments(MonetaClient moneta, AccessToken token) { + this.me = moneta.with(token); + } + + /** Money received over the last week, booked. */ + public List receivedThisWeek() { + var today = LocalDate.now(ZoneId.of("Europe/Prague")); + return me.transactions().between(today.minusDays(7), today).incoming().booked().list(); + } +} +``` + +
Kotlin + +```kotlin +import cz.insuit.moneta.AccessToken +import cz.insuit.moneta.MonetaClient +import cz.insuit.moneta.Transaction +import jakarta.enterprise.context.ApplicationScoped +import java.time.LocalDate +import java.time.ZoneId + +@ApplicationScoped +class Payments(moneta: MonetaClient, token: AccessToken) { + private val me = moneta.with(token) + + /** Money received over the last week, booked. */ + fun receivedThisWeek(): List { + val today = LocalDate.now(ZoneId.of("Europe/Prague")) + return me.transactions().between(today.minusDays(7), today).incoming().booked().list() + } +} +``` + +
+ +It reads the only account the token opens and throws `IllegalStateException` when there are more; then, or with +one token per account holder, see [More than one account](../../.docs/usage.md#more-than-one-account). + | Property | Default | |----------|---------| | `quarkus.moneta.token` | none: no `AccessToken` bean | diff --git a/modules/moneta-api-spring-boot-starter/README.md b/modules/moneta-api-spring-boot-starter/README.md index d77ee53..0f61698 100644 --- a/modules/moneta-api-spring-boot-starter/README.md +++ b/modules/moneta-api-spring-boot-starter/README.md @@ -3,15 +3,113 @@ Spring Boot auto-configuration for the client: add the dependency, set `moneta.*`, inject `MonetaClient`. Works on Spring Boot 3.5 and 4; [testing/spring-boot](../../testing/README.md) checks each. +## Install + +```kotlin +// build.gradle.kts +dependencies { + implementation("cz.insuit:moneta-api-spring-boot-starter:0.2.0") +} +``` + +
Maven + +```xml + + + cz.insuit + moneta-api-spring-boot-starter + 0.2.0 + +``` + +
+ +GitHub Packages asks for a token even for a public package: the repository block and the token are in +[Installing](../../.docs/install.md). + +## With a coding assistant + +Paste this into it: + +```text +Add the MONETA API client to this Spring Boot project. + +1. Read https://github.com/landsman/moneta-api-java/blob/main/llms.txt and https://github.com/landsman/moneta-api-java/blob/main/modules/moneta-api-spring-boot-starter/README.md first. Do not guess the API. +2. Dependency: cz.insuit:moneta-api-spring-boot-starter:0.2.0 from GitHub Packages, with Gradle or Maven, as + https://github.com/landsman/moneta-api-java/blob/main/.docs/install.md shows. Never write a token into the project. +3. Configuration: moneta.token: ${MONETA_TOKEN} in application.yml. +4. Code: inject MonetaClient and AccessToken. Bind the token with moneta.with(token), read with + transactions().between(from, to), narrowed by incoming(), outgoing(), transfers(), + cards(), booked() or pending(), then list(). +5. Act on booked movements only, unless I ask for pending ones. +``` + +## Configure + ```yaml moneta: token: ${MONETA_TOKEN} ``` That is the whole setup for an application reading one account: inject `MonetaClient` and `AccessToken`. One -serving many merchants leaves `moneta.token` unset and passes each one's token with the call. Keep the token in +serving many account holders leaves `moneta.token` unset and passes each one's token with the call. Keep the token in the environment, never in a committed file. +```java +import cz.insuit.moneta.AccessToken; +import cz.insuit.moneta.AccountHolder; +import cz.insuit.moneta.MonetaClient; +import cz.insuit.moneta.Transaction; +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.List; +import org.springframework.stereotype.Service; + +@Service +public class Payments { + private final AccountHolder me; + + public Payments(MonetaClient moneta, AccessToken token) { + this.me = moneta.with(token); + } + + /** Money received over the last week, booked. */ + public List receivedThisWeek() { + var today = LocalDate.now(ZoneId.of("Europe/Prague")); + return me.transactions().between(today.minusDays(7), today).incoming().booked().list(); + } +} +``` + +
Kotlin + +```kotlin +import cz.insuit.moneta.AccessToken +import cz.insuit.moneta.MonetaClient +import cz.insuit.moneta.Transaction +import java.time.LocalDate +import java.time.ZoneId +import org.springframework.stereotype.Service + +@Service +class Payments(moneta: MonetaClient, token: AccessToken) { + private val me = moneta.with(token) + + /** Money received over the last week, booked. */ + fun receivedThisWeek(): List { + val today = LocalDate.now(ZoneId.of("Europe/Prague")) + return me.transactions().between(today.minusDays(7), today).incoming().booked().list() + } +} +``` + +
+ +It reads the only account the token opens and throws `IllegalStateException` when there are more; then, or with +one token per account holder, see [More than one account](../../.docs/usage.md#more-than-one-account). +Without `moneta.token` there is no `AccessToken` bean to inject. + A `MonetaClient` bean of the application's own replaces the auto-configured one. A token the client would refuse stops the application at startup, not at the first call. IntelliJ completes the properties from the metadata the starter ships. @@ -26,7 +124,7 @@ None is needed against production. |----------|---------|---| | `moneta.base-uri` | production, `MonetaClient.PRODUCTION` | the sandbox's host once the portal gives it | | `moneta.request-timeout` | the client's, 30 s | one call to MONETA | -| `moneta.partner.identification-number`, `.business-name`, `.application-name` | — | the registered application's headers, all three or none, ASCII only | +| `moneta.partner.identification-number`, `.business-name`, `.application-name` | none | the registered application's headers, all three or none, ASCII only | MONETA does not ask for the partner headers: every call so far went through without ([how it behaves](../../.docs/moneta-api.md)). Likely for an application approved on the developer portal; set them once the diff --git a/modules/moneta-api/README.md b/modules/moneta-api/README.md new file mode 100644 index 0000000..5e661ad --- /dev/null +++ b/modules/moneta-api/README.md @@ -0,0 +1,57 @@ +# moneta-api + +The MONETA API client for Java 21+ and Kotlin, without a framework. With Spring Boot, Play, Micronaut or Quarkus, +take that module instead; it brings this one. + +## Install + +```kotlin +// build.gradle.kts +dependencies { + implementation("cz.insuit:moneta-api:0.2.0") +} +``` + +
Maven + +```xml + + + cz.insuit + moneta-api + 0.2.0 + +``` + +
+ +GitHub Packages asks for a token even for a public package: the repository block and the token are in +[Installing](../../.docs/install.md). + +## With a coding assistant + +Paste this into it: + +```text +Add the MONETA API client to this plain Java or Kotlin project. + +1. Read https://github.com/landsman/moneta-api-java/blob/main/llms.txt and https://github.com/landsman/moneta-api-java/blob/main/modules/moneta-api/README.md first. Do not guess the API. +2. Dependency: cz.insuit:moneta-api:0.2.0 from GitHub Packages, with Gradle or Maven, as + https://github.com/landsman/moneta-api-java/blob/main/.docs/install.md shows. Never write a token into the project. +3. Configuration: the token from the MONETA_TOKEN environment variable, wrapped in AccessToken. +4. Code: make one MonetaClient for the application and close it when the application stops. Bind the token with moneta.with(token), read with + transactions().between(from, to), narrowed by incoming(), outgoing(), transfers(), + cards(), booked() or pending(), then list(). +5. Act on booked movements only, unless I ask for pending ones. +``` + +## Use + +```java +try (var moneta = new MonetaClient()) { // one per application; close it when the application stops + var me = moneta.with(new AccessToken(System.getenv("MONETA_TOKEN"))); + me.transactions().between(LocalDate.of(2026, 11, 1), LocalDate.of(2026, 11, 30)).incoming().booked().list(); +} +``` + +The rest is in [Using the client](../../.docs/usage.md).