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

abeizn pushed a commit to branch release-v0.12
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git


The following commit(s) were added to refs/heads/release-v0.12 by this push:
     new 6a6030bd fix: patch /blueprints/:id wouldn't handle byte[]
6a6030bd is described below

commit 6a6030bd9b17d937c75d288ee382576977dde69b
Author: Klesh Wong <[email protected]>
AuthorDate: Tue Aug 2 22:13:13 2022 +0800

    fix: patch /blueprints/:id wouldn't handle byte[]
---
 models/blueprint.go                      | 33 +++++++-------
 plugins/helper/iso8601time.go            | 46 -------------------
 plugins/helper/mapstructure.go           | 76 ++++++++++++++++++++++++++++++++
 plugins/helper/mapstructure_test.go      | 58 ++++++++++++++++++++++++
 scripts/pm/framework/blueprint-create.sh | 65 +++++++++++++++++++++++++++
 scripts/pm/framework/blueprint-update.sh | 68 ++++++++++++++++++++++++++++
 services/blueprint.go                    | 14 +++---
 7 files changed, 292 insertions(+), 68 deletions(-)

diff --git a/models/blueprint.go b/models/blueprint.go
index adced10e..413bb9aa 100644
--- a/models/blueprint.go
+++ b/models/blueprint.go
@@ -22,20 +22,21 @@ import (
 
        "github.com/apache/incubator-devlake/models/common"
        "github.com/apache/incubator-devlake/plugins/core"
-       "gorm.io/datatypes"
 )
 
-const BLUEPRINT_MODE_NORMAL = "NORMAL"
-const BLUEPRINT_MODE_ADVANCED = "ADVANCED"
+const (
+       BLUEPRINT_MODE_NORMAL   = "NORMAL"
+       BLUEPRINT_MODE_ADVANCED = "ADVANCED"
+)
 
 type Blueprint struct {
-       Name       string         `json:"name" validate:"required"`
-       Mode       string         `json:"mode" gorm:"varchar(20)" 
validate:"required,oneof=NORMAL ADVANCED"`
-       Plan       datatypes.JSON `json:"plan"`
-       Enable     bool           `json:"enable"`
-       CronConfig string         `json:"cronConfig"`
-       IsManual   bool           `json:"isManual"`
-       Settings   datatypes.JSON `json:"settings"`
+       Name       string          `json:"name" validate:"required"`
+       Mode       string          `json:"mode" gorm:"varchar(20)" 
validate:"required,oneof=NORMAL ADVANCED"`
+       Plan       json.RawMessage `json:"plan"`
+       Enable     bool            `json:"enable"`
+       CronConfig string          `json:"cronConfig"`
+       IsManual   bool            `json:"isManual"`
+       Settings   json.RawMessage `json:"settings"`
        common.Model
 }
 
