Summary
The processing_steps JSON parse/serialize logic is duplicated between models_classic.go and models_wired.go in the Kibana Streams package. Both models contain near-identical blocks (~15 lines each) for unmarshalling processing steps from the API and marshalling them back, with only minor error-handling differences.
Duplication Details
Pattern: Duplicated processing_steps Parse/Serialize Logic
- Severity: Medium
- Occurrences: 2 (classic and wired stream models)
- Locations:
internal/kibana/streams/models_classic.go (lines 51–65 for read, 127–138 for write)
internal/kibana/streams/models_wired.go (lines 53–69 for read, 150–161 for write)
Read-path duplicate (appears in both classicConfigModel.populateFromAPI and wiredConfigModel.populateFromAPI):
// Processing steps — each step is a JSON-encoded streamlang object.
// An empty array from the API is treated as null (no steps configured).
if len(ingest.Processing.Steps) > 0 {
var rawSteps []json.RawMessage
if err := json.Unmarshal(ingest.Processing.Steps, &rawSteps); err != nil {
// classic: silently sets null
// wired: returns a diagnostic error
}
if len(rawSteps) > 0 {
elems := make([]attr.Value, len(rawSteps))
for i, raw := range rawSteps {
elems[i] = jsontypes.NewNormalizedValue(string(raw))
}
m.ProcessingSteps = types.ListValueMust(jsontypes.NormalizedType{}, elems)
} else {
m.ProcessingSteps = types.ListNull(jsontypes.NormalizedType{})
}
} else {
m.ProcessingSteps = types.ListNull(jsontypes.NormalizedType{})
}
Write-path duplicate (appears in both models' toAPIIngest functions):
rawSteps := make([]json.RawMessage, 0)
for _, elem := range m.ProcessingSteps.Elements() {
if norm, ok := elem.(jsontypes.Normalized); ok {
rawSteps = append(rawSteps, json.RawMessage(norm.ValueString()))
}
}
stepsJSON, err := json.Marshal(rawSteps)
// ... error handling ...
ingest.Processing.Steps = stepsJSON
Notable divergence: The classic model silently sets null on unmarshal error; the wired model returns a diag.Diagnostics error. This inconsistency is itself a risk introduced by the duplication.
Impact Analysis
- Maintainability: Any change to how processing steps are serialized (e.g., supporting step metadata) must be applied in two places.
- Bug Risk: The existing inconsistency in error handling between classic (silent null) and wired (diagnostic error) was likely an accidental divergence from copy-paste. Future edits may introduce more such inconsistencies.
- Code Bloat: ~30 lines of duplicated logic that could be a single shared helper.
Refactoring Recommendations
-
Extract shared helpers
populateProcessingStepsFromAPI(ingest *kibanaoapi.StreamIngest) (types.List, diag.Diagnostics) — handles the read path for both models
processingStepsToAPI(steps types.List) (json.RawMessage, diag.Diagnostics) — handles the write path
- Place in
internal/kibana/streams/models.go (already the shared models file for the package)
- Estimated effort: low (~2 hours); straightforward extraction with uniform error handling
-
Standardize error handling
- During extraction, adopt the wired model's pattern (return
diag.Diagnostics on error) for both models — silent errors are harder to debug
- Update
classicConfigModel.populateFromAPI signature to return diag.Diagnostics to match wired
Implementation Checklist
Analysis Metadata
- Analyzed Files:
internal/kibana/streams/models_classic.go, internal/kibana/streams/models_wired.go
- Detection Method: Semantic code analysis — structural comparison of
populateFromAPI and toAPIIngest methods
- Commit: 0317686 (kibana/streams test coverage update that touched these files)
- Analysis Date: 2026-06-22
Generated by Duplicate Code Detector · sonnet46 2.6M · ◷
Summary
The
processing_stepsJSON parse/serialize logic is duplicated betweenmodels_classic.goandmodels_wired.goin the Kibana Streams package. Both models contain near-identical blocks (~15 lines each) for unmarshalling processing steps from the API and marshalling them back, with only minor error-handling differences.Duplication Details
Pattern: Duplicated
processing_stepsParse/Serialize Logicinternal/kibana/streams/models_classic.go(lines 51–65 for read, 127–138 for write)internal/kibana/streams/models_wired.go(lines 53–69 for read, 150–161 for write)Read-path duplicate (appears in both
classicConfigModel.populateFromAPIandwiredConfigModel.populateFromAPI):Write-path duplicate (appears in both models'
toAPIIngestfunctions):Notable divergence: The classic model silently sets null on unmarshal error; the wired model returns a
diag.Diagnosticserror. This inconsistency is itself a risk introduced by the duplication.Impact Analysis
Refactoring Recommendations
Extract shared helpers
populateProcessingStepsFromAPI(ingest *kibanaoapi.StreamIngest) (types.List, diag.Diagnostics)— handles the read path for both modelsprocessingStepsToAPI(steps types.List) (json.RawMessage, diag.Diagnostics)— handles the write pathinternal/kibana/streams/models.go(already the shared models file for the package)Standardize error handling
diag.Diagnosticson error) for both models — silent errors are harder to debugclassicConfigModel.populateFromAPIsignature to returndiag.Diagnosticsto match wiredImplementation Checklist
populateProcessingStepsFromAPIhelper tomodels.goprocessingStepsToAPIhelper tomodels.gomodels_classic.goandmodels_wired.goto call shared helpersmake buildto confirm no compilation errorsAnalysis Metadata
internal/kibana/streams/models_classic.go,internal/kibana/streams/models_wired.gopopulateFromAPIandtoAPIIngestmethods