feat(DJ-92): account summary endpoint and Angular dashboard - #31
feat(DJ-92): account summary endpoint and Angular dashboard#31devin-ai-integration[bot] wants to merge 6 commits into
Conversation
…bagent-b' into devin/1786976886-account-summary-dashboard
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
| this.dashboardService.getAccountSummary().subscribe({ | ||
| next: summary => this.accountSummary.set(summary), | ||
| complete: () => this.isLoading.set(false), | ||
| error: () => this.isLoading.set(false), | ||
| }); |
There was a problem hiding this comment.
🟡 Dashboard page shows nothing at all when the data request fails
When the dashboard data request fails the page silently stops showing the loading text (error: () => this.isLoading.set(false) at src/main/webapp/app/dashboard/dashboard.ts:29) without ever showing an error message, so users see an empty page with no explanation.
Impact: A backend outage or an expired session makes the dashboard look permanently blank instead of telling the user something went wrong.
Template only renders content when a summary object exists
src/main/webapp/app/dashboard/dashboard.html:4-6 renders the loading paragraph while isLoading() is true and otherwise only renders content when accountSummary() is non-null. On the error path the signal stays null and isLoading becomes false, so both branches are skipped and only the <h2> heading remains. Other pages in the app surface failures (e.g. via alert service / error handling); the dashboard has no such fallback.
Prompt for agents
The dashboard component (src/main/webapp/app/dashboard/dashboard.ts) subscribes to DashboardService.getAccountSummary() and, on error, only clears the loading flag. Because src/main/webapp/app/dashboard/dashboard.html renders content only when accountSummary() is non-null, an HTTP failure leaves the page blank with no feedback. Consider tracking an error state signal (or surfacing the failure through the app's existing alert/error handling) and adding an error branch to the template with a translated message in src/main/webapp/i18n/en/dashboard.json.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Confirmed: on the error path accountSummary() stays null and isLoading() becomes false, so dashboard.html renders neither branch and only the heading remains. Worth fixing with an error signal plus a translated message branch; holding the change until the in-flight end-to-end test run finishes so it doesn't shift the revision under test.
| public AccountSummaryDTO getSummary() { | ||
| List<Object[]> accountRows = bankAccountRepository.findSummaryData(); | ||
| Map<Long, Long> operationCounts = operationRepository | ||
| .countByBankAccount() | ||
| .stream() | ||
| .collect(Collectors.toMap(row -> (Long) row[0], row -> (Long) row[1])); | ||
| List<AccountBalanceDTO> accounts = accountRows | ||
| .stream() | ||
| .map(row -> | ||
| new AccountBalanceDTO((Long) row[0], (String) row[1], (BigDecimal) row[2], operationCounts.getOrDefault((Long) row[0], 0L)) | ||
| ) | ||
| .toList(); | ||
|
|
||
| AccountSummaryDTO summary = new AccountSummaryDTO(); | ||
| summary.setTotalBalance( | ||
| accountRows | ||
| .stream() | ||
| .map(row -> (BigDecimal) row[2]) | ||
| .reduce(BigDecimal.ZERO, BigDecimal::add) | ||
| ); | ||
| summary.setAccountCount(accounts.size()); | ||
| summary.setOperationCount(operationRepository.countAllOperations()); | ||
| summary.setAccounts(accounts); | ||
| summary.setRecentOperations( | ||
| operationRepository.findRecentOperations(PageRequest.of(0, 10)).stream().map(RecentOperationDTO::new).toList() | ||
| ); |
There was a problem hiding this comment.
🟨 Dashboard endpoint exposes every user's account balances and operations to any authenticated user
The new endpoint aggregates all bank accounts and all operations globally (bankAccountRepository.findSummaryData() / operationRepository.findRecentOperations(...) at src/main/java/io/github/jhipster/sample/service/AccountSummaryService.java:30-54) with no filtering by the current user, while the UI presents it as the signed-in user's personal dashboard (src/main/webapp/app/home/home.html:20). Any authenticated (non-admin) user therefore sees other users' account names, balances, operation descriptions and amounts.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Accurate description of the behaviour, but it matches the locked contract for this ticket, which specifies aggregation "across all accounts" with no per-user scoping — and it mirrors the existing app: BankAccountResource.getAllBankAccounts() also returns every account to any authenticated caller (no user filter anywhere in that resource), so the dashboard is not widening exposure beyond what the entity screens already show.
BankAccount does have a user relation, so scoping is feasible (filter findSummaryData() / findRecentOperations() by SecurityUtils.getCurrentUserLogin()), but that would be a deliberate change to the agreed contract and to the app's existing visibility model. Flagging it for a decision rather than changing it unilaterally.
✅ E2E tested locally (Devin)Ran the app locally (Spring Boot dev/H2 on :8080, Angular via Browsersync on :9000), created data through the existing Bank Account / Operation entity screens, and cross-checked Figures match data created via entity screens (click to collapse)Starting from the faker seed (10 accounts / 10 operations / $164,539.81) I created
Recent operations: exactly 10 rows even though 13 operations exist, sorted date DESC across accounts, negative amount rendered Raw API matched the page field-for-field: Empty database → zeros + empty statesRestarted the backend with Auth gatingLogged out, the navbar Dashboard item and the home-page button are gone, and direct navigation to i18n / consoleNo raw Minor note: seeded operations with no bank account render an empty "Bank account" cell (allowed by the contract, but a dash/placeholder might read better). Not covered: non-admin ( |
Summary
DJ-92: adds an aggregated account overview — a new authenticated backend endpoint plus an Angular
/dashboardpage that consumes it. Backend and frontend were built in parallel against a contract locked up front (below); the frontend's temporary mock was removed during integration, so the shipped page calls the real endpoint.Locked contract (single source of truth)
GET /api/account-summary, authenticated only,200 application/json:{ "totalBalance": 12345.67, "accountCount": 3, "operationCount": 42, "accounts": [ { "id": 1, "name": "Current account", "balance": 4200.00, "operationCount": 17 } ], "recentOperations": [ { "id": 9, "date": "2026-08-01T10:15:30Z", "description": "Groceries", "amount": -54.20, "bankAccountName": "Current account" } ] }Rules:
recentOperations= the 10 most recentOperationrows bydateDESC across all accounts; monetary values areBigDecimalscale 2;dateis ISO-8601 UTC (Instant); an empty database returnstotalBalance0,accountCount0,operationCount0 and empty arrays (nevernull).Backend
AccountSummaryResource→AccountSummaryService→ two set-based JPQL queries, so per-account counts are one grouped query rather than one query per account:Scale-2 rounding and never-null collections are enforced in the DTOs themselves (setters normalize), so the contract holds regardless of caller. No security annotation on the resource:
SecurityConfigurationalready gates/api/**with.authenticated(), which is what produces the 401 asserted by the IT.Frontend
app/dashboard/standalone component (signals +inject()), lazy-loaded route guarded byUserRouteAccessServicewithauthorities: [], plus navbar and home entry points andi18n/en/dashboard.json. Currency via Angularcurrencypipe, dates via the sharedFormatMediumDatetimePipe.Verification
Gates green:
./mvnw verify,./npmw test,./npmw run lint,./mvnw -Pprod clean verify.AccountSummaryResourceITcovers aggregation figures + the 10-row DESC limit, the empty-DB zeros/empty-arrays case, and the anonymous 401. Verified manually against seeded H2 data (2 accounts, 12 operations): raw JSON totals and the rendered page agree ($3,580.00, 2 accounts, 12 operations, per-account 5/7), and an anonymous visit to/dashboardredirects to login.Link to Devin session: https://partner-workshops.devinenterprise.com/sessions/631b9c1e27864c639552a1b92cf279ee