fix(ui): show operation state on applications list page (#29063)
Project: argoproj/argo-cd | Language: Go | Concepts: api-design, testing-strategy, refactoring | Difficulty: mid
Link: https://github.com/argoproj/argo-cd/pull/29064
The problem
On the applications list page in the Argo CD UI, the operation state ("OPERATION" column/filter) was always shown as "Unknown" for every application, even though the backend and CLI displayed the correct operation state. This was a regression: the UI was not receiving the necessary field (status.operationState.operation.sync) in the projected API payload, so it could not determine if an application had a sync operation.
Why it's non-trivial
A naive fix might simply re-add the missing field to the list of projected fields, but this could result in sending large, unnecessary data (like manifests and resources) to the UI, which only needs to know if the field exists. Additionally, the backend previously returned the wrong field for this projection, so simply restoring the old code would not be correct.
The approach
1. Fix the backend projection to return only a presence marker for operation.sync:
// In pkg/apiclient/application/forwarder_overwrite.go
"status.operationState.operation.sync": func(app *v1alpha1.Application) any {
if app.Status.OperationState != nil && app.Status.OperationState.Operation.Sync != nil {
// The UI only needs the field's presence to classify the operation type,
// so project an empty object as a pure presence marker and drop the
// potentially large fields such as resources, manifests and sources to
// keep the list/watch payload small.
return &v1alpha1.SyncOperation{}
}
return nil
},
This code ensures that the projected field is present only as an empty object if a sync operation exists, avoiding unnecessary payload bloat.
2. Add the field back to the list of fields requested by the UI:
// In ui/src/app/applications/components/applications-list/applications-list.tsx
const APP_FIELDS = [
'status.sync.revision',
'status.health',
'status.operationState.phase',
+ 'status.operationState.operation.sync',
'status.operationState.startedAt',
'status.operationState.finishedAt'
];
This change tells the frontend to request the operation.sync field so it can display the correct operation state.
3. Add unit tests to ensure correct projection behavior:
func TestProcessApplicationListField_OperationStateOperationSync(t *testing.T) {
// ...setup omitted...
syncMap, ok, err := unstructured.NestedMap(item, "status", "operationState", "operation", "sync")
require.NoError(t, err)
require.True(t, ok)
// The field is a pure presence marker: it must be an empty object, with none
// of the operation's payload (revision, resources, manifests, sources).
require.Empty(t, syncMap)
}
This test checks that the projected field is present as an empty object and does not leak large or unnecessary data.
From the review thread
No notable review discussion on this PR.
Takeaways
- When projecting fields for API responses, only include the minimum data needed by consumers—use presence markers if the consumer only cares about existence, not content.
- Unit tests should verify not only the presence of fields but also the absence of unnecessary nested data, to prevent unintentional payload bloat.
- Fixing regressions may require both frontend and backend changes to ensure that API contracts are correctly implemented and maintained.