Skip to content

feat(auth): Phase C Wave 2 — Login end-to-end Store5 migration - #2687

Merged
therajanmaurya merged 1 commit into
openMF:store5Migrationfrom
therajanmaurya:feat/store5-w2-auth-e2e
May 21, 2026
Merged

feat(auth): Phase C Wave 2 — Login end-to-end Store5 migration#2687
therajanmaurya merged 1 commit into
openMF:store5Migrationfrom
therajanmaurya:feat/store5-w2-auth-e2e

Conversation

@therajanmaurya

Copy link
Copy Markdown
Member

Summary

Phase C Wave 2 — second end-to-end feature wave. Follows the Wave 1 template (merged via #2686) and applies it to the login flow.

The login flow is harder than activate because the VM has to:

  1. Run pre-submit validation (username + password length) that doesn't go through SubmitHandler
  2. On Submitted, persist the user via prefManager.updateUser (side effect) BEFORE navigating
  3. Handle the auth-rejected case (response.authenticated != true) by routing to ShowError
  4. Surface navigation as a one-shot event (passcode intent), not a state

MVI's LoginState + LoginEvent + LoginAction triple handles this cleanly: state holds validation errors (replayable), events emit NavigateToPasscode / ShowError(StringResource) (one-shot), actions are Submit(username, password) / DismissError.

Changes (21 files; +280 / -310 net)

core/network/auth/

  • NEW core/network/auth/api/AuthApi.kt — suspend Ktorfit interface for POST authentication returning PostAuthenticationResponse. Future OTP / refresh / logout endpoints land here.
  • core/network/di/NetworkModule.kt: + single { get<Ktorfit>().createAuthApi() }
  • Method-level @Deprecated on DataManagerAuth.login + ClientService.authenticate — both stay compileable for un-migrated callers.

core/data/auth/

  • git mv core/data/repository/LoginRepository.kt → core/data/auth/LoginRepository.kt
  • git mv core/data/repositoryImp/LoginRepositoryImp.kt → core/data/auth/impl/LoginRepositoryImpl.kt
  • LoginRepositoryImpl constructor: (authApi: AuthApi) — no DataManagerAuth.
  • RepositoryModule.kt: import update + singleOf(::LoginRepositoryImpl) bind LoginRepository::class. Repository binding stays in core/data per the Koin DI ownership boundary.

core/domain

  • DELETED useCases/LoginUseCase.kt (pure delegator)
  • UseCaseModule.kt: removed factoryOf(::LoginUseCase) + import.
  • Kept: UsernameValidationUseCase, PasswordValidationUseCase (validators).

feature/auth/{ui, di, navigation}/ restructure

Old flat layout (feature/auth/login/{LoginViewModel, LoginScreen, LoginUiState}.kt) eliminated. New:

feature/auth/.../
├── ui/
│   ├── LoginState.kt     — validation-error data class (pre-submit gate)
│   ├── LoginAction.kt    — sealed Submit / DismissError
│   ├── LoginEvent.kt     — sealed NavigateToPasscode / ShowError(StringResource)
│   ├── LoginViewModel.kt — extends BaseViewModel<LoginState, LoginEvent, LoginAction>
│   │                       with SubmitHandler<PostAuthenticationResponse>
│   └── LoginScreen.kt    — submit overlay + snackbar + passcode nav; 3 @Preview functions
├── di/
│   └── AuthModule.kt     — viewModelOf(::LoginViewModel) ONLY (no Repository binding)
└── navigation/
    ├── LoginRoute.kt     — @Serializable object (split from AuthNavigation)
    └── AuthNavigation.kt — NavGraphBuilder.authNavGraph + nav extensions

VM logic flow:

  • handleAction(Submit(u, p)) → validate inputs → if valid, submit.submit { repo.login(u, p) }
  • submit.state.collect { ... }:
    • Submitted → check response.authenticated == true; if yes, persist user + emit NavigateToPasscode; if no, emit ShowError
    • Failed → emit ShowError(feature_auth_error_login_failed)
  • handleAction(DismissError)submit.reset() after snackbar consumes the error event

Screen consumes stateFlow (validation errors) + submitState (submitting overlay) + eventFlow (one-shot navigation/error).

No hardcoded user-facing strings

New composeResources/values/strings.xml entries:

  • feature_auth_update_server_configuration
  • feature_auth_cd_arrow_forward
  • feature_auth_cd_error_icon
  • feature_auth_cd_password_visibility

All button labels and content descriptions consume stringResource(...). Log strings (Logger.d("Login", ...)) remain as literals — allowed per plan (log tags are not user-facing).

feature/auth/build.gradle.kts

  • + implementation(projects.core.ui) (for BaseViewModel)
  • core.data + core.domain kept (validators stay in domain)
  • No direct coreBase.* deps

Grep invariants

Check Result
grep -rn "DataState|asDataStateFlow" feature/auth/ core/data/.../auth/ core/domain/useCases/LoginUseCase* zero matches
grep -rn "template\.core\.base" feature/auth/ 1 match (KptTheme — Theme, accessed via api chain)
grep -rn "DataManagerAuth|ClientService\.authenticate" feature/auth/ core/data/.../auth/ zero matches (repository uses AuthApi)
User-facing string literals in feature/auth/src/commonMain/kotlin/ zero (only Logger.d tags remain)

Test plan

  • ./gradlew :feature:auth:build compiles cleanly
  • ./gradlew :cmp-android:assembleDebug succeeds; Koin DI resolves
  • Login happy path: enter creds → progress overlay → passcode screen
  • Login failure (wrong creds): progress overlay → error snackbar → dismiss
  • Validation-only failure: short username → inline error on field, no network call
  • Auth-rejected case (server returns authenticated: false): error snackbar
  • Compose Multiplatform Previews render in IDE: Idle / Submitting / ValidationError variants

Phase status

  • ✅ Phase A (infra sync)
  • ✅ Phase B (core/store seam)
  • ✅ Phase C Wave 1 — activate (merged via #2686)
  • 🟢 Phase C Wave 2 — auth (this PR)
  • 🔵 Waves 3–7 (search, search-record, groups, center, note, document, offline, path-tracking, collectionSheet, report, savings, recurringDeposit, checker-inbox-task, loan, client, data-table, settings/passcode)
  • 🔵 Phase D — batch-delete deprecated DataManagers + Services + BaseApiManager + FlowConverterFactory + DataState + legacy Mifos* composables

Second end-to-end feature wave. Adds the auth resource API + relocates
the Login chain following the Wave 1 template.

core/network/auth/api/AuthApi.kt (NEW)
  Suspend Ktorfit interface for Fineract authentication endpoints.
  Future auth-wave additions (OTP, refresh, logout) extend this same file.

core/network/di/NetworkModule.kt
  + single { get<Ktorfit>().createAuthApi() }

Method-level @deprecated on legacy login paths:
  - DataManagerAuth.login
  - ClientService.authenticate
Both surfaces stay compileable for un-migrated callers; Phase D deletes
them once nothing references them.

core/data/auth/ (relocate + rewrite)
  git mv core/data/repository/LoginRepository.kt
      -> core/data/auth/LoginRepository.kt
  git mv core/data/repositoryImp/LoginRepositoryImp.kt
      -> core/data/auth/impl/LoginRepositoryImpl.kt (renamed)
  Impl now consumes AuthApi directly; no DataManagerAuth dependency.
  RepositoryModule import updated; binding remains in core/data.

core/domain (delete pure-delegator use case)
  - useCases/LoginUseCase.kt           (deleted)
  - di/UseCaseModule.kt: factoryOf(::LoginUseCase) removed
  Surviving use cases: UsernameValidationUseCase + PasswordValidationUseCase
  (validators — kept per the core/domain rule).

feature/auth/{ui, di, navigation}/ (restructure with MVI triple)
  ui/
    LoginState.kt    — validation-error state (pre-submit gate)
    LoginAction.kt   — sealed Submit / DismissError
    LoginEvent.kt    — sealed NavigateToPasscode / ShowError(StringResource)
    LoginViewModel.kt — extends BaseViewModel<LoginState, LoginEvent, LoginAction>
                        with SubmitHandler<PostAuthenticationResponse>.
                        Submitted -> persist user via prefManager, emit
                        NavigateToPasscode event. authenticated != true also
                        routes to ShowError. Failed -> ShowError.
    LoginScreen.kt    — uses MifosProgressIndicatorOverlay during submit;
                        Snackbar for error events; passcode navigation on
                        success. Includes 3 @Preview functions (Idle,
                        Submitting, ValidationError) using
                        org.jetbrains.compose.ui.tooling.preview.* (KMP-aware).
  di/AuthModule.kt    — import updated to ui/ package; viewModelOf-only
                        (Repository binding lives in core/data
                        RepositoryModule per the Koin DI ownership boundary).
  navigation/
    LoginRoute.kt     — @serializable object (split from AuthNavigation)
    AuthNavigation.kt — NavGraphBuilder.authNavGraph + nav extensions

No hardcoded user-facing strings:
  composeResources additions:
    feature_auth_update_server_configuration
    feature_auth_cd_arrow_forward
    feature_auth_cd_error_icon
    feature_auth_cd_password_visibility
  All button labels, content descriptions consume stringResource(...).

feature/auth/build.gradle.kts
  + implementation(projects.core.ui)
  Other deps unchanged (core.data + core.domain for validators).

Grep invariants pass: zero DataState, zero MifosProgressIndicator legacy
imports, zero user-facing string literals, file layout matches plan.
@therajanmaurya
therajanmaurya requested a review from a team May 21, 2026 14:24
@coderabbitai

coderabbitai Bot commented May 21, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review (1)
  • development

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 714ac328-41e8-4835-a8ae-25ac11fce584

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sonarqubecloud

Copy link
Copy Markdown

@therajanmaurya
therajanmaurya merged commit 920c681 into openMF:store5Migration May 21, 2026
3 of 4 checks passed
therajanmaurya added a commit to mobilebytesensei/mifos-x-claude-cycle-workspaces that referenced this pull request May 21, 2026
field-officer submodule HEAD: 920c6810 (Wave 2 — auth login E2E merged
via openMF/mifos-x-field-officer-app#2687).

Co-authored-by: Rajan Maurya <therajanmaurya@Rajans-MacBook-Pro.local>
therajanmaurya added a commit to therajanmaurya/mifos-x-claude-cycle-workspaces that referenced this pull request Jun 19, 2026
…bilebytesensei#37)

field-officer submodule HEAD: 920c6810 (Wave 2 — auth login E2E merged
via openMF/mifos-x-field-officer-app#2687).

Co-authored-by: Rajan Maurya <therajanmaurya@Rajans-MacBook-Pro.local>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant