Analytics: compare two finished runs
New page /analytics/compare and API GET /admin/analytics/compare (+ /lists/{group}):
the administrator picks an old (A) and a new (B) run; the report shows the new
addresses (only in B), the ones that left (only in A) and the common ones whose
membership in the seven indicators (pass, partial, fail, egress https any/all,
ingress ssh any/all) differs, with a "what changed" summary per address; the
dynamics of each indicator (delta = new - left + entered - exited) and a verdict
transition matrix. Every number opens a list with CSV. Cancelled addresses are not
part of a run. The list dialog moved to a shared analytics-dialog.js and template;
/analytics got a "compare with another run" button.
Docs, plan and summary in docs/changes/.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
2f038f8362
commit
068c10ea1c
28 files changed
+2057
-138
No files matched your search
@@ -456,7 +456,11 @@ func (c *client) GetAnalyticsList(ctx context.Context, runID int64, kind, class
|
||||
// GetAnalyticsListCSV returns the CSV file of one address table, with the
|
||||
// file name control-api proposed.
|
||||
func (c *client) GetAnalyticsListCSV(ctx context.Context, runID int64, kind, class string) ([]byte, string, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, c.baseURL+analyticsListPath(runID, kind, class, true), nil)
|
||||
return c.getCSV(ctx, analyticsListPath(runID, kind, class, true))
|
||||
}
|
||||
|
||||
func (c *client) getCSV(ctx context.Context, path string) ([]byte, string, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, c.baseURL+path, nil)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("build request: %w", err)
|
||||
}
|
||||
@@ -480,6 +484,49 @@ func (c *client) GetAnalyticsListCSV(ctx context.Context, runID int64, kind, cla
|
||||
return body, resp.Header.Get("Content-Disposition"), nil
|
||||
}
|
||||
|
||||
// compareFilter narrows one list of the comparison of two runs.
|
||||
type compareFilter struct{ Indicator, From, To string }
|
||||
|
||||
func analyticsCompareQuery(base, target int64) url.Values {
|
||||
return url.Values{"base": {strconv.FormatInt(base, 10)}, "target": {strconv.FormatInt(target, 10)}}
|
||||
}
|
||||
|
||||
func analyticsCompareListPath(base, target int64, group string, f compareFilter, csv bool) string {
|
||||
v := analyticsCompareQuery(base, target)
|
||||
if f.Indicator != "" {
|
||||
v.Set("indicator", f.Indicator)
|
||||
}
|
||||
if f.From != "" || f.To != "" {
|
||||
v.Set("from", f.From)
|
||||
v.Set("to", f.To)
|
||||
}
|
||||
if csv {
|
||||
v.Set("format", "csv")
|
||||
}
|
||||
return "/api/v1/admin/analytics/compare/lists/" + url.PathEscape(group) + "?" + v.Encode()
|
||||
}
|
||||
|
||||
// GetAnalyticsCompare returns the comparison of two finished runs (base is the
|
||||
// older one) as the raw JSON control-api computed.
|
||||
func (c *client) GetAnalyticsCompare(ctx context.Context, base, target int64) (json.RawMessage, error) {
|
||||
var out json.RawMessage
|
||||
err := c.do(ctx, http.MethodGet, "/api/v1/admin/analytics/compare?"+analyticsCompareQuery(base, target).Encode(), nil, &out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// GetAnalyticsCompareList returns one address table (JSON) of the comparison.
|
||||
func (c *client) GetAnalyticsCompareList(ctx context.Context, base, target int64, group string, f compareFilter) (json.RawMessage, error) {
|
||||
var out json.RawMessage
|
||||
err := c.do(ctx, http.MethodGet, analyticsCompareListPath(base, target, group, f, false), nil, &out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// GetAnalyticsCompareListCSV returns the CSV file of one comparison table,
|
||||
// with the file name control-api proposed.
|
||||
func (c *client) GetAnalyticsCompareListCSV(ctx context.Context, base, target int64, group string, f compareFilter) ([]byte, string, error) {
|
||||
return c.getCSV(ctx, analyticsCompareListPath(base, target, group, f, true))
|
||||
}
|
||||
|
||||
// subnetEntry is one line of the subnet list (GET/PUT /admin/config/subnets).
|
||||
type subnetEntry struct {
|
||||
CIDR string `json:"cidr"`
|
||||
|
||||
Reference in new issue
Block a user