From 8fef381eb594df504470af1d3769b9eea51ef256 Mon Sep 17 00:00:00 2001 From: Saurabhdarekar <59426117+Saurabhdarekar@users.noreply.github.com> Date: Wed, 13 Aug 2025 16:03:01 +0000 Subject: [PATCH 01/10] Add int64 and string support for AttributeTypeInt64 --- web/json_object_test.go | 445 ++++++++++++++++++++++++++++++++++++++++ web/json_value.go | 30 ++- web/json_value_test.go | 72 +++++++ 3 files changed, 542 insertions(+), 5 deletions(-) diff --git a/web/json_object_test.go b/web/json_object_test.go index 32ec9c0..49d6139 100644 --- a/web/json_object_test.go +++ b/web/json_object_test.go @@ -1259,3 +1259,448 @@ func TestConvertJSONObject_JSONPath(t *testing.T) { }) } } + +func TestConvertJSONObject_Int64Support(t *testing.T) { + testJSONOptions := defaultJSONOptions() + + tests := map[string]struct { + entity *framework.EntityConfig + objectJSON string + opts *jsonOptions + wantObject framework.Object + wantError error + }{ + "int64_from_json_number": { + entity: &framework.EntityConfig{ + ExternalId: "test", + Attributes: []*framework.AttributeConfig{ + { + ExternalId: "id", + Type: framework.AttributeTypeInt64, + }, + }, + }, + objectJSON: `{"id": 123456789012345}`, + opts: testJSONOptions, + wantObject: framework.Object{ + "id": int64(123456789012345), + }, + wantError: nil, + }, + "int64_from_string": { + entity: &framework.EntityConfig{ + ExternalId: "test", + Attributes: []*framework.AttributeConfig{ + { + ExternalId: "id", + Type: framework.AttributeTypeInt64, + }, + }, + }, + objectJSON: `{"id": "9223372036854775807"}`, // MaxInt64 + opts: testJSONOptions, + wantObject: framework.Object{ + "id": int64(9223372036854775807), + }, + wantError: nil, + }, + "int64_list_mixed_sources": { + entity: &framework.EntityConfig{ + ExternalId: "test", + Attributes: []*framework.AttributeConfig{ + { + ExternalId: "ids", + Type: framework.AttributeTypeInt64, + List: true, + }, + }, + }, + objectJSON: `{"ids": [123, "456", 789]}`, + opts: testJSONOptions, + wantObject: framework.Object{ + "ids": []int64{123, 456, 789}, + }, + wantError: nil, + }, + "int64_precision_error": { + entity: &framework.EntityConfig{ + ExternalId: "test", + Attributes: []*framework.AttributeConfig{ + { + ExternalId: "id", + Type: framework.AttributeTypeInt64, + }, + }, + }, + objectJSON: `{"id": 9007199254740992}`, // 2^53 (unsafe) + opts: testJSONOptions, + wantObject: nil, + wantError: errors.New("attribute id cannot be accurately converted from float64 to int64: value 9.007199254740992e+15 is outside the safe integer range (±9007199254740991)"), + }, + "int64_fractional_error": { + entity: &framework.EntityConfig{ + ExternalId: "test", + Attributes: []*framework.AttributeConfig{ + { + ExternalId: "id", + Type: framework.AttributeTypeInt64, + }, + }, + }, + objectJSON: `{"id": 123.45}`, + opts: testJSONOptions, + wantObject: nil, + wantError: errors.New("attribute id cannot be converted to int64: value 123.45 has a fractional part"), + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + var object map[string]any + err := json.Unmarshal([]byte(tc.objectJSON), &object) + if err != nil { + t.Fatalf("Failed to unmarshal test input JSON object: %v", err) + } + + gotObject, gotError := convertJSONObject(tc.entity, object, tc.opts, nil) + + if tc.wantError != nil { + AssertDeepEqual(t, tc.wantError.Error(), gotError.Error()) + } else { + AssertDeepEqual(t, tc.wantError, gotError) + } + AssertDeepEqual(t, tc.wantObject, gotObject) + }) + } + + // Special test case: Direct int64 value (bypasses JSON unmarshaling) + t.Run("direct_int64_value", func(t *testing.T) { + entity := &framework.EntityConfig{ + ExternalId: "test", + Attributes: []*framework.AttributeConfig{ + { + ExternalId: "id", + Type: framework.AttributeTypeInt64, + }, + }, + } + + // Create object with actual int64 value (not from JSON) + object := map[string]any{ + "id": int64(123456789012345), // Direct int64, not from JSON unmarshaling + } + + gotObject, gotError := convertJSONObject(entity, object, testJSONOptions, nil) + + AssertDeepEqual(t, nil, gotError) + wantObject := framework.Object{ + "id": int64(123456789012345), + } + AssertDeepEqual(t, wantObject, gotObject) + }) +} + +func TestConvertJSONObject_AllDataTypes(t *testing.T) { + testJSONOptions := defaultJSONOptions() + + tests := map[string]struct { + entity *framework.EntityConfig + objectJSON string + opts *jsonOptions + wantObject framework.Object + wantError error + }{ + "mixed_attribute_types": { + entity: &framework.EntityConfig{ + ExternalId: "test", + Attributes: []*framework.AttributeConfig{ + { + ExternalId: "stringAttr", + Type: framework.AttributeTypeString, + }, + { + ExternalId: "int64Attr", + Type: framework.AttributeTypeInt64, + }, + { + ExternalId: "boolAttr", + Type: framework.AttributeTypeBool, + }, + { + ExternalId: "doubleAttr", + Type: framework.AttributeTypeDouble, + }, + { + ExternalId: "dateTimeAttr", + Type: framework.AttributeTypeDateTime, + }, + }, + }, + objectJSON: `{ + "stringAttr": "test value", + "int64Attr": 123456789, + "boolAttr": true, + "doubleAttr": 123.45, + "dateTimeAttr": "2023-06-23T12:34:56Z" + }`, + opts: &jsonOptions{ + dateTimeFormats: []DateTimeFormatWithTimeZone{ + {Format: "2006-01-02T15:04:05Z", HasTimeZone: true}, + }, + }, + wantObject: framework.Object{ + "stringAttr": "test value", + "int64Attr": int64(123456789), + "boolAttr": true, + "doubleAttr": 123.45, + "dateTimeAttr": MustParseTime(t, "2023-06-23T12:34:56Z"), + }, + wantError: nil, + }, + "mixed_list_types": { + entity: &framework.EntityConfig{ + ExternalId: "test", + Attributes: []*framework.AttributeConfig{ + { + ExternalId: "stringList", + Type: framework.AttributeTypeString, + List: true, + }, + { + ExternalId: "int64List", + Type: framework.AttributeTypeInt64, + List: true, + }, + { + ExternalId: "boolList", + Type: framework.AttributeTypeBool, + List: true, + }, + { + ExternalId: "doubleList", + Type: framework.AttributeTypeDouble, + List: true, + }, + }, + }, + objectJSON: `{ + "stringList": ["a", "b", "c"], + "int64List": [1, 2, 3], + "boolList": [true, false, true], + "doubleList": [1.1, 2.2, 3.3] + }`, + opts: testJSONOptions, + wantObject: framework.Object{ + "stringList": []string{"a", "b", "c"}, + "int64List": []int64{1, 2, 3}, + "boolList": []bool{true, false, true}, + "doubleList": []float64{1.1, 2.2, 3.3}, + }, + wantError: nil, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + var object map[string]any + err := json.Unmarshal([]byte(tc.objectJSON), &object) + if err != nil { + t.Fatalf("Failed to unmarshal test input JSON object: %v", err) + } + + gotObject, gotError := convertJSONObject(tc.entity, object, tc.opts, nil) + + AssertDeepEqual(t, tc.wantError, gotError) + AssertDeepEqual(t, tc.wantObject, gotObject) + }) + } +} + +func TestConvertJSONObject_ErrorHandling(t *testing.T) { + testJSONOptions := defaultJSONOptions() + + tests := map[string]struct { + entity *framework.EntityConfig + objectJSON string + opts *jsonOptions + wantObject framework.Object + wantError error + }{ + "invalid_attribute_type": { + entity: &framework.EntityConfig{ + ExternalId: "test", + Attributes: []*framework.AttributeConfig{ + { + ExternalId: "stringAttr", + Type: framework.AttributeTypeString, + }, + }, + }, + objectJSON: `{"stringAttr": 123}`, // number instead of string + opts: testJSONOptions, + wantObject: nil, + wantError: errors.New("attribute stringAttr cannot be parsed into a string value"), + }, + "invalid_bool_conversion": { + entity: &framework.EntityConfig{ + ExternalId: "test", + Attributes: []*framework.AttributeConfig{ + { + ExternalId: "boolAttr", + Type: framework.AttributeTypeBool, + }, + }, + }, + objectJSON: `{"boolAttr": "invalid"}`, + opts: testJSONOptions, + wantObject: nil, + wantError: errors.New("attribute boolAttr cannot be parsed into a bool value"), + }, + "child_entity_with_invalid_attribute": { + entity: &framework.EntityConfig{ + ExternalId: "test", + ChildEntities: []*framework.EntityConfig{ + { + ExternalId: "children", + Attributes: []*framework.AttributeConfig{ + { + ExternalId: "id", + Type: framework.AttributeTypeInt64, + }, + }, + }, + }, + }, + objectJSON: `{"children": [{"id": "invalid_int64"}]}`, + opts: testJSONOptions, + wantObject: nil, + wantError: errors.New("failed to parse objects for child entity children: attribute id cannot be parsed into an int64 value: strconv.ParseInt: parsing \"invalid_int64\": invalid syntax"), + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + var object map[string]any + err := json.Unmarshal([]byte(tc.objectJSON), &object) + if err != nil { + t.Fatalf("Failed to unmarshal test input JSON object: %v", err) + } + + gotObject, gotError := convertJSONObject(tc.entity, object, tc.opts, nil) + + if tc.wantError != nil { + AssertDeepEqual(t, tc.wantError.Error(), gotError.Error()) + } else { + AssertDeepEqual(t, tc.wantError, gotError) + } + AssertDeepEqual(t, tc.wantObject, gotObject) + }) + } +} + +func TestConvertJSONObject_EdgeCases(t *testing.T) { + testJSONOptions := defaultJSONOptions() + + tests := map[string]struct { + entity *framework.EntityConfig + objectJSON string + opts *jsonOptions + wantObject framework.Object + wantError error + }{ + "empty_lists": { + entity: &framework.EntityConfig{ + ExternalId: "test", + Attributes: []*framework.AttributeConfig{ + { + ExternalId: "emptyStringList", + Type: framework.AttributeTypeString, + List: true, + }, + }, + ChildEntities: []*framework.EntityConfig{ + { + ExternalId: "emptyChildList", + Attributes: []*framework.AttributeConfig{ + { + ExternalId: "name", + Type: framework.AttributeTypeString, + }, + }, + }, + }, + }, + objectJSON: `{ + "emptyStringList": [], + "emptyChildList": [] + }`, + opts: testJSONOptions, + wantObject: framework.Object{ + "emptyStringList": []interface{}{}, + }, + wantError: nil, + }, + "null_values": { + entity: &framework.EntityConfig{ + ExternalId: "test", + Attributes: []*framework.AttributeConfig{ + { + ExternalId: "nullString", + Type: framework.AttributeTypeString, + }, + { + ExternalId: "nullInt64", + Type: framework.AttributeTypeInt64, + }, + { + ExternalId: "validString", + Type: framework.AttributeTypeString, + }, + }, + }, + objectJSON: `{ + "nullString": null, + "nullInt64": null, + "validString": "test" + }`, + opts: testJSONOptions, + wantObject: framework.Object{ + "validString": "test", + }, + wantError: nil, + }, + "list_with_nulls": { + entity: &framework.EntityConfig{ + ExternalId: "test", + Attributes: []*framework.AttributeConfig{ + { + ExternalId: "mixedList", + Type: framework.AttributeTypeString, + List: true, + }, + }, + }, + objectJSON: `{"mixedList": ["a", null, "b", null, "c"]}`, + opts: testJSONOptions, + wantObject: framework.Object{ + "mixedList": []string{"a", "b", "c"}, // nulls filtered out + }, + wantError: nil, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + var object map[string]any + err := json.Unmarshal([]byte(tc.objectJSON), &object) + if err != nil { + t.Fatalf("Failed to unmarshal test input JSON object: %v", err) + } + + gotObject, gotError := convertJSONObject(tc.entity, object, tc.opts, nil) + + AssertDeepEqual(t, tc.wantError, gotError) + AssertDeepEqual(t, tc.wantObject, gotObject) + }) + } +} diff --git a/web/json_value.go b/web/json_value.go index ff846c3..986790e 100644 --- a/web/json_value.go +++ b/web/json_value.go @@ -131,12 +131,32 @@ func convertJSONAttributeValue(attribute *framework.AttributeConfig, value any, } case framework.AttributeTypeInt64: - // All numbers are unmarshalled into float64. Convert into int64. - v, ok := value.(float64) - if !ok { - return nil, fmt.Errorf("attribute %s cannot be parsed into an int64 value", attribute.ExternalId) + switch v := value.(type) { + case int64: + fmt.Printf("Got Int64 Value") + return v, nil + case float64: + // Check if the float64 value is within the safe integer range for accurate conversion + const maxSafeInteger = 1<<53 - 1 // 2^53 - 1 + fmt.Printf("Got Float Value") + if v > maxSafeInteger || v < -maxSafeInteger { + return nil, fmt.Errorf("attribute %s cannot be accurately converted from float64 to int64: value %g is outside the safe integer range (±%d)", attribute.ExternalId, v, maxSafeInteger) + } + // Ensure the value is actually an integer (no fractional part) + if v != math.Trunc(v) { + return nil, fmt.Errorf("attribute %s cannot be converted to int64: value %g has a fractional part", attribute.ExternalId, v) + } + return int64(v), nil + case string: + fmt.Printf("Got String Value") + parsed, err := strconv.ParseInt(v, 10, 64) + if err != nil { + return nil, fmt.Errorf("attribute %s cannot be parsed into an int64 value: %w", attribute.ExternalId, err) + } + return parsed, nil + default: + return nil, fmt.Errorf("attribute %s cannot be parsed into an int64 value from type %T", attribute.ExternalId, v) } - return int64(v), nil case framework.AttributeTypeString: v, ok := value.(string) diff --git a/web/json_value_test.go b/web/json_value_test.go index 5af29c2..63cf9fd 100644 --- a/web/json_value_test.go +++ b/web/json_value_test.go @@ -623,6 +623,78 @@ func TestConvertJSONAttributeValue(t *testing.T) { valueJSON: `[12, 34, 56]`, wantValue: []int64{12, 34, 56}, }, + "int64_from_string": { + attribute: &framework.AttributeConfig{ + ExternalId: "a", + Type: framework.AttributeTypeInt64, + }, + valueJSON: `"123456789012345"`, + wantValue: int64(123456789012345), + }, + "int64_from_string_max_value": { + attribute: &framework.AttributeConfig{ + ExternalId: "a", + Type: framework.AttributeTypeInt64, + }, + valueJSON: `"9223372036854775807"`, // MaxInt64 + wantValue: int64(9223372036854775807), + }, + "int64_from_string_min_value": { + attribute: &framework.AttributeConfig{ + ExternalId: "a", + Type: framework.AttributeTypeInt64, + }, + valueJSON: `"-9223372036854775808"`, // MinInt64 + wantValue: int64(-9223372036854775808), + }, + "int64_from_string_invalid": { + attribute: &framework.AttributeConfig{ + ExternalId: "a", + Type: framework.AttributeTypeInt64, + }, + valueJSON: `"not_a_number"`, + wantError: errors.New("attribute a cannot be parsed into an int64 value: strconv.ParseInt: parsing \"not_a_number\": invalid syntax"), + }, + "int64_from_string_overflow": { + attribute: &framework.AttributeConfig{ + ExternalId: "a", + Type: framework.AttributeTypeInt64, + }, + valueJSON: `"9223372036854775808"`, // MaxInt64 + 1 + wantError: errors.New("attribute a cannot be parsed into an int64 value: strconv.ParseInt: parsing \"9223372036854775808\": value out of range"), + }, + "int64_from_float64_safe_range": { + attribute: &framework.AttributeConfig{ + ExternalId: "a", + Type: framework.AttributeTypeInt64, + }, + valueJSON: `9007199254740991`, // 2^53 - 1 (max safe integer) + wantValue: int64(9007199254740991), + }, + "int64_from_float64_unsafe_range": { + attribute: &framework.AttributeConfig{ + ExternalId: "a", + Type: framework.AttributeTypeInt64, + }, + valueJSON: `9007199254740992`, // 2^53 (unsafe) + wantError: errors.New("attribute a cannot be accurately converted from float64 to int64: value 9.007199254740992e+15 is outside the safe integer range (±9007199254740991)"), + }, + "int64_from_float64_with_fractional": { + attribute: &framework.AttributeConfig{ + ExternalId: "a", + Type: framework.AttributeTypeInt64, + }, + valueJSON: `123.45`, + wantError: errors.New("attribute a cannot be converted to int64: value 123.45 has a fractional part"), + }, + "int64_from_unsupported_type": { + attribute: &framework.AttributeConfig{ + ExternalId: "a", + Type: framework.AttributeTypeInt64, + }, + valueJSON: `true`, + wantError: errors.New("attribute a cannot be parsed into an int64 value from type bool"), + }, "string": { attribute: &framework.AttributeConfig{ ExternalId: "a", From 01ca6177298b5496232bad37431fc25668ee7e0b Mon Sep 17 00:00:00 2001 From: Saurabhdarekar <59426117+Saurabhdarekar@users.noreply.github.com> Date: Wed, 13 Aug 2025 16:19:32 +0000 Subject: [PATCH 02/10] Removed prints --- web/json_value.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/web/json_value.go b/web/json_value.go index 986790e..da20583 100644 --- a/web/json_value.go +++ b/web/json_value.go @@ -133,12 +133,10 @@ func convertJSONAttributeValue(attribute *framework.AttributeConfig, value any, case framework.AttributeTypeInt64: switch v := value.(type) { case int64: - fmt.Printf("Got Int64 Value") return v, nil case float64: // Check if the float64 value is within the safe integer range for accurate conversion const maxSafeInteger = 1<<53 - 1 // 2^53 - 1 - fmt.Printf("Got Float Value") if v > maxSafeInteger || v < -maxSafeInteger { return nil, fmt.Errorf("attribute %s cannot be accurately converted from float64 to int64: value %g is outside the safe integer range (±%d)", attribute.ExternalId, v, maxSafeInteger) } @@ -148,7 +146,6 @@ func convertJSONAttributeValue(attribute *framework.AttributeConfig, value any, } return int64(v), nil case string: - fmt.Printf("Got String Value") parsed, err := strconv.ParseInt(v, 10, 64) if err != nil { return nil, fmt.Errorf("attribute %s cannot be parsed into an int64 value: %w", attribute.ExternalId, err) From ecddaa68d476901147c690207c935844660ff918 Mon Sep 17 00:00:00 2001 From: Saurabhdarekar <59426117+Saurabhdarekar@users.noreply.github.com> Date: Tue, 19 Aug 2025 17:20:29 +0000 Subject: [PATCH 03/10] Resolved Comments --- web/json_object_test.go | 4 +-- web/json_value.go | 23 +++++++++------ web/json_value_test.go | 63 +++++++++++++++++++++++++++++++++++++++-- 3 files changed, 77 insertions(+), 13 deletions(-) diff --git a/web/json_object_test.go b/web/json_object_test.go index 49d6139..0f06233 100644 --- a/web/json_object_test.go +++ b/web/json_object_test.go @@ -1335,7 +1335,7 @@ func TestConvertJSONObject_Int64Support(t *testing.T) { objectJSON: `{"id": 9007199254740992}`, // 2^53 (unsafe) opts: testJSONOptions, wantObject: nil, - wantError: errors.New("attribute id cannot be accurately converted from float64 to int64: value 9.007199254740992e+15 is outside the safe integer range (±9007199254740991)"), + wantError: errors.New("attribute id cannot be parsed into an int64 because the value 9.007199254740992e+15 is outside the safe integer range (±9007199254740991) and would lead into precision loss"), }, "int64_fractional_error": { entity: &framework.EntityConfig{ @@ -1350,7 +1350,7 @@ func TestConvertJSONObject_Int64Support(t *testing.T) { objectJSON: `{"id": 123.45}`, opts: testJSONOptions, wantObject: nil, - wantError: errors.New("attribute id cannot be converted to int64: value 123.45 has a fractional part"), + wantError: errors.New("attribute id cannot be parsed into an int64 because the value is not an integer and has a fractional part"), }, } diff --git a/web/json_value.go b/web/json_value.go index da20583..38e58b6 100644 --- a/web/json_value.go +++ b/web/json_value.go @@ -112,11 +112,18 @@ func convertJSONAttributeValue(attribute *framework.AttributeConfig, value any, return t, nil case framework.AttributeTypeDouble: - v, ok := value.(float64) - if !ok { - return nil, fmt.Errorf("attribute %s cannot be parsed into a float64 value", attribute.ExternalId) + switch v := value.(type) { + case float64: + return v, nil + case string: + parsed, err := strconv.ParseFloat(v, 64) + if err != nil { + return nil, fmt.Errorf("attribute %s cannot be parsed into a float64 value: %w", attribute.ExternalId, err) + } + return parsed, nil + default: + return nil, fmt.Errorf("attribute %s cannot be parsed into a float64 due to invalid type %T", attribute.ExternalId, v) } - return v, nil case framework.AttributeTypeDuration: switch v := value.(type) { @@ -138,11 +145,11 @@ func convertJSONAttributeValue(attribute *framework.AttributeConfig, value any, // Check if the float64 value is within the safe integer range for accurate conversion const maxSafeInteger = 1<<53 - 1 // 2^53 - 1 if v > maxSafeInteger || v < -maxSafeInteger { - return nil, fmt.Errorf("attribute %s cannot be accurately converted from float64 to int64: value %g is outside the safe integer range (±%d)", attribute.ExternalId, v, maxSafeInteger) + return nil, fmt.Errorf("attribute %s cannot be parsed into an int64 because the value %g is outside the safe integer range (±%d) and would lead into precision loss", attribute.ExternalId, v, maxSafeInteger) } // Ensure the value is actually an integer (no fractional part) - if v != math.Trunc(v) { - return nil, fmt.Errorf("attribute %s cannot be converted to int64: value %g has a fractional part", attribute.ExternalId, v) + if float64(int64(v)) != v { + return nil, fmt.Errorf("attribute %s cannot be parsed into an int64 because the value is not an integer and has a fractional part", attribute.ExternalId) } return int64(v), nil case string: @@ -152,7 +159,7 @@ func convertJSONAttributeValue(attribute *framework.AttributeConfig, value any, } return parsed, nil default: - return nil, fmt.Errorf("attribute %s cannot be parsed into an int64 value from type %T", attribute.ExternalId, v) + return nil, fmt.Errorf("attribute %s cannot be parsed into an int64 due to invalid type %T", attribute.ExternalId, v) } case framework.AttributeTypeString: diff --git a/web/json_value_test.go b/web/json_value_test.go index 63cf9fd..9d886d8 100644 --- a/web/json_value_test.go +++ b/web/json_value_test.go @@ -17,6 +17,7 @@ package web import ( "encoding/json" "errors" + "math" "testing" "time" @@ -451,6 +452,62 @@ func TestConvertJSONAttributeValue(t *testing.T) { valueJSON: `[12, 34, 56]`, wantValue: []float64{12, 34, 56}, }, + "double_from_string": { + attribute: &framework.AttributeConfig{ + ExternalId: "a", + Type: framework.AttributeTypeDouble, + }, + valueJSON: `"123.456"`, + wantValue: float64(123.456), + }, + "double_from_string_integer": { + attribute: &framework.AttributeConfig{ + ExternalId: "a", + Type: framework.AttributeTypeDouble, + }, + valueJSON: `"123"`, + wantValue: float64(123), + }, + "double_from_string_scientific": { + attribute: &framework.AttributeConfig{ + ExternalId: "a", + Type: framework.AttributeTypeDouble, + }, + valueJSON: `"1.23e-4"`, + wantValue: float64(0.000123), + }, + "double_from_string_infinity": { + attribute: &framework.AttributeConfig{ + ExternalId: "a", + Type: framework.AttributeTypeDouble, + }, + valueJSON: `"Inf"`, + wantValue: math.Inf(1), + }, + "double_from_string_negative_infinity": { + attribute: &framework.AttributeConfig{ + ExternalId: "a", + Type: framework.AttributeTypeDouble, + }, + valueJSON: `"-Inf"`, + wantValue: math.Inf(-1), + }, + "double_from_string_invalid": { + attribute: &framework.AttributeConfig{ + ExternalId: "a", + Type: framework.AttributeTypeDouble, + }, + valueJSON: `"not_a_number"`, + wantError: errors.New("attribute a cannot be parsed into a float64 value: strconv.ParseFloat: parsing \"not_a_number\": invalid syntax"), + }, + "double_from_unsupported_type": { + attribute: &framework.AttributeConfig{ + ExternalId: "a", + Type: framework.AttributeTypeDouble, + }, + valueJSON: `true`, + wantError: errors.New("attribute a cannot be parsed into a float64 value"), + }, "duration_iso8601_valid": { attribute: &framework.AttributeConfig{ ExternalId: "a", @@ -677,7 +734,7 @@ func TestConvertJSONAttributeValue(t *testing.T) { Type: framework.AttributeTypeInt64, }, valueJSON: `9007199254740992`, // 2^53 (unsafe) - wantError: errors.New("attribute a cannot be accurately converted from float64 to int64: value 9.007199254740992e+15 is outside the safe integer range (±9007199254740991)"), + wantError: errors.New("attribute a cannot be parsed into an int64 because the value 9.007199254740992e+15 is outside the safe integer range (±9007199254740991) and would lead into precision loss"), }, "int64_from_float64_with_fractional": { attribute: &framework.AttributeConfig{ @@ -685,7 +742,7 @@ func TestConvertJSONAttributeValue(t *testing.T) { Type: framework.AttributeTypeInt64, }, valueJSON: `123.45`, - wantError: errors.New("attribute a cannot be converted to int64: value 123.45 has a fractional part"), + wantError: errors.New("attribute a cannot be parsed into an int64 because the value is not an integer and has a fractional part"), }, "int64_from_unsupported_type": { attribute: &framework.AttributeConfig{ @@ -693,7 +750,7 @@ func TestConvertJSONAttributeValue(t *testing.T) { Type: framework.AttributeTypeInt64, }, valueJSON: `true`, - wantError: errors.New("attribute a cannot be parsed into an int64 value from type bool"), + wantError: errors.New("attribute a cannot be parsed into an int64 due to invalid type bool"), }, "string": { attribute: &framework.AttributeConfig{ From 02fd37c52581d811270a640ffcc264f860712e23 Mon Sep 17 00:00:00 2001 From: Saurabhdarekar <59426117+Saurabhdarekar@users.noreply.github.com> Date: Tue, 19 Aug 2025 17:35:16 +0000 Subject: [PATCH 04/10] Fixed failed test --- web/json_value_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/json_value_test.go b/web/json_value_test.go index 9d886d8..5f2c54c 100644 --- a/web/json_value_test.go +++ b/web/json_value_test.go @@ -506,7 +506,7 @@ func TestConvertJSONAttributeValue(t *testing.T) { Type: framework.AttributeTypeDouble, }, valueJSON: `true`, - wantError: errors.New("attribute a cannot be parsed into a float64 value"), + wantError: errors.New("attribute a cannot be parsed into a float64 due to invalid type bool"), }, "duration_iso8601_valid": { attribute: &framework.AttributeConfig{ From b283f36a45e723a00f6dcc55fd1682e17a1fbfd5 Mon Sep 17 00:00:00 2001 From: Saurabhdarekar <59426117+Saurabhdarekar@users.noreply.github.com> Date: Wed, 20 Aug 2025 15:05:37 -0500 Subject: [PATCH 05/10] Update web/json_value.go Co-authored-by: Nick Holbrook --- web/json_value.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/json_value.go b/web/json_value.go index 38e58b6..b44a77d 100644 --- a/web/json_value.go +++ b/web/json_value.go @@ -145,7 +145,7 @@ func convertJSONAttributeValue(attribute *framework.AttributeConfig, value any, // Check if the float64 value is within the safe integer range for accurate conversion const maxSafeInteger = 1<<53 - 1 // 2^53 - 1 if v > maxSafeInteger || v < -maxSafeInteger { - return nil, fmt.Errorf("attribute %s cannot be parsed into an int64 because the value %g is outside the safe integer range (±%d) and would lead into precision loss", attribute.ExternalId, v, maxSafeInteger) + return nil, fmt.Errorf("attribute %s cannot be parsed into an int64 because the value %g is outside the safe integer range (±%d) and would lead to precision loss", attribute.ExternalId, v, maxSafeInteger) } // Ensure the value is actually an integer (no fractional part) if float64(int64(v)) != v { From e5d85ef3ebcbc3fbf3e6239b86b409cff2657a38 Mon Sep 17 00:00:00 2001 From: Saurabhdarekar <59426117+Saurabhdarekar@users.noreply.github.com> Date: Wed, 20 Aug 2025 15:05:52 -0500 Subject: [PATCH 06/10] Update web/json_value.go Co-authored-by: Nick Holbrook --- web/json_value.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/json_value.go b/web/json_value.go index b44a77d..c55a3ef 100644 --- a/web/json_value.go +++ b/web/json_value.go @@ -122,7 +122,7 @@ func convertJSONAttributeValue(attribute *framework.AttributeConfig, value any, } return parsed, nil default: - return nil, fmt.Errorf("attribute %s cannot be parsed into a float64 due to invalid type %T", attribute.ExternalId, v) + return nil, fmt.Errorf("attribute %s cannot be parsed into a float64 due to invalid type: %T", attribute.ExternalId, v) } case framework.AttributeTypeDuration: From 421569d20f11662097d5b9b7a1e8b7478bd39cc9 Mon Sep 17 00:00:00 2001 From: Saurabhdarekar <59426117+Saurabhdarekar@users.noreply.github.com> Date: Wed, 20 Aug 2025 15:06:03 -0500 Subject: [PATCH 07/10] Update web/json_value.go Co-authored-by: Nick Holbrook --- web/json_value.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/json_value.go b/web/json_value.go index c55a3ef..a10d9f0 100644 --- a/web/json_value.go +++ b/web/json_value.go @@ -159,7 +159,7 @@ func convertJSONAttributeValue(attribute *framework.AttributeConfig, value any, } return parsed, nil default: - return nil, fmt.Errorf("attribute %s cannot be parsed into an int64 due to invalid type %T", attribute.ExternalId, v) + return nil, fmt.Errorf("attribute %s cannot be parsed into an int64 due to invalid type: %T", attribute.ExternalId, v) } case framework.AttributeTypeString: From ebb5d51992309e8fe66fa54a43293fd214aec4c2 Mon Sep 17 00:00:00 2001 From: Saurabhdarekar <59426117+Saurabhdarekar@users.noreply.github.com> Date: Wed, 20 Aug 2025 21:02:36 +0000 Subject: [PATCH 08/10] Use Scientific Notation for maxInt values --- web/json_object_test.go | 4 ++-- web/json_value.go | 2 +- web/json_value_test.go | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/web/json_object_test.go b/web/json_object_test.go index 0f06233..1f70752 100644 --- a/web/json_object_test.go +++ b/web/json_object_test.go @@ -1332,10 +1332,10 @@ func TestConvertJSONObject_Int64Support(t *testing.T) { }, }, }, - objectJSON: `{"id": 9007199254740992}`, // 2^53 (unsafe) + objectJSON: `{"id": 9007199254740992}`, // 2^53 (unsafe) = 9.007199254740992e+15 opts: testJSONOptions, wantObject: nil, - wantError: errors.New("attribute id cannot be parsed into an int64 because the value 9.007199254740992e+15 is outside the safe integer range (±9007199254740991) and would lead into precision loss"), + wantError: errors.New("attribute id cannot be parsed into an int64 because the value 9.007199254740992e+15 is outside the safe integer range (±9.007199254740991e+15) and would lead to precision loss"), }, "int64_fractional_error": { entity: &framework.EntityConfig{ diff --git a/web/json_value.go b/web/json_value.go index a10d9f0..48804aa 100644 --- a/web/json_value.go +++ b/web/json_value.go @@ -145,7 +145,7 @@ func convertJSONAttributeValue(attribute *framework.AttributeConfig, value any, // Check if the float64 value is within the safe integer range for accurate conversion const maxSafeInteger = 1<<53 - 1 // 2^53 - 1 if v > maxSafeInteger || v < -maxSafeInteger { - return nil, fmt.Errorf("attribute %s cannot be parsed into an int64 because the value %g is outside the safe integer range (±%d) and would lead to precision loss", attribute.ExternalId, v, maxSafeInteger) + return nil, fmt.Errorf("attribute %s cannot be parsed into an int64 because the value %g is outside the safe integer range (±%g) and would lead to precision loss", attribute.ExternalId, v, float64(maxSafeInteger)) } // Ensure the value is actually an integer (no fractional part) if float64(int64(v)) != v { diff --git a/web/json_value_test.go b/web/json_value_test.go index 5f2c54c..e556571 100644 --- a/web/json_value_test.go +++ b/web/json_value_test.go @@ -506,7 +506,7 @@ func TestConvertJSONAttributeValue(t *testing.T) { Type: framework.AttributeTypeDouble, }, valueJSON: `true`, - wantError: errors.New("attribute a cannot be parsed into a float64 due to invalid type bool"), + wantError: errors.New("attribute a cannot be parsed into a float64 due to invalid type: bool"), }, "duration_iso8601_valid": { attribute: &framework.AttributeConfig{ @@ -733,8 +733,8 @@ func TestConvertJSONAttributeValue(t *testing.T) { ExternalId: "a", Type: framework.AttributeTypeInt64, }, - valueJSON: `9007199254740992`, // 2^53 (unsafe) - wantError: errors.New("attribute a cannot be parsed into an int64 because the value 9.007199254740992e+15 is outside the safe integer range (±9007199254740991) and would lead into precision loss"), + valueJSON: `9007199254740992`, // 2^53 (unsafe) = 9.007199254740992e+15 + wantError: errors.New("attribute a cannot be parsed into an int64 because the value 9.007199254740992e+15 is outside the safe integer range (±9.007199254740991e+15) and would lead to precision loss"), }, "int64_from_float64_with_fractional": { attribute: &framework.AttributeConfig{ @@ -750,7 +750,7 @@ func TestConvertJSONAttributeValue(t *testing.T) { Type: framework.AttributeTypeInt64, }, valueJSON: `true`, - wantError: errors.New("attribute a cannot be parsed into an int64 due to invalid type bool"), + wantError: errors.New("attribute a cannot be parsed into an int64 due to invalid type: bool"), }, "string": { attribute: &framework.AttributeConfig{ From 4c285aa6e22946cef4d119eae770af0c4a4c0a42 Mon Sep 17 00:00:00 2001 From: Saurabhdarekar <59426117+Saurabhdarekar@users.noreply.github.com> Date: Wed, 3 Sep 2025 17:32:04 +0000 Subject: [PATCH 09/10] Adding CodeQL workflow --- .github/workflows/codeql.yml | 75 ++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..f0eb6c5 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,75 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: ["main"] + pull_request: + # The branches below must be a subset of the branches above + branches: ["main"] + schedule: + - cron: "27 6 * * 2" + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: ["go"] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Use only 'java' to analyze code written in Java, Kotlin or both + # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v3 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{matrix.language}}" \ No newline at end of file From 3f8a4f18ad43dc48ed06e309dd0ce2cf61cc6f5e Mon Sep 17 00:00:00 2001 From: Saurabhdarekar <59426117+Saurabhdarekar@users.noreply.github.com> Date: Wed, 3 Sep 2025 20:28:02 +0000 Subject: [PATCH 10/10] Removing codeQL workflow --- .github/workflows/codeql.yml | 75 ------------------------------------ 1 file changed, 75 deletions(-) delete mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml deleted file mode 100644 index f0eb6c5..0000000 --- a/.github/workflows/codeql.yml +++ /dev/null @@ -1,75 +0,0 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. -# -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. -# -# ******** NOTE ******** -# We have attempted to detect the languages in your repository. Please check -# the `language` matrix defined below to confirm you have the correct set of -# supported CodeQL languages. -# -name: "CodeQL" - -on: - push: - branches: ["main"] - pull_request: - # The branches below must be a subset of the branches above - branches: ["main"] - schedule: - - cron: "27 6 * * 2" - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - - strategy: - fail-fast: false - matrix: - language: ["go"] - # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] - # Use only 'java' to analyze code written in Java, Kotlin or both - # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both - # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v3 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - - # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs - # queries: security-extended,security-and-quality - - # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v3 - - # ℹ️ Command-line programs to run using the OS shell. - # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun - - # If the Autobuild fails above, remove it and uncomment the following three lines. - # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. - - # - run: | - # echo "Run, Build Application using script" - # ./location_of_script_within_repo/buildscript.sh - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 - with: - category: "/language:${{matrix.language}}" \ No newline at end of file