This is an automated email from the ASF dual-hosted git repository.

klesh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git


The following commit(s) were added to refs/heads/main by this push:
     new 84fee8a55 fix(ZenTao): add support for non-date string handling in 
UnmarshalJSON and introduce related tests (#8589)
84fee8a55 is described below

commit 84fee8a5585a1ac826bfb864eb905bf77fdd7d3f
Author: Bamboo <[email protected]>
AuthorDate: Thu Sep 25 14:16:56 2025 +0800

    fix(ZenTao): add support for non-date string handling in UnmarshalJSON and 
introduce related tests (#8589)
    
    * fix(jira): update epic collector to use new API endpoint and include all 
fields
    
    * fix(jira): enhance epic collector to dynamically select API endpoint 
based on JIRA version
    
    * fix(jira): update epic collector to use correct API endpoint for JIRA 
Cloud and Server versions
    
    * fix(jira): refactor epic collector to streamline API endpoint selection 
and enhance error handling
    
    * fix(jira): fix type for Jira issue descriptions
    
    * refactor(jira): update comment and worklog models to use 
FlexibleDescription type for comments
    
    * docs(jira): add ADF reference for FlexibleDescription type in issue model
    
    * refactor(migrations): enhance file meta migration to check column 
existence and nullability before modification
    
    * feat(gitextractor): add support for excluding file extensions in commit 
stats
    
    * fix(ZenTao): add support for non-date string handling in UnmarshalJSON 
and introduce related tests
    
    * Revert "feat(gitextractor): add support for excluding file extensions in 
commit stats"
    
    This reverts commit 71b27ba9bfef830370ea68cef2aaf6cb1bea35e3.
    
    * refactor(api): instantiate team and user objects directly in CreateTeam 
and CreateUser methods
---
 backend/core/models/common/iso8601time.go      |  36 ++++++++
 backend/core/models/common/iso8601time_test.go | 111 +++++++++++++++++++++++++
 backend/plugins/org/api/team.go                |   3 +-
 backend/plugins/org/api/user.go                |   3 +-
 4 files changed, 149 insertions(+), 4 deletions(-)

diff --git a/backend/core/models/common/iso8601time.go 
b/backend/core/models/common/iso8601time.go
index cc467db48..8b49de208 100644
--- a/backend/core/models/common/iso8601time.go
+++ b/backend/core/models/common/iso8601time.go
@@ -111,6 +111,14 @@ func (jt *Iso8601Time) UnmarshalJSON(b []byte) error {
                return nil
        }
        timeString = strings.Trim(timeString, `"`)
+
+       // Handle special cases for non-standard date representations
+       // Some systems may use text like "长期" (long-term) instead of actual 
dates
+       if isNonDateString(timeString) {
+               jt.Time = time.Time{}
+               return nil
+       }
+
        t, err := ConvertStringToTime(timeString)
        if err != nil {
                return err
@@ -119,6 +127,34 @@ func (jt *Iso8601Time) UnmarshalJSON(b []byte) error {
        return nil
 }
 
+// isNonDateString checks if a string represents a non-date value like 
"long-term"
+func isNonDateString(s string) bool {
+       // Handle various representations of "long-term" in different systems
+       nonDateStrings := []string{
+               "长期",                 // Chinese for "long-term"
+               "\\u957f\\u671f",     // Unicode escape sequence for "长期"
+               "\\\\u957f\\\\u671f", // Double-escaped Unicode sequence
+               "long-term",          // English
+               "永久",                 // Chinese for "permanent"
+               "indefinite",         // English
+               "unlimited",          // English
+       }
+
+       for _, nonDate := range nonDateStrings {
+               if s == nonDate {
+                       return true
+               }
+       }
+
+       // Also check if the string contains the Unicode escape pattern for "长期"
+       // This handles cases where escape sequences might be processed 
differently
+       if strings.Contains(s, "957f") && strings.Contains(s, "671f") {
+               return true
+       }
+
+       return false
+}
+
 // ToTime FIXME ...
 func (jt *Iso8601Time) ToTime() time.Time {
        return jt.Time
diff --git a/backend/core/models/common/iso8601time_test.go 
b/backend/core/models/common/iso8601time_test.go
index 810d5acca..b984a6632 100644
--- a/backend/core/models/common/iso8601time_test.go
+++ b/backend/core/models/common/iso8601time_test.go
@@ -21,6 +21,7 @@ import (
        "database/sql/driver"
        "fmt"
        "reflect"
+       "strings"
        "testing"
        "time"
 
@@ -162,6 +163,116 @@ func TestConvertStringToTime(t *testing.T) {
        }
 }
 
+func TestIsNonDateString(t *testing.T) {
+       testCases := []struct {
+               name   string
+               input  string
+               output bool
+       }{
+               {
+                       name:   "ZenTao long-term in Chinese",
+                       input:  "长期",
+                       output: true,
+               },
+               {
+                       name:   "ZenTao long-term in Unicode escape",
+                       input:  "\\u957f\\u671f",
+                       output: true,
+               },
+               {
+                       name:   "ZenTao long-term in double-escaped Unicode",
+                       input:  "\\\\u957f\\\\u671f",
+                       output: true,
+               },
+               {
+                       name:   "English long-term",
+                       input:  "long-term",
+                       output: true,
+               },
+               {
+                       name:   "Chinese permanent",
+                       input:  "永久",
+                       output: true,
+               },
+               {
+                       name:   "English indefinite",
+                       input:  "indefinite",
+                       output: true,
+               },
+               {
+                       name:   "English unlimited",
+                       input:  "unlimited",
+                       output: true,
+               },
+               {
+                       name:   "Valid date string",
+                       input:  "2023-03-01",
+                       output: false,
+               },
+               {
+                       name:   "Valid datetime string",
+                       input:  "2023-03-01T12:30:00Z",
+                       output: false,
+               },
+               {
+                       name:   "Random string",
+                       input:  "random",
+                       output: false,
+               },
+       }
+       for _, tc := range testCases {
+               t.Run(tc.name, func(t *testing.T) {
+                       output := isNonDateString(tc.input)
+                       assert.Equal(t, tc.output, output, "Expected output to 
be %v, but got %v", tc.output, output)
+               })
+       }
+}
+
+func TestIso8601Time_UnmarshalJSON_NonDateStrings(t *testing.T) {
+       testCases := []struct {
+               name      string
+               input     string
+               shouldErr bool
+       }{
+               {
+                       name:      "ZenTao long-term in Chinese",
+                       input:     `"长期"`,
+                       shouldErr: false,
+               },
+               {
+                       name:      "ZenTao long-term in Unicode escape",
+                       input:     `"\\u957f\\u671f"`,
+                       shouldErr: false,
+               },
+               {
+                       name:      "English long-term",
+                       input:     `"long-term"`,
+                       shouldErr: false,
+               },
+               {
+                       name:      "Valid date",
+                       input:     `"2023-03-01T12:30:00Z"`,
+                       shouldErr: false,
+               },
+       }
+
+       for _, tc := range testCases {
+               t.Run(tc.name, func(t *testing.T) {
+                       var iso8601Time Iso8601Time
+                       err := iso8601Time.UnmarshalJSON([]byte(tc.input))
+                       if tc.shouldErr {
+                               assert.Error(t, err)
+                       } else {
+                               assert.NoError(t, err)
+                               // For non-date strings, the time should be zero
+                               if isNonDateString(strings.Trim(tc.input, `"`)) 
{
+                                       assert.True(t, 
iso8601Time.Time.IsZero(), "Expected zero time for non-date string")
+                               }
+                       }
+               })
+       }
+}
+
 func TestConvertStringToTimeInLoc(t *testing.T) {
        loc, err := time.LoadLocation("Asia/Shanghai")
        if err != nil {
diff --git a/backend/plugins/org/api/team.go b/backend/plugins/org/api/team.go
index 36c697a05..133a97e8e 100644
--- a/backend/plugins/org/api/team.go
+++ b/backend/plugins/org/api/team.go
@@ -80,9 +80,8 @@ func (h *Handlers) CreateTeam(input *plugin.ApiResourceInput) 
(*plugin.ApiResour
        if err != nil {
                return nil, err
        }
-       var t *team
        var items []interface{}
-       for _, tm := range t.toDomainLayer(tt) {
+       for _, tm := range (&team{}).toDomainLayer(tt) {
                items = append(items, tm)
        }
        err = h.store.deleteAll(&crossdomain.Team{})
diff --git a/backend/plugins/org/api/user.go b/backend/plugins/org/api/user.go
index e4daad412..addd034c7 100644
--- a/backend/plugins/org/api/user.go
+++ b/backend/plugins/org/api/user.go
@@ -79,9 +79,8 @@ func (h *Handlers) CreateUser(input *plugin.ApiResourceInput) 
(*plugin.ApiResour
        if err != nil {
                return nil, err
        }
-       var u *user
        var items []interface{}
-       users, teamUsers := u.toDomainLayer(uu)
+       users, teamUsers := (&user{}).toDomainLayer(uu)
        for _, user := range users {
                items = append(items, user)
        }

Reply via email to