diff --git a/Makefile b/Makefile index a3ce89b..21bee64 100644 --- a/Makefile +++ b/Makefile @@ -42,7 +42,8 @@ ci-build-configuration: clean build-supergraph-test: cd tests/engine && \ ddn connector-link update prometheus --add-all-resources --subgraph ./app/subgraph.yaml && \ - ddn supergraph build local + ddn supergraph build local && \ + docker compose up -d --build engine .PHONY: generate-api-types generate-api-types: diff --git a/README.md b/README.md index b8f4b43..49890e4 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,7 @@ The `timestamp` and `value` fields are the result of the instant query. If the r - `offset`: the offset modifier allows changing the time offset for individual instant and range vectors in a query. - `timeout`: the evaluation timeout of the request. - `fn`: the array of composable PromQL functions. +- `flat`: flatten grouped values out the root array. Use the runtime setting if the value is null. #### Aggregation @@ -154,6 +155,7 @@ The native query is exposed as a read-only function with 2 required fields `job` - `time`: Evaluation timestamp. Use this argument if you want to run an instant query. - `step`: the query resolution step width in duration format or float number of seconds. The step should be explicitly set for range queries. Even though the connector can estimate the approximate step width the result may be empty due to too far interval. - `timeout`: the evaluation timeout of the request. +- `flat`: flatten grouped values out the root array. Use the runtime setting if the value is null ### Prometheus APIs @@ -259,7 +261,7 @@ runtime: #### Flatten values -By default, values are grouped by the label set. If you want to flatten the values array set `flat=true`. +By default, values are grouped by the label set. If you want to flatten out the values array, set `flat=true`. #### Unix timestamp's unit diff --git a/connector/internal/expression_label.go b/connector/internal/expression_label.go index e052117..75048c3 100644 --- a/connector/internal/expression_label.go +++ b/connector/internal/expression_label.go @@ -1,7 +1,6 @@ package internal import ( - "encoding/json" "fmt" "regexp" "slices" @@ -212,37 +211,3 @@ func (le *LabelExpressionBuilder) evalLabelComparison(operator string, value any return true, nil } - -func decodeStringSlice(value any) ([]string, error) { - if utils.IsNil(value) { - return nil, nil - } - var err error - sliceValue := []string{} - if str, ok := value.(string); ok { - // try to parse the slice from the json string - err = json.Unmarshal([]byte(str), &sliceValue) - } else { - sliceValue, err = utils.DecodeStringSlice(value) - } - if err != nil { - return nil, err - } - - return sliceValue, nil -} - -func intersection[T comparable](sliceA []T, sliceB []T) []T { - var result []T - if len(sliceA) == 0 || len(sliceB) == 0 { - return result - } - - for _, a := range sliceA { - if slices.Contains(sliceB, a) { - result = append(result, a) - } - } - - return result -} diff --git a/connector/internal/native_query.go b/connector/internal/native_query.go index fb32cb3..aa66e5a 100644 --- a/connector/internal/native_query.go +++ b/connector/internal/native_query.go @@ -4,13 +4,11 @@ import ( "context" "fmt" "strconv" - "time" "github.com/hasura/ndc-prometheus/connector/client" "github.com/hasura/ndc-prometheus/connector/metadata" "github.com/hasura/ndc-sdk-go/schema" "github.com/hasura/ndc-sdk-go/utils" - "github.com/prometheus/common/model" "go.opentelemetry.io/otel/trace" ) @@ -21,6 +19,14 @@ type nativeQueryParameters struct { End any Timeout any Step any + Where map[string]NativeQueryLabelBoolExp +} + +// NewNativeQueryParameters creates a nativeQueryParameters instance +func NewNativeQueryParameters() *nativeQueryParameters { + return &nativeQueryParameters{ + Where: make(map[string]NativeQueryLabelBoolExp), + } } type NativeQueryExecutor struct { @@ -37,7 +43,7 @@ type NativeQueryExecutor struct { // Explain explains the query request func (nqe *NativeQueryExecutor) Explain(ctx context.Context) (*nativeQueryParameters, string, error) { var err error - params := &nativeQueryParameters{} + params := NewNativeQueryParameters() queryString := nqe.NativeQuery.Query for key, arg := range nqe.Arguments { switch key { @@ -51,6 +57,16 @@ func (nqe *NativeQueryExecutor) Explain(ctx context.Context) (*nativeQueryParame params.Timestamp = arg case metadata.ArgumentKeyTimeout: params.Timeout = arg + case metadata.ArgumentKeyWhere: + if utils.IsNil(arg) { + continue + } + + boolExps, err := decodeNativeQueryLabelBoolExps(arg) + if err != nil { + return nil, "", schema.UnprocessableContentError(err.Error(), nil) + } + params.Where = boolExps default: argInfo, ok := nqe.NativeQuery.Arguments[key] if ok { @@ -99,7 +115,7 @@ func (nqe *NativeQueryExecutor) Explain(ctx context.Context) (*nativeQueryParame // ExplainRaw explains the raw promQL query request func (nqe *NativeQueryExecutor) ExplainRaw(ctx context.Context) (*nativeQueryParameters, string, error) { - params := &nativeQueryParameters{} + params := NewNativeQueryParameters() var err error var queryString string for key, arg := range nqe.Arguments { @@ -199,6 +215,11 @@ func (nqe *NativeQueryExecutor) queryInstant(ctx context.Context, queryString st if err != nil { return nil, schema.UnprocessableContentError(err.Error(), nil) } + + span := trace.SpanFromContext(ctx) + span.AddEvent("post_filter_results", trace.WithAttributes(utils.JSONAttribute("where", params.Where))) + vector = nqe.filterVectorResults(vector, params.Where) + results := createQueryResultsFromVector(vector, nqe.NativeQuery.Labels, nqe.Runtime, flat) return results, nil @@ -210,120 +231,10 @@ func (nqe *NativeQueryExecutor) queryRange(ctx context.Context, queryString stri return nil, schema.UnprocessableContentError(err.Error(), nil) } + span := trace.SpanFromContext(ctx) + span.AddEvent("post_filter_results", trace.WithAttributes(utils.JSONAttribute("where", params.Where))) + matrix = nqe.filterMatrixResults(matrix, params.Where) results := createQueryResultsFromMatrix(matrix, nqe.NativeQuery.Labels, nqe.Runtime, flat) return results, nil } - -func createQueryResultsFromVector(vector model.Vector, labels map[string]metadata.LabelInfo, runtime *metadata.RuntimeSettings, flat bool) []map[string]any { - results := make([]map[string]any, len(vector)) - for i, item := range vector { - ts := formatTimestamp(item.Timestamp, runtime.Format.Timestamp, runtime.UnixTimeUnit) - value := formatValue(item.Value, runtime.Format.Value) - r := map[string]any{ - metadata.TimestampKey: ts, - metadata.ValueKey: value, - metadata.LabelsKey: item.Metric, - } - - for label := range labels { - r[label] = string(item.Metric[model.LabelName(label)]) - } - if !flat { - r[metadata.ValuesKey] = []map[string]any{ - { - metadata.TimestampKey: ts, - metadata.ValueKey: value, - }, - } - } - - results[i] = r - } - - return results -} - -func createQueryResultsFromMatrix(matrix model.Matrix, labels map[string]metadata.LabelInfo, runtime *metadata.RuntimeSettings, flat bool) []map[string]any { - if flat { - return createFlatQueryResultsFromMatrix(matrix, labels, runtime) - } - - return createGroupQueryResultsFromMatrix(matrix, labels, runtime) -} - -func createGroupQueryResultsFromMatrix(matrix model.Matrix, labels map[string]metadata.LabelInfo, runtime *metadata.RuntimeSettings) []map[string]any { - results := make([]map[string]any, len(matrix)) - for i, item := range matrix { - r := map[string]any{ - metadata.LabelsKey: item.Metric, - } - - for label := range labels { - r[label] = string(item.Metric[model.LabelName(label)]) - } - - valuesLen := len(item.Values) - values := make([]map[string]any, valuesLen) - for i, value := range item.Values { - ts := formatTimestamp(value.Timestamp, runtime.Format.Timestamp, runtime.UnixTimeUnit) - v := formatValue(value.Value, runtime.Format.Value) - values[i] = map[string]any{ - metadata.TimestampKey: ts, - metadata.ValueKey: v, - } - if i == valuesLen-1 { - r[metadata.TimestampKey] = ts - r[metadata.ValueKey] = v - } - } - - r[metadata.ValuesKey] = values - results[i] = r - } - - return results -} - -func createFlatQueryResultsFromMatrix(matrix model.Matrix, labels map[string]metadata.LabelInfo, runtime *metadata.RuntimeSettings) []map[string]any { - results := []map[string]any{} - - for _, item := range matrix { - for _, value := range item.Values { - ts := formatTimestamp(value.Timestamp, runtime.Format.Timestamp, runtime.UnixTimeUnit) - v := formatValue(value.Value, runtime.Format.Value) - r := map[string]any{ - metadata.LabelsKey: item.Metric, - metadata.TimestampKey: ts, - metadata.ValueKey: v, - metadata.ValuesKey: nil, - } - - for label := range labels { - r[label] = string(item.Metric[model.LabelName(label)]) - } - - results = append(results, r) - } - } - - return results -} - -func formatTimestamp(ts model.Time, format metadata.TimestampFormat, unixTime client.UnixTimeUnit) any { - switch format { - case metadata.TimestampRFC3339: - return ts.Time().Format(time.RFC3339) - default: - return ts.Unix() * int64(time.Second/unixTime.Duration()) - } -} - -func formatValue(value model.SampleValue, format metadata.ValueFormat) any { - switch format { - case metadata.ValueFloat64: - return float64(value) - default: - return value.String() - } -} diff --git a/connector/internal/native_query_bool_exp.go b/connector/internal/native_query_bool_exp.go new file mode 100644 index 0000000..a161402 --- /dev/null +++ b/connector/internal/native_query_bool_exp.go @@ -0,0 +1,151 @@ +package internal + +import ( + "fmt" + "regexp" + "slices" + + "github.com/hasura/ndc-prometheus/connector/metadata" + "github.com/hasura/ndc-sdk-go/utils" + "github.com/prometheus/common/model" +) + +// NativeQueryLabelBoolExp represents the boolean expression object type +type NativeQueryLabelBoolExp struct { + Equal *string + NotEqual *string + In []string + NotIn []string + Regex *regexp.Regexp + NotRegex *regexp.Regexp +} + +// Validate validates the value +func (be NativeQueryLabelBoolExp) Validate(value string) bool { + return (be.Equal == nil || *be.Equal == value) && + (be.NotEqual == nil || *be.NotEqual != value) && + (be.In == nil || slices.Contains(be.In, value)) && + (be.NotIn == nil || !slices.Contains(be.NotIn, value)) && + (be.Regex == nil || be.Regex.MatchString(value)) && + (be.NotRegex == nil || !be.NotRegex.MatchString(value)) +} + +// FromValue decode any value to NativeQueryLabelBoolExp. +func (be *NativeQueryLabelBoolExp) FromValue(value any) error { + valueMap, ok := value.(map[string]any) + if !ok { + return fmt.Errorf("invalid boolean expression argument %v", value) + } + if len(valueMap) == 0 { + return nil + } + + var err error + be.Equal, err = utils.GetNullableString(valueMap, metadata.Equal) + if err != nil { + return err + } + be.NotEqual, err = utils.GetNullableString(valueMap, metadata.NotEqual) + if err != nil { + return err + } + rawRegex, err := utils.GetNullableString(valueMap, metadata.Regex) + if err != nil { + return err + } + + if rawRegex != nil { + regex, err := regexp.Compile(*rawRegex) + if err != nil { + return fmt.Errorf("invalid _regex: %s", err) + } + be.Regex = regex + } + + rawNotRegex, err := utils.GetNullableString(valueMap, metadata.NotRegex) + if err != nil { + return err + } + + if rawNotRegex != nil { + nregex, err := regexp.Compile(*rawNotRegex) + if err != nil { + return fmt.Errorf("invalid _nregex: %s", err) + } + be.NotRegex = nregex + } + + if v, ok := valueMap[metadata.In]; ok { + be.In, err = decodeStringSlice(v) + if err != nil { + return err + } + } + if v, ok := valueMap[metadata.NotIn]; ok { + be.NotIn, err = decodeStringSlice(v) + if err != nil { + return err + } + } + + return nil +} + +func (nqe *NativeQueryExecutor) filterVectorResults(vector model.Vector, where map[string]NativeQueryLabelBoolExp) model.Vector { + if len(where) == 0 || len(vector) == 0 { + return vector + } + results := model.Vector{} + for _, item := range vector { + if nqe.validateLabelBoolExp(item.Metric, where) { + results = append(results, item) + } + } + return results +} + +func (nqe *NativeQueryExecutor) filterMatrixResults(matrix model.Matrix, where map[string]NativeQueryLabelBoolExp) model.Matrix { + if len(where) == 0 || len(matrix) == 0 { + return matrix + } + results := model.Matrix{} + for _, item := range matrix { + if nqe.validateLabelBoolExp(item.Metric, where) { + results = append(results, item) + } + } + return results +} + +func (nqe *NativeQueryExecutor) validateLabelBoolExp(labels model.Metric, where map[string]NativeQueryLabelBoolExp) bool { + for key, boolExp := range where { + if labelValue, ok := labels[model.LabelName(key)]; ok { + if !boolExp.Validate(string(labelValue)) { + return false + } + } + } + return true +} + +func decodeNativeQueryLabelBoolExps(value any) (map[string]NativeQueryLabelBoolExp, error) { + results := make(map[string]NativeQueryLabelBoolExp) + if utils.IsNil(value) { + return results, nil + } + + valueMap, ok := value.(map[string]any) + if !ok { + return nil, fmt.Errorf("invalid where; expected map, got: %v", value) + } + + for k, v := range valueMap { + boolExp := NativeQueryLabelBoolExp{} + if err := boolExp.FromValue(v); err != nil { + return nil, err + } + results[k] = boolExp + } + + return results, nil +} diff --git a/connector/internal/utils.go b/connector/internal/utils.go new file mode 100644 index 0000000..9ddc059 --- /dev/null +++ b/connector/internal/utils.go @@ -0,0 +1,159 @@ +package internal + +import ( + "encoding/json" + "slices" + "time" + + "github.com/hasura/ndc-prometheus/connector/client" + "github.com/hasura/ndc-prometheus/connector/metadata" + "github.com/hasura/ndc-sdk-go/utils" + "github.com/prometheus/common/model" +) + +func createQueryResultsFromVector(vector model.Vector, labels map[string]metadata.LabelInfo, runtime *metadata.RuntimeSettings, flat bool) []map[string]any { + results := make([]map[string]any, len(vector)) + for i, item := range vector { + ts := formatTimestamp(item.Timestamp, runtime.Format.Timestamp, runtime.UnixTimeUnit) + value := formatValue(item.Value, runtime.Format.Value) + r := map[string]any{ + metadata.TimestampKey: ts, + metadata.ValueKey: value, + metadata.LabelsKey: item.Metric, + } + + for label := range labels { + r[label] = string(item.Metric[model.LabelName(label)]) + } + if !flat { + r[metadata.ValuesKey] = []map[string]any{ + { + metadata.TimestampKey: ts, + metadata.ValueKey: value, + }, + } + } + + results[i] = r + } + + return results +} + +func createQueryResultsFromMatrix(matrix model.Matrix, labels map[string]metadata.LabelInfo, runtime *metadata.RuntimeSettings, flat bool) []map[string]any { + if flat { + return createFlatQueryResultsFromMatrix(matrix, labels, runtime) + } + + return createGroupQueryResultsFromMatrix(matrix, labels, runtime) +} + +func createGroupQueryResultsFromMatrix(matrix model.Matrix, labels map[string]metadata.LabelInfo, runtime *metadata.RuntimeSettings) []map[string]any { + results := make([]map[string]any, len(matrix)) + for i, item := range matrix { + r := map[string]any{ + metadata.LabelsKey: item.Metric, + } + + for label := range labels { + r[label] = string(item.Metric[model.LabelName(label)]) + } + + valuesLen := len(item.Values) + values := make([]map[string]any, valuesLen) + for i, value := range item.Values { + ts := formatTimestamp(value.Timestamp, runtime.Format.Timestamp, runtime.UnixTimeUnit) + v := formatValue(value.Value, runtime.Format.Value) + values[i] = map[string]any{ + metadata.TimestampKey: ts, + metadata.ValueKey: v, + } + if i == valuesLen-1 { + r[metadata.TimestampKey] = ts + r[metadata.ValueKey] = v + } + } + + r[metadata.ValuesKey] = values + results[i] = r + } + + return results +} + +func createFlatQueryResultsFromMatrix(matrix model.Matrix, labels map[string]metadata.LabelInfo, runtime *metadata.RuntimeSettings) []map[string]any { + results := []map[string]any{} + + for _, item := range matrix { + for _, value := range item.Values { + ts := formatTimestamp(value.Timestamp, runtime.Format.Timestamp, runtime.UnixTimeUnit) + v := formatValue(value.Value, runtime.Format.Value) + r := map[string]any{ + metadata.LabelsKey: item.Metric, + metadata.TimestampKey: ts, + metadata.ValueKey: v, + metadata.ValuesKey: nil, + } + + for label := range labels { + r[label] = string(item.Metric[model.LabelName(label)]) + } + + results = append(results, r) + } + } + + return results +} + +func formatTimestamp(ts model.Time, format metadata.TimestampFormat, unixTime client.UnixTimeUnit) any { + switch format { + case metadata.TimestampRFC3339: + return ts.Time().Format(time.RFC3339) + default: + return ts.Unix() * int64(time.Second/unixTime.Duration()) + } +} + +func formatValue(value model.SampleValue, format metadata.ValueFormat) any { + switch format { + case metadata.ValueFloat64: + return float64(value) + default: + return value.String() + } +} + +func decodeStringSlice(value any) ([]string, error) { + if utils.IsNil(value) { + return nil, nil + } + var err error + sliceValue := []string{} + if str, ok := value.(string); ok { + // try to parse the slice from the json string + err = json.Unmarshal([]byte(str), &sliceValue) + } else { + sliceValue, err = utils.DecodeStringSlice(value) + } + if err != nil { + return nil, err + } + + return sliceValue, nil +} + +func intersection[T comparable](sliceA []T, sliceB []T) []T { + var result []T + if len(sliceA) == 0 || len(sliceB) == 0 { + return result + } + + for _, a := range sliceA { + if slices.Contains(sliceB, a) { + result = append(result, a) + } + } + + return result +} diff --git a/connector/metadata/const.go b/connector/metadata/const.go index 3cd754f..291e6ac 100644 --- a/connector/metadata/const.go +++ b/connector/metadata/const.go @@ -195,6 +195,7 @@ const ( objectName_HoltWintersInput = "HoltWintersInput" objectName_PredictLinearInput = "PredictLinearInput" objectName_QuantileOverTimeInput = "QuantileOverTimeInput" + objectName_NativeQueryLabelBoolExp = "NativeQueryLabelBoolExp" ) var defaultObjectTypes = map[string]schema.ObjectType{ @@ -262,6 +263,35 @@ var defaultObjectTypes = map[string]schema.ObjectType{ }, }, }, + objectName_NativeQueryLabelBoolExp: { + Description: utils.ToPtr("The boolean expression for native query labels"), + Fields: schema.ObjectTypeFields{ + Equal: schema.ObjectField{ + Description: utils.ToPtr("The equality operator"), + Type: schema.NewNullableNamedType(string(ScalarString)).Encode(), + }, + NotEqual: schema.ObjectField{ + Description: utils.ToPtr("The not-equality operator"), + Type: schema.NewNullableNamedType(string(ScalarString)).Encode(), + }, + In: schema.ObjectField{ + Description: utils.ToPtr("The in-array operator"), + Type: schema.NewNullableNamedType(string(ScalarJSON)).Encode(), + }, + NotIn: schema.ObjectField{ + Description: utils.ToPtr("The not-in-array operator"), + Type: schema.NewNullableNamedType(string(ScalarJSON)).Encode(), + }, + Regex: schema.ObjectField{ + Description: utils.ToPtr("The regular expression operator"), + Type: schema.NewNullableNamedType(string(ScalarString)).Encode(), + }, + NotRegex: schema.ObjectField{ + Description: utils.ToPtr("The falsy regular expression operator"), + Type: schema.NewNullableNamedType(string(ScalarString)).Encode(), + }, + }, + }, } const ( @@ -274,6 +304,7 @@ const ( ArgumentKeyOffset = "offset" ArgumentKeyQuery = "query" ArgumentKeyFunctions = "fn" + ArgumentKeyWhere = "where" ) var defaultArgumentInfos = map[string]schema.ArgumentInfo{ @@ -302,7 +333,7 @@ var defaultArgumentInfos = map[string]schema.ArgumentInfo{ Type: schema.NewNullableNamedType(string(ScalarDuration)).Encode(), }, ArgumentKeyFlat: { - Description: utils.ToPtr("Flatten nested the values group to the root array"), + Description: utils.ToPtr("Flatten grouped values out the root array"), Type: schema.NewNullableNamedType(string(ScalarBoolean)).Encode(), }, } diff --git a/connector/metadata/native_operation.go b/connector/metadata/native_operation.go index 5d4d202..e9d272d 100644 --- a/connector/metadata/native_operation.go +++ b/connector/metadata/native_operation.go @@ -7,6 +7,7 @@ import ( "github.com/hasura/ndc-sdk-go/schema" "github.com/hasura/ndc-sdk-go/utils" + "github.com/iancoleman/strcase" ) // The variable syntax for native queries is ${} which is compatible with Grafana @@ -39,6 +40,76 @@ type NativeQuery struct { Arguments map[string]NativeQueryArgumentInfo `json:"arguments" yaml:"arguments"` } +func (scb *connectorSchemaBuilder) buildNativeQueries() error { + for name, nq := range scb.Metadata.NativeOperations.Queries { + if err := scb.checkDuplicatedOperation(name); err != nil { + return err + } + if err := scb.buildNativeQuery(name, &nq); err != nil { + return err + } + } + return nil +} + +func (scb *connectorSchemaBuilder) buildNativeQuery(name string, query *NativeQuery) error { + fn := schema.FunctionInfo{ + Name: name, + Description: query.Description, + Arguments: createNativeQueryArguments(), + } + for key, arg := range query.Arguments { + if _, ok := fn.Arguments[key]; ok { + return fmt.Errorf("argument `%s` is already used by the function", key) + } + fn.Arguments[key] = schema.ArgumentInfo{ + Description: arg.Description, + Type: schema.NewNamedType(string(ScalarString)).Encode(), + } + } + + if len(query.Labels) > 0 { + resultType := schema.ObjectType{ + Fields: createQueryResultValuesObjectFields(), + } + + boolExpType := schema.ObjectType{ + Description: utils.ToPtr(fmt.Sprintf("Boolean expression of the native query %s", name)), + Fields: schema.ObjectTypeFields{}, + } + + for key, label := range query.Labels { + // build boolean expression argument + boolExpType.Fields[key] = schema.ObjectField{ + Type: schema.NewNullableNamedType(objectName_NativeQueryLabelBoolExp).Encode(), + } + + // build the result object type + resultType.Fields[key] = schema.ObjectField{ + Description: label.Description, + Type: schema.NewNamedType(string(ScalarString)).Encode(), + } + } + + objectName := fmt.Sprintf("%sResult", strcase.ToCamel(name)) + scb.ObjectTypes[objectName] = resultType + + boolExpObjectName := fmt.Sprintf("%sBoolExp", strcase.ToCamel(name)) + scb.ObjectTypes[boolExpObjectName] = boolExpType + fn.Arguments[ArgumentKeyWhere] = schema.ArgumentInfo{ + Description: boolExpType.Description, + Type: schema.NewNullableNamedType(boolExpObjectName).Encode(), + } + + fn.ResultType = schema.NewArrayType(schema.NewNamedType(objectName)).Encode() + } else { + fn.ResultType = schema.NewArrayType(schema.NewNamedType(objectName_QueryResultValues)).Encode() + } + + scb.Functions[name] = fn + return nil +} + // FindNativeQueryVariableNames find possible variables in the native query func FindNativeQueryVariableNames(query string) []string { matches := promQLVariableRegex.FindAllStringSubmatch(query, -1) diff --git a/connector/metadata/schema.go b/connector/metadata/schema.go index 43f0f33..d606123 100644 --- a/connector/metadata/schema.go +++ b/connector/metadata/schema.go @@ -153,57 +153,6 @@ func (scb *connectorSchemaBuilder) buildMetricsItem(name string, info MetricInfo return nil } -func (scb *connectorSchemaBuilder) buildNativeQueries() error { - for name, nq := range scb.Metadata.NativeOperations.Queries { - if err := scb.checkDuplicatedOperation(name); err != nil { - return err - } - if err := scb.buildNativeQuery(name, &nq); err != nil { - return err - } - } - return nil -} - -func (scb *connectorSchemaBuilder) buildNativeQuery(name string, query *NativeQuery) error { - fn := schema.FunctionInfo{ - Name: name, - Description: query.Description, - Arguments: createNativeQueryArguments(), - } - for key, arg := range query.Arguments { - if _, ok := fn.Arguments[key]; ok { - return fmt.Errorf("argument `%s` is already used by the function", key) - } - fn.Arguments[key] = schema.ArgumentInfo{ - Description: arg.Description, - Type: schema.NewNamedType(string(ScalarString)).Encode(), - } - } - - if len(query.Labels) > 0 { - objectType := schema.ObjectType{ - Fields: createQueryResultValuesObjectFields(), - } - - for key, label := range query.Labels { - objectType.Fields[key] = schema.ObjectField{ - Description: label.Description, - Type: schema.NewNamedType(string(ScalarString)).Encode(), - } - } - - objectName := fmt.Sprintf("%sResult", strcase.ToCamel(name)) - scb.ObjectTypes[objectName] = objectType - fn.ResultType = schema.NewArrayType(schema.NewNamedType(objectName)).Encode() - } else { - fn.ResultType = schema.NewArrayType(schema.NewNamedType(objectName_QueryResultValues)).Encode() - } - - scb.Functions[name] = fn - return nil -} - func (scb *connectorSchemaBuilder) checkDuplicatedOperation(name string) error { err := fmt.Errorf("duplicated operation name: %s", name) if _, ok := scb.Functions[name]; ok { diff --git a/tests/engine/app/metadata/otel_scope_info.hml b/tests/engine/app/metadata/otel_scope_info.hml index 6b85257..809d2fd 100644 --- a/tests/engine/app/metadata/otel_scope_info.hml +++ b/tests/engine/app/metadata/otel_scope_info.hml @@ -82,16 +82,19 @@ definition: fields: - name: abs type: Boolean - description: Returns the input vector with all sample values converted to their + description: + Returns the input vector with all sample values converted to their absolute value - name: absent type: Boolean - description: Returns an empty vector if the vector passed to it has any elements + description: + Returns an empty vector if the vector passed to it has any elements (floats or native histograms) and a 1-element vector with the value 1 if the vector passed to it has no elements - name: absent_over_time type: Duration - description: Returns an empty vector if the range vector passed to it has any + description: + Returns an empty vector if the range vector passed to it has any elements (floats or native histograms) and a 1-element vector with the value 1 if the range vector passed to it has no elements - name: acos @@ -122,7 +125,8 @@ definition: description: Smallest k elements by sample value - name: ceil type: Boolean - description: Rounds the sample values of all elements in v up to the nearest + description: + Rounds the sample values of all elements in v up to the nearest integer value greater than or equal to v - name: changes type: Duration @@ -130,15 +134,18 @@ definition: provided time range as an instant vector - name: clamp type: ValueBoundaryInput - description: Clamps the sample values of all elements in v to have a lower limit + description: + Clamps the sample values of all elements in v to have a lower limit of min and an upper limit of max - name: clamp_max type: Float64 - description: Clamps the sample values of all elements in v to have an upper + description: + Clamps the sample values of all elements in v to have an upper limit of max - name: clamp_min type: Float64 - description: Clamps the sample values of all elements in v to have a lower limit + description: + Clamps the sample values of all elements in v to have a lower limit of min - name: cos type: Boolean @@ -158,19 +165,22 @@ definition: description: Converts radians to degrees for all elements in v - name: delta type: Duration - description: Calculates the difference between the first and last value of each + description: + Calculates the difference between the first and last value of each time series element in a range vector v, returning an instant vector with the given deltas and equivalent labels - name: deriv type: Duration - description: Calculates the per-second derivative of the time series in a range + description: + Calculates the per-second derivative of the time series in a range vector v, using simple linear regression - name: exp type: Boolean description: Calculates the exponential function for all elements in v - name: floor type: Boolean - description: Rounds the sample values of all elements in v down to the nearest + description: + Rounds the sample values of all elements in v down to the nearest integer value smaller than or equal to v - name: group type: "[OtelScopeInfoLabel!]" @@ -181,17 +191,20 @@ definition: do not show up in the returned vector - name: histogram_count type: Boolean - description: Returns the count of observations stored in a native histogram. + description: + Returns the count of observations stored in a native histogram. Samples that are not native histograms are ignored and do not show up in the returned vector - name: histogram_fraction type: ValueBoundaryInput - description: Returns the estimated fraction of observations between the provided + description: + Returns the estimated fraction of observations between the provided lower and upper values. Samples that are not native histograms are ignored and do not show up in the returned vector - name: histogram_quantile type: Float64 - description: Calculates the φ-quantile (0 ≤ φ ≤ 1) from a classic histogram or + description: + Calculates the φ-quantile (0 ≤ φ ≤ 1) from a classic histogram or from a native histogram - name: histogram_stddev type: Boolean @@ -201,7 +214,8 @@ definition: do not show up in the returned vector - name: histogram_stdvar type: Boolean - description: Returns the estimated standard variance of observations in a native + description: + Returns the estimated standard variance of observations in a native histogram - name: histogram_sum type: Boolean @@ -211,26 +225,31 @@ definition: description: Produces a smoothed value for time series based on the range in v - name: idelta type: Duration - description: Calculates the difference between the last two samples in the range + description: + Calculates the difference between the last two samples in the range vector v, returning an instant vector with the given deltas and equivalent labels - name: increase type: Duration - description: Calculates the increase in the time series in the range vector. + description: + Calculates the increase in the time series in the range vector. Breaks in monotonicity (such as counter resets due to target restarts) are automatically adjusted for - name: irate type: Duration - description: Calculates the per-second instant rate of increase of the time + description: + Calculates the per-second instant rate of increase of the time series in the range vector. This is based on the last two data points - name: label_join type: OtelScopeInfoLabelJoinInput - description: Joins all the values of all the src_labels using separator and + description: + Joins all the values of all the src_labels using separator and returns the timeseries with the label dst_label containing the joined value - name: label_replace type: OtelScopeInfoLabelReplaceInput - description: Matches the regular expression regex against the value of the label + description: + Matches the regular expression regex against the value of the label src_label. If it matches, the value of the label dst_label in the returned timeseries will be the expansion of replacement, together with the original labels in the input @@ -268,7 +287,8 @@ definition: description: The minimum value of all points in the specified interval - name: predict_linear type: PredictLinearInput - description: Predicts the value of time series t seconds from now, based on the + description: + Predicts the value of time series t seconds from now, based on the range vector v, using simple linear regression - name: present_over_time type: Duration @@ -284,11 +304,13 @@ definition: description: Converts degrees to radians for all elements in v - name: rate type: Duration - description: Calculates the per-second average rate of increase of the time + description: + Calculates the per-second average rate of increase of the time series in the range vector - name: resets type: Duration - description: Returns the number of counter resets within the provided time range + description: + Returns the number of counter resets within the provided time range as an instant vector - name: round type: Float64 @@ -298,7 +320,8 @@ definition: description: Returns the sample value of that single element as a scalar - name: sgn type: Boolean - description: "Returns a vector with all sample values converted to their sign, + description: + "Returns a vector with all sample values converted to their sign, defined as this: 1 if v is positive, -1 if v is negative and 0 if v is equal to zero" - name: sin @@ -309,11 +332,13 @@ definition: description: Calculates the hyperbolic sine of all elements in v - name: sort type: Boolean - description: Returns vector elements sorted by their sample values, in ascending + description: + Returns vector elements sorted by their sample values, in ascending order. Native histograms are sorted by their sum of observations - name: sort_by_label type: "[OtelScopeInfoLabel!]" - description: Returns vector elements sorted by their label values and sample + description: + Returns vector elements sorted by their label values and sample value in case of label values being equal, in ascending order - name: sort_by_label_desc type: "[OtelScopeInfoLabel!]" @@ -347,7 +372,8 @@ definition: description: Calculates the hyperbolic tangent of all elements in v - name: timestamp type: Boolean - description: Returns the timestamp of each of the samples of the given vector as + description: + Returns the timestamp of each of the samples of the given vector as the number of seconds since January 1, 1970 UTC. It also works with histogram samples - name: topk @@ -473,8 +499,6 @@ definition: - name: values type: "[QueryResultValue!]!" description: An array of query result values - - name: otel_scope_version - type: String! graphql: typeName: OtelScopeInfo inputTypeName: OtelScopeInfo_input @@ -498,7 +522,6 @@ definition: - timestamp - value - values - - otel_scope_version --- kind: BooleanExpressionType @@ -521,8 +544,6 @@ definition: booleanExpressionType: Timestamp_bool_exp - fieldName: value booleanExpressionType: Decimal_bool_exp - - fieldName: otel_scope_version - booleanExpressionType: String_bool_exp comparableRelationships: [] logicalOperators: enable: true @@ -543,11 +564,13 @@ definition: description: PromQL aggregation operators and functions for otel_scope_info - name: offset type: Duration - description: The offset modifier allows changing the time offset for individual + description: + The offset modifier allows changing the time offset for individual instant and range vectors in a query. - name: step type: Duration - description: Query resolution step width in duration format or float number of + description: + Query resolution step width in duration format or float number of seconds. - name: timeout type: Duration @@ -578,9 +601,6 @@ definition: - fieldName: value orderByDirections: enableAll: true - - fieldName: otel_scope_version - orderByDirections: - enableAll: true graphql: selectMany: queryRootField: otel_scope_info @@ -598,4 +618,3 @@ definition: - role: admin select: filter: null - diff --git a/tests/engine/app/metadata/prometheus.hml b/tests/engine/app/metadata/prometheus.hml index 318a35c..e9a401c 100644 --- a/tests/engine/app/metadata/prometheus.hml +++ b/tests/engine/app/metadata/prometheus.hml @@ -238,7 +238,6 @@ definition: - instance - job - otel_scope_name - - otel_scope_version aggregate_functions: {} comparison_operators: {} ProcessCpuSecondsTotalLabel: @@ -3865,6 +3864,51 @@ definition: type: type: named name: String + NativeQueryLabelBoolExp: + description: The boolean expression for native query labels + fields: + _eq: + description: The equality operator + type: + type: nullable + underlying_type: + type: named + name: String + _in: + description: The in-array operator + type: + type: nullable + underlying_type: + type: named + name: JSON + _neq: + description: The not-equality operator + type: + type: nullable + underlying_type: + type: named + name: String + _nin: + description: The not-in-array operator + type: + type: nullable + underlying_type: + type: named + name: JSON + _nregex: + description: The falsy regular expression operator + type: + type: nullable + underlying_type: + type: named + name: String + _regex: + description: The regular expression operator + type: + type: nullable + underlying_type: + type: named + name: String NdcPrometheusQueryTotal: fields: collection: @@ -9060,10 +9104,6 @@ definition: type: type: named name: String - otel_scope_version: - type: - type: named - name: String timestamp: description: An instant timestamp or the last timestamp of a range query result type: @@ -17509,6 +17549,21 @@ definition: element_type: type: named name: RuleGroup + ServiceUpBoolExp: + description: Boolean expression of the native query service_up + fields: + instance: + type: + type: nullable + underlying_type: + type: named + name: NativeQueryLabelBoolExp + job: + type: + type: nullable + underlying_type: + type: named + name: NativeQueryLabelBoolExp ServiceUpResult: fields: instance: @@ -18234,7 +18289,7 @@ definition: description: Measures the duration of outbound HTTP requests. arguments: flat: - description: Flatten nested the values group to the root array + description: Flatten grouped values out the root array type: type: nullable underlying_type: @@ -18277,7 +18332,7 @@ definition: description: Measures the duration of outbound HTTP requests. arguments: flat: - description: Flatten nested the values group to the root array + description: Flatten grouped values out the root array type: type: nullable underlying_type: @@ -18320,7 +18375,7 @@ definition: description: Measures the duration of outbound HTTP requests. arguments: flat: - description: Flatten nested the values group to the root array + description: Flatten grouped values out the root array type: type: nullable underlying_type: @@ -18363,7 +18418,7 @@ definition: description: Measures the size of HTTP request messages. arguments: flat: - description: Flatten nested the values group to the root array + description: Flatten grouped values out the root array type: type: nullable underlying_type: @@ -18406,7 +18461,7 @@ definition: description: Measures the size of HTTP response messages. arguments: flat: - description: Flatten nested the values group to the root array + description: Flatten grouped values out the root array type: type: nullable underlying_type: @@ -18449,7 +18504,7 @@ definition: description: Total number of query requests arguments: flat: - description: Flatten nested the values group to the root array + description: Flatten grouped values out the root array type: type: nullable underlying_type: @@ -18492,7 +18547,7 @@ definition: description: Total time taken to plan and execute a query, in seconds arguments: flat: - description: Flatten nested the values group to the root array + description: Flatten grouped values out the root array type: type: nullable underlying_type: @@ -18535,7 +18590,7 @@ definition: description: Total time taken to plan and execute a query, in seconds arguments: flat: - description: Flatten nested the values group to the root array + description: Flatten grouped values out the root array type: type: nullable underlying_type: @@ -18578,7 +18633,7 @@ definition: description: Total time taken to plan and execute a query, in seconds arguments: flat: - description: Flatten nested the values group to the root array + description: Flatten grouped values out the root array type: type: nullable underlying_type: @@ -18621,7 +18676,7 @@ definition: description: Total number of connections attempted by the given dialer a given name. arguments: flat: - description: Flatten nested the values group to the root array + description: Flatten grouped values out the root array type: type: nullable underlying_type: @@ -18664,7 +18719,7 @@ definition: description: Total number of connections closed which originated from the dialer of a given name. arguments: flat: - description: Flatten nested the values group to the root array + description: Flatten grouped values out the root array type: type: nullable underlying_type: @@ -18707,7 +18762,7 @@ definition: description: Total number of connections successfully established by the given dialer a given name. arguments: flat: - description: Flatten nested the values group to the root array + description: Flatten grouped values out the root array type: type: nullable underlying_type: @@ -18750,7 +18805,7 @@ definition: description: Total number of connections failed to dial by the dialer a given name. arguments: flat: - description: Flatten nested the values group to the root array + description: Flatten grouped values out the root array type: type: nullable underlying_type: @@ -18793,7 +18848,7 @@ definition: description: Instrumentation Scope metadata arguments: flat: - description: Flatten nested the values group to the root array + description: Flatten grouped values out the root array type: type: nullable underlying_type: @@ -18836,7 +18891,7 @@ definition: description: Total user and system CPU time spent in seconds. arguments: flat: - description: Flatten nested the values group to the root array + description: Flatten grouped values out the root array type: type: nullable underlying_type: @@ -18879,7 +18934,7 @@ definition: description: Maximum number of open file descriptors. arguments: flat: - description: Flatten nested the values group to the root array + description: Flatten grouped values out the root array type: type: nullable underlying_type: @@ -18922,7 +18977,7 @@ definition: description: Number of bytes received by the process over the network. arguments: flat: - description: Flatten nested the values group to the root array + description: Flatten grouped values out the root array type: type: nullable underlying_type: @@ -18965,7 +19020,7 @@ definition: description: Number of bytes sent by the process over the network. arguments: flat: - description: Flatten nested the values group to the root array + description: Flatten grouped values out the root array type: type: nullable underlying_type: @@ -19008,7 +19063,7 @@ definition: description: Number of open file descriptors. arguments: flat: - description: Flatten nested the values group to the root array + description: Flatten grouped values out the root array type: type: nullable underlying_type: @@ -19051,7 +19106,7 @@ definition: description: Resident memory size in bytes. arguments: flat: - description: Flatten nested the values group to the root array + description: Flatten grouped values out the root array type: type: nullable underlying_type: @@ -19094,7 +19149,7 @@ definition: description: Start time of the process since unix epoch in seconds. arguments: flat: - description: Flatten nested the values group to the root array + description: Flatten grouped values out the root array type: type: nullable underlying_type: @@ -19137,7 +19192,7 @@ definition: description: Virtual memory size in bytes. arguments: flat: - description: Flatten nested the values group to the root array + description: Flatten grouped values out the root array type: type: nullable underlying_type: @@ -19180,7 +19235,7 @@ definition: description: Maximum amount of virtual memory available in bytes. arguments: flat: - description: Flatten nested the values group to the root array + description: Flatten grouped values out the root array type: type: nullable underlying_type: @@ -19223,7 +19278,7 @@ definition: description: Total number of internal errors encountered by the promhttp metric handler. arguments: flat: - description: Flatten nested the values group to the root array + description: Flatten grouped values out the root array type: type: nullable underlying_type: @@ -19266,7 +19321,7 @@ definition: description: Current number of scrapes being served. arguments: flat: - description: Flatten nested the values group to the root array + description: Flatten grouped values out the root array type: type: nullable underlying_type: @@ -19309,7 +19364,7 @@ definition: description: Total number of scrapes by HTTP status code. arguments: flat: - description: Flatten nested the values group to the root array + description: Flatten grouped values out the root array type: type: nullable underlying_type: @@ -19352,7 +19407,7 @@ definition: description: Target metadata arguments: flat: - description: Flatten nested the values group to the root array + description: Flatten grouped values out the root array type: type: nullable underlying_type: @@ -19554,7 +19609,7 @@ definition: type: named name: Timestamp flat: - description: Flatten nested the values group to the root array + description: Flatten grouped values out the root array type: type: nullable underlying_type: @@ -19608,7 +19663,7 @@ definition: type: named name: Timestamp flat: - description: Flatten nested the values group to the root array + description: Flatten grouped values out the root array type: type: nullable underlying_type: @@ -19650,6 +19705,13 @@ definition: underlying_type: type: named name: Duration + where: + description: Boolean expression of the native query service_up + type: + type: nullable + underlying_type: + type: named + name: ServiceUpBoolExp result_type: type: array element_type: diff --git a/tests/engine/app/metadata/service_up.hml b/tests/engine/app/metadata/service_up.hml index f6d2904..b480473 100644 --- a/tests/engine/app/metadata/service_up.hml +++ b/tests/engine/app/metadata/service_up.hml @@ -74,6 +74,9 @@ definition: - name: flat type: Boolean description: Flatten nested the values group to the root array + - name: where + type: ServiceUpBoolExp + description: Boolean expression of the native query service_up source: dataConnectorName: prometheus dataConnectorCommand: @@ -91,3 +94,81 @@ definition: - role: admin allowExecution: true +--- +kind: ObjectType +version: v1 +definition: + name: NativeQueryLabelBoolExp + description: The boolean expression for native query labels + fields: + - name: _eq + type: String + description: The equality operator + - name: _in + type: JSON + description: The in-array operator + - name: _neq + type: String + description: The not-equality operator + - name: _nin + type: JSON + description: The not-in-array operator + - name: _nregex + type: String + description: The falsy regular expression operator + - name: _regex + type: String + description: The regular expression operator + graphql: + typeName: NativeQueryLabelBoolExp + inputTypeName: NativeQueryLabelBoolExp_input + dataConnectorTypeMapping: + - dataConnectorName: prometheus + dataConnectorObjectType: NativeQueryLabelBoolExp + +--- +kind: TypePermissions +version: v1 +definition: + typeName: NativeQueryLabelBoolExp + permissions: + - role: admin + output: + allowedFields: + - _eq + - _in + - _neq + - _nin + - _nregex + - _regex + +--- +kind: ObjectType +version: v1 +definition: + name: ServiceUpBoolExp + description: Boolean expression of the native query service_up + fields: + - name: instance + type: NativeQueryLabelBoolExp + - name: job + type: NativeQueryLabelBoolExp + graphql: + typeName: ServiceUpBoolExp + inputTypeName: ServiceUpBoolExp_input + dataConnectorTypeMapping: + - dataConnectorName: prometheus + dataConnectorObjectType: ServiceUpBoolExp + +--- +kind: TypePermissions +version: v1 +definition: + typeName: ServiceUpBoolExp + permissions: + - role: admin + output: + allowedFields: + - instance + - job +