diff --git a/deploy/docker-swarm/clickhouse-setup/clickhouse-storage.xml b/deploy/docker-swarm/clickhouse-setup/clickhouse-storage.xml
index 2b2f4010ac..54ec4976f5 100644
--- a/deploy/docker-swarm/clickhouse-setup/clickhouse-storage.xml
+++ b/deploy/docker-swarm/clickhouse-setup/clickhouse-storage.xml
@@ -7,9 +7,21 @@
s3
- https://BUCKET-NAME.s3.amazonaws.com/data/
+
+ https://BUCKET-NAME.s3-REGION-NAME.amazonaws.com/data/
ACCESS-KEY-ID
SECRET-ACCESS-KEY
+
+
+
+
diff --git a/deploy/docker/clickhouse-setup/clickhouse-storage.xml b/deploy/docker/clickhouse-setup/clickhouse-storage.xml
index 2b2f4010ac..54ec4976f5 100644
--- a/deploy/docker/clickhouse-setup/clickhouse-storage.xml
+++ b/deploy/docker/clickhouse-setup/clickhouse-storage.xml
@@ -7,9 +7,21 @@
s3
- https://BUCKET-NAME.s3.amazonaws.com/data/
+
+ https://BUCKET-NAME.s3-REGION-NAME.amazonaws.com/data/
ACCESS-KEY-ID
SECRET-ACCESS-KEY
+
+
+
+
diff --git a/pkg/query-service/utils/format.go b/pkg/query-service/utils/format.go
index af722a70d3..3316205966 100644
--- a/pkg/query-service/utils/format.go
+++ b/pkg/query-service/utils/format.go
@@ -3,11 +3,143 @@ package utils
import (
"fmt"
"reflect"
+ "strconv"
"strings"
+ v3 "go.signoz.io/signoz/pkg/query-service/model/v3"
"go.uber.org/zap"
)
+func ValidateAndCastValue(v interface{}, dataType v3.AttributeKeyDataType) (interface{}, error) {
+ switch dataType {
+ case v3.AttributeKeyDataTypeString:
+ switch x := v.(type) {
+ case string, int, int64, float32, float64, bool:
+ return fmt.Sprintf("%v", x), nil
+ case []interface{}:
+ for i, val := range x {
+ // if val is not string and it is int, int64, float, bool, convert it to string
+ if _, ok := val.(string); ok {
+ continue
+ } else if _, ok := val.(int); ok {
+ x[i] = fmt.Sprintf("%v", val)
+ } else if _, ok := val.(int64); ok {
+ x[i] = fmt.Sprintf("%v", val)
+ } else if _, ok := val.(float32); ok {
+ x[i] = fmt.Sprintf("%v", val)
+ } else if _, ok := val.(float64); ok {
+ x[i] = fmt.Sprintf("%v", val)
+ } else if _, ok := val.(bool); ok {
+ x[i] = fmt.Sprintf("%v", val)
+ } else {
+ return nil, fmt.Errorf("invalid data type, expected string, got %v", reflect.TypeOf(val))
+ }
+ }
+ return x, nil
+ default:
+ return nil, fmt.Errorf("invalid data type, expected string, got %v", reflect.TypeOf(v))
+ }
+ case v3.AttributeKeyDataTypeBool:
+ switch x := v.(type) {
+ case []interface{}:
+ for i, val := range x {
+ if _, ok := val.(string); ok {
+ boolean, err := strconv.ParseBool(val.(string))
+ if err != nil {
+ return nil, fmt.Errorf("invalid data type, expected bool, got %v", reflect.TypeOf(val))
+ }
+ x[i] = boolean
+ } else if _, ok := val.(bool); !ok {
+ return nil, fmt.Errorf("invalid data type, expected bool, got %v", reflect.TypeOf(val))
+ } else {
+ x[i] = val.(bool)
+ }
+ }
+ return x, nil
+ case bool:
+ return x, nil
+ case string:
+ boolean, err := strconv.ParseBool(x)
+ if err != nil {
+ return nil, fmt.Errorf("invalid data type, expected bool, got %v", reflect.TypeOf(v))
+ }
+ return boolean, nil
+ default:
+ return nil, fmt.Errorf("invalid data type, expected bool, got %v", reflect.TypeOf(v))
+ }
+ case v3.AttributeKeyDataTypeInt64:
+ switch x := v.(type) {
+ case []interface{}:
+ for i, val := range x {
+ if _, ok := val.(string); ok {
+ int64val, err := strconv.ParseInt(val.(string), 10, 64)
+ if err != nil {
+ return nil, fmt.Errorf("invalid data type, expected int, got %v", reflect.TypeOf(val))
+ }
+ x[i] = int64val
+ } else if _, ok := val.(int); ok {
+ x[i] = int64(val.(int))
+ } else if _, ok := val.(int64); !ok {
+ return nil, fmt.Errorf("invalid data type, expected int, got %v", reflect.TypeOf(val))
+ } else {
+ x[i] = val.(int64)
+ }
+ }
+ return x, nil
+ case int, int64:
+ return x, nil
+ case string:
+ int64val, err := strconv.ParseInt(x, 10, 64)
+ if err != nil {
+ return nil, fmt.Errorf("invalid data type, expected int, got %v", reflect.TypeOf(v))
+ }
+ return int64val, nil
+ default:
+ return nil, fmt.Errorf("invalid data type, expected int, got %v", reflect.TypeOf(v))
+ }
+ case v3.AttributeKeyDataTypeFloat64:
+ switch x := v.(type) {
+ case []interface{}:
+ for i, val := range x {
+ if _, ok := val.(string); ok {
+ float64val, err := strconv.ParseFloat(val.(string), 64)
+ if err != nil {
+ return nil, fmt.Errorf("invalid data type, expected float, got %v", reflect.TypeOf(val))
+ }
+ x[i] = float64val
+ } else if _, ok := val.(float32); ok {
+ x[i] = float64(val.(float32))
+ } else if _, ok := val.(int); ok {
+ x[i] = float64(val.(int))
+ } else if _, ok := val.(int64); ok {
+ x[i] = float64(val.(int64))
+ } else if _, ok := val.(float64); !ok {
+ return nil, fmt.Errorf("invalid data type, expected float, got %v", reflect.TypeOf(val))
+ } else {
+ x[i] = val.(float64)
+ }
+ }
+ return x, nil
+ case float32, float64:
+ return x, nil
+ case string:
+ float64val, err := strconv.ParseFloat(x, 64)
+ if err != nil {
+ return nil, fmt.Errorf("invalid data type, expected float, got %v", reflect.TypeOf(v))
+ }
+ return float64val, nil
+ case int:
+ return float64(x), nil
+ case int64:
+ return float64(x), nil
+ default:
+ return nil, fmt.Errorf("invalid data type, expected float, got %v", reflect.TypeOf(v))
+ }
+ default:
+ return nil, fmt.Errorf("invalid data type, expected float, bool, int, string or []interface{} but got %v", dataType)
+ }
+}
+
// ClickHouseFormattedValue formats the value to be used in clickhouse query
func ClickHouseFormattedValue(v interface{}) string {
switch x := v.(type) {
diff --git a/pkg/query-service/utils/format_test.go b/pkg/query-service/utils/format_test.go
new file mode 100644
index 0000000000..85482381ff
--- /dev/null
+++ b/pkg/query-service/utils/format_test.go
@@ -0,0 +1,293 @@
+package utils
+
+import (
+ "reflect"
+ "testing"
+
+ v3 "go.signoz.io/signoz/pkg/query-service/model/v3"
+)
+
+type args struct {
+ v interface{}
+ dataType v3.AttributeKeyDataType
+}
+
+var testValidateAndCastValueData = []struct {
+ name string
+ args args
+ want interface{}
+ wantErr bool
+}{
+ // Test cases for v3.AttributeKeyDataTypeString
+ {
+ name: "v3.AttributeKeyDataTypeString: Valid string",
+ args: args{
+ v: "test",
+ dataType: v3.AttributeKeyDataTypeString,
+ },
+ want: "test",
+ wantErr: false,
+ },
+ {
+ name: "v3.AttributeKeyDataTypeString: Valid int",
+ args: args{
+ v: 1,
+ dataType: v3.AttributeKeyDataTypeString,
+ },
+ want: "1",
+ wantErr: false,
+ },
+ {
+ name: "v3.AttributeKeyDataTypeString: Valid float32",
+ args: args{
+ v: float32(1.1),
+ dataType: v3.AttributeKeyDataTypeString,
+ },
+ want: "1.1",
+ wantErr: false,
+ },
+ {
+ name: "v3.AttributeKeyDataTypeString: Valid float64",
+ args: args{
+ v: float64(1.1),
+ dataType: v3.AttributeKeyDataTypeString,
+ },
+ want: "1.1",
+ wantErr: false,
+ },
+ {
+ name: "v3.AttributeKeyDataTypeString: Valid bool",
+ args: args{
+ v: true,
+ dataType: v3.AttributeKeyDataTypeString,
+ },
+ want: "true",
+ wantErr: false,
+ },
+ {
+ name: "v3.AttributeKeyDataTypeString: Valid []interface{}",
+ args: args{
+ v: []interface{}{"test", "test2"},
+ dataType: v3.AttributeKeyDataTypeString,
+ },
+ want: []interface{}{"test", "test2"},
+ wantErr: false,
+ },
+ {
+ name: "v3.AttributeKeyDataTypeString: Valid []interface{}",
+ args: args{
+ v: []interface{}{"test", 1},
+ dataType: v3.AttributeKeyDataTypeString,
+ },
+ want: []interface{}{"test", "1"},
+ wantErr: false,
+ },
+ {
+ name: "v3.AttributeKeyDataTypeString: Invalid []interface{}",
+ args: args{
+ v: []interface{}{"test", [1]string{"string Array"}},
+ dataType: v3.AttributeKeyDataTypeString,
+ },
+ want: nil,
+ wantErr: true,
+ },
+ {
+ name: "v3.AttributeKeyDataTypeString: Invalid type",
+ args: args{
+ v: map[string]interface{}{"test": "test"},
+ dataType: v3.AttributeKeyDataTypeString,
+ },
+ want: nil,
+ wantErr: true,
+ },
+ // Test cases for v3.AttributeKeyDataTypeBool
+ {
+ name: "v3.AttributeKeyDataTypeBool: Valid bool",
+ args: args{
+ v: true,
+ dataType: v3.AttributeKeyDataTypeBool,
+ },
+ want: true,
+ wantErr: false,
+ },
+ {
+ name: "v3.AttributeKeyDataTypeBool: Valid string",
+ args: args{
+ v: "true",
+ dataType: v3.AttributeKeyDataTypeBool,
+ },
+ want: true,
+ wantErr: false,
+ },
+ {
+ name: "v3.AttributeKeyDataTypeBool: Valid []interface{}",
+ args: args{
+ v: []interface{}{"true", false},
+ dataType: v3.AttributeKeyDataTypeBool,
+ },
+ want: []interface{}{true, false},
+ wantErr: false,
+ },
+ {
+ name: "v3.AttributeKeyDataTypeBool: Invalid type",
+ args: args{
+ v: 1,
+ dataType: v3.AttributeKeyDataTypeBool,
+ },
+ want: nil,
+ wantErr: true,
+ },
+ {
+ name: "v3.AttributeKeyDataTypeBool: Invalid []interface{}",
+ args: args{
+ v: []interface{}{1, false},
+ dataType: v3.AttributeKeyDataTypeBool,
+ },
+ want: nil,
+ wantErr: true,
+ },
+ // Test cases for v3.AttributeKeyDataTypeInt64
+ {
+ name: "v3.AttributeKeyDataTypeInt64: Valid int",
+ args: args{
+ v: 1,
+ dataType: v3.AttributeKeyDataTypeInt64,
+ },
+ want: 1,
+ wantErr: false,
+ },
+ {
+ name: "v3.AttributeKeyDataTypeInt64: Valid int64",
+ args: args{
+ v: int64(1),
+ dataType: v3.AttributeKeyDataTypeInt64,
+ },
+ want: int64(1),
+ wantErr: false,
+ },
+ {
+ name: "v3.AttributeKeyDataTypeInt64: Valid string",
+ args: args{
+ v: "1",
+ dataType: v3.AttributeKeyDataTypeInt64,
+ },
+ want: int64(1),
+ wantErr: false,
+ },
+ {
+ name: "v3.AttributeKeyDataTypeInt64: Valid []interface{}",
+ args: args{
+ v: []interface{}{"1", 2},
+ dataType: v3.AttributeKeyDataTypeInt64,
+ },
+ want: []interface{}{int64(1), int64(2)},
+ wantErr: false,
+ },
+ {
+ name: "v3.AttributeKeyDataTypeInt64: Invalid []interface{}",
+ args: args{
+ v: []interface{}{"1", false},
+ dataType: v3.AttributeKeyDataTypeInt64,
+ },
+ want: nil,
+ wantErr: true,
+ },
+ {
+ name: "v3.AttributeKeyDataTypeInt64: Invalid type",
+ args: args{
+ v: true,
+ dataType: v3.AttributeKeyDataTypeInt64,
+ },
+ want: nil,
+ wantErr: true,
+ },
+ // Test cases for v3.AttributeKeyDataTypeFloat64
+ {
+ name: "v3.AttributeKeyDataTypeFloat64: Valid float32",
+ args: args{
+ v: float32(1.1),
+ dataType: v3.AttributeKeyDataTypeFloat64,
+ },
+ want: float32(1.1),
+ wantErr: false,
+ },
+ {
+ name: "v3.AttributeKeyDataTypeFloat64: Valid float64",
+ args: args{
+ v: float64(1.1),
+ dataType: v3.AttributeKeyDataTypeFloat64,
+ },
+ want: float64(1.1),
+ wantErr: false,
+ },
+ {
+ name: "v3.AttributeKeyDataTypeFloat64: Valid int",
+ args: args{
+ v: 1,
+ dataType: v3.AttributeKeyDataTypeFloat64,
+ },
+ want: float64(1),
+ wantErr: false,
+ },
+ {
+ name: "v3.AttributeKeyDataTypeFloat64: Valid string",
+ args: args{
+ v: "1.1",
+ dataType: v3.AttributeKeyDataTypeFloat64,
+ },
+ want: float64(1.1),
+ wantErr: false,
+ },
+ {
+ name: "v3.AttributeKeyDataTypeFloat: Valid []interface{}",
+ args: args{
+ v: []interface{}{4, 3},
+ dataType: v3.AttributeKeyDataTypeFloat64,
+ },
+ want: []interface{}{float64(4), float64(3)},
+ wantErr: false,
+ },
+ {
+ name: "v3.AttributeKeyDataTypeFloat: Valid []interface{}",
+ args: args{
+ v: []interface{}{4, "3"},
+ dataType: v3.AttributeKeyDataTypeFloat64,
+ },
+ want: []interface{}{float64(4), float64(3)},
+ wantErr: false,
+ },
+ {
+ name: "v3.AttributeKeyDataTypeFloat: Invalid []interface{}",
+ args: args{
+ v: []interface{}{4, "true"},
+ dataType: v3.AttributeKeyDataTypeFloat64,
+ },
+ want: nil,
+ wantErr: true,
+ },
+ {
+ name: "v3.AttributeKeyDataTypeFloat64: Invalid type",
+ args: args{
+ v: true,
+ dataType: v3.AttributeKeyDataTypeFloat64,
+ },
+ want: nil,
+ wantErr: true,
+ },
+}
+
+// Test cases for ValidateAndCastValue function in pkg/query-service/utils/format.go
+func TestValidateAndCastValue(t *testing.T) {
+ for _, tt := range testValidateAndCastValueData {
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := ValidateAndCastValue(tt.args.v, tt.args.dataType)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("ValidateAndCastValue() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if !reflect.DeepEqual(got, tt.want) && !reflect.DeepEqual(err, tt.wantErr) {
+ t.Errorf("ValidateAndCastValue() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}