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 d58803baf refactor(zentao): add custom types OperatedBy and 
StringFloat64 to simplify codes (#6188)
d58803baf is described below

commit d58803baff0ca5929b6e3b0d151e60ca95ea0291
Author: Lynwee <[email protected]>
AuthorDate: Sun Oct 8 14:39:23 2023 +0800

    refactor(zentao): add custom types OperatedBy and StringFloat64 to simplify 
codes (#6188)
    
    * refactor(zentao): add custom types OperatedBy and StringFloat64 to 
simplify codes
    
    * fix(zentao): add Scan and Value interface for custom types
---
 backend/core/models/common/string_float64.go |  85 ++++++++++++++++
 backend/plugins/zentao/api/remote.go         |   9 --
 backend/plugins/zentao/models/project.go     | 140 +++++++++++++++++----------
 3 files changed, 176 insertions(+), 58 deletions(-)

diff --git a/backend/core/models/common/string_float64.go 
b/backend/core/models/common/string_float64.go
new file mode 100644
index 000000000..a6c9c7402
--- /dev/null
+++ b/backend/core/models/common/string_float64.go
@@ -0,0 +1,85 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package common
+
+import (
+       "database/sql/driver"
+       "encoding/json"
+       "fmt"
+       "github.com/spf13/cast"
+)
+
+type StringFloat64 struct {
+       v float64
+       t string
+}
+
+func (f *StringFloat64) MarshalJSON() ([]byte, error) {
+       return json.Marshal(f.String())
+}
+
+func (f *StringFloat64) String() string {
+       if f.t == "string" {
+               return fmt.Sprintf("\"%v\"", f.v)
+       }
+       return fmt.Sprintf("%v", f.v)
+}
+
+func (f *StringFloat64) UnmarshalJSON(data []byte) error {
+       var i interface{}
+       if err := json.Unmarshal(data, &i); err != nil {
+               return err
+       }
+       switch i.(type) {
+       case float64:
+               f.t = "float64"
+       case string:
+               f.t = "string"
+       }
+       value, err := cast.ToFloat64E(i)
+       if err != nil {
+               return err
+       }
+       f.v = value
+       return nil
+}
+
+func (f *StringFloat64) Value() (driver.Value, error) {
+       if f == nil {
+               return nil, nil
+       }
+       return f.v, nil
+}
+
+func (f *StringFloat64) Scan(v interface{}) error {
+       switch value := v.(type) {
+       case float64:
+               *f = StringFloat64{
+                       v: value,
+                       t: "float64",
+               }
+       case string:
+               *f = StringFloat64{
+                       v: cast.ToFloat64(value),
+                       t: "string",
+               }
+       default:
+               return fmt.Errorf("%+v is an unknown type, with value: %v", v, 
value)
+       }
+       return nil
+}
diff --git a/backend/plugins/zentao/api/remote.go 
b/backend/plugins/zentao/api/remote.go
index 46965821f..7717eeb25 100644
--- a/backend/plugins/zentao/api/remote.go
+++ b/backend/plugins/zentao/api/remote.go
@@ -36,12 +36,6 @@ type ProjectResponse struct {
        Values []models.ZentaoProject `json:"projects"`
 }
 
-func (pr *ProjectResponse) ConvertFix() {
-       for i := range pr.Values {
-               pr.Values[i].ConvertFix()
-       }
-}
-
 func getGroup(basicRes context.BasicRes, gid string, queryData 
*api.RemoteQueryData, connection models.ZentaoConnection) 
([]api.BaseRemoteGroupResponse, errors.Error) {
        return []api.BaseRemoteGroupResponse{
                /*{
@@ -100,9 +94,6 @@ func RemoteScopes(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput, er
                                        logger.Error(err, "unmarshal projects 
response")
                                        return nil, err
                                }
-
-                               resBody.ConvertFix()
-
                                return resBody.Values, nil
                        })
        }
diff --git a/backend/plugins/zentao/models/project.go 
b/backend/plugins/zentao/models/project.go
index 3db2744f2..64dfed0b3 100644
--- a/backend/plugins/zentao/models/project.go
+++ b/backend/plugins/zentao/models/project.go
@@ -18,15 +18,98 @@ limitations under the License.
 package models
 
 import (
+       "database/sql/driver"
+       "encoding/json"
        "fmt"
-       "strconv"
-
        "github.com/spf13/cast"
+       "strconv"
 
        "github.com/apache/incubator-devlake/core/models/common"
        "github.com/apache/incubator-devlake/core/plugin"
 )
 
+type OperatedBy struct {
+       Raw interface{}
+       CoreOperatedBy
+}
+
+type CoreOperatedBy struct {
+       Type     string
+       Account  string
+       RealName string
+}
+
+func (by *OperatedBy) Value() (driver.Value, error) {
+       if by == nil {
+               return nil, nil
+       }
+       b, err := json.Marshal(by.CoreOperatedBy)
+       if err != nil {
+               return nil, err
+       }
+       return string(b), nil
+}
+
+func (by *OperatedBy) Scan(v interface{}) error {
+       switch value := v.(type) {
+       case string:
+               var coreOperatedBy CoreOperatedBy
+               if err := json.Unmarshal([]byte(value), &coreOperatedBy); err 
!= nil {
+                       return err
+               }
+               *by = OperatedBy{
+                       Raw:            nil,
+                       CoreOperatedBy: coreOperatedBy,
+               }
+       default:
+               return fmt.Errorf("%+v is an unknown type, with value: %v", v, 
value)
+       }
+       return nil
+}
+
+func (by *OperatedBy) MarshalJSON() ([]byte, error) {
+       if by == nil {
+               return json.Marshal(nil)
+       }
+       return json.Marshal(by.Raw)
+}
+
+func (by *OperatedBy) UnmarshalJSON(data []byte) error {
+       var i interface{}
+       if err := json.Unmarshal(data, &i); err != nil {
+               return err
+       }
+       by.Raw = i
+       switch i.(type) {
+       case string:
+               by.Type = "string"
+               by.Account = cast.ToString(by.Raw)
+               by.RealName = cast.ToString(by.Raw)
+       default:
+               by.Type = "struct"
+               type ByUser struct {
+                       ID       int    `json:"id"`
+                       Account  string `json:"account"`
+                       Avatar   string `json:"avatar"`
+                       RealName string `json:"realname"`
+               }
+               var byUser ByUser
+               if err := json.Unmarshal(data, &byUser); err != nil {
+                       return err
+               }
+               by.Account = byUser.Account
+               by.RealName = byUser.RealName
+       }
+       return nil
+}
+
+func (by *OperatedBy) String() string {
+       if by == nil {
+               return "<nil>"
+       }
+       return by.Account
+}
+
 type ZentaoProject struct {
        common.Scope
        Id            int64               `json:"id" mapstructure:"id" 
gorm:"primaryKey;type:BIGINT  NOT NULL;autoIncrement:false"`
@@ -65,11 +148,9 @@ type ZentaoProject struct {
        OpenedVersion string              `json:"openedVersion" 
mapstructure:"openedVersion"`
        //LastEditedBy   string              `json:"lastEditedBy" 
mapstructure:"lastEditedBy"`
        LastEditedDate *common.Iso8601Time `json:"lastEditedDate" 
mapstructure:"lastEditedDate"`
-       ClosedBy       string
-       ClosedByRes    interface{}         `json:"closedBy" 
mapstructure:"closedBy" gorm:"-"`
+       ClosedBy       *OperatedBy         `json:"closedBy" 
mapstructure:"closedBy" gorm:"-"`
        ClosedDate     *common.Iso8601Time `json:"closedDate" 
mapstructure:"closedDate"`
-       CanceledBy     string
-       CanceledByRes  interface{}         `json:"canceledBy" 
mapstructure:"canceledBy" gorm:"-"`
+       CanceledBy     *OperatedBy         `json:"canceledBy" 
mapstructure:"canceledBy" gorm:"-"`
        CanceledDate   *common.Iso8601Time `json:"canceledDate" 
mapstructure:"canceledDate"`
        SuspendedDate  *common.Iso8601Time `json:"suspendedDate" 
mapstructure:"suspendedDate"`
        PO             string              `json:"po" mapstructure:"po"`
@@ -89,11 +170,10 @@ type ZentaoProject struct {
        TeamCount      int    `json:"teamCount" mapstructure:"teamCount"`
        LeftTasks      string `json:"leftTasks" mapstructure:"leftTasks"`
        //TeamMembers   []interface{} `json:"teamMembers" gorm:"-"`
-       TotalEstimate float64 `json:"totalEstimate" 
mapstructure:"totalEstimate"`
-       TotalConsumed float64 `json:"totalConsumed" 
mapstructure:"totalConsumed"`
-       TotalLeft     float64 `json:"totalLeft" mapstructure:"totalLeft"`
-       Progress      float64
-       ProgressRes   interface{} `json:"progress" mapstructure:"progress" 
gorm:"-"`
+       TotalEstimate float64               `json:"totalEstimate" 
mapstructure:"totalEstimate"`
+       TotalConsumed float64               `json:"totalConsumed" 
mapstructure:"totalConsumed"`
+       TotalLeft     float64               `json:"totalLeft" 
mapstructure:"totalLeft"`
+       Progress      *common.StringFloat64 `json:"progress" 
mapstructure:"progress" gorm:"-"`
 }
 
 type PM struct {
@@ -116,44 +196,6 @@ type Hours struct {
        HoursTotalReal     float64 `json:"totalReal" mapstructure:"totalReal"`
 }
 
-func (p *ZentaoProject) fixProgressField() {
-       p.Progress = cast.ToFloat64(p.ProgressRes)
-}
-
-func (p *ZentaoProject) fixClosedByResField() {
-       switch cb := p.ClosedByRes.(type) {
-       case string:
-               p.ClosedBy = cb
-       default:
-               if cb == nil {
-                       p.ClosedBy = ""
-               } else {
-                       p.ClosedBy = fmt.Sprintf("%v", cb)
-               }
-       }
-       p.ClosedByRes = p.ClosedBy
-}
-
-func (p *ZentaoProject) fixCanceledByResField() {
-       switch cb := p.CanceledByRes.(type) {
-       case string:
-               p.CanceledBy = cb
-       default:
-               if cb == nil {
-                       p.CanceledBy = ""
-               } else {
-                       p.CanceledBy = fmt.Sprintf("%v", cb)
-               }
-       }
-       p.CanceledByRes = p.CanceledBy
-}
-
-func (p *ZentaoProject) ConvertFix() {
-       p.fixProgressField()
-       p.fixClosedByResField()
-       p.fixCanceledByResField()
-}
-
 func (p ZentaoProject) TableName() string {
        return "_tool_zentao_projects"
 }

Reply via email to