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

abeizn 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 db1c42669 feat: Jira dev panel regex APIs (#5423)
db1c42669 is described below

commit db1c42669cc2f4306141574d869910af1dd3206d
Author: Liang Zhang <[email protected]>
AuthorDate: Tue Jun 13 16:11:39 2023 +0800

    feat: Jira dev panel regex APIs (#5423)
    
    * feat: Jira dev panel regex APIs
    
    * fix: unit test
---
 backend/plugins/jira/api/scope_config.go           | 130 +++++++++++++++++
 backend/plugins/jira/api/scope_config_test.go      | 161 +++++++++++++++++++++
 backend/plugins/jira/e2e/issue_repo_commit_test.go |   8 +-
 backend/plugins/jira/e2e/remotelink_test.go        |   7 +-
 backend/plugins/jira/impl/impl.go                  |   6 +
 .../20230609_clear_repo_pattern.go                 |  49 +++++++
 .../jira/models/migrationscripts/register.go       |   1 +
 .../jira/tasks/issue_repo_commit_convertor.go      |   2 +-
 backend/plugins/jira/tasks/remotelink_extractor.go |   2 +-
 backend/plugins/jira/tasks/task_data.go            |  25 ++--
 backend/plugins/jira/tasks/task_data_test.go       |   4 +-
 11 files changed, 376 insertions(+), 19 deletions(-)

diff --git a/backend/plugins/jira/api/scope_config.go 
b/backend/plugins/jira/api/scope_config.go
index e2746e2df..178c94a32 100644
--- a/backend/plugins/jira/api/scope_config.go
+++ b/backend/plugins/jira/api/scope_config.go
@@ -22,8 +22,10 @@ import (
        "fmt"
        "net/http"
        "net/url"
+       "regexp"
        "sort"
        "strconv"
+       "strings"
 
        "github.com/apache/incubator-devlake/core/dal"
        "github.com/apache/incubator-devlake/core/errors"
@@ -34,6 +36,25 @@ import (
        "github.com/mitchellh/mapstructure"
 )
 
+type genRegexReq struct {
+       Pattern string `json:"pattern"`
+}
+
+type genRegexResp struct {
+       Regex string `json:"regex"`
+}
+
+type applyRegexReq struct {
+       Regex string   `json:"regex"`
+       Urls  []string `json:"urls"`
+}
+
+type repo struct {
+       Namespace string `json:"namespace"`
+       RepoName  string `json:"repo_name"`
+       CommitSha string `json:"commit_sha"`
+}
+
 // CreateScopeConfig create scope config for Jira
 // @Summary create scope config for Jira
 // @Description create scope config for Jira
@@ -313,3 +334,112 @@ func GetCommitsURLs(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput,
        }
        return &plugin.ApiResourceOutput{Body: commitURLs, Status: 
http.StatusOK}, nil
 }
+
+// GenRegex generate regex from url
+// @Summary generate regex from url
+// @Description generate regex from url
+// @Tags plugins/jira
+// @Param generate-regex body genRegexReq true "generate regex"
+// @Success 200  {object} genRegexResp
+// @Failure 400  {object} shared.ApiBody "Bad Request"
+// @Failure 500  {object} shared.ApiBody "Internal Error"
+// @Router /plugins/jira/generate-regex [POST]
+func GenRegex(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, 
errors.Error) {
+       var req genRegexReq
+       err := api.Decode(input.Body, &req, nil)
+       if err != nil {
+               return nil, err
+       }
+       err = checkInput(req.Pattern)
+       if err != nil {
+               return nil, err
+       }
+       reg := genRegex(req.Pattern)
+       _, e := regexp.Compile(reg)
+       if e != nil {
+               return nil, errors.BadInput.Wrap(e, "invalid url")
+       }
+
+       return &plugin.ApiResourceOutput{Body: genRegexResp{Regex: reg}, 
Status: http.StatusOK}, nil
+}
+
+func checkInput(input string) errors.Error {
+       input = strings.TrimSpace(input)
+       if input == "" {
+               return errors.BadInput.New("empty input")
+       }
+       if !strings.Contains(input, "{namespace}") {
+               return errors.BadInput.New("missing {namespace}")
+       }
+       if !strings.Contains(input, "{repo_name}") {
+               return errors.BadInput.New("missing {repo_name}")
+       }
+       if !strings.Contains(input, "{commit_sha}") {
+               return errors.BadInput.New("missing {commit_sha}")
+       }
+       return nil
+}
+
+func genRegex(s string) string {
+       s = strings.TrimSpace(s)
+       s = strings.Replace(s, "{namespace}", `(?P<namespace>\S+)`, -1)
+       s = strings.Replace(s, "{repo_name}", `(?P<repo_name>\S+)`, -1)
+       s = strings.Replace(s, "{commit_sha}", `(?P<commit_sha>\w{40})`, -1)
+       return s
+}
+
+// ApplyRegex return parsed commits URLs
+// @Summary return parsed commits URLs
+// @Description return parsed commits URLs
+// @Tags plugins/jira
+// @Param apply-regex body applyRegexReq true "apply regex"
+// @Success 200  {object} []string
+// @Failure 400  {object} shared.ApiBody "Bad Request"
+// @Failure 500  {object} shared.ApiBody "Internal Error"
+// @Router /plugins/jira/apply-regex [POST]
+func ApplyRegex(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, 
errors.Error) {
+       var req applyRegexReq
+       err := api.Decode(input.Body, &req, nil)
+       if err != nil {
+               return nil, err
+       }
+       var repos []*repo
+       for _, u := range req.Urls {
+               r, e1 := applyRegex(req.Regex, u)
+               if e1 != nil {
+                       return nil, err
+               }
+               repos = append(repos, r)
+       }
+       return &plugin.ApiResourceOutput{Body: repos, Status: http.StatusOK}, 
nil
+}
+
+func applyRegex(regexStr, commitUrl string) (*repo, errors.Error) {
+       pattern, e := regexp.Compile(regexStr)
+       if e != nil {
+               return nil, errors.BadInput.Wrap(e, "invalid regex")
+       }
+       if !pattern.MatchString(commitUrl) {
+               return nil, errors.BadInput.New("invalid url")
+       }
+       group := pattern.FindStringSubmatch(commitUrl)
+       if len(group) != 4 {
+               return nil, errors.BadInput.New("invalid group count")
+       }
+       r := new(repo)
+       for i, name := range pattern.SubexpNames() {
+               if i != 0 && name != "" {
+                       switch name {
+                       case "namespace":
+                               r.Namespace = group[i]
+                       case "repo_name":
+                               r.RepoName = group[i]
+                       case "commit_sha":
+                               r.CommitSha = group[i]
+                       default:
+                               return nil, errors.BadInput.New("invalid group 
name")
+                       }
+               }
+       }
+       return r, nil
+}
diff --git a/backend/plugins/jira/api/scope_config_test.go 
b/backend/plugins/jira/api/scope_config_test.go
new file mode 100644
index 000000000..faca407f1
--- /dev/null
+++ b/backend/plugins/jira/api/scope_config_test.go
@@ -0,0 +1,161 @@
+/*
+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 api
+
+import (
+       "testing"
+
+       "github.com/apache/incubator-devlake/core/errors"
+       "github.com/stretchr/testify/assert"
+)
+
+func generateThenApplyRegex(pattern, commitUrl string) (*repo, errors.Error) {
+       reg := genRegex(pattern)
+       return applyRegex(reg, commitUrl)
+}
+
+func Test_genRegex(t *testing.T) {
+       type args struct {
+               url string
+       }
+       tests := []struct {
+               name string
+               args args
+               want string
+       }{
+               {
+                       "test1",
+                       args{
+                               
"https://gitlab.com/{namespace}/{repo_name}/-/commit/{commit_sha}";,
+                       },
+                       
`https://gitlab.com/(?P<namespace>\S+)/(?P<repo_name>\S+)/-/commit/(?P<commit_sha>\w{40})`,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       assert.Equalf(t, tt.want, genRegex(tt.args.url), 
"genRegex(%v)", tt.args.url)
+               })
+       }
+}
+
+func Test_applyRegex(t *testing.T) {
+       type args struct {
+               regexStr  string
+               commitUrl string
+       }
+       tests := []struct {
+               name  string
+               args  args
+               want  *repo
+               want1 errors.Error
+       }{
+               {
+                       "test1",
+                       args{
+                               
`https://gitlab.com/(?P<namespace>[^/]+)/(?P<repo_name>[^/]+)/-/commit/(?P<commit_sha>\w{40})`,
+                               
"https://gitlab.com/apache/incubator-devlake/-/commit/1234567890123456789012345678901234567890";,
+                       },
+                       &repo{
+                               "apache",
+                               "incubator-devlake",
+                               "1234567890123456789012345678901234567890",
+                       },
+                       nil,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       got, got1 := applyRegex(tt.args.regexStr, 
tt.args.commitUrl)
+                       assert.Equalf(t, tt.want, got, "applyRegex(%v, %v)", 
tt.args.regexStr, tt.args.commitUrl)
+                       assert.Equalf(t, tt.want1, got1, "applyRegex(%v, %v)", 
tt.args.regexStr, tt.args.commitUrl)
+               })
+       }
+}
+
+func Test_generateThenApplyRegex(t *testing.T) {
+       type args struct {
+               pattern   string
+               commitUrl string
+       }
+       tests := []struct {
+               name  string
+               args  args
+               want  *repo
+               want1 errors.Error
+       }{
+               {
+                       "test1",
+                       args{
+                               
"https://gitlab.com/{namespace}/{repo_name}/-/commit/{commit_sha}";,
+                               
"https://gitlab.com/apache/incubator-devlake/-/commit/1234567890123456789012345678901234567890";,
+                       },
+                       &repo{
+                               "apache",
+                               "incubator-devlake",
+                               "1234567890123456789012345678901234567890",
+                       },
+                       nil,
+               },
+               {
+                       "test2",
+                       args{
+                               
"https://bitbucket.org/{namespace}/{repo_name}/commits/{commit_sha}";,
+                               
"https://bitbucket.org/mynamespace/incubator-devlake/commits/fef8d697fbb9a2b336be6fa2e2848f585c86a622";,
+                       },
+                       &repo{
+                               "mynamespace",
+                               "incubator-devlake",
+                               "fef8d697fbb9a2b336be6fa2e2848f585c86a622",
+                       },
+                       nil,
+               },
+               {
+                       "test3",
+                       args{
+                               
"https://example.com/bitbucket/projects/{namespace}/repos/{repo_name}/commits/{commit_sha}";,
+                               
"https://example.com/bitbucket/projects/PROJECTNAME/repos/ui_jira/commits/1e23e7f1a0cb539c7408c38e5a37de3bc836bc94";,
+                       },
+                       &repo{
+                               "PROJECTNAME",
+                               "ui_jira",
+                               "1e23e7f1a0cb539c7408c38e5a37de3bc836bc94",
+                       },
+                       nil,
+               },
+               {
+                       "test4",
+                       args{
+                               
"https://gitlab.com/{namespace}/{repo_name}/commits/{commit_sha}";,
+                               
"https://gitlab.com/namespace1/namespace2/myrepo/commits/050baf4575caf069275f5fa14db9ad4a21a79883";,
+                       },
+                       &repo{
+                               "namespace1/namespace2",
+                               "myrepo",
+                               "050baf4575caf069275f5fa14db9ad4a21a79883",
+                       },
+                       nil,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       got, got1 := generateThenApplyRegex(tt.args.pattern, 
tt.args.commitUrl)
+                       assert.Equalf(t, tt.want, got, 
"generateThenApplyRegex(%v, %v)", tt.args.pattern, tt.args.commitUrl)
+                       assert.Equalf(t, tt.want1, got1, 
"generateThenApplyRegex(%v, %v)", tt.args.pattern, tt.args.commitUrl)
+               })
+       }
+}
diff --git a/backend/plugins/jira/e2e/issue_repo_commit_test.go 
b/backend/plugins/jira/e2e/issue_repo_commit_test.go
index 4a5f3e0df..c060fcf12 100644
--- a/backend/plugins/jira/e2e/issue_repo_commit_test.go
+++ b/backend/plugins/jira/e2e/issue_repo_commit_test.go
@@ -37,10 +37,10 @@ func TestConvertIssueRepoCommitsDataFlow(t *testing.T) {
                        BoardId:      8,
                        ScopeConfig: &tasks.JiraScopeConfig{
                                RemotelinkCommitShaPattern: `.*/commit/(.*)`,
-                               RemotelinkRepoPattern: []string{
-                                       
`https://bitbucket.org/(?P<namespace>[^/]+)/(?P<repo_name>[^/]+)/commits/(?P<commit_sha>\w{40})`,
-                                       
`https://gitlab.com/(?P<namespace>\S+)/(?P<repo_name>\S+)/-/commit/(?P<commit_sha>\w{40})`,
-                                       
`https://github.com/(?P<namespace>[^/]+)/(?P<repo_name>[^/]+)/commit/(?P<commit_sha>\w{40})`,
+                               RemotelinkRepoPattern: []tasks.CommitUrlPattern{
+                                       {"", 
`https://bitbucket.org/(?P<namespace>[^/]+)/(?P<repo_name>[^/]+)/commits/(?P<commit_sha>\w{40})`},
+                                       {"", 
`https://gitlab.com/(?P<namespace>\S+)/(?P<repo_name>\S+)/-/commit/(?P<commit_sha>\w{40})`},
+                                       {"", 
`https://github.com/(?P<namespace>[^/]+)/(?P<repo_name>[^/]+)/commit/(?P<commit_sha>\w{40})`},
                                },
                        },
                },
diff --git a/backend/plugins/jira/e2e/remotelink_test.go 
b/backend/plugins/jira/e2e/remotelink_test.go
index 5192e4b97..ee773f74f 100644
--- a/backend/plugins/jira/e2e/remotelink_test.go
+++ b/backend/plugins/jira/e2e/remotelink_test.go
@@ -36,7 +36,12 @@ func TestRemotelinkDataFlow(t *testing.T) {
                        BoardId:      8,
                        ScopeConfig: &tasks.JiraScopeConfig{
                                RemotelinkCommitShaPattern: ".*/commit/(.*)",
-                               RemotelinkRepoPattern:      
[]string{`https://example.com/(?P<namespace>\S+)/(?P<repo_name>\S+)/-/commits/(?P<commit_sha>\w{40})`},
+                               RemotelinkRepoPattern: []tasks.CommitUrlPattern{
+                                       {
+                                               Pattern: "",
+                                               Regex:   
`https://example.com/(?P<namespace>\S+)/(?P<repo_name>\S+)/-/commits/(?P<commit_sha>\w{40})`,
+                                       },
+                               },
                        },
                },
        }
