Abhishek Chaudhary created FINERACT-2671:
--------------------------------------------
Summary: Client family member and GSIM account lookups return HTTP
500 instead of 404 when the record does not exist
Key: FINERACT-2671
URL: https://issues.apache.org/jira/browse/FINERACT-2671
Project: Apache Fineract
Issue Type: Bug
Components: Client, Savings
Affects Versions: 1.14.0
Reporter: Abhishek Chaudhary
*Observed behavior*
Two read endpoints return a raw HTTP 500 due to an unhandled
{{EmptyResultDataAccessException}} when the requested resource does not exist:
{noformat}
GET /fineract-provider/api/v1/clients/{clientId}/familymembers/{familyMemberId}
GET /fineract-provider/api/v1/groups/{groupId}/gsimaccounts?parentGSIMId={id}
{noformat}
This can be reproduced on the current {{develop}} branch and is also present in
{{1.14.0}}.
*Root cause*
* {{ClientFamilyMembersReadPlatformServiceImpl.getClientFamilyMember()}} calls
{{jdbcTemplate.queryForObject(...)}} without handling the case where no rows
are returned.
* {{GSIMReadPlatformServiceImpl.findGSIMAccountByGSIMId()}} follows the same
pattern. It is invoked from {{GroupsApiResource.retrieveGsimAccounts()}}
whenever the {{parentGSIMId}} query parameter is supplied.
When {{JdbcTemplate.queryForObject()}} finds no matching row, Spring throws
{{EmptyResultDataAccessException}}. Since there is no dedicated exception
mapper for it under
{{org.apache.fineract.infrastructure.core.exceptionmapper}}, it falls through
to {{DefaultExceptionMapper}}, resulting in an HTTP 500 instead of a 404.
*Additional defect (Family Members)*
The family-member lookup does not validate the {{clientId}} path parameter. The
query filters only by {{fmb.id}}, meaning requests such as:
{noformat}
GET /clients/999/familymembers/5
{noformat}
can return a family member belonging to a different client.
The lookup should instead be scoped as:
{code:sql}
WHERE fmb.client_id = ? AND fmb.id = ?
{code}
If the family member does not belong to the client specified in the path, the
endpoint should return HTTP 404.
*Expected behavior*
Both endpoints should return HTTP 404 with the standard platform error payload
when the requested resource does not exist.
This should follow the existing Fineract pattern:
* Catch {{EmptyResultDataAccessException}}.
* Throw a domain-specific exception extending
{{AbstractPlatformResourceNotFoundException}}.
* Let {{PlatformResourceNotFoundExceptionMapper}} translate it into an HTTP 404
response.
*Steps to reproduce*
# Call:
{noformat}
GET /fineract-provider/api/v1/clients/1/familymembers/999999
{noformat}
Current: HTTP 500
Expected: HTTP 404
# Call:
{noformat}
GET /fineract-provider/api/v1/groups/1/gsimaccounts?parentGSIMId=999999
{noformat}
Current: HTTP 500
Expected: HTTP 404
# Create Client A with a family member M and Client B. Then call:
{noformat}
GET /clients/{B}/familymembers/{M}
{noformat}
Current: Returns the family member belonging to Client A.
Expected: HTTP 404.
*Proposed fix*
* Introduce {{FamilyMemberNotFoundException}} and
{{GSIMAccountNotFoundException}}, both extending
{{AbstractPlatformResourceNotFoundException}}.
* Catch {{EmptyResultDataAccessException}} in the corresponding read-platform
service methods and rethrow the appropriate domain-specific exception.
* Scope the family-member lookup by both {{client_id}} and {{familyMemberId}}.
* Add integration tests covering:
** Non-existent family member → HTTP 404.
** Non-existent GSIM account → HTTP 404.
** Family member belonging to a different client → HTTP 404.
I found this issue while reviewing the read-platform services and would like to
work on it. Please assign it to me.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)