Skip to content

MX-388: Add tenant branding endpoint for client applications - #184

Merged
IOhacker merged 1 commit into
openMF:developfrom
YousufFFFF:feat/tenant-branding-primary-color
Aug 3, 2026
Merged

MX-388: Add tenant branding endpoint for client applications#184
IOhacker merged 1 commit into
openMF:developfrom
YousufFFFF:feat/tenant-branding-primary-color

Conversation

@YousufFFFF

@YousufFFFF YousufFFFF commented Aug 2, 2026

Copy link
Copy Markdown
Member

What

Adds a tenant scoped primary colour so a single Mifos web app deployment can brand each tenant, exposed as GET/PUT /v1/branding.

Jira: #MX-388

Why here rather than in Apache Fineract

This started as a global configuration PR against apache/fineract (apache/fineract#6212, FINERACT-2731). Maintainers declined it: Fineract is a client agnostic core banking backend and should not carry settings that exist to serve one specific frontend. This implementation keeps the data in the plugin's own table, so nothing in Apache Fineract changes.

Changes

  • 063-add-tenant-branding.xml creates m_selfservice_tenant_branding
    (tenant_id unique, primary_color, timestamps), modelled on
    061-add-date-time-preferences.xml. Picked up by the existing
    SelfServiceLiquibaseConfig, so it runs per tenant with no new wiring.
  • TenantBranding, TenantBrandingRepository, TenantBrandingService,
    TenantBrandingApiResource.
  • Reading requires only an authenticated user; updating requires
    UPDATE_CONFIGURATION.
  • Unknown, unset or malformed values fall back to blue, so branding can never
    stop a client from rendering.

Why the endpoint is not under /v1/self

SelfServiceSecurityConfiguration uses .securityMatcher("/api/v1/self/**", "/v1/self/**") and authenticates self-service users only. Branding is read by staff facing clients, so the resource sits at /v1/branding, leaving it on Fineract's main security chain where ordinary platform credentials apply. Registration is unaffected: JerseyConfig registers every @Path bean regardless of path.

Testing

19 unit tests across the service, resource and entity. Full suite passes (360 tests, 0 failures) with the JaCoCo coverage gate met. Coverage of the new code: TenantBrandingApiResource 100%, TenantBranding 100%, TenantBrandingData 100%, TenantBrandingService 98%.

Also verified end to end against Fineract 1.16.0-SNAPSHOT with PostgreSQL 18.3, with both plugin jars mounted on /app/plugins:

  • changeset runs per tenant and creates the table
  • GET /v1/branding returns {"primaryColor":"blue"} with staff credentials
  • PUT {"primaryColor":"green"} persists and reads back
  • row confirmed in m_selfservice_tenant_branding, audit timestamps populated
  • unsupported colour returns HTTP 400 with the standard validation envelope
  • anonymous request returns 401

Deployment note

The self-service plugin cannot boot alone: it needs savings-plugin on the classpath for BccrExchangeRateService, otherwise Fineract fails to start with NoClassDefFoundError. Pre-existing and documented in pom.xml, but worth knowing if you reproduce the run above.

Open question

The table is named m_selfservice_tenant_branding to follow the repo's m_selfservice_* convention and avoid colliding with a future Fineract core table, though branding is not strictly a self-service concern. Happy to rename if you'd prefer something neutral.

Related

Consumed by openMF/web-app #3784 (#WEB-1084), which degrades to the default colour and reports the feature unavailable when this plugin is not installed.

Summary by CodeRabbit

  • New Features

    • Added tenant branding support through a new branding API.
    • Authenticated users can retrieve the current primary branding color.
    • Authorized administrators can update the primary color using supported values.
    • New tenants default to a blue primary color, with branding stored per tenant.
  • Tests

    • Added coverage for authentication, permissions, defaults, validation, tenant isolation, updates, and timestamp behavior.

@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@YousufFFFF, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 37 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: dbd3eefb-78ba-49c1-a7dd-531cc823d047

📥 Commits

Reviewing files that changed from the base of the PR and between 6101040 and f12162f.

📒 Files selected for processing (10)
  • src/main/java/org/apache/fineract/branding/api/TenantBrandingApiResource.java
  • src/main/java/org/apache/fineract/branding/data/TenantBrandingData.java
  • src/main/java/org/apache/fineract/branding/domain/TenantBranding.java
  • src/main/java/org/apache/fineract/branding/domain/TenantBrandingRepository.java
  • src/main/java/org/apache/fineract/branding/service/TenantBrandingService.java
  • src/main/resources/db/changelog/tenant/module/selfservice/module-changelog-master.xml
  • src/main/resources/db/changelog/tenant/module/selfservice/parts/063-add-tenant-branding.xml
  • src/test/java/org/apache/fineract/branding/api/TenantBrandingApiResourceTest.java
  • src/test/java/org/apache/fineract/branding/domain/TenantBrandingTest.java
  • src/test/java/org/apache/fineract/branding/service/TenantBrandingServiceTest.java
📝 Walkthrough

Walkthrough

Adds tenant-scoped branding storage with a default blue color, supported-color validation, authenticated retrieval, and permission-protected updates through /v1/branding.

Changes

Tenant branding

Layer / File(s) Summary
Branding persistence contract
src/main/java/org/apache/fineract/branding/data/TenantBrandingData.java, src/main/java/org/apache/fineract/branding/domain/*, src/main/resources/db/changelog/tenant/module/selfservice/*, src/test/java/org/apache/fineract/branding/domain/TenantBrandingTest.java
Adds the branding record, tenant-scoped entity, repository lookup, Liquibase table creation, and timestamp tests.
Tenant branding service
src/main/java/org/apache/fineract/branding/service/TenantBrandingService.java, src/test/java/org/apache/fineract/branding/service/TenantBrandingServiceTest.java
Retrieves stored branding or the blue default. Validates and normalizes supported colors. Creates or updates tenant branding records.
Branding API operations
src/main/java/org/apache/fineract/branding/api/TenantBrandingApiResource.java, src/test/java/org/apache/fineract/branding/api/TenantBrandingApiResourceTest.java
Adds authenticated GET and UPDATE_CONFIGURATION-protected PUT operations with JSON parsing and serialization.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AuthenticatedClient
  participant TenantBrandingApiResource
  participant TenantBrandingService
  participant TenantBrandingRepository
  AuthenticatedClient->>TenantBrandingApiResource: Request tenant branding
  TenantBrandingApiResource->>TenantBrandingService: Retrieve or update branding
  TenantBrandingService->>TenantBrandingRepository: Read or save tenant branding
  TenantBrandingRepository-->>TenantBrandingService: Branding data
  TenantBrandingService-->>TenantBrandingApiResource: TenantBrandingData
  TenantBrandingApiResource-->>AuthenticatedClient: JSON response
Loading

Suggested labels: ⏱️ 10-30 Min Review

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.12% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the addition of the tenant branding endpoint for client applications.
✨ 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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (3)
src/main/java/org/apache/fineract/branding/api/TenantBrandingApiResource.java (1)

61-106: 🔒 Security & Privacy | 🔵 Trivial | ⚖️ Poor tradeoff

Security enforcement uses PlatformSecurityContext rather than @PreAuthorize.

The coding guideline for **/*.java calls for method-level security via @PreAuthorize. This resource instead uses context.authenticatedUser().validateHasPermissionTo(...), matching the established Fineract JAX-RS security pattern rather than Spring MVC's @PreAuthorize. Functionally, permission checks are enforced correctly for both retrieveBranding() and updateBranding(). Treat this as a codebase-wide convention rather than a defect specific to this file.

As per coding guidelines, "All endpoints must be secured with appropriate permissions using @PreAuthorize annotations for method-level security."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/org/apache/fineract/branding/api/TenantBrandingApiResource.java`
around lines 61 - 106, Replace the manual PlatformSecurityContext permission
checks in retrieveBranding() and updateBranding() with appropriate method-level
`@PreAuthorize` annotations, preserving the existing authentication and
UPDATE_PERMISSION requirements for each endpoint. Remove only the redundant
in-method security enforcement while keeping branding retrieval and update
behavior unchanged.

Source: Coding guidelines

src/main/java/org/apache/fineract/branding/service/TenantBrandingService.java (1)

29-31: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Package placement deviates from the project's DDD structure guideline.

This service sits under org.apache.fineract.branding.service. The project structure guideline for src/main/java/** expects services and orchestrators under an application package. This is likely consistent with the rest of the codebase's module layout, so treat this as an optional, deferred consideration rather than something to change for this PR alone.

As per path instructions, "Follow DDD-inspired modular structure" with services under the application package.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/org/apache/fineract/branding/service/TenantBrandingService.java`
around lines 29 - 31, Defer package relocation for TenantBrandingService; no
code change is required in this PR. Retain the current
org.apache.fineract.branding.service placement and consider moving it under an
application package in a separate structural change.

Source: Path instructions

src/main/java/org/apache/fineract/branding/domain/TenantBranding.java (1)

27-60: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

JPA annotations in the domain package deviate from the layered-architecture guideline.

This entity is placed under org.apache.fineract.branding.domain and uses jakarta.persistence annotations directly. The domain-layer path instruction expects entities free of framework dependencies. This appears consistent with the existing pattern of JPA entities living under domain packages elsewhere in the codebase, so treat this as an optional, deferred cleanup rather than a blocking concern for this PR.

As per path instructions, the domain layer should have "No dependencies on Spring, frameworks, or infrastructure."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/org/apache/fineract/branding/domain/TenantBranding.java` around
lines 27 - 60, Defer this optional cleanup; no blocking change is required for
TenantBranding. If addressing it later, move persistence-specific mapping out of
the domain-layer TenantBranding class into the established infrastructure or
persistence entity pattern, while preserving its tenant uniqueness, default
color, identifiers, timestamps, and lifecycle behavior.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/java/org/apache/fineract/branding/domain/TenantBranding.java`:
- Around line 27-31: Replace Lombok `@Data` on the TenantBranding entity with
`@Getter` and `@Setter` to avoid generating lifecycle-unstable equals() and
hashCode() from the generated id. If equality is required, define equals() and
hashCode() explicitly using the natural key tenantId.

In
`@src/main/java/org/apache/fineract/branding/service/TenantBrandingService.java`:
- Around line 50-56: Update retrieveCurrentTenantBranding() so an existing
branding row’s primaryColor is normalized before constructing
TenantBrandingData: retain it only when non-null and included in
SUPPORTED_PRIMARY_COLORS, otherwise use DEFAULT_PRIMARY_COLOR. Preserve the same
default for tenants with no row and ensure the returned primaryColor is never
null.

In
`@src/main/resources/db/changelog/tenant/module/selfservice/parts/063-add-tenant-branding.xml`:
- Line 27: Update the primary_color column definition in the tenant branding
changelog to enforce NOT NULL and use a computed default fallback, preserving
"blue" as the database-level default for inserted rows.
- Around line 24-34: Remove the redundant uniqueness declaration in the tenant
branding table definition: retain either the tenant_id column’s unique="true"
constraint or the addUniqueConstraint block named uk_tenant_branding, but not
both.

---

Nitpick comments:
In
`@src/main/java/org/apache/fineract/branding/api/TenantBrandingApiResource.java`:
- Around line 61-106: Replace the manual PlatformSecurityContext permission
checks in retrieveBranding() and updateBranding() with appropriate method-level
`@PreAuthorize` annotations, preserving the existing authentication and
UPDATE_PERMISSION requirements for each endpoint. Remove only the redundant
in-method security enforcement while keeping branding retrieval and update
behavior unchanged.

In `@src/main/java/org/apache/fineract/branding/domain/TenantBranding.java`:
- Around line 27-60: Defer this optional cleanup; no blocking change is required
for TenantBranding. If addressing it later, move persistence-specific mapping
out of the domain-layer TenantBranding class into the established infrastructure
or persistence entity pattern, while preserving its tenant uniqueness, default
color, identifiers, timestamps, and lifecycle behavior.

In
`@src/main/java/org/apache/fineract/branding/service/TenantBrandingService.java`:
- Around line 29-31: Defer package relocation for TenantBrandingService; no code
change is required in this PR. Retain the current
org.apache.fineract.branding.service placement and consider moving it under an
application package in a separate structural change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 5612f5a7-7b02-4331-97cf-74a1ef6e51f8

📥 Commits

Reviewing files that changed from the base of the PR and between 8033aea and 6101040.

📒 Files selected for processing (10)
  • src/main/java/org/apache/fineract/branding/api/TenantBrandingApiResource.java
  • src/main/java/org/apache/fineract/branding/data/TenantBrandingData.java
  • src/main/java/org/apache/fineract/branding/domain/TenantBranding.java
  • src/main/java/org/apache/fineract/branding/domain/TenantBrandingRepository.java
  • src/main/java/org/apache/fineract/branding/service/TenantBrandingService.java
  • src/main/resources/db/changelog/tenant/module/selfservice/module-changelog-master.xml
  • src/main/resources/db/changelog/tenant/module/selfservice/parts/063-add-tenant-branding.xml
  • src/test/java/org/apache/fineract/branding/api/TenantBrandingApiResourceTest.java
  • src/test/java/org/apache/fineract/branding/domain/TenantBrandingTest.java
  • src/test/java/org/apache/fineract/branding/service/TenantBrandingServiceTest.java

@YousufFFFF
YousufFFFF force-pushed the feat/tenant-branding-primary-color branch from 6101040 to f12162f Compare August 2, 2026 23:34
@YousufFFFF

Copy link
Copy Markdown
Member Author

@IOhacker This PR is ready for your review!

@IOhacker
IOhacker merged commit 1e782a7 into openMF:develop Aug 3, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants