Release: develop -> main#3753
Merged
Merged
Conversation
…GISTERED status (#3733) * feat(user/realunit): expose user capabilities + structured ALREADY_REGISTERED status Wave 3 of the realunit-app API-as-Decision-Authority plan (`DFXswiss/realunit-app:docs/api-authority-plan.md`). Two changes that let the realunit-app stop interpreting backend state into UI affordances: UserV2Dto.capabilities ---------------------- New `UserCapabilitiesDto` field on the `/v2/user` response. Surfaces per-action flags the realunit-app cubits were re-deriving locally from KYC step status (`settings_user_data_page.dart:239`, `settings_edit_name_cubit.dart:22`, `settings_contact_page.dart:54-67`): - `canEditName` / `canEditAddress`: false once PersonalData is in any review or completed state (data is locked to keep client and KYC attestation aligned). - `canEditMail` / `canEditPhone`: false only on KYC-terminated accounts. - `supportAvailable`: requires a verified mail. `UserDtoMapper.computeCapabilities` mirrors the rules the cubits encode today. A separate app-side PR consumes the field and drops the local interpretation. RealUnitRegistrationStatus.ALREADY_REGISTERED --------------------------------------------- New enum value. `completeRegistration` and `completeRegistrationForWalletAddress` return it instead of throwing `BadRequestException` when the wallet is already registered for the user. The realunit-app currently catches the 400 and treats it as a success — surfacing the success as a structured status removes the "papering over an error" pattern and lets the app distinguish the merge / retry path cleanly from other 400s. Backwards compatibility ----------------------- Both changes are additive. Old clients ignore the new `capabilities` field and continue to derive editability locally. The `ALREADY_REGISTERED` status is a new enum value — existing clients that switch-fall-through will treat it the same as `FORWARDING_FAILED` (no behaviour change worse than a generic failure). Tests ----- - `user-dto.mapper.spec.ts` adds a `mapUser: capabilities` block covering all five flags across happy path, PersonalData-locked, KYC-terminated, and no-mail fixtures. - Existing tests cover the two `return RealUnitRegistrationStatus.ALREADY_REGISTERED` call sites by absence of any new throw (`completeRegistration` / `completeRegistrationForWalletAddress` did not have happy-path tests for the already-registered branch; PR #3731 addresses the idempotency semantics in the same area and adds dedicated coverage). Local verification ------------------ - `npm run type-check` — clean - `npm run lint` — clean - `npm test` — **943 / 943 passing** * style: prettier * fix(realunit): return 201 for ALREADY_REGISTERED like COMPLETED ALREADY_REGISTERED fell through to 202 ACCEPTED. The idempotent retry path returned 201 before (via COMPLETED); keep the HTTP status stable and let the structured status field carry the distinction. --------- Co-authored-by: TaprootFreak <142087526+TaprootFreak@users.noreply.github.com>
* feat(realunit): add DEV-only request/response tracing
Adds an Express middleware that logs full request/response detail for
calls originating from the realunit-app, scoped via the X-Client header
or a /v{n}/realunit/* path. Active only in the DEV environment.
Secrets (auth header, JWT tokens, signatures, credentials) are masked
and long string values truncated; KYC/PII bodies are kept intact for
the internal test phase. Output goes to stdout for container logs.
* feat(realunit): enable API tracing on PRD in addition to DEV
The internal testers run the mainnet app build, which talks to the PRD
API — DEV-only gating would capture nothing. Tracing now runs on both
DEV and PRD. On PRD this writes real customer KYC/PII to the container
logs by design (secrets stay masked).
* feat(support): add support issue templates (DEV-4571) * fix(support): allow clearing english template content * fix(support): rename template content column to contentDe * fix: migrate support issue template to PostgreSQL - Entity: length 'MAX' → type 'text' - Migration: regenerated with PostgreSQL syntax --------- Co-authored-by: David May <david.leo.may@gmail.com>
…nts can stop re-implementing routing locally (#3732) * feat(kyc): expose decision fields so clients can stop re-implementing routing locally The realunit-app's `KycCubit` (`lib/screens/kyc/cubits/kyc/kyc_cubit.dart`) was rebuilding the API's own routing rule client-side: a hardcoded `_requiredStepNames` set, an `actionableStatuses` set, and a `_minLevelForActions = 30` threshold. That setup misroutes any high-level user whose Ident step has been re-issued by `checkDfxApproval` (2026-05-21 incident — user_data 338759, kycLevel 53 + Outdated Ident + InProgress Ident@seq 1 → app stuck on KycIdentPage). The app shouldn't be deciding that. Surface the three signals it needs directly in the DTOs so it can render verbatim: - `KycStepDto.isRequired: boolean` — populated from `requiredKycSteps(userData)` at mapping time. Clients drop their own duplicate sets and iterate `kycSteps.filter(s => s.isRequired)` instead. - `UserKycDto.canTrade: boolean` (`/v2/user`) — authoritative trading-permission flag, computed from kycLevel + required-step completion + non-blocking Ident/FinancialData state. A level-50 user with an Outdated Ident now correctly reports `canTrade: false`. - `KycLevelDto.processStatus: KycProcessStatus` (`InProgress | PendingReview | Completed | Failed`) — high-level KYC process state for clients that don't need step granularity. All additions are optional / nullable-shaped on the wire: existing clients keep working, new clients consume the new fields and delete their local logic in the matching app-side PR. Mapper-side change in one spot: - `KycInfoMapper.toDto` computes `requiredStepNames` once and threads it through `KycStepMapper.toStep(..., isRequired)` + the new `computeProcessStatus` helper. `KycStepMapper.toStep` gains a third (optional) `isRequired` parameter — defaulted to `false` so the ~hundred existing call sites compile unchanged. - `UserDtoMapper.computeCanTrade` mirrors the cubit's routing semantics exactly (level ≥ LEVEL_30 + all required steps Completed + no Outdated/InProgress/OnHold Ident or FinancialData step). Comment links back to the app-side `docs/api-authority-plan.md` Wave 2. Tests: - `user-dto.mapper.spec.ts` adds a fixture-based regression block that reproduces the 2026-05-21 user_data 338759 shape (level 50 + completed Ident + outdated Ident + in-progress Ident@seq 1 → `canTrade: false`) and the surrounding cases (clean level 50, level 20, outdated FinancialData, terminated KYC). - `new ConfigService()` in `beforeAll` to wire the `Config` singleton for sub-LEVEL_50 `tradingLimit` resolution. Local verification: - `npm run type-check` — clean - `npm run lint` — clean - `npm test` — **943 / 943 passing** (5 new tests; 938 baseline kept green) Wave 2.1 of the realunit-app API-as-Decision-Authority plan (`DFXswiss/realunit-app:docs/api-authority-plan.md`). * style: prettier * fix(kyc): address review feedback on decision fields - Make requiredStepNames a required param in KycStepMapper.toStep so no caller can silently omit it and default every step to isRequired=false - Add FINISHED, PARTIALLY_APPROVED, PAUSED to computeProcessStatus pending set — they were falling through to Completed - Simplify computeCanTrade to match actual trade-endpoint gates (level >= 30, not terminated, not blocked) instead of duplicating stricter step-status logic the endpoints don't enforce - Update @ApiProperty doc and tests accordingly * fix: remove stale comment on computeCanTrade * fix(kyc): move DATA_REQUESTED to pending set in computeProcessStatus Aligns with isInReview on the KYC step entity. A step in DataRequested is waiting, not user-actionable. * fix(kyc): remove canTrade field Not used by any client. The app routes on processStatus + isRequired; the trade endpoints themselves are the authority for whether a request succeeds. * test(kyc): cover computeProcessStatus and isRequired Add a kyc-info.mapper spec exercising the processStatus verdict (terminated, not-started, in-review, actionable, DataRequested and the multi-sequence ident shape) and the isRequired step flag. The canTrade removal had dropped the only tests this PR carried. --------- Co-authored-by: David May <david.leo.may@gmail.com> Co-authored-by: TaprootFreak <142087526+TaprootFreak@users.noreply.github.com>
TaprootFreak
approved these changes
May 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Automatic Release PR
This PR was automatically created after changes were pushed to develop.
Commits: 1 new commit(s)
Checklist