Aman-Mittal opened a new pull request, #211:
URL: https://github.com/apache/fineract-backoffice-ui/pull/211

   Closes #210.
   
   ## What this does
   
   Adds `src/app/core/adapters/`: one contract (interface + `InjectionToken`) 
per capability, one implementation naming the library it wraps.
   
   | Token | Replaces | Sites migrated |
   | --- | --- | --- |
   | `I18N` | `TranslateService`, `\| translate` | core services + error 
interceptor |
   | `OVERLAY` | Ionic `Toast`/`ModalController` | all 12 |
   | `STORAGE` | `localStorage` / `sessionStorage` | all 12 |
   | `DOWNLOAD` | object URLs + download anchors | all 18 |
   
   Three design points worth reviewing rather than skimming:
   
   - **Tokens carry a default binding** (`providedIn: 'root'` + factory). 
Ionic's controllers and `TranslateService` are root-provided, so before this a 
TestBed rendering a component that shows a toast needed no configuration. A 
token with no default would have made every such spec — and every future one — 
declare a provider it has no opinion about. Each implementation imports its 
contract with `import type`, so naming the class from the token does not close 
a runtime cycle.
   - **`IonicOverlayAdapter` resolves its controllers per call, not as 
fields.** The contract covers toasts and modals together, but `ToastController` 
is available everywhere while `ModalController` needs `provideIonicAngular()`. 
Injecting both eagerly broke 7 specs of components that only ever toast.
   - **The `| appTranslate` pipe is impure**, as ngx-translate's own is. A pure 
pipe caches on its inputs and the key does not change when the language does; 
reading the signal inside `transform` does not rescue that, because 
`ɵɵpipeBind` returns the cached value without calling `transform` again. It 
memoises on `lang + key + params`, so the per-change-detection cost is a string 
comparison.
   
   ### Bugs fixed on the way
   
   These were found by building the boundary, not sought:
   
   - **`logout()` left the endpoint override in place** — it cleared the 
session key and left `fineract_runtime_config`, the API URL every subsequent 
request and set of credentials goes to, across sign-outs. `STORAGE_KEYS` is now 
the reviewable inventory and `logout()` clears a scope rather than a remembered 
list.
   - **`AuthService.getStoredSession()` parsed unguarded.** One bad character 
in `fineract_session` threw during bootstrap and left the app blank with no 
route to the login page. Reads now fall back.
   - **Object URLs leaked on the failure path.** Now revoked in a `finally`.
   - **Server-supplied download filenames went through unmodified.** Now 
reduced to a sanitised basename in one place (`../../etc/passwd` → `passwd`), 
with spaces kept and the extension preserved when truncating.
   
   ### Deliberately out of scope
   
   - **`<ion-*>` components.** They are the UI layer per `AGENTS.md`, 250 
files, and migrate one component at a time. Only Ionic's imperative surface is 
behind the boundary. The structure allows adding them later without moving what 
exists.
   - **A facade over the generated OpenAPI client.** ADR 0001 considered 
exactly that and rejected it on maintenance cost. That decision stands — see 
below for the complement.
   
   ## Also in this PR
   
   **`npm run api:surface`** records the 413 operations across 127 generated 
services the app calls, and verifies each still exists. ADR 0001 stabilised 
generated *method names* against generator churn; this covers what it does not 
— change originating upstream. When Fineract removes an endpoint, this reports 
one message naming the operation and its callers, instead of compile errors 
scattered across every feature that used it.
   
   **`npm run ga:check`** encodes the machine-checkable part of what would have 
to be true for `security.md` to stop opening with "not release-ready". Eight 
gates; **three pass today**:
   
   ```
     FAIL  NGINX sets HTTP security headers
     FAIL  API endpoint override is validated against an allow-list
     FAIL  Authorization header is restricted to the API origin
     PASS  No sanitizer bypasses or raw HTML sinks
     FAIL  Login form offers no third-party API hosts
     FAIL  Adapter boundary migration backlog (advisory)
     PASS  Generated API surface matches the manifest
     PASS  No known vulnerabilities in production dependencies
   ```
   
   **Not wired into `ci.yml`**, deliberately: a check that is red on every PR 
from the day it lands is one people learn to scroll past. It is there to be 
run, and to be wired in once the blockers close. A gate that cannot be decided 
from the repository reports `unknown` rather than `pass` — recording a security 
control as satisfied because nothing contradicted it is worse than recording 
nothing.
   
   ## Enforcement
   
   `eslint.config.js` restricts the wrapped dependencies outside 
`src/app/core/adapters/**` via `no-restricted-imports` (ngx-translate; Ionic 
controllers by `importNames`, so `<ion-*>` is unaffected), 
`no-restricted-globals` (Web Storage) and `no-restricted-properties` 
(`URL.createObjectURL`).
   
   The **435 existing violations** — almost all `| translate` in components — 
go into `eslint-suppressions.json`, the shrink-only baseline this repo already 
uses for its migration ratchets. A new violation fails lint immediately; a 
fixed one must be pruned, so it cannot come back.
   
   ## Breaking changes
   
   - **`DialogService.open()`'s third parameter** is now a `DialogOptions` 
object rather than a bare `cssClass`. No production caller passed it; one spec 
did.
   - **The theme key moves** from `'theme'` to `'fineract_theme'`, per the 
`AGENTS.md` prefix rule. No migration shim: each existing user's theme falls 
back to their OS preference once, which `ThemeService` already handles as the 
no-value case.
   
   ## Verification
   
   - `tsc --noEmit` clean (app and spec configs)
   - `npm run lint` clean
   - `npm run format:check` clean
   - **759 unit tests passing** — baseline on `main` is 744; this adds 15
   - `npm run api:surface` passes
   
   ## Documentation
   
   - `DOCS/adr/0003-adapter-boundary.md` — the decision, the alternatives 
considered, the costs accepted
   - `DOCS/ADAPTERS.md` — day-to-day use, testing with the fakes, how to add a 
fifth adapter
   - `DOCS/CI_CHECKS.md` — the two new checks, the suppressions baseline, the 
GA gate table
   - `AGENTS.md` — boundary summary for contributors
   
   ## Review suggestion
   
   Two commits, and they read independently: `feat(core)` is the boundary and 
its migrations, `feat(scripts)` is the checks. The ADR is the best entry point 
for the *why*; `src/app/testing/adapters.ts` is the shortest demonstration of 
what the boundary buys — a spec that asserts on the request the code made 
rather than on the DOM a library built from it.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to