Farooq Ayoade created FINERACT-2776:
---------------------------------------
Summary: GET /datatables response schema omits entitySubType, so
generated clients cannot read it
Key: FINERACT-2776
URL: https://issues.apache.org/jira/browse/FINERACT-2776
Project: Apache Fineract
Issue Type: Bug
Components: System
Reporter: Farooq Ayoade
h3. Summary
{{POST /datatables}} accepts an {{entitySubType}} and the platform stores it in
{{x_registered_table.entity_subtype}}. Both GET routes return it at runtime:
{{DatatableData}} carries the
field, and {{DatatableReadServiceImpl}} selects {{entity_subtype}} and passes
it into
{{DatatableData.create(...)}} on the list path and the single-table path alike.
The one place it does not appear is the class both routes name as their
{{@ApiResponse}} schema.
{{DatatablesApiResourceSwagger.PostDataTablesRequest}} declares
{{entitySubType}};
{{GetDataTablesResponse}} does not. The published OpenAPI document therefore
describes a datatable as
having only {{applicationTableName}}, {{registeredTableName}} and
{{columnHeaderData}}.
The asymmetry is the bug: the API lets a caller *set* a property and then
documents a response in which
that property does not exist.
h3. Impact
A hand-written client never notices, because the JSON has the field. A
generated client cannot read it at
all -- the member is not on the type, so the natural workaround is to cast away
the generated type at
exactly the point where it is most worth having.
That matters because of what the field is for. {{entitySubType}} scopes a
datatable to a client legal form
(Person / Entity), so a UI rendering custom fields on a customer has to read it
to know whether a given
table belongs on an individual or on a corporate. Without it, every
{{m_client}} datatable shows on every
customer regardless of what it was registered against.
h3. Steps to reproduce
# {{POST /datatables}} with {{apptableName: "m_client"}} and {{entitySubType:
"Person"}}.
# {{GET /datatables?apptable=m_client}} -- the JSON response *does* contain
{{"entitySubType": "Person"}}.
# Generate a client from the published OpenAPI document (any generator).
# The type for the GET response has no {{entitySubType}} member.
h3. Actual
The runtime JSON and the published schema disagree. A generated type cannot
express a field the endpoint
always returns.
h3. Expected
{{GetDataTablesResponse}} declares {{entitySubType}}, matching both the runtime
response and the request
DTO that already declares it.
h3. Evidence
All references below are against {{develop}}.
|| Where || What it shows ||
| {{DatatableData}} | declares {{entitySubType}}, takes it in {{create(...)}}
and assigns it; nothing excludes it from serialisation |
| {{DatatableReadServiceImpl}} lines 72, 93, 96 | list path selects
{{entity_subtype}} and passes it to {{DatatableData.create(...)}} |
| {{DatatableReadServiceImpl}} lines 106, 119, 123 | single-table path does the
same |
| {{DatatablesApiResourceSwagger}} line 81 | {{PostDataTablesRequest}} declares
{{entitySubType}} |
| {{DatatablesApiResourceSwagger.GetDataTablesResponse}} | does not declare it |
| {{DatatablesApiResource}} lines 101, 233 | {{getDatatables}} and
{{getDatatable}} both name {{GetDataTablesResponse}} as their {{@ApiResponse}}
schema, so one omission affects both endpoints |
The clearest evidence is in Fineract's own test suite.
{{integration-tests/.../datatable/DatatableIntegrationTest}} imports the
*generated* client model
{{org.apache.fineract.client.models.GetDataTablesResponse}} and sets
{{entitySubType}} on eleven separate
datatable creations -- and never once asserts it comes back, because the
generated model has no getter for
it. {{validateCreateAndEditDatatable}} creates a datatable with
{{CLIENT_PERSON_SUBTYPE_NAME}} ("Person"),
reads it back with {{getDataTableDetails(...)}}, and can assert on the column
headers but not on the sub
type it just set.
h3. Proposed fix
One field on {{GetDataTablesResponse}}:
{code:java}
@Schema(example = "Person", description = "The entity sub type the datatable is
registered against, "
+ "when the application table supports one (for example Person or
Entity on m_client). "
+ "Null when the registration is not scoped to a sub type.")
public String entitySubType;
{code}
No runtime behaviour changes; the response body is unchanged byte for byte.
h3. Test
{{fineract-client}} is generated from this spec ({{inputSpec =
file(swaggerFile)}},
{{dependsOn(':fineract-provider:resolve')}}) and {{integration-tests}} depends
on it, so a client-side
assertion is a real test of this defect rather than a restatement of behaviour
that already worked. One
line inside the existing {{validateCreateAndEditDatatable}}, after the
{{getDataTableDetails(...)}} call
that is already there:
{code:java}
assertEquals(CLIENT_PERSON_SUBTYPE_NAME, dataTable.getEntitySubType());
{code}
Verified by generating the Java client both ways: from {{develop}}'s spec the
model has three getters
({{getApplicationTableName}}, {{getColumnHeaderData}},
{{getRegisteredTableName}}); regenerated with the
fix it has four, the new one being {{getEntitySubType()}}. So the assertion
does not compile before the
change and passes after it.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)