diff --git a/backend/plugins/jira/impl/impl.go 
b/backend/plugins/jira/impl/impl.go
index 933d0c0cf..4ced7222b 100644
--- a/backend/plugins/jira/impl/impl.go
+++ b/backend/plugins/jira/impl/impl.go
@@ -311,6 +311,12 @@ func (p Jira) ApiResources() 
map[string]map[string]plugin.ApiResourceHandler {
                "connections/:connectionId/dev-panel-commits": {
                        "GET": api.GetCommitsURLs,
                },
+               "generate-regex": {
+                       "POST": api.GenRegex,
+               },
+               "apply-regex": {
+                       "POST": api.ApplyRegex,
+               },
        }
 }
 
diff --git 
a/backend/plugins/jira/models/migrationscripts/20230609_clear_repo_pattern.go 
b/backend/plugins/jira/models/migrationscripts/20230609_clear_repo_pattern.go
new file mode 100644
index 000000000..c1d4a41c4
--- /dev/null
+++ 
b/backend/plugins/jira/models/migrationscripts/20230609_clear_repo_pattern.go
@@ -0,0 +1,49 @@
+/*
+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 migrationscripts
+
+import (
+       "encoding/json"
+
+       "github.com/apache/incubator-devlake/core/context"
+       "github.com/apache/incubator-devlake/core/dal"
+       "github.com/apache/incubator-devlake/core/errors"
+)
+
+type scopeConfig20230609 struct {
+       RemotelinkRepoPattern json.RawMessage 
`mapstructure:"remotelinkRepoPattern,omitempty" json:"remotelinkRepoPattern"`
+}
+
+func (r scopeConfig20230609) TableName() string {
+       return "_tool_jira_scope_configs"
+}
+
+type clearRepoPattern struct{}
+
+func (script *clearRepoPattern) Up(basicRes context.BasicRes) errors.Error {
+       db := basicRes.GetDal()
+       return db.UpdateColumn(&scopeConfig20230609{}, 
"remotelink_repo_pattern", []byte{}, dal.Where("1=1"))
+}
+
+func (*clearRepoPattern) Version() uint64 {
+       return 20230609093856
+}
+
+func (*clearRepoPattern) Name() string {
+       return "clear the value of 
_tool_jira_scope_configs.remotelink_repo_pattern"
+}
diff --git a/backend/plugins/jira/models/migrationscripts/register.go 
b/backend/plugins/jira/models/migrationscripts/register.go
index d7f3d095f..56a882e60 100644
--- a/backend/plugins/jira/models/migrationscripts/register.go
+++ b/backend/plugins/jira/models/migrationscripts/register.go
@@ -40,5 +40,6 @@ func All() []plugin.MigrationScript {
                new(renameTr2ScopeConfig),
                new(addRepoUrl),
                new(addApplicationType),
+               new(clearRepoPattern),
        }
 }
diff --git a/backend/plugins/jira/tasks/issue_repo_commit_convertor.go 
b/backend/plugins/jira/tasks/issue_repo_commit_convertor.go
index c62892d55..afe150e96 100644
--- a/backend/plugins/jira/tasks/issue_repo_commit_convertor.go
+++ b/backend/plugins/jira/tasks/issue_repo_commit_convertor.go
@@ -56,7 +56,7 @@ func ConvertIssueRepoCommits(taskCtx plugin.SubTaskContext) 
errors.Error {
        var commitRepoUrlRegexps []*regexp.Regexp
        if sc := data.Options.ScopeConfig; sc != nil {
                for _, s := range sc.RemotelinkRepoPattern {
-                       pattern, e := regexp.Compile(s)
+                       pattern, e := regexp.Compile(s.Regex)
                        if e != nil {
                                return errors.Convert(e)
                        }
diff --git a/backend/plugins/jira/tasks/remotelink_extractor.go 
b/backend/plugins/jira/tasks/remotelink_extractor.go
index 2c2367409..d2585ac48 100644
--- a/backend/plugins/jira/tasks/remotelink_extractor.go
+++ b/backend/plugins/jira/tasks/remotelink_extractor.go
@@ -60,7 +60,7 @@ func ExtractRemotelinks(taskCtx plugin.SubTaskContext) 
errors.Error {
        var commitRepoUrlRegexps []*regexp.Regexp
        if sc := data.Options.ScopeConfig; sc != nil {
                for _, s := range sc.RemotelinkRepoPattern {
-                       pattern, e := regexp.Compile(s)
+                       pattern, e := regexp.Compile(s.Regex)
                        if e != nil {
                                return errors.Convert(e)
                        }
diff --git a/backend/plugins/jira/tasks/task_data.go 
b/backend/plugins/jira/tasks/task_data.go
index 68a3f67c5..5bb3eb79a 100644
--- a/backend/plugins/jira/tasks/task_data.go
+++ b/backend/plugins/jira/tasks/task_data.go
@@ -38,18 +38,23 @@ type TypeMapping struct {
        StatusMappings StatusMappings `json:"statusMappings"`
 }
 
+type CommitUrlPattern struct {
+       Pattern string `json:"pattern"`
+       Regex   string `json:"regex"`
+}
+
 type TypeMappings map[string]TypeMapping
 
 type JiraScopeConfig struct {
-       Entities                   []string     `json:"entities"`
-       ConnectionId               uint64       `mapstructure:"connectionId" 
json:"connectionId"`
-       Name                       string       `gorm:"type:varchar(255)" 
validate:"required"`
-       EpicKeyField               string       `json:"epicKeyField"`
-       StoryPointField            string       `json:"storyPointField"`
-       RemotelinkCommitShaPattern string       
`json:"remotelinkCommitShaPattern"`
-       RemotelinkRepoPattern      []string     `json:"remotelinkRepoPattern"`
-       TypeMappings               TypeMappings `json:"typeMappings"`
-       ApplicationType            string       `json:"applicationType"`
+       Entities                   []string           `json:"entities"`
+       ConnectionId               uint64             
`mapstructure:"connectionId" json:"connectionId"`
+       Name                       string             `gorm:"type:varchar(255)" 
validate:"required"`
+       EpicKeyField               string             `json:"epicKeyField"`
+       StoryPointField            string             `json:"storyPointField"`
+       RemotelinkCommitShaPattern string             
`json:"remotelinkCommitShaPattern"`
+       RemotelinkRepoPattern      []CommitUrlPattern 
`json:"remotelinkRepoPattern"`
+       TypeMappings               TypeMappings       `json:"typeMappings"`
+       ApplicationType            string             `json:"applicationType"`
 }
 
 func (r *JiraScopeConfig) ToDb() (*models.JiraScopeConfig, errors.Error) {
@@ -90,7 +95,7 @@ func MakeScopeConfig(rule models.JiraScopeConfig) 
(*JiraScopeConfig, errors.Erro
                        return nil, errors.Default.Wrap(err, "unable to 
unmarshal the typeMapping")
                }
        }
-       var remotelinkRepoPattern []string
+       var remotelinkRepoPattern []CommitUrlPattern
        if len(rule.RemotelinkRepoPattern) > 0 {
                err = json.Unmarshal(rule.RemotelinkRepoPattern, 
&remotelinkRepoPattern)
                if err != nil {
diff --git a/backend/plugins/jira/tasks/task_data_test.go 
b/backend/plugins/jira/tasks/task_data_test.go
index 04088ac5b..3e546f566 100644
--- a/backend/plugins/jira/tasks/task_data_test.go
+++ b/backend/plugins/jira/tasks/task_data_test.go
@@ -41,7 +41,7 @@ func TestMakeScopeConfigs(t *testing.T) {
                                EpicKeyField:               "epic",
                                StoryPointField:            "story",
                                RemotelinkCommitShaPattern: "commit sha 
pattern",
-                               RemotelinkRepoPattern:      
[]byte(`["abc","efg"]`),
+                               RemotelinkRepoPattern:      
[]byte(`[{"pattern":"","regex":"abc"},{"pattern":"","regex":"efg"}]`),
                                TypeMappings:               
[]byte(`{"10040":{"standardType":"Incident","statusMappings":null}}`),
                        }},
                        &JiraScopeConfig{
@@ -49,7 +49,7 @@ func TestMakeScopeConfigs(t *testing.T) {
                                EpicKeyField:               "epic",
                                StoryPointField:            "story",
                                RemotelinkCommitShaPattern: "commit sha 
pattern",
-                               RemotelinkRepoPattern:      []string{"abc", 
"efg"},
+                               RemotelinkRepoPattern:      
[]CommitUrlPattern{{"", "abc"}, {"", "efg"}},
                                TypeMappings: map[string]TypeMapping{"10040": {
                                        StandardType:   "Incident",
                                        StatusMappings: nil,

Reply via email to