This is an automated email from the ASF dual-hosted git repository.
zhangliang2022 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 bd5ce8df fix: bitbucket generated wrong clone url (#3315)
bd5ce8df is described below
commit bd5ce8dfa7beb05f06fa14917058dcc0d9d57d0c
Author: mindlesscloud <[email protected]>
AuthorDate: Sun Oct 9 20:32:02 2022 +0800
fix: bitbucket generated wrong clone url (#3315)
* fix: bitbucket generated wrong clone url
* fix: e2e test
* refactor: unit test instead of e2e
---
plugins/bitbucket/api/blueprint.go | 111 +++++++++++++--------
plugins/bitbucket/api/blueprint_test.go | 57 +++++++++++
.../e2e/snapshot_tables/_tool_bitbucket_repos.csv | 2 +-
plugins/bitbucket/e2e/snapshot_tables/boards.csv | 2 +-
plugins/bitbucket/e2e/snapshot_tables/repos.csv | 2 +-
plugins/bitbucket/tasks/api_common.go | 4 +-
plugins/bitbucket/tasks/repo_extractor.go | 2 +-
7 files changed, 131 insertions(+), 49 deletions(-)
diff --git a/plugins/bitbucket/api/blueprint.go
b/plugins/bitbucket/api/blueprint.go
index c0f8ebe5..825670e6 100644
--- a/plugins/bitbucket/api/blueprint.go
+++ b/plugins/bitbucket/api/blueprint.go
@@ -21,13 +21,14 @@ import (
"context"
"encoding/json"
"fmt"
- "github.com/apache/incubator-devlake/errors"
"io"
"net/http"
"net/url"
+ "path"
"strings"
"time"
+ "github.com/apache/incubator-devlake/errors"
"github.com/apache/incubator-devlake/models/domainlayer/didgen"
"github.com/apache/incubator-devlake/plugins/bitbucket/models"
"github.com/apache/incubator-devlake/plugins/bitbucket/tasks"
@@ -36,7 +37,66 @@ import (
"github.com/apache/incubator-devlake/utils"
)
+type repoGetter func(connectionId uint64, owner, repo string) (string, string,
errors.Error)
+
func MakePipelinePlan(subtaskMetas []core.SubTaskMeta, connectionId uint64,
scope []*core.BlueprintScopeV100) (core.PipelinePlan, errors.Error) {
+ return makePipelinePlan(subtaskMetas, connectionId,
getBitbucketApiRepo, scope)
+}
+func getBitbucketApiRepo(connectionId uint64, owner, repo string) (string,
string, errors.Error) {
+ // here is the tricky part, we have to obtain the repo id beforehand
+ connection := new(models.BitbucketConnection)
+ err := connectionHelper.FirstById(connection, connectionId)
+ if err != nil {
+ return "", "", err
+ }
+ tokens := strings.Split(connection.GetEncodedToken(), ",")
+ if len(tokens) == 0 {
+ return "", "", errors.Default.New("no token")
+ }
+ token := tokens[0]
+ apiClient, err := helper.NewApiClient(
+ context.TODO(),
+ connection.Endpoint,
+ map[string]string{
+ "Authorization": fmt.Sprintf("Basic %s", token),
+ },
+ 10*time.Second,
+ connection.Proxy,
+ basicRes,
+ )
+ if err != nil {
+ return "", "", err
+ }
+
+ res, err := apiClient.Get(path.Join("repositories", owner, repo), nil,
nil)
+ if err != nil {
+ return "", "", err
+ }
+ defer res.Body.Close()
+ if res.StatusCode != http.StatusOK {
+ return "", "", errors.Default.New(fmt.Sprintf(
+ "unexpected status code when requesting repo detail %d
%s",
+ res.StatusCode, res.Request.URL.String(),
+ ))
+ }
+ body, err := errors.Convert01(io.ReadAll(res.Body))
+ if err != nil {
+ return "", "", err
+ }
+ apiRepo := new(tasks.BitbucketApiRepo)
+ err = errors.Convert(json.Unmarshal(body, apiRepo))
+ if err != nil {
+ return "", "", err
+ }
+ for _, u := range apiRepo.Links.Clone {
+ if u.Name == "https" {
+ return u.Href, connection.Password, nil
+ }
+ }
+ return "", "", errors.Default.New("no clone url")
+}
+
+func makePipelinePlan(subtaskMetas []core.SubTaskMeta, connectionId uint64,
getter repoGetter, scope []*core.BlueprintScopeV100) (core.PipelinePlan,
errors.Error) {
var err errors.Error
plan := make(core.PipelinePlan, len(scope))
for i, scopeElem := range scope {
@@ -93,58 +153,23 @@ func MakePipelinePlan(subtaskMetas []core.SubTaskMeta,
connectionId uint64, scop
})
// collect git data by gitextractor if CODE was requested
if utils.StringsContains(scopeElem.Entities,
core.DOMAIN_TYPE_CODE) {
- // here is the tricky part, we have to obtain the repo
id beforehand
- connection := new(models.BitbucketConnection)
- err = connectionHelper.FirstById(connection,
connectionId)
- if err != nil {
- return nil, err
- }
- token := strings.Split(connection.GetEncodedToken(),
",")[0]
- apiClient, err := helper.NewApiClient(
- context.TODO(),
- connection.Endpoint,
- map[string]string{
- "Authorization": fmt.Sprintf("Basic
%s", token),
- },
- 10*time.Second,
- connection.Proxy,
- basicRes,
- )
- if err != nil {
- return nil, err
- }
- res, err :=
apiClient.Get(fmt.Sprintf("repositories/%s/%s", op.Owner, op.Repo), nil, nil)
- if err != nil {
- return nil, err
+ original, password, err1 := getter(connectionId,
op.Owner, op.Repo)
+ if err1 != nil {
+ return nil, err1
}
- defer res.Body.Close()
- if res.StatusCode != http.StatusOK {
- return nil, errors.Default.New(fmt.Sprintf(
- "unexpected status code when requesting
repo detail %d %s",
- res.StatusCode,
res.Request.URL.String(),
- ))
- }
- body, err := errors.Convert01(io.ReadAll(res.Body))
- if err != nil {
- return nil, err
- }
- apiRepo := new(tasks.BitbucketApiRepo)
- err = errors.Convert(json.Unmarshal(body, apiRepo))
+ cloneUrl, err := errors.Convert01(url.Parse(original))
if err != nil {
return nil, err
}
- cloneUrl, err :=
errors.Convert01(url.Parse(apiRepo.Links.Clone[0].Href))
- if err != nil {
- return nil, err
- }
- cloneUrl.User = url.UserPassword("git", token)
+ cloneUrl.User = url.UserPassword(op.Owner, password)
stage = append(stage, &core.PipelineTask{
Plugin: "gitextractor",
Options: map[string]interface{}{
"url": cloneUrl.String(),
- "repoId":
didgen.NewDomainIdGenerator(&models.BitbucketRepo{}).Generate(connectionId,
apiRepo.BitbucketId),
+ "repoId":
didgen.NewDomainIdGenerator(&models.BitbucketRepo{}).Generate(connectionId,
fmt.Sprintf("%s/%s", op.Owner, op.Repo)),
},
})
+
}
plan[i] = stage
}
diff --git a/plugins/bitbucket/api/blueprint_test.go
b/plugins/bitbucket/api/blueprint_test.go
new file mode 100644
index 00000000..14e284a3
--- /dev/null
+++ b/plugins/bitbucket/api/blueprint_test.go
@@ -0,0 +1,57 @@
+/*
+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 (
+ "github.com/apache/incubator-devlake/mocks"
+ "testing"
+
+ "github.com/apache/incubator-devlake/errors"
+ "github.com/apache/incubator-devlake/plugins/core"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestMakePipelinePlan(t *testing.T) {
+ var mockGetter repoGetter
+ mockGetter = func(connectionId uint64, owner, repo string) (string,
string, errors.Error) {
+ return "https://[email protected]/thenicetgp/lake.git",
"secret", nil
+ }
+ scope := &core.BlueprintScopeV100{
+ Entities: []string{core.DOMAIN_TYPE_CODE,
core.DOMAIN_TYPE_TICKET, core.DOMAIN_TYPE_CODE_REVIEW, core.DOMAIN_TYPE_CROSS},
+ Options: []byte(`{
+ "owner": "thenicetgp",
+ "repo": "lake"
+ }`),
+ Transformation: nil,
+ }
+ mockMeta := mocks.NewPluginMeta(t)
+
mockMeta.On("RootPkgPath").Return("github.com/apache/incubator-devlake/plugins/bitbucket")
+ err := core.RegisterPlugin("bitbucket", mockMeta)
+ assert.Nil(t, err)
+ plan, err := makePipelinePlan(nil, 1, mockGetter,
[]*core.BlueprintScopeV100{scope})
+ assert.Nil(t, err)
+ for _, stage := range plan {
+ for _, task := range stage {
+ if task.Plugin == "gitextractor" {
+ assert.Equal(t, task.Options["url"],
"https://thenicetgp:[email protected]/thenicetgp/lake.git")
+ return
+ }
+ }
+ }
+ t.Fatal("no gitextractor plugin")
+}
diff --git a/plugins/bitbucket/e2e/snapshot_tables/_tool_bitbucket_repos.csv
b/plugins/bitbucket/e2e/snapshot_tables/_tool_bitbucket_repos.csv
index a3883fbb..0a13fe85 100644
--- a/plugins/bitbucket/e2e/snapshot_tables/_tool_bitbucket_repos.csv
+++ b/plugins/bitbucket/e2e/snapshot_tables/_tool_bitbucket_repos.csv
@@ -1,2 +1,2 @@
connection_id,bitbucket_id,name,html_url,description,owner_id,language,created_date,updated_date,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
-1,repositories/panjf2000/ants,panjf2000/ants,https://bitbucket.org/panjf2000/ants,,,,2022-06-17T03:27:18.865+00:00,2022-08-12T15:40:12.409+00:00,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_repositories,2,
+1,panjf2000/ants,panjf2000/ants,https://bitbucket.org/panjf2000/ants,,,,2022-06-17T03:27:18.865+00:00,2022-08-12T15:40:12.409+00:00,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_repositories,2,
diff --git a/plugins/bitbucket/e2e/snapshot_tables/boards.csv
b/plugins/bitbucket/e2e/snapshot_tables/boards.csv
index d4cbb227..99f1824d 100644
--- a/plugins/bitbucket/e2e/snapshot_tables/boards.csv
+++ b/plugins/bitbucket/e2e/snapshot_tables/boards.csv
@@ -1,2 +1,2 @@
id,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark,name,description,url,created_date
-bitbucket:BitbucketRepo:1:repositories/panjf2000/ants,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_repositories,2,,panjf2000/ants,,https://bitbucket.org/panjf2000/ants/issues,2022-06-17T03:27:18.865+00:00
+bitbucket:BitbucketRepo:1:panjf2000/ants,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_repositories,2,,panjf2000/ants,,https://bitbucket.org/panjf2000/ants/issues,2022-06-17T03:27:18.865+00:00
diff --git a/plugins/bitbucket/e2e/snapshot_tables/repos.csv
b/plugins/bitbucket/e2e/snapshot_tables/repos.csv
index 32c0ecc0..646a4390 100644
--- a/plugins/bitbucket/e2e/snapshot_tables/repos.csv
+++ b/plugins/bitbucket/e2e/snapshot_tables/repos.csv
@@ -1,2 +1,2 @@
id,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark,name,url,description,owner_id,language,forked_from,deleted
-bitbucket:BitbucketRepo:1:repositories/panjf2000/ants,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_repositories,2,,panjf2000/ants,https://bitbucket.org/panjf2000/ants,,,,,0
+bitbucket:BitbucketRepo:1:panjf2000/ants,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_repositories,2,,panjf2000/ants,https://bitbucket.org/panjf2000/ants,,,,,0
diff --git a/plugins/bitbucket/tasks/api_common.go
b/plugins/bitbucket/tasks/api_common.go
index 000089da..b931a8a4 100644
--- a/plugins/bitbucket/tasks/api_common.go
+++ b/plugins/bitbucket/tasks/api_common.go
@@ -113,7 +113,7 @@ func GetPullRequestsIterator(taskCtx core.SubTaskContext)
(*helper.DalCursorIter
dal.From("_tool_bitbucket_pull_requests bpr"),
dal.Where(
`bpr.repo_id = ? and bpr.connection_id = ?`,
-
"repositories/"+data.Options.Owner+"/"+data.Options.Repo,
data.Options.ConnectionId,
+ fmt.Sprintf("%s/%s", data.Options.Owner,
data.Options.Repo), data.Options.ConnectionId,
),
}
// construct the input iterator
@@ -133,7 +133,7 @@ func GetIssuesIterator(taskCtx core.SubTaskContext)
(*helper.DalCursorIterator,
dal.From("_tool_bitbucket_issues bpr"),
dal.Where(
`bpr.repo_id = ? and bpr.connection_id = ?`,
-
"repositories/"+data.Options.Owner+"/"+data.Options.Repo,
data.Options.ConnectionId,
+ fmt.Sprintf("%s/%s", data.Options.Owner,
data.Options.Repo), data.Options.ConnectionId,
),
}
// construct the input iterator
diff --git a/plugins/bitbucket/tasks/repo_extractor.go
b/plugins/bitbucket/tasks/repo_extractor.go
index d79f5c36..c39e19c6 100644
--- a/plugins/bitbucket/tasks/repo_extractor.go
+++ b/plugins/bitbucket/tasks/repo_extractor.go
@@ -97,7 +97,7 @@ func ExtractApiRepositories(taskCtx core.SubTaskContext)
errors.Error {
results := make([]interface{}, 0, 1)
bitbucketRepository := &models.BitbucketRepo{
ConnectionId: data.Options.ConnectionId,
- BitbucketId: "repositories/" + body.FullName,
+ BitbucketId: body.FullName,
Name: body.FullName,
HTMLUrl: body.Links.Html.Href,
Description: body.Description,