FINERACT-2832: Migrate groups and centers to CommandDispatcher - #6487
Open
avivijay19 wants to merge 1 commit into
Open
avivijay19 wants to merge 1 commit into
avivijay19 wants to merge 1 commit into
Conversation
avivijay19
force-pushed
the
FINERACT-2832
branch
from
September 21, 2026 12:31
e4d3f6d to
33b2666
Compare
Typed request DTOs, commands and handlers for every group and center write operation, with per-command authorization in SecurityConfig and Jakarta validation. Reads keep the existing serializer; the OpenAPI schemas are generated from the real classes, which closes the reported gaps.
avivijay19
force-pushed
the
FINERACT-2832
branch
from
September 21, 2026 12:40
33b2666 to
0bbdf8c
Compare
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.
Description
Moves the write side of
GroupsApiResourceandCentersApiResourceto the typedCommandDispatcher(same pattern as FINERACT-2288 Staff and the Meetings/Notes migrations): one request DTO,CommandandCommandHandlerper action, typed services (GroupingTypesWriteService,GroupRolesWriteService), programmatic Jakarta validation, and per-command authorization inSecurityConfigthrough a query-parameter-aware request matcher for the?command=endpoints (existingACTIVATE_GROUP,ASSOCIATECLIENTS_GROUP, … authorities). The bulk-import handlers dispatch typed requests; the client-transfer and collection-sheet services each gain one typed adapter method (transferClientsBetweenGroups(request),saveGroupCollectionSheet(request)), other callers untouched. The legacy path — the 22@CommandTypehandlers (includingTransferClientsBetweenGroupsCommandHandler) plus the unreferencedUnassignStaffFromCenterCommandHandler, the Gson validators,GroupingTypesWritePlatformService,GroupRolesWritePlatformService, theCommandWrapperBuildergroup/center builders and the hand-writtenGroupsApiResourceSwagger/CentersApiResourceSwaggerstubs — is removed.Request and write-response schemas are now generated from the real classes, and the read schemas reference the real DTOs (
GroupGeneralData,CenterData,GroupsPageResponse,CentersPageResponse,AccountSummaryCollectionData) instead of the stubs — this closes the gaps reported in FINERACT-2832 (accountNoon the list items;staffId,centerId,clientMembers,datatableson the create request) by construction. The read endpoints themselves are unchanged: they keep the existing JSON serializer,?fields=support and date format; theirREAD_GROUP/READ_CENTERcheck moves from the resource intoSecurityConfig, scoped to the routes these two resources serve.Wire contract: same URLs, methods, query parameters and
?command=values, same field names, dates, validation rules and error-body shape. Verified with the existing integration suites (GroupTest, CenterIntegrationTest, LoanReschedulingWithinCenterTest, GroupSavingsIntegrationTest, GroupLoanIntegrationTest, MakercheckerTest, AccountNumberPreferencesTest) and by exercising every write endpoint and?command=value over HTTP, including the roles, client-transfer, collection-sheet and bulk-import paths, which have no integration suite.Deliberate differences, consistent with the other CommandDispatcher migrations:
*_GROUP/*_CENTERtasks, and the legacy webhook events forGROUP/CENTERno longer fire;m_commandinstead ofm_portfolio_command_source;{"errors":[{parameterName,…}]}shape but carry the constraint'sorg.apache.fineract.portfolio.group.<parameter>.<rule>globalisation codes instead ofvalidation.msg.group.<parameter>.…; the unsupported-?command=error lists the real command values (the legacy list was incomplete and contained the typoupdateassignRole);PUT /groups/{id}andPUT /centers/{id}with an explicit"staffId": null/"centerId": nullno longer unassign — use?command=unassignStaff/disassociateGroupsas before;DELETE /groups/{id}and?command=unassignStaffpreviously returnedofficeIdandgroupIdswapped; they now carry the correct values;POST /groupsacceptscenterIdwithoutofficeId(the office is taken from the centre), matching what the service always did;?command=transferClientsrejects an emptyclientslist with 400 (legacy silently did nothing);?command=generateCollectionSheetnow requiresREAD_GROUP/READ_CENTER(legacy checked authentication only);PostGroupsRequest→GroupCreateRequest,GetGroupsGroupIdResponse→GroupGeneralData, …), so the api-compatibility check reports renames.Authorization hardening for these two resources: the matchers accept a
?command=value with surrounding whitespace and a trailing slash on the path, exactly as the resource and Jersey do, so a request cannot reach the handler without passing the matcher. Note for follow-up: the matchers of the already-migrated Staff/Meetings/Notes resources do not tolerate a trailing slash while Jersey does; that is outside this ticket.Also worth knowing: a
?command=request with no body now gets a 400 (the typed request must be present; send{}forunassignRole/unassignStaff), where the legacy resource tolerated an empty body; and a missingroleIdonupdateRole/unassignRolestill 500s exactly as on develop.Observed while testing, not changed here:
?command=generateCollectionSheeton both/groups/{id}and/centers/{id}fails on PostgreSQL withoperator does not exist: boolean = integer(CollectionSheetReadPlatformServiceImpl), on develop as well.Checklist