Fix manual de-assert and group small vendors in chart

Allow COMPONENT_REMOVED patches to bypass server-only restriction on
/installation/* paths so manual de-assert works for user source type.

Group vendors with <1% share into "Others" in the Vendor Distribution
chart; cap total slices at 15.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Mikhail Chusavitin
2026-03-18 23:01:16 +03:00
parent 40b5c06a52
commit 9cb7626a01
2 changed files with 31 additions and 1 deletions

View File

@@ -2679,6 +2679,36 @@ func (h uiHandlers) handleComponentModels(w http.ResponseWriter, r *http.Request
sort.Slice(vendorChartList, func(i, j int) bool {
return vendorChartList[i].Value > vendorChartList[j].Value
})
// Group small vendors: keep top 14, collapse the rest (and any <1%) into "Others"
const maxVendorSlices = 15
if len(vendorChartList) > 0 {
grandTotal := 0
for _, e := range vendorChartList {
grandTotal += e.Value
}
cutoff := maxVendorSlices - 1 // reserve last slot for "Others"
othersValue := 0
keep := vendorChartList
if len(vendorChartList) > cutoff {
for _, e := range vendorChartList[cutoff:] {
othersValue += e.Value
}
keep = vendorChartList[:cutoff]
}
// Also absorb entries <1% from the kept list into Others
filtered := keep[:0]
for _, e := range keep {
if grandTotal > 0 && float64(e.Value)/float64(grandTotal) < 0.01 {
othersValue += e.Value
} else {
filtered = append(filtered, e)
}
}
if othersValue > 0 {
filtered = append(filtered, vendorChartEntry{Label: "Others", Value: othersValue})
}
vendorChartList = filtered
}
vendorChartJSON, _ := json.Marshal(vendorChartList)
page := parsePage(r.URL.Query(), "page")

View File

@@ -1062,7 +1062,7 @@ func validatePatchOps(entityType, changeType, sourceType string, ops []PatchOp)
if entityType == "asset" && sourceType == "user" && (strings.HasPrefix(op.Path, "/inventory/") || strings.HasPrefix(op.Path, "/metadata/")) {
return fmt.Errorf("%w: path %q is server-only", ErrInvalidPatch, op.Path)
}
if entityType == "component" && sourceType == "user" && strings.HasPrefix(op.Path, "/installation/") && op.Path != "/installation/slot_name" {
if entityType == "component" && sourceType == "user" && changeType != "COMPONENT_REMOVED" && strings.HasPrefix(op.Path, "/installation/") && op.Path != "/installation/slot_name" {
return fmt.Errorf("%w: path %q is server-only", ErrInvalidPatch, op.Path)
}
if entityType == "component" && sourceType == "user" && op.Path == "/metadata/component_type" {