@@ -50,10 +51,10 @@ type BlueprintSettings struct {
 
 // UnmarshalPlan unmarshals Plan in JSON to strong-typed core.PipelinePlan
 func (bp *Blueprint) UnmarshalPlan() (core.PipelinePlan, error) {
-               var plan core.PipelinePlan
-               err := json.Unmarshal(bp.Plan, &plan)
-               if err != nil {
-                       return nil, err
-               }
-               return plan, nil
+       var plan core.PipelinePlan
+       err := json.Unmarshal(bp.Plan, &plan)
+       if err != nil {
+               return nil, err
+       }
+       return plan, nil
 }
diff --git a/plugins/helper/iso8601time.go b/plugins/helper/iso8601time.go
index 6232eb83..5b02e9c9 100644
--- a/plugins/helper/iso8601time.go
+++ b/plugins/helper/iso8601time.go
@@ -19,12 +19,9 @@ package helper
 
 import (
        "fmt"
-       "reflect"
        "regexp"
        "strings"
        "time"
-
-       "github.com/mitchellh/mapstructure"
 )
 
 /*
@@ -134,46 +131,3 @@ func Iso8601TimeToTime(iso8601Time *Iso8601Time) 
*time.Time {
        t := iso8601Time.ToTime()
        return &t
 }
-
-// DecodeMapStruct with time.Time and Iso8601Time support
-func DecodeMapStruct(input map[string]interface{}, result interface{}) error {
-       decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
-               Metadata: nil,
-               DecodeHook: mapstructure.ComposeDecodeHookFunc(
-                       func(f reflect.Type, t reflect.Type, data interface{}) 
(interface{}, error) {
-                               if t != reflect.TypeOf(Iso8601Time{}) && t != 
reflect.TypeOf(time.Time{}) {
-                                       return data, nil
-                               }
-
-                               var tt time.Time
-                               var err error
-
-                               switch f.Kind() {
-                               case reflect.String:
-                                       tt, err = 
ConvertStringToTime(data.(string))
-                               case reflect.Float64:
-                                       tt = time.Unix(0, 
int64(data.(float64))*int64(time.Millisecond))
-                               case reflect.Int64:
-                                       tt = time.Unix(0, 
data.(int64)*int64(time.Millisecond))
-                               }
-                               if err != nil {
-                                       return data, nil
-                               }
-
-                               if t == reflect.TypeOf(Iso8601Time{}) {
-                                       return Iso8601Time{time: tt}, nil
-                               }
-                               return tt, nil
-                       },
-               ),
-               Result: result,
-       })
-       if err != nil {
-               return err
-       }
-
-       if err := decoder.Decode(input); err != nil {
-               return err
-       }
-       return err
-}
diff --git a/plugins/helper/mapstructure.go b/plugins/helper/mapstructure.go
new file mode 100644
index 00000000..17e74df4
--- /dev/null
+++ b/plugins/helper/mapstructure.go
@@ -0,0 +1,76 @@
+/*
+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 helper
+
+import (
+       "encoding/json"
+       "reflect"
+       "time"
+
+       "github.com/mitchellh/mapstructure"
+)
+
+// DecodeMapStruct with time.Time and Iso8601Time support
+func DecodeMapStruct(input map[string]interface{}, result interface{}) error {
+       decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
+               ZeroFields: true,
+               DecodeHook: mapstructure.ComposeDecodeHookFunc(
+                       func(f reflect.Type, t reflect.Type, data interface{}) 
(interface{}, error) {
+                               if data == nil {
+                                       return nil, nil
+                               }
+                               if t == reflect.TypeOf(json.RawMessage{}) {
+                                       return json.Marshal(data)
+                               }
+
+                               if t != reflect.TypeOf(Iso8601Time{}) && t != 
reflect.TypeOf(time.Time{}) {
+                                       return data, nil
+                               }
+
+                               var tt time.Time
+                               var err error
+
+                               switch f.Kind() {
+                               case reflect.String:
+                                       tt, err = 
ConvertStringToTime(data.(string))
+                               case reflect.Float64:
+                                       tt = time.Unix(0, 
int64(data.(float64))*int64(time.Millisecond))
+                               case reflect.Int64:
+                                       tt = time.Unix(0, 
data.(int64)*int64(time.Millisecond))
+                               }
+                               if err != nil {
+                                       return data, nil
+                               }
+
+                               if t == reflect.TypeOf(Iso8601Time{}) {
+                                       return Iso8601Time{time: tt}, nil
+                               }
+                               return tt, nil
+                       },
+               ),
+               Result: result,
+       })
+       if err != nil {
+               return err
+       }
+
+       if err := decoder.Decode(input); err != nil {
+               return err
+       }
+       return err
+}
diff --git a/plugins/helper/mapstructure_test.go 
b/plugins/helper/mapstructure_test.go
new file mode 100644
index 00000000..34ab85e3
--- /dev/null
+++ b/plugins/helper/mapstructure_test.go
@@ -0,0 +1,58 @@
+/*
+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 helper
+
+import (
+       "encoding/json"
+       "fmt"
+       "testing"
+
+       "github.com/stretchr/testify/assert"
+)
+
+type DecodeMapStructJson struct {
+       Id       int
+       Settings json.RawMessage
+       Plan     json.RawMessage
+       Existing json.RawMessage
+}
+
+func TestDecodeMapStructJsonRawMessage(t *testing.T) {
+       input := map[string]interface{}{
+               "id": 100,
+               "settings": map[string]interface{}{
+                       "version": "1.0.0",
+               },
+       }
+
+       decoded := &DecodeMapStructJson{
+               Settings: json.RawMessage(`{"version": "1.0.101"}`),
+               Existing: json.RawMessage(`{"hello", "world"}`),
+       }
+       err := DecodeMapStruct(input, decoded)
+       fmt.Println(string(decoded.Settings))
+       assert.Nil(t, err)
+       assert.Equal(t, decoded.Id, 100)
+       assert.Nil(t, decoded.Plan)
+       assert.NotNil(t, decoded.Settings)
+       settings := make(map[string]string)
+       err = json.Unmarshal(decoded.Settings, &settings)
+       assert.Nil(t, err)
+       assert.Equal(t, settings["version"], "1.0.0")
+       assert.Equal(t, decoded.Existing, json.RawMessage(`{"hello", "world"}`))
+}
diff --git a/scripts/pm/framework/blueprint-create.sh 
b/scripts/pm/framework/blueprint-create.sh
new file mode 100755
index 00000000..be6cabcd
--- /dev/null
+++ b/scripts/pm/framework/blueprint-create.sh
@@ -0,0 +1,65 @@
+#!/bin/sh
+#
+# 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.
+#
+
+. "$(dirname $0)/../vars/active-vars.sh"
+
+curl -sv $LAKE_ENDPOINT/blueprints -H "Content-Type: application/json" --data 
@- <<JSON | jq
+{
+    "name": "MY BLUEPRINT2",
+    "cronConfig": "0 0 * * *",
+    "settings": {
+        "version": "1.0.0",
+        "connections": [
+            {
+                "plugin": "jira",
+                "connectionId": 1,
+                "scope": [
+                    {
+                        "transformation": {
+                            "epicKeyField": "customfield_10014",
+                            "typeMappings": {
+                                "缺陷": {
+                                    "standardType": "Bug"
+                                },
+                                "线上事故": {
+                                    "standardType": "Incident"
+                                },
+                                "故事": {
+                                    "standardType": "Requirement"
+                                }
+                            },
+                            "storyPointField": "customfield_10024",
+                            "remotelinkCommitShaPattern": 
"/commit/([0-9a-f]{40})$"
+                        },
+                        "options": {
+                            "boardId": 70
+                        },
+                        "entities": [
+                            "TICKET",
+                            "CROSS"
+                        ]
+                    }
+                ]
+            }
+        ]
+    },
+    "enable": true,
+    "mode": "NORMAL",
+    "isManual": false
+}
+JSON
diff --git a/scripts/pm/framework/blueprint-update.sh 
b/scripts/pm/framework/blueprint-update.sh
new file mode 100755
index 00000000..13d8a71c
--- /dev/null
+++ b/scripts/pm/framework/blueprint-update.sh
@@ -0,0 +1,68 @@
+#!/bin/sh
+#
+# 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.
+#
+
+. "$(dirname $0)/../vars/active-vars.sh"
+
+pipeline_id=${1-8}
+
+curl -sv -XPATCH $LAKE_ENDPOINT/blueprints/$pipeline_id \
+    -H "Content-Type: application/json" --data @- <<JSON | jq
+{
+    "name": "MY BLUEPRINT2",
+    "cronConfig": "0 0 * * *",
+    "settings": {
+        "version": "1.0.0",
+        "connections": [
+            {
+                "plugin": "jira",
+                "connectionId": 1,
+                "scope": [
+                    {
+                        "transformation": {
+                            "epicKeyField": "customfield_10014",
+                            "typeMappings": {
+                                "缺陷": {
+                                    "standardType": "Bug"
+                                },
+                                "线上事故": {
+                                    "standardType": "Incident"
+                                },
+                                "故事": {
+                                    "standardType": "Requirement"
+                                }
+                            },
+                            "storyPointField": "customfield_10024",
+                            "remotelinkCommitShaPattern": 
"/commit/([0-9a-f]{40})$"
+                        },
+                        "options": {
+                            "boardId": 70
+                        },
+                        "entities": [
+                            "TICKET",
+                            "CROSS"
+                        ]
+                    }
+                ]
+            }
+        ]
+    },
+    "enable": true,
+    "mode": "NORMAL",
+    "isManual": false
+}
+JSON
diff --git a/services/blueprint.go b/services/blueprint.go
index c49599b9..4709a664 100644
--- a/services/blueprint.go
+++ b/services/blueprint.go
@@ -26,10 +26,9 @@ import (
        "github.com/apache/incubator-devlake/logger"
        "github.com/apache/incubator-devlake/models"
        "github.com/apache/incubator-devlake/plugins/core"
+       "github.com/apache/incubator-devlake/plugins/helper"
        "github.com/go-playground/validator/v10"
-       "github.com/mitchellh/mapstructure"
        "github.com/robfig/cron/v3"
-       "gorm.io/datatypes"
        "gorm.io/gorm"
 )
 
@@ -40,8 +39,10 @@ type BlueprintQuery struct {
        PageSize int   `form:"pageSize"`
 }
 
-var blueprintLog = logger.Global.Nested("blueprint")
-var vld = validator.New()
+var (
+       blueprintLog = logger.Global.Nested("blueprint")
+       vld          = validator.New()
+)
 
 // CreateBlueprint accepts a Blueprint instance and insert it to database
 func CreateBlueprint(blueprint *models.Blueprint) error {
@@ -139,7 +140,7 @@ func PatchBlueprint(id uint64, body map[string]interface{}) 
(*models.Blueprint,
                return nil, err
        }
        originMode := blueprint.Mode
-       err = mapstructure.Decode(body, blueprint)
+       err = helper.DecodeMapStruct(body, blueprint)
        if err != nil {
                return nil, err
        }
@@ -234,10 +235,11 @@ func createPipelineByBlueprint(blueprintId uint64, name 
string, plan core.Pipeli
 }
 
 // GeneratePlanJson generates pipeline plan by version
-func GeneratePlanJson(settings datatypes.JSON) (datatypes.JSON, error) {
+func GeneratePlanJson(settings json.RawMessage) (json.RawMessage, error) {
        bpSettings := new(models.BlueprintSettings)
        err := json.Unmarshal(settings, bpSettings)
        if err != nil {
+               fmt.Println(string(settings))
                return nil, err
        }
        var plan interface{}

Reply via email to