Files
o11y/querycore.go
zeekayandHanzo Dev cae99adb44 The relay reads the operation zip served
An op learns its address from zip.OpOf and zip.AddressOf, so the claim table,
the five declaring verbs and the address bookkeeping are gone. A host that
declares one of these addresses itself now gets a Build conflict from zip.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-17 15:02:30 -07:00

633 lines
27 KiB
Go

package o11y
// The QUERY-CORE face — the composite query engine and the reads that feed it:
// the v5 querier (query_range, its dry-run preview, variable substitution), the
// legacy metrics range read and the builder-format echo, attribute autocomplete
// for filter building, the field catalog, and the saved explorer views — as
// TYPED ops.
//
// These were sixteen of the routes that reached traffic only through the
// delegation wildcard, and a route behind a wildcard is in no document: no SDK
// method, no CLI command, no agent tool, no reference page. A customer could not
// run a query, complete a filter or open a saved view from anything but a
// hand-written HTTP call. Typing them is what puts the sixteen operations in the
// document and therefore in every projection built from it.
//
// THE WIRE DOES NOT MOVE, and the way it does not move is that these ops do not
// re-implement the reads and writes. Each hands the call to the SAME runtime
// handler the wildcard delegates to (see relayAt in logs.go), so identity
// resolution, the org gate, the role check — viewer on the reads and the query
// engine, editor on the saved-view writes, the exact ViewAccess/EditAccess gates
// the runtime has always run — the validation, the audit record and the success
// envelope are all still the runtime's, executed in the order they always were.
// What is new here is the TYPE — the In the caller may send and the Out they get
// back — and the prose that goes with it.
//
// THREE routes of this slice are deliberately NOT typed, and stay on the
// delegation wildcard byte-identical to what they always were:
//
// - GET /v1/o11y/query_progress upgrades the connection to a websocket when
// the caller asks (GetQueryProgressUpdates) and long-polls when it does not,
// and the relay buffers a whole answer through an httptest recorder that
// cannot hijack a connection — a typed progress op would never complete the
// handshake.
// - POST /v1/o11y/export_raw_data streams a chunked CSV/JSONL download with a
// Content-Disposition attachment and an X-Response-Complete trailer; it has
// no JSON answer to name, and buffering an unbounded export to re-marshal it
// would defeat the stream. It stays a binary download.
//
// Those three are the slice's escape hatches; this comment is their record.
//
// Registered ahead of the wildcard, and specific-beats-wildcard is what the
// router does regardless of registration order, so these sixteen paths dispatch
// here and every other path under the prefix — the three streams included —
// still reaches the runtime untouched.
import (
"context"
"encoding/json"
v3 "github.com/hanzoai/o11y/pkg/query-service/model/v3"
qbtypes "github.com/hanzoai/o11y/pkg/types/querybuildertypes/querybuildertypesv5"
"github.com/hanzoai/o11y/pkg/types/telemetrytypes"
"github.com/hanzoai/o11y/pkg/valuer"
"github.com/zap-proto/zip"
)
// mountQueryCore registers the sixteen typed query-core ops on the native
// router, all on the shared /v1/o11y group (o11yRoot, spelled once in logs.go).
// The saved-view collection routes register before the parameterised ones so a
// view id can never shadow the collection, exactly as the mux tree ordered them.
func mountQueryCore(app *zip.App) {
g := app.Group(o11yRoot)
// the v5 composite query engine
g.Post("/query_range", querierQueryRange)
g.Post("/query_range/preview", querierQueryRangePreview)
g.Post("/substitute_vars", querierReplaceVariables)
// the legacy metrics range read and the builder-format echo
g.Get("/query_range", metricsQueryRange)
g.Post("/query_range/format", queryRangeFormat)
// attribute autocomplete for filter building
g.Get("/autocomplete/aggregate_attributes", autocompleteAggregate)
g.Get("/autocomplete/attribute_keys", autocompleteKeys)
g.Get("/autocomplete/attribute_values", autocompleteValues)
g.Post("/auto_complete/attribute_values", autocompleteValuesPost)
// the field catalog
g.Get("/fields/keys", fieldKeys)
g.Get("/fields/values", fieldValues)
// saved explorer views
g.Get("/explorer/views", savedViewList)
g.Post("/explorer/views", savedViewCreate)
g.Get("/explorer/views/:viewId", savedViewGet)
g.Put("/explorer/views/:viewId", savedViewUpdate)
g.Delete("/explorer/views/:viewId", savedViewDelete)
}
// ── the composite query engine ─────────────────────────────────────────────────
// querierQueryRange executes a composite query over a time range: builder
// queries over traces, logs and metrics, formulas, trace operators, PromQL and
// Datastore SQL, answering time series, scalars or raw records as the request
// type asks.
//
// Callers need the viewer role; the runtime's own gate enforces it.
func querierQueryRange(ctx context.Context, in *qbtypes.QueryRangeRequest) (*O11yQueryRangeOut, error) {
out := new(O11yQueryRangeOut)
return out, relay(ctx, nil, in, out)
}
// querierQueryRangePreview validates a composite query and renders the Datastore
// statements it would run WITHOUT executing it — a dry run for agentic and
// tooling use. verbose=false trades the rendered SQL and EXPLAIN for a
// lightweight per-query verdict.
//
// Callers need the viewer role; the runtime's own gate enforces it.
func querierQueryRangePreview(ctx context.Context, in *O11yQueryRangePreviewIn) (*O11yQueryRangePreviewOut, error) {
out := new(O11yQueryRangePreviewOut)
// verbose rides the query string, the request rides the body — the same
// split the runtime reads.
return out, relay(ctx, query("verbose", in.Verbose), &in.QueryRangeRequest, out)
}
// querierReplaceVariables substitutes a query's variables and returns the
// resolved request, without running it — what a dashboard does before it
// queries.
//
// Callers need the viewer role; the runtime's own gate enforces it.
func querierReplaceVariables(ctx context.Context, in *qbtypes.QueryRangeRequest) (*O11ySubstituteVarsOut, error) {
out := new(O11ySubstituteVarsOut)
return out, relay(ctx, nil, in, out)
}
// metricsQueryRange runs a Prometheus-style range query over metrics — the
// legacy read that predates the v5 querier — and returns the matrix, vector or
// scalar the query resolved to.
//
// Callers need the viewer role; the runtime's own gate enforces it.
func metricsQueryRange(ctx context.Context, in *O11yMetricsQueryRangeIn) (*O11yMetricsQueryRangeOut, error) {
out := new(O11yMetricsQueryRangeOut)
return out, relay(ctx, query(
"start", in.Start,
"end", in.End,
"step", in.Step,
"query", in.Query,
"stats", in.Stats,
"timeout", in.Timeout,
), nil, out)
}
// queryRangeFormat parses a builder query and echoes it back normalized to the
// v3 shape — the endpoint the UI uses to canonicalize a query without running
// it.
//
// Callers need the viewer role; the runtime's own gate enforces it.
func queryRangeFormat(ctx context.Context, in *v3.QueryRangeParamsV3) (*O11yQueryRangeFormatOut, error) {
out := new(O11yQueryRangeFormatOut)
return out, relay(ctx, nil, in, out)
}
// ── attribute autocomplete ─────────────────────────────────────────────────────
// autocompleteAggregate lists the attributes usable as an aggregate target for
// the given telemetry and operator — what a filter builder offers after the
// aggregation is chosen.
//
// Callers need the viewer role; the runtime's own gate enforces it.
func autocompleteAggregate(ctx context.Context, in *O11yAggregateAttributesIn) (*O11yAggregateAttributesOut, error) {
out := new(O11yAggregateAttributesOut)
return out, relay(ctx, query(
"aggregateOperator", in.AggregateOperator,
"dataSource", in.DataSource,
"searchText", in.SearchText,
"limit", in.Limit,
), nil, out)
}
// autocompleteKeys lists the attribute keys available for filtering the given
// telemetry, each with its data type and whether it is a materialized column.
//
// Callers need the viewer role; the runtime's own gate enforces it.
func autocompleteKeys(ctx context.Context, in *O11yAttributeKeysIn) (*O11yAttributeKeysOut, error) {
out := new(O11yAttributeKeysOut)
return out, relay(ctx, query(
"dataSource", in.DataSource,
"aggregateOperator", in.AggregateOperator,
"aggregateAttribute", in.AggregateAttribute,
"searchText", in.SearchText,
"tagType", in.TagType,
"limit", in.Limit,
), nil, out)
}
// autocompleteValues lists the values one attribute key has taken — string,
// number and bool values in their own lists — for completing a filter.
//
// Callers need the viewer role; the runtime's own gate enforces it.
func autocompleteValues(ctx context.Context, in *O11yAttributeValuesIn) (*O11yAttributeValuesOut, error) {
out := new(O11yAttributeValuesOut)
return out, relay(ctx, query(
"dataSource", in.DataSource,
"aggregateOperator", in.AggregateOperator,
"aggregateAttribute", in.AggregateAttribute,
"attributeKey", in.AttributeKey,
"filterAttributeKeyDataType", in.FilterAttributeKeyDataType,
"searchText", in.SearchText,
"tagType", in.TagType,
"limit", in.Limit,
), nil, out)
}
// autocompleteValuesPost reads the attribute-value request from the body rather
// than off the query string — the spelling the newer builder uses to send its
// filters alongside the request.
//
// Callers need the viewer role; the runtime's own gate enforces it.
func autocompleteValuesPost(ctx context.Context, in *v3.FilterAttributeValueRequest) (*O11yAttributeValuesOut, error) {
out := new(O11yAttributeValuesOut)
return out, relay(ctx, nil, in, out)
}
// ── the field catalog ──────────────────────────────────────────────────────────
// fieldKeys returns the telemetry field keys matching the selector — the
// signal's fields grouped by name, and whether the catalog is complete.
//
// Callers need the viewer role; the runtime's own gate enforces it.
func fieldKeys(ctx context.Context, in *O11yFieldKeysIn) (*O11yFieldKeysOut, error) {
out := new(O11yFieldKeysOut)
return out, relay(ctx, query(
"signal", in.Signal,
"source", in.Source,
"limit", in.Limit,
"startUnixMilli", nanos(in.StartUnixMilli),
"endUnixMilli", nanos(in.EndUnixMilli),
"fieldContext", in.FieldContext,
"fieldDataType", in.FieldDataType,
"metricName", in.MetricName,
"metricNamespace", in.MetricNamespace,
"searchText", in.SearchText,
), nil, out)
}
// fieldValues returns the values one telemetry field has taken — string, bool,
// number and related values — and whether the value list is complete.
//
// Callers need the viewer role; the runtime's own gate enforces it.
func fieldValues(ctx context.Context, in *O11yFieldValuesIn) (*O11yFieldValuesOut, error) {
out := new(O11yFieldValuesOut)
return out, relay(ctx, query(
"signal", in.Signal,
"source", in.Source,
"limit", in.Limit,
"startUnixMilli", nanos(in.StartUnixMilli),
"endUnixMilli", nanos(in.EndUnixMilli),
"fieldContext", in.FieldContext,
"fieldDataType", in.FieldDataType,
"metricName", in.MetricName,
"metricNamespace", in.MetricNamespace,
"searchText", in.SearchText,
"name", in.Name,
"existingQuery", in.ExistingQuery,
), nil, out)
}
// ── saved explorer views ───────────────────────────────────────────────────────
// savedViewList lists the caller's org's saved explorer views, optionally
// narrowed to one source page, name or category.
//
// Callers need the viewer role; the runtime's own gate enforces it.
func savedViewList(ctx context.Context, in *O11ySavedViewListIn) (*O11ySavedViewListOut, error) {
out := new(O11ySavedViewListOut)
return out, relay(ctx, query(
"sourcePage", in.SourcePage,
"name", in.Name,
"category", in.Category,
), nil, out)
}
// savedViewCreate saves a new explorer view for the caller's org and returns its
// id.
//
// Callers need the editor role; the runtime's own gate enforces it.
func savedViewCreate(ctx context.Context, in *v3.SavedView) (*O11ySavedViewCreateOut, error) {
out := new(O11ySavedViewCreateOut)
return out, relay(ctx, nil, in, out)
}
// savedViewGet returns one saved explorer view by id.
//
// Callers need the viewer role; the runtime's own gate enforces it.
func savedViewGet(ctx context.Context, in *O11ySavedViewRef) (*O11ySavedViewOut, error) {
out := new(O11ySavedViewOut)
return out, relay(ctx, nil, nil, out)
}
// savedViewUpdate replaces one saved explorer view by id with the given view and
// echoes it back.
//
// Callers need the editor role; the runtime's own gate enforces it.
func savedViewUpdate(ctx context.Context, in *O11ySavedViewUpdateIn) (*O11ySavedViewOut, error) {
out := new(O11ySavedViewOut)
// The view id addresses the target from the URL; the body carries the new
// view, relayed without the id so the URL stays the one authority.
return out, relay(ctx, nil, &in.SavedView, out)
}
// savedViewDelete deletes one saved explorer view by id.
//
// Callers need the editor role; the runtime's own gate enforces it.
func savedViewDelete(ctx context.Context, in *O11ySavedViewRef) (*O11ySavedViewDeleteOut, error) {
out := new(O11ySavedViewDeleteOut)
return out, relay(ctx, nil, nil, out)
}
// ── inputs ──────────────────────────────────────────────────────────────────────
//
// The query engine's three write-shaped ops take the runtime's OWN request types
// — qbtypes.QueryRangeRequest and v3.QueryRangeParamsV3 — verbatim: they are the
// canonical bodies the runtime already parses, so naming them here is DRY rather
// than a second definition free to drift. The GET reads carry a flat input whose
// json tags ARE the query keys the runtime reads, so zip binds the caller's query
// string onto the input and the op renders the same keys back onto the wire.
// O11yQueryRangePreviewIn is a dry-run request: the composite query in the body,
// plus the verbose flag off the query string.
type O11yQueryRangePreviewIn struct {
// QueryRangeRequest is the composite query to render, embedded so the request
// body stays the bare query the execute endpoint takes.
qbtypes.QueryRangeRequest
// Verbose selects the answer's depth. Empty or "true" renders the underlying
// Datastore SQL with EXPLAIN and granule analysis; "false" returns only the
// per-query valid/error/warnings verdict with no Datastore round trips.
Verbose string `json:"verbose,omitempty"`
}
// O11yMetricsQueryRangeIn selects a Prometheus-style metrics range query. Every
// type here carries the O11y qualifier because these names enter a fleet-wide
// document, where one name may have exactly one shape.
type O11yMetricsQueryRangeIn struct {
// Start is the window start — a unix timestamp (seconds, with optional
// fraction) or an RFC 3339 time. Required.
Start string `json:"start" validate:"required"`
// End is the window end, in the same form as Start, and not before it.
// Required.
End string `json:"end" validate:"required"`
// Step is the query resolution, e.g. 60s, 1m, 1h — a positive duration.
// Required.
Step string `json:"step" validate:"required"`
// Query is the PromQL expression to evaluate. Required.
Query string `json:"query" validate:"required"`
// Stats, when "all", asks for query statistics alongside the result.
Stats string `json:"stats,omitempty"`
// Timeout caps how long the query may run, e.g. 30s, 1m — a positive
// duration. Absent means the server default.
Timeout string `json:"timeout,omitempty"`
}
// O11yAggregateAttributesIn selects which aggregate-target attributes to list.
type O11yAggregateAttributesIn struct {
// DataSource is the telemetry the attributes come from — traces, logs,
// metrics or meter. The runtime requires it.
DataSource string `json:"dataSource"`
// AggregateOperator is the aggregation the attribute will be used under, e.g.
// count, avg, sum. The runtime requires it for non-metrics sources.
AggregateOperator string `json:"aggregateOperator"`
// SearchText narrows the attributes to those containing it.
SearchText string `json:"searchText"`
// Limit caps how many attributes come back. Absent means 50.
Limit int `json:"limit"`
}
// O11yAttributeKeysIn selects which attribute keys to list.
type O11yAttributeKeysIn struct {
// DataSource is the telemetry the keys come from — traces, logs, metrics or
// meter. The runtime requires it.
DataSource string `json:"dataSource"`
// AggregateOperator is the aggregation the keys will be used under. The
// runtime requires it for non-metrics sources.
AggregateOperator string `json:"aggregateOperator"`
// AggregateAttribute is the metric the keys must appear on.
AggregateAttribute string `json:"aggregateAttribute"`
// SearchText narrows the keys to those containing it.
SearchText string `json:"searchText"`
// TagType narrows the keys to one kind — tag or resource. Empty means all;
// an invalid value reads as empty.
TagType string `json:"tagType"`
// Limit caps how many keys come back. Absent means 50.
Limit int `json:"limit"`
}
// O11yAttributeValuesIn selects which values of one attribute key to list.
type O11yAttributeValuesIn struct {
// DataSource is the telemetry the values come from — traces, logs or
// metrics. The runtime requires it.
DataSource string `json:"dataSource"`
// AggregateOperator is the aggregation the values will be used under. The
// runtime requires it for non-metrics sources.
AggregateOperator string `json:"aggregateOperator"`
// AggregateAttribute is the metric the values must appear on.
AggregateAttribute string `json:"aggregateAttribute"`
// AttributeKey is the key whose values to list.
AttributeKey string `json:"attributeKey"`
// FilterAttributeKeyDataType is the key's data type — string, int64, float64
// or bool. Empty means unspecified.
FilterAttributeKeyDataType string `json:"filterAttributeKeyDataType"`
// SearchText narrows the values to those containing it.
SearchText string `json:"searchText"`
// TagType narrows the search to one kind of key — tag or resource.
TagType string `json:"tagType"`
// Limit caps how many values come back. Absent means 50.
Limit int `json:"limit"`
}
// O11yFieldKeysIn selects which telemetry field keys to list. Its json tags are
// the query keys the field catalog reads.
type O11yFieldKeysIn struct {
// Signal is the telemetry to read the fields of — traces, logs or metrics.
Signal string `json:"signal"`
// Source narrows the fields to one source within the signal.
Source string `json:"source"`
// Limit caps how many keys come back.
Limit int `json:"limit"`
// StartUnixMilli is the window start as a unix millisecond epoch. Zero reads
// as unset.
StartUnixMilli int64 `json:"startUnixMilli"`
// EndUnixMilli is the window end as a unix millisecond epoch. Zero reads as
// unset.
EndUnixMilli int64 `json:"endUnixMilli"`
// FieldContext narrows the keys to one context — resource, scope, attribute,
// span, log or metric.
FieldContext string `json:"fieldContext"`
// FieldDataType narrows the keys to one data type.
FieldDataType string `json:"fieldDataType"`
// MetricName narrows the keys to those on one metric.
MetricName string `json:"metricName"`
// MetricNamespace narrows the keys to one metric namespace.
MetricNamespace string `json:"metricNamespace"`
// SearchText narrows the keys to those containing it.
SearchText string `json:"searchText"`
}
// O11yFieldValuesIn selects which telemetry field values to list. It carries the
// field-keys selector plus the field to read and the query it appears in.
type O11yFieldValuesIn struct {
// Signal is the telemetry to read the field of — traces, logs or metrics.
Signal string `json:"signal"`
// Source narrows the field to one source within the signal.
Source string `json:"source"`
// Limit caps how many values come back.
Limit int `json:"limit"`
// StartUnixMilli is the window start as a unix millisecond epoch. Zero reads
// as unset.
StartUnixMilli int64 `json:"startUnixMilli"`
// EndUnixMilli is the window end as a unix millisecond epoch. Zero reads as
// unset.
EndUnixMilli int64 `json:"endUnixMilli"`
// FieldContext narrows the field to one context.
FieldContext string `json:"fieldContext"`
// FieldDataType narrows the field to one data type.
FieldDataType string `json:"fieldDataType"`
// MetricName narrows the field to one metric.
MetricName string `json:"metricName"`
// MetricNamespace narrows the field to one metric namespace.
MetricNamespace string `json:"metricNamespace"`
// SearchText narrows the values to those containing it.
SearchText string `json:"searchText"`
// Name is the field whose values to read.
Name string `json:"name"`
// ExistingQuery is the query the field appears in, so related values can be
// suggested for it.
ExistingQuery string `json:"existingQuery"`
}
// O11ySavedViewListIn narrows the saved-view listing.
type O11ySavedViewListIn struct {
// SourcePage narrows the views to one source page, e.g. traces, logs.
SourcePage string `json:"sourcePage"`
// Name narrows the views to one name.
Name string `json:"name"`
// Category narrows the views to one category.
Category string `json:"category"`
}
// O11ySavedViewRef names one saved explorer view to read or delete.
type O11ySavedViewRef struct {
// ViewID is the view's id.
ViewID string `json:"viewId" validate:"required"`
}
// O11ySavedViewUpdateIn is one view update: the id from the URL and the new view
// in the body.
type O11ySavedViewUpdateIn struct {
// SavedView is the new view content, embedded so the request body stays the
// bare view the create endpoint takes.
v3.SavedView
// ViewID is the id of the view to replace, taken from the URL.
ViewID string `json:"viewId"`
}
// ── outputs ──────────────────────────────────────────────────────────────────────
//
// Each Out names the runtime's answer: the {status, data} envelope every read
// and write on this face has always answered with (render.Success and package
// app's Respond write the same shape), around the runtime's own response type.
// Status is declared first because the runtime writes it first, and a typed op
// that reorders or drops a byte is a break, not a migration.
//
// Where the runtime's answer is a concrete type it is REUSED as Data — DRY, and
// byte-identical because the bytes were marshaled from that same type. Where the
// answer is polymorphic — the query engine's result carries a result set that is
// one of time-series, scalar or raw, PromQL's value is one of matrix/vector/
// scalar, a preview embeds a rendered-error union — Data is a json.RawMessage
// that carries those bytes VERBATIM, so the op re-answers exactly what the
// runtime wrote rather than round-tripping a union through a Go interface and
// re-sorting its keys.
// O11yQueryRangeOut is a composite query result. Data is verbatim: its result set
// is a union of time-series, scalar and raw shapes the request type chooses.
type O11yQueryRangeOut struct {
// Status is "success".
Status string `json:"status"`
// Data is the query result — the result type, the result set, execution stats
// and any warning — as the engine rendered it.
Data json.RawMessage `json:"data,omitempty"`
}
// O11yQueryRangePreviewOut is a dry-run result. Data is verbatim: each query's
// entry carries a rendered-error union the runtime encodes.
type O11yQueryRangePreviewOut struct {
// Status is "success".
Status string `json:"status"`
// Data holds one preview per query, keyed by query name — the rendered
// statements with optional EXPLAIN and granule analysis, or the per-query
// verdict.
Data json.RawMessage `json:"data,omitempty"`
}
// O11ySubstituteVarsOut is a variable-substituted query. Data is verbatim: it is
// a composite query, whose builder queries are a polymorphic set.
type O11ySubstituteVarsOut struct {
// Status is "success".
Status string `json:"status"`
// Data is the resolved composite query, with its variables substituted.
Data json.RawMessage `json:"data,omitempty"`
}
// O11yMetricsQueryRangeOut is a Prometheus-style range result. Data is verbatim:
// the value is a matrix, a vector or a scalar.
type O11yMetricsQueryRangeOut struct {
// Status is "success".
Status string `json:"status"`
// Data is the result — its result type, the value, and query stats when
// asked.
Data json.RawMessage `json:"data,omitempty"`
}
// O11yQueryRangeFormatOut is a normalized builder query. Data is verbatim: a
// composite query whose queries are a polymorphic set.
type O11yQueryRangeFormatOut struct {
// Status is "success".
Status string `json:"status"`
// Data is the query, normalized to the v3 shape.
Data json.RawMessage `json:"data,omitempty"`
}
// O11yAggregateAttributesOut is a page of aggregate-target attributes.
type O11yAggregateAttributesOut struct {
// Status is "success".
Status string `json:"status"`
// Data holds the attribute keys.
Data v3.AggregateAttributeResponse `json:"data,omitempty"`
}
// O11yAttributeKeysOut is a page of attribute keys.
type O11yAttributeKeysOut struct {
// Status is "success".
Status string `json:"status"`
// Data holds the attribute keys.
Data v3.FilterAttributeKeyResponse `json:"data,omitempty"`
}
// O11yAttributeValuesOut is a page of one attribute key's values.
type O11yAttributeValuesOut struct {
// Status is "success".
Status string `json:"status"`
// Data holds the values, split by data type.
Data v3.FilterAttributeValueResponse `json:"data,omitempty"`
}
// O11yFieldKeysOut is a page of telemetry field keys.
type O11yFieldKeysOut struct {
// Status is "success".
Status string `json:"status"`
// Data holds the field keys grouped by name, and whether the catalog is
// complete.
Data telemetrytypes.GettableFieldKeys `json:"data,omitempty"`
}
// O11yFieldValuesOut is a page of one telemetry field's values.
type O11yFieldValuesOut struct {
// Status is "success".
Status string `json:"status"`
// Data holds the values by data type, and whether the value list is complete.
Data telemetrytypes.GettableFieldValues `json:"data,omitempty"`
}
// O11ySavedViewListOut is the saved explorer views. Data has no omitempty: the
// runtime answers a null data for no views and an empty array for none matched,
// and the op keeps that distinction.
type O11ySavedViewListOut struct {
// Status is "success".
Status string `json:"status"`
// Data holds the views.
Data []v3.SavedView `json:"data"`
}
// O11ySavedViewOut is one saved explorer view.
type O11ySavedViewOut struct {
// Status is "success".
Status string `json:"status"`
// Data holds the view.
Data v3.SavedView `json:"data,omitempty"`
}
// O11ySavedViewCreateOut acknowledges a saved-view creation with its id.
type O11ySavedViewCreateOut struct {
// Status is "success".
Status string `json:"status"`
// Data is the new view's id.
Data valuer.UUID `json:"data,omitempty"`
}
// O11ySavedViewDeleteOut acknowledges a saved-view deletion.
type O11ySavedViewDeleteOut struct {
// Status is "success".
Status string `json:"status"`
}