From 96f9917adaaf07e67593311c9d3750f956fd965f Mon Sep 17 00:00:00 2001
From: podlLev
Date: Mon, 3 Aug 2026 08:42:55 +0300
Subject: [PATCH 1/4] ci: add coverage-badge job to generate and commit JaCoCo
badge
---
.github/workflows/ci.yml | 41 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 40 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index ac8ae7b..0e926df 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -66,7 +66,46 @@ jobs:
path: target/*.jar
# ---------------------------------------------------------------------
- # 2. Build + push a versioned image to Docker Hub
+ # 2. Regenerate the coverage badge from the JaCoCo report and commit it
+ # - runs ONLY on: pushes to main or dev
+ # - the ref check alone excludes pull_request events: their ref is
+ # refs/pull//merge, never refs/heads/main or .../dev
+ # - actions/checkout defaults to the triggering ref, so the commit
+ # lands back on whichever branch (main or dev) was just pushed
+ # - reads target/site/jacoco/jacoco.csv from the `test` job above
+ # ---------------------------------------------------------------------
+ coverage-badge:
+ name: Update Coverage Badge
+ needs: test
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v7
+
+ - name: Download JaCoCo report
+ uses: actions/download-artifact@v7
+ with:
+ name: jacoco-report
+ path: target/site/jacoco
+
+ - name: Generate coverage badge
+ uses: cicirello/jacoco-badge-generator@v2
+ with:
+ jacoco-csv-file: target/site/jacoco/jacoco.csv
+ badges-directory: .github/badges
+ generate-branches-badge: false
+ generate-summary: false
+
+ - name: Commit badge
+ uses: stefanzweifel/git-auto-commit-action@v5
+ with:
+ commit_message: "chore: update coverage badge [skip ci]"
+ file_pattern: .github/badges/*.svg
+
+ # ---------------------------------------------------------------------
+ # 3. Build + push a versioned image to Docker Hub
# - runs ONLY on: pushes to main
# - every push to main -> : :latest
# - pushes to dev, and all PRs -> this job does not run at all
From faf53b00b3b7aad54837c55a26b5dbbb8b126602 Mon Sep 17 00:00:00 2001
From: podlLev
Date: Mon, 3 Aug 2026 08:44:20 +0300
Subject: [PATCH 2/4] docs: add CI/coverage badges and architecture diagrams to
README
---
README.md | 34 ++++++++++++++++++++++++++++++++--
1 file changed, 32 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 17951d2..ccc85cb 100644
--- a/README.md
+++ b/README.md
@@ -10,13 +10,15 @@
-
+
+
Overview ·
Features ·
Tech Stack ·
+ Architecture ·
Getting Started ·
API Docs ·
Observability ·
@@ -74,6 +76,33 @@ WeatherViewer is a personal weather dashboard for tracking the places you care a
| CI/CD | GitHub Actions (build/test, coverage, Docker Hub image push) |
| External API | [OpenWeatherMap](https://openweathermap.org/api) (current weather, forecast, geocoding) |
+## Architecture
+
+Two request paths matter most for reliability: reads that hit the OpenWeatherMap API, and emails triggered by account actions. Both are built so a slow or failing dependency degrades gracefully instead of taking the app down with it.
+
+**Weather read path** — a cache-aside read guarded by retry and a circuit breaker:
+
+```mermaid
+flowchart LR
+ A[Controller] --> B["Cache @Cacheable"]
+ B --> C["Client retry + breaker"]
+ C --> D[("Weather API")]
+ C -. fallback .-> E["Fallback service unavailable"]
+```
+
+A cache hit never reaches `WeatherApiClient`. On a miss, every outbound call is wrapped with Resilience4j: transient failures are retried with backoff, and once OpenWeatherMap is failing consistently the breaker opens and short-circuits straight to the fallback instead of piling up slow requests — so one saved location failing to load doesn't take the rest of the dashboard down with it.
+
+**Async mail path** — a write that only sends mail after its transaction commits:
+
+```mermaid
+flowchart LR
+ F["Service writes token"] --> G["Event after commit"]
+ G --> H["Listener @Async"]
+ H --> I[("SMTP")]
+```
+
+Verification and password-reset emails are sent from a `@TransactionalEventListener(phase = AFTER_COMMIT)`, so an email can never reference a token whose transaction rolled back. The send itself runs `@Async` on a dedicated pool, so a slow SMTP server can't add latency to the request that triggered it. `MailService` retries transient SMTP failures on its own and never throws — a failure there is logged and goes no further.
+
## Prerequisites
- Java 17+
@@ -215,7 +244,8 @@ Tests run against an in-memory H2 database, so no external services are required
Every push to `main` and every pull request into `main`/`dev` runs through GitHub Actions:
1. **Build & Test** — compiles the project and runs the full test suite against H2, publishing a JUnit test report and a JaCoCo coverage report as workflow artifacts.
-2. **Docker build & push** — on pushes to `main`, builds the application image and pushes it to Docker Hub as `podllev/weather-viewer`.
+2. **Update coverage badge** — on pushes to `main`, regenerates the `.github/badges/jacoco.svg` badge from that JaCoCo report and commits it back to the repo.
+3. **Docker build & push** — on pushes to `main`, builds the application image and pushes it to Docker Hub as `podllev/weather-viewer`.
See `.github/workflows/ci.yml` for the full pipeline.
From e0b33b6c1b1ed60a4f134610945359346cb39615 Mon Sep 17 00:00:00 2001
From: podlLev
Date: Mon, 3 Aug 2026 08:48:59 +0300
Subject: [PATCH 3/4] docs: add system architecture overview diagram to README
---
README.md | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/README.md b/README.md
index ccc85cb..a1fe036 100644
--- a/README.md
+++ b/README.md
@@ -78,6 +78,33 @@ WeatherViewer is a personal weather dashboard for tracking the places you care a
## Architecture
+**System overview** — the app sits between the browser and four external dependencies. Every HTTP request passes through the rate limiter and the security filter chain before reaching a controller; live dashboard/forecast updates instead flow over a persistent WebSocket connection, pushed on a schedule rather than requested:
+
+```mermaid
+flowchart TB
+ Client[Browser client]
+ RL[Rate limiter]
+ Sec[Security filter chain]
+ Web[Controllers + REST]
+ WS[WebSocket / STOMP]
+ Svc[Services]
+ DB[(PostgreSQL)]
+ Cache[(Redis)]
+ Weather[(OpenWeatherMap API)]
+ Mail[(SMTP)]
+
+ Client --> RL --> Sec --> Web
+ Client -. live updates .-> WS
+ Web --> Svc
+ WS --> Svc
+ Svc --> DB
+ Svc --> Cache
+ Svc --> Weather
+ Svc --> Mail
+```
+
+Postgres holds users, locations, and tokens (schema managed by Liquibase). Redis backs both the rate limiter's fixed-window counters and the weather/forecast/geocoding cache. The two flows below zoom into the parts of this picture that need to tolerate a flaky dependency: weather reads and outbound mail.
+
Two request paths matter most for reliability: reads that hit the OpenWeatherMap API, and emails triggered by account actions. Both are built so a slow or failing dependency degrades gracefully instead of taking the app down with it.
**Weather read path** — a cache-aside read guarded by retry and a circuit breaker:
From a13ac365524dd5b9f87c8d62f7e52482f2b12b53 Mon Sep 17 00:00:00 2001
From: podlLev <92114713+podlLev@users.noreply.github.com>
Date: Mon, 3 Aug 2026 05:54:49 +0000
Subject: [PATCH 4/4] chore: update coverage badge [skip ci]
---
.github/badges/jacoco.svg | 1 +
1 file changed, 1 insertion(+)
create mode 100644 .github/badges/jacoco.svg
diff --git a/.github/badges/jacoco.svg b/.github/badges/jacoco.svg
new file mode 100644
index 0000000..3d59126
--- /dev/null
+++ b/.github/badges/jacoco.svg
@@ -0,0 +1 @@
+
\ No newline at end of file