You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@devlake.apache.org by zh...@apache.org on 2022/08/16 17:04:51 UTC
[incubator-devlake] branch main updated: feat: add a plugin bitbucket (#2458)
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 f527fb56 feat: add a plugin bitbucket (#2458)
f527fb56 is described below
commit f527fb568920d0fb5a33e18e34060fa390cc9c21
Author: tsoc <47...@users.noreply.github.com>
AuthorDate: Wed Aug 17 01:04:47 2022 +0800
feat: add a plugin bitbucket (#2458)
* feat: add bitbucket connection and repo
* fix: fix api/connection.go
* feat: add pr and issues
* feat: add issue_comments and pr_comments
* feat: add accounts
* fix: gofmt account_convertor.go
* fix: fix accountIdGen
* feat: add e2e test
* fix: gofmt fix
* fix: delete snapshot_tables
* fix: ignore created_at updated_at and created_date
* fix: Limit the length of primarykey and index
* refactor: refactor some files
* refactor: refactor some files
* style: gofmt
* feat: add field author_name type in table pullrequests
* fix: change data in raw tables
* fix: fix some bug about bitbucket pr
* feat: add pr headref baseref etc
* feat: add pr headref baseref etc
* fix: change snapshot_tables
* fix: change e2e testdata
* perf: delete additions deleteions in pr,add reviewer,reporter in apipr etc.
* fix: add field body,type in table pr_comments and issue_comments
* perf: change code comments in connection.go
* fix: change ratelimitto let collector faster
* fix: add field issue_key and original_status in table issues
* perf: change e2e testdatas
* perf: convin raw table issue and pr
---
plugins/bitbucket/api/blueprint.go | 151 +++
plugins/bitbucket/api/connection.go | 165 +++
plugins/bitbucket/api/init.go | 39 +
plugins/bitbucket/bitbucket.go | 66 +
plugins/bitbucket/e2e/account_test.go | 84 ++
plugins/bitbucket/e2e/comment_test.go | 169 +++
plugins/bitbucket/e2e/issue_test.go | 177 +++
plugins/bitbucket/e2e/pr_test.go | 149 +++
.../_raw_bitbucket_api_issue_comments.csv | 29 +
.../e2e/raw_tables/_raw_bitbucket_api_issues.csv | 31 +
.../_raw_bitbucket_api_pull_requests.csv | 58 +
.../_raw_bitbucket_api_pullrequest_comments.csv | 19 +
.../raw_tables/_raw_bitbucket_api_repositories.csv | 3 +
plugins/bitbucket/e2e/repo_test.go | 138 +++
.../snapshot_tables/_tool_bitbucket_accounts.csv | 2 +
.../_tool_bitbucket_accounts_in_comments.csv | 2 +
.../_tool_bitbucket_accounts_in_issue.csv | 2 +
.../_tool_bitbucket_accounts_in_pr.csv | 2 +
.../_tool_bitbucket_issue_comments.csv | 29 +
.../_tool_bitbucket_issue_labels.csv | 1 +
.../e2e/snapshot_tables/_tool_bitbucket_issues.csv | 55 +
.../_tool_bitbucket_pull_request_comments.csv | 19 +
.../_tool_bitbucket_pull_requests.csv | 58 +
.../e2e/snapshot_tables/_tool_bitbucket_repos.csv | 2 +
plugins/bitbucket/e2e/snapshot_tables/accounts.csv | 2 +
.../bitbucket/e2e/snapshot_tables/board_issues.csv | 31 +
plugins/bitbucket/e2e/snapshot_tables/boards.csv | 2 +
.../e2e/snapshot_tables/issue_comments.csv | 29 +
plugins/bitbucket/e2e/snapshot_tables/issues.csv | 55 +
.../e2e/snapshot_tables/pull_request_comments.csv | 21 +
.../e2e/snapshot_tables/pull_requests.csv | 1257 ++++++++++++++++++++
plugins/bitbucket/e2e/snapshot_tables/repos.csv | 2 +
plugins/bitbucket/impl/impl.go | 125 ++
plugins/bitbucket/models/account.go | 39 +
plugins/bitbucket/models/commit.go | 42 +
plugins/bitbucket/models/connection.go | 69 ++
plugins/bitbucket/models/issue.go | 52 +
plugins/bitbucket/models/issue_comment.go | 40 +
plugins/bitbucket/models/issue_event.go | 37 +
plugins/bitbucket/models/issue_label.go | 36 +
.../migrationscripts/20220803_add_init_tables.go | 101 ++
.../models/migrationscripts/archived/account.go | 39 +
.../models/migrationscripts/archived/commit.go | 42 +
.../models/migrationscripts/archived/connection.go | 69 ++
.../models/migrationscripts/archived/issue.go | 53 +
.../migrationscripts/archived/issue_comment.go | 40 +
.../migrationscripts/archived/issue_event.go | 37 +
.../migrationscripts/archived/issue_label.go | 36 +
.../models/migrationscripts/archived/pr.go | 57 +
.../models/migrationscripts/archived/pr_comment.go | 41 +
.../models/migrationscripts/archived/repo.go | 40 +
.../migrationscripts/archived/repo_commit.go | 31 +
.../models/migrationscripts/archived/user.go | 33 +
.../bitbucket/models/migrationscripts/register.go | 29 +
plugins/bitbucket/models/pr.go | 57 +
plugins/bitbucket/models/pr_comment.go | 41 +
plugins/bitbucket/models/repo.go | 40 +
plugins/bitbucket/models/repo_commit.go | 31 +
plugins/bitbucket/models/user.go | 35 +
plugins/bitbucket/tasks/account_convertor.go | 87 ++
plugins/bitbucket/tasks/account_extractor.go | 52 +
plugins/bitbucket/tasks/api_client.go | 86 ++
plugins/bitbucket/tasks/api_common.go | 137 +++
plugins/bitbucket/tasks/commit_collector.go | 56 +
plugins/bitbucket/tasks/commit_convertor.go | 103 ++
plugins/bitbucket/tasks/commit_extractor.go | 123 ++
plugins/bitbucket/tasks/issue_collector.go | 55 +
plugins/bitbucket/tasks/issue_comment_collector.go | 61 +
plugins/bitbucket/tasks/issue_comment_convertor.go | 92 ++
plugins/bitbucket/tasks/issue_comment_extractor.go | 122 ++
plugins/bitbucket/tasks/issue_convertor.go | 120 ++
plugins/bitbucket/tasks/issue_extractor.go | 179 +++
plugins/bitbucket/tasks/pr_collector.go | 58 +
plugins/bitbucket/tasks/pr_comment_collector.go | 61 +
plugins/bitbucket/tasks/pr_comment_convertor.go | 96 ++
plugins/bitbucket/tasks/pr_comment_extractor.go | 124 ++
plugins/bitbucket/tasks/pr_convertor.go | 108 ++
plugins/bitbucket/tasks/pr_extractor.go | 166 +++
plugins/bitbucket/tasks/repo_collector.go | 63 +
plugins/bitbucket/tasks/repo_convertor.go | 106 ++
plugins/bitbucket/tasks/repo_extractor.go | 126 ++
plugins/bitbucket/tasks/task_data.go | 90 ++
plugins/bitbucket/utils/utils.go | 152 +++
83 files changed, 6564 insertions(+)
diff --git a/plugins/bitbucket/api/blueprint.go b/plugins/bitbucket/api/blueprint.go
new file mode 100644
index 00000000..71951934
--- /dev/null
+++ b/plugins/bitbucket/api/blueprint.go
@@ -0,0 +1,151 @@
+/*
+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 (
+ "context"
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+ "strings"
+ "time"
+
+ "github.com/apache/incubator-devlake/models/domainlayer/didgen"
+ "github.com/apache/incubator-devlake/plugins/bitbucket/models"
+ "github.com/apache/incubator-devlake/plugins/bitbucket/tasks"
+ "github.com/apache/incubator-devlake/plugins/core"
+ "github.com/apache/incubator-devlake/plugins/helper"
+ "github.com/apache/incubator-devlake/utils"
+)
+
+func MakePipelinePlan(subtaskMetas []core.SubTaskMeta, connectionId uint64, scope []*core.BlueprintScopeV100) (core.PipelinePlan, error) {
+ var err error
+ plan := make(core.PipelinePlan, len(scope))
+ for i, scopeElem := range scope {
+ // handle taskOptions and transformationRules, by dumping them to taskOptions
+ transformationRules := make(map[string]interface{})
+ if len(scopeElem.Transformation) > 0 {
+ err = json.Unmarshal(scopeElem.Transformation, &transformationRules)
+ if err != nil {
+ return nil, err
+ }
+ }
+ // refdiff
+ if refdiffRules, ok := transformationRules["refdiff"]; ok {
+ // add a new task to next stage
+ j := i + 1
+ if j == len(plan) {
+ plan = append(plan, nil)
+ }
+ plan[j] = core.PipelineStage{
+ {
+ Plugin: "refdiff",
+ Options: refdiffRules.(map[string]interface{}),
+ },
+ }
+ // remove it from bitbucket transformationRules
+ delete(transformationRules, "refdiff")
+ }
+ // construct task options for bitbucket
+ options := make(map[string]interface{})
+ err = json.Unmarshal(scopeElem.Options, &options)
+ if err != nil {
+ return nil, err
+ }
+ options["connectionId"] = connectionId
+ options["transformationRules"] = transformationRules
+ // make sure task options is valid
+ op, err := tasks.DecodeAndValidateTaskOptions(options)
+ if err != nil {
+ return nil, err
+ }
+ // construct subtasks
+ subtasks, err := helper.MakePipelinePlanSubtasks(subtaskMetas, scopeElem.Entities)
+ if err != nil {
+ return nil, err
+ }
+ stage := plan[i]
+ if stage == nil {
+ stage = core.PipelineStage{}
+ }
+ stage = append(stage, &core.PipelineTask{
+ Plugin: "bitbucket",
+ Subtasks: subtasks,
+ Options: options,
+ })
+ // 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
+ }
+ defer res.Body.Close()
+ if res.StatusCode != http.StatusOK {
+ return nil, fmt.Errorf(
+ "unexpected status code when requesting repo detail %d %s",
+ res.StatusCode, res.Request.URL.String(),
+ )
+ }
+ body, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ return nil, err
+ }
+ apiRepo := new(tasks.BitbucketApiRepo)
+ err = json.Unmarshal(body, apiRepo)
+ if err != nil {
+ return nil, err
+ }
+ cloneUrl, err := url.Parse(apiRepo.Links.Clone[0].Href)
+ if err != nil {
+ return nil, err
+ }
+ cloneUrl.User = url.UserPassword("git", token)
+ stage = append(stage, &core.PipelineTask{
+ Plugin: "gitextractor",
+ Options: map[string]interface{}{
+ "url": cloneUrl.String(),
+ "repoId": didgen.NewDomainIdGenerator(&models.BitbucketRepo{}).Generate(connectionId, apiRepo.BitbucketId),
+ },
+ })
+ }
+ plan[i] = stage
+ }
+ return plan, nil
+}
diff --git a/plugins/bitbucket/api/connection.go b/plugins/bitbucket/api/connection.go
new file mode 100644
index 00000000..5969690c
--- /dev/null
+++ b/plugins/bitbucket/api/connection.go
@@ -0,0 +1,165 @@
+/*
+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 (
+ "context"
+ "fmt"
+ "net/http"
+ "time"
+
+ "github.com/apache/incubator-devlake/plugins/bitbucket/models"
+ "github.com/apache/incubator-devlake/plugins/core"
+ "github.com/apache/incubator-devlake/plugins/helper"
+ "github.com/mitchellh/mapstructure"
+)
+
+// @Summary test bitbucket connection
+// @Description Test bitbucket Connection
+// @Tags plugins/bitbucket
+// @Param body body models.TestConnectionRequest true "json body"
+// @Success 200 {object} shared.ApiBody "Success"
+// @Failure 400 {string} errcode.Error "Bad Request"
+// @Failure 500 {string} errcode.Error "Internel Error"
+// @Router /plugins/bitbucket/test [POST]
+func TestConnection(input *core.ApiResourceInput) (*core.ApiResourceOutput, error) {
+
+ // decode
+ var err error
+ var connection models.TestConnectionRequest
+ err = mapstructure.Decode(input.Body, &connection)
+ if err != nil {
+ return nil, err
+ }
+ // validate
+ err = vld.Struct(connection)
+ if err != nil {
+ return nil, err
+ }
+
+ // test connection
+ apiClient, err := helper.NewApiClient(
+ context.TODO(),
+ connection.Endpoint,
+ map[string]string{
+ "Authorization": fmt.Sprintf("Basic %v", connection.GetEncodedToken()),
+ },
+ 3*time.Second,
+ connection.Proxy,
+ basicRes,
+ )
+ if err != nil {
+ return nil, err
+ }
+ res, err := apiClient.Get("user", nil, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ resBody := &models.ApiUserResponse{}
+ err = helper.UnmarshalResponse(res, resBody)
+ if err != nil {
+ return nil, err
+ }
+
+ if res.StatusCode != http.StatusOK {
+ return nil, fmt.Errorf("unexpected status code: %d", res.StatusCode)
+ }
+ return nil, nil
+}
+
+// @Summary create bitbucket connection
+// @Description Create bitbucket connection
+// @Tags plugins/bitbucket
+// @Param body body models.BitbucketConnection true "json body"
+// @Success 200 {object} models.BitbucketConnection
+// @Failure 400 {string} errcode.Error "Bad Request"
+// @Failure 500 {string} errcode.Error "Internel Error"
+// @Router /plugins/bitbucket/connections [POST]
+func PostConnections(input *core.ApiResourceInput) (*core.ApiResourceOutput, error) {
+ // update from request and save to database
+ connection := &models.BitbucketConnection{}
+ err := connectionHelper.Create(connection, input)
+ if err != nil {
+ return nil, err
+ }
+ return &core.ApiResourceOutput{Body: connection, Status: http.StatusOK}, nil
+}
+
+// @Summary patch bitbucket connection
+// @Description Patch bitbucket connection
+// @Tags plugins/bitbucket
+// @Param body body models.BitbucketConnection true "json body"
+// @Success 200 {object} models.BitbucketConnection
+// @Failure 400 {string} errcode.Error "Bad Request"
+// @Failure 500 {string} errcode.Error "Internel Error"
+// @Router /plugins/bitbucket/connections/{connectionId} [PATCH]
+func PatchConnection(input *core.ApiResourceInput) (*core.ApiResourceOutput, error) {
+ connection := &models.BitbucketConnection{}
+ err := connectionHelper.Patch(connection, input)
+ if err != nil {
+ return nil, err
+ }
+ return &core.ApiResourceOutput{Body: connection}, nil
+}
+
+// @Summary delete a bitbucket connection
+// @Description Delete a bitbucket connection
+// @Tags plugins/bitbucket
+// @Success 200 {object} models.BitbucketConnection
+// @Failure 400 {string} errcode.Error "Bad Request"
+// @Failure 500 {string} errcode.Error "Internel Error"
+// @Router /plugins/bitbucket/connections/{connectionId} [DELETE]
+func DeleteConnection(input *core.ApiResourceInput) (*core.ApiResourceOutput, error) {
+ connection := &models.BitbucketConnection{}
+ err := connectionHelper.First(connection, input.Params)
+ if err != nil {
+ return nil, err
+ }
+ err = connectionHelper.Delete(connection)
+ return &core.ApiResourceOutput{Body: connection}, err
+}
+
+// @Summary get all bitbucket connections
+// @Description Get all bitbucket connections
+// @Tags plugins/bitbucket
+// @Success 200 {object} []models.BitbucketConnection
+// @Failure 400 {string} errcode.Error "Bad Request"
+// @Failure 500 {string} errcode.Error "Internel Error"
+// @Router /plugins/bitbucket/connections [GET]
+func ListConnections(input *core.ApiResourceInput) (*core.ApiResourceOutput, error) {
+ var connections []models.BitbucketConnection
+ err := connectionHelper.List(&connections)
+ if err != nil {
+ return nil, err
+ }
+ return &core.ApiResourceOutput{Body: connections, Status: http.StatusOK}, nil
+}
+
+// @Summary get bitbucket connection detail
+// @Description Get bitbucket connection detail
+// @Tags plugins/bitbucket
+// @Success 200 {object} models.BitbucketConnection
+// @Failure 400 {string} errcode.Error "Bad Request"
+// @Failure 500 {string} errcode.Error "Internel Error"
+// @Router /plugins/bitbucket/connections/{connectionId} [GET]
+func GetConnection(input *core.ApiResourceInput) (*core.ApiResourceOutput, error) {
+ connection := &models.BitbucketConnection{}
+ err := connectionHelper.First(connection, input.Params)
+ return &core.ApiResourceOutput{Body: connection}, err
+}
diff --git a/plugins/bitbucket/api/init.go b/plugins/bitbucket/api/init.go
new file mode 100644
index 00000000..6774e148
--- /dev/null
+++ b/plugins/bitbucket/api/init.go
@@ -0,0 +1,39 @@
+/*
+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/plugins/core"
+ "github.com/apache/incubator-devlake/plugins/helper"
+ "github.com/go-playground/validator/v10"
+ "github.com/spf13/viper"
+ "gorm.io/gorm"
+)
+
+var vld *validator.Validate
+var connectionHelper *helper.ConnectionApiHelper
+var basicRes core.BasicRes
+
+func Init(config *viper.Viper, logger core.Logger, database *gorm.DB) {
+ basicRes = helper.NewDefaultBasicRes(config, logger, database)
+ vld = validator.New()
+ connectionHelper = helper.NewConnectionHelper(
+ basicRes,
+ vld,
+ )
+}
diff --git a/plugins/bitbucket/bitbucket.go b/plugins/bitbucket/bitbucket.go
new file mode 100644
index 00000000..d80dec18
--- /dev/null
+++ b/plugins/bitbucket/bitbucket.go
@@ -0,0 +1,66 @@
+/*
+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 main // must be main for plugin entry point
+
+import (
+ "github.com/apache/incubator-devlake/plugins/bitbucket/impl"
+ "github.com/apache/incubator-devlake/runner"
+ "github.com/spf13/cobra"
+)
+
+// Export a variable named PluginEntry for Framework to search and load
+var PluginEntry impl.Bitbucket //nolint
+
+// standalone mode for debugging
+func main() {
+ cmd := &cobra.Command{Use: "bitbucket"}
+ connectionId := cmd.Flags().Uint64P("connectionId", "c", 0, "bitbucket connection id")
+ owner := cmd.Flags().StringP("owner", "o", "", "bitbucket owner")
+ repo := cmd.Flags().StringP("repo", "r", "", "bitbucket repo")
+ _ = cmd.MarkFlagRequired("connectionId")
+ _ = cmd.MarkFlagRequired("owner")
+ _ = cmd.MarkFlagRequired("repo")
+
+ prType := cmd.Flags().String("prType", "type/(.*)$", "pr type")
+ prComponent := cmd.Flags().String("prComponent", "component/(.*)$", "pr component")
+ prBodyClosePattern := cmd.Flags().String("prBodyClosePattern", "(?mi)(fix|close|resolve|fixes|closes|resolves|fixed|closed|resolved)[\\s]*.*(((and )?(#|https:\\/\\/api.bitbucket.org\\/2.0\\/%s\\/%s\\/issues\\/)\\d+[ ]*)+)", "pr body close pattern")
+ issueSeverity := cmd.Flags().String("issueSeverity", "severity/(.*)$", "issue severity")
+ issuePriority := cmd.Flags().String("issuePriority", "^(highest|high|medium|low)$", "issue priority")
+ issueComponent := cmd.Flags().String("issueComponent", "component/(.*)$", "issue component")
+ issueTypeBug := cmd.Flags().String("issueTypeBug", "^(bug|failure|error)$", "issue type bug")
+ issueTypeIncident := cmd.Flags().String("issueTypeIncident", "", "issue type incident")
+ issueTypeRequirement := cmd.Flags().String("issueTypeRequirement", "^(feat|feature|proposal|requirement)$", "issue type requirement")
+
+ cmd.Run = func(cmd *cobra.Command, args []string) {
+ runner.DirectRun(cmd, args, PluginEntry, map[string]interface{}{
+ "connectionId": *connectionId,
+ "owner": *owner,
+ "repo": *repo,
+ "prType": *prType,
+ "prComponent": *prComponent,
+ "prBodyClosePattern": *prBodyClosePattern,
+ "issueSeverity": *issueSeverity,
+ "issuePriority": *issuePriority,
+ "issueComponent": *issueComponent,
+ "issueTypeBug": *issueTypeBug,
+ "issueTypeIncident": *issueTypeIncident,
+ "issueTypeRequirement": *issueTypeRequirement,
+ })
+ }
+ runner.RunCmd(cmd)
+}
diff --git a/plugins/bitbucket/e2e/account_test.go b/plugins/bitbucket/e2e/account_test.go
new file mode 100644
index 00000000..cedf5863
--- /dev/null
+++ b/plugins/bitbucket/e2e/account_test.go
@@ -0,0 +1,84 @@
+/*
+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 e2e
+
+import (
+ "fmt"
+ "github.com/apache/incubator-devlake/models/common"
+ "github.com/apache/incubator-devlake/models/domainlayer/crossdomain"
+ "github.com/apache/incubator-devlake/plugins/bitbucket/models"
+ "testing"
+
+ "github.com/apache/incubator-devlake/helpers/e2ehelper"
+ "github.com/apache/incubator-devlake/plugins/bitbucket/impl"
+ "github.com/apache/incubator-devlake/plugins/bitbucket/tasks"
+)
+
+func TestAccountDataFlow(t *testing.T) {
+ var plugin impl.Bitbucket
+ dataflowTester := e2ehelper.NewDataFlowTester(t, "bitbucket", plugin)
+
+ bitbucketRepository := &models.BitbucketRepo{
+ BitbucketId: "panjf2000/ants",
+ }
+ taskData := &tasks.BitbucketTaskData{
+ Options: &tasks.BitbucketOptions{
+ ConnectionId: 1,
+ Owner: "panjf2000",
+ Repo: "ants",
+ },
+ Repo: bitbucketRepository,
+ }
+
+ // import raw data table
+ dataflowTester.ImportCsvIntoRawTable("./raw_tables/_raw_bitbucket_api_issues.csv", "_raw_bitbucket_api_issues")
+ dataflowTester.ImportCsvIntoRawTable("./raw_tables/_raw_bitbucket_api_pull_requests.csv", "_raw_bitbucket_api_pull_requests")
+
+ // verify extraction
+ dataflowTester.FlushTabler(&models.BitbucketAccount{})
+ dataflowTester.FlushTabler(&models.BitbucketIssue{})
+ dataflowTester.FlushTabler(&models.BitbucketPullRequest{})
+ dataflowTester.Subtask(tasks.ExtractApiIssuesMeta, taskData)
+ dataflowTester.Subtask(tasks.ExtractApiPullRequestsMeta, taskData)
+ dataflowTester.VerifyTable(
+ models.BitbucketAccount{},
+ fmt.Sprintf("./snapshot_tables/%s.csv", models.BitbucketAccount{}.TableName()),
+ []string{
+ "connection_id",
+ "user_name",
+ "account_id",
+ "account_status",
+ "display_name",
+ "avatar_url",
+ "html_url",
+ "uuid",
+ "has2_fa_enabled",
+ "_raw_data_params",
+ "_raw_data_table",
+ "_raw_data_id",
+ "_raw_data_remark",
+ },
+ )
+ // verify converter
+ dataflowTester.FlushTabler(&crossdomain.Account{})
+ dataflowTester.Subtask(tasks.ConvertAccountsMeta, taskData)
+ dataflowTester.VerifyTableWithOptions(&crossdomain.Account{}, e2ehelper.TableOptions{
+ CSVRelPath: "./snapshot_tables/accounts.csv",
+ IgnoreTypes: []interface{}{common.NoPKModel{}},
+ })
+}
diff --git a/plugins/bitbucket/e2e/comment_test.go b/plugins/bitbucket/e2e/comment_test.go
new file mode 100644
index 00000000..c6a45935
--- /dev/null
+++ b/plugins/bitbucket/e2e/comment_test.go
@@ -0,0 +1,169 @@
+/*
+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 e2e
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/apache/incubator-devlake/models/domainlayer/code"
+ "github.com/apache/incubator-devlake/models/domainlayer/ticket"
+ "github.com/apache/incubator-devlake/plugins/bitbucket/models"
+ "github.com/apache/incubator-devlake/plugins/bitbucket/tasks"
+
+ "github.com/apache/incubator-devlake/helpers/e2ehelper"
+ "github.com/apache/incubator-devlake/plugins/bitbucket/impl"
+)
+
+func TestCommentDataFlow(t *testing.T) {
+ var plugin impl.Bitbucket
+ dataflowTester := e2ehelper.NewDataFlowTester(t, "bitbucket", plugin)
+
+ bitbucketRepository := &models.BitbucketRepo{
+ BitbucketId: "panjf2000/ants",
+ }
+ taskData := &tasks.BitbucketTaskData{
+ Options: &tasks.BitbucketOptions{
+ ConnectionId: 1,
+ Owner: "panjf2000",
+ Repo: "ants",
+ TransformationRules: models.TransformationRules{
+ PrType: "type/(.*)$",
+ PrComponent: "component/(.*)$",
+ PrBodyClosePattern: "(?mi)(fix|close|resolve|fixes|closes|resolves|fixed|closed|resolved)[\\s]*.*(((and )?(#|https:\\/\\/api.bitbucket.org\\/2.0\\/%s\\/%s\\/issues\\/)\\d+[ ]*)+)",
+ IssueSeverity: "severity/(.*)$",
+ IssuePriority: "^(highest|high|medium|low)$",
+ IssueComponent: "component/(.*)$",
+ IssueTypeBug: "^(bug|failure|error)$",
+ IssueTypeIncident: "",
+ IssueTypeRequirement: "^(feat|feature|proposal|requirement)$",
+ },
+ },
+ Repo: bitbucketRepository,
+ }
+
+ // import raw data table
+ dataflowTester.ImportCsvIntoRawTable("./raw_tables/_raw_bitbucket_api_issue_comments.csv", "_raw_bitbucket_api_issue_comments")
+ dataflowTester.ImportCsvIntoRawTable("./raw_tables/_raw_bitbucket_api_pullrequest_comments.csv", "_raw_bitbucket_api_pull_request_comments")
+
+ // verify extraction
+ dataflowTester.FlushTabler(&models.BitbucketIssueComment{})
+ dataflowTester.FlushTabler(&models.BitbucketPrComment{})
+ dataflowTester.FlushTabler(&models.BitbucketAccount{})
+ dataflowTester.Subtask(tasks.ExtractApiIssueCommentsMeta, taskData)
+ dataflowTester.Subtask(tasks.ExtractApiPrCommentsMeta, taskData)
+ dataflowTester.VerifyTable(
+ models.BitbucketIssueComment{},
+ fmt.Sprintf("./snapshot_tables/%s.csv", models.BitbucketIssueComment{}.TableName()),
+ []string{
+ "connection_id",
+ "bitbucket_id",
+ "issue_id",
+ "author_name",
+ "author_id",
+ "bitbucket_created_at",
+ "bitbucket_updated_at",
+ "type",
+ "_raw_data_params",
+ "_raw_data_table",
+ "_raw_data_id",
+ "_raw_data_remark",
+ },
+ )
+ dataflowTester.VerifyTable(
+ models.BitbucketPrComment{},
+ fmt.Sprintf("./snapshot_tables/%s.csv", models.BitbucketPrComment{}.TableName()),
+ []string{
+ "connection_id",
+ "bitbucket_id",
+ "pull_request_id",
+ "author_name",
+ "author_id",
+ "bitbucket_created_at",
+ "bitbucket_updated_at",
+ "type",
+ "_raw_data_params",
+ "_raw_data_table",
+ "_raw_data_id",
+ "_raw_data_remark",
+ },
+ )
+ dataflowTester.VerifyTable(
+ models.BitbucketAccount{},
+ "./snapshot_tables/_tool_bitbucket_accounts_in_comments.csv",
+ []string{
+ "connection_id",
+ "user_name",
+ "account_id",
+ "account_status",
+ "display_name",
+ "avatar_url",
+ "html_url",
+ "uuid",
+ "has2_fa_enabled",
+ "_raw_data_params",
+ "_raw_data_table",
+ "_raw_data_id",
+ "_raw_data_remark",
+ },
+ )
+
+ // verify comment conversion
+ dataflowTester.FlushTabler(&ticket.IssueComment{})
+ dataflowTester.FlushTabler(&code.PullRequestComment{})
+ dataflowTester.Subtask(tasks.ConvertIssueCommentsMeta, taskData)
+ dataflowTester.Subtask(tasks.ConvertPrCommentsMeta, taskData)
+
+ dataflowTester.VerifyTable(
+ ticket.IssueComment{},
+ fmt.Sprintf("./snapshot_tables/%s.csv", ticket.IssueComment{}.TableName()),
+ []string{
+ "id",
+ "_raw_data_params",
+ "_raw_data_table",
+ "_raw_data_id",
+ "_raw_data_remark",
+ "issue_id",
+ "body",
+ "user_id",
+ },
+ )
+
+ // verify relation in pr and comment conversion
+ dataflowTester.FlushTabler(&code.PullRequestComment{})
+ dataflowTester.Subtask(tasks.ConvertPrCommentsMeta, taskData)
+ dataflowTester.VerifyTable(
+ code.PullRequestComment{},
+ fmt.Sprintf("./snapshot_tables/%s.csv", code.PullRequestComment{}.TableName()),
+ []string{
+ "id",
+ "_raw_data_params",
+ "_raw_data_table",
+ "_raw_data_id",
+ "_raw_data_remark",
+ "pull_request_id",
+ "body",
+ "user_id",
+ "commit_sha",
+ "position",
+ "type",
+ "review_id",
+ "status",
+ },
+ )
+}
diff --git a/plugins/bitbucket/e2e/issue_test.go b/plugins/bitbucket/e2e/issue_test.go
new file mode 100644
index 00000000..799a9cd1
--- /dev/null
+++ b/plugins/bitbucket/e2e/issue_test.go
@@ -0,0 +1,177 @@
+/*
+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 e2e
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/apache/incubator-devlake/models/domainlayer/ticket"
+ "github.com/apache/incubator-devlake/plugins/bitbucket/models"
+
+ "github.com/apache/incubator-devlake/helpers/e2ehelper"
+ "github.com/apache/incubator-devlake/plugins/bitbucket/impl"
+ "github.com/apache/incubator-devlake/plugins/bitbucket/tasks"
+)
+
+func TestIssueDataFlow(t *testing.T) {
+ var plugin impl.Bitbucket
+ dataflowTester := e2ehelper.NewDataFlowTester(t, "bitbucket", plugin)
+
+ bitbucketRepository := &models.BitbucketRepo{
+ BitbucketId: "panjf2000/ants",
+ }
+ taskData := &tasks.BitbucketTaskData{
+ Options: &tasks.BitbucketOptions{
+ ConnectionId: 1,
+ Owner: "panjf2000",
+ Repo: "ants",
+ TransformationRules: models.TransformationRules{
+ PrType: "type/(.*)$",
+ PrComponent: "component/(.*)$",
+ PrBodyClosePattern: "(?mi)(fix|close|resolve|fixes|closes|resolves|fixed|closed|resolved)[\\s]*.*(((and )?(#|https:\\/\\/api.bitbucket.org\\/2.0\\/%s\\/%s\\/issues\\/)\\d+[ ]*)+)",
+ IssueSeverity: "severity/(.*)$",
+ IssuePriority: "^(highest|high|medium|low)$",
+ IssueComponent: "component/(.*)$",
+ IssueTypeBug: "^(bug|failure|error)$",
+ IssueTypeIncident: "",
+ IssueTypeRequirement: "^(feat|feature|proposal|requirement)$",
+ },
+ },
+ Repo: bitbucketRepository,
+ }
+
+ // import raw data table
+ dataflowTester.ImportCsvIntoRawTable("./raw_tables/_raw_bitbucket_api_issues.csv", "_raw_bitbucket_api_issues")
+
+ // verify issue extraction
+ dataflowTester.FlushTabler(&models.BitbucketIssue{})
+ dataflowTester.FlushTabler(&models.BitbucketIssueLabel{})
+ dataflowTester.FlushTabler(&models.BitbucketAccount{})
+ dataflowTester.Subtask(tasks.ExtractApiIssuesMeta, taskData)
+ dataflowTester.VerifyTable(
+ models.BitbucketIssue{},
+ fmt.Sprintf("./snapshot_tables/%s.csv", models.BitbucketIssue{}.TableName()),
+ []string{
+ "connection_id",
+ "bitbucket_id",
+ "repo_id",
+ "number",
+ "state",
+ "title",
+ "body",
+ "priority",
+ "type",
+ "author_id",
+ "author_name",
+ "assignee_id",
+ "assignee_name",
+ "milestone_id",
+ "lead_time_minutes",
+ "url",
+ "closed_at",
+ "bitbucket_created_at",
+ "bitbucket_updated_at",
+ "severity",
+ "component",
+ "_raw_data_params",
+ "_raw_data_table",
+ "_raw_data_id",
+ "_raw_data_remark",
+ },
+ )
+ dataflowTester.VerifyTable(
+ models.BitbucketIssueLabel{},
+ fmt.Sprintf("./snapshot_tables/%s.csv", models.BitbucketIssueLabel{}.TableName()),
+ []string{
+ "connection_id",
+ "issue_id",
+ "label_name",
+ "_raw_data_params",
+ "_raw_data_table",
+ "_raw_data_id",
+ "_raw_data_remark",
+ },
+ )
+ dataflowTester.VerifyTable(
+ models.BitbucketAccount{},
+ "./snapshot_tables/_tool_bitbucket_accounts_in_issue.csv",
+ []string{
+ "connection_id",
+ "user_name",
+ "account_id",
+ "account_status",
+ "display_name",
+ "avatar_url",
+ "html_url",
+ "uuid",
+ "has2_fa_enabled",
+ "_raw_data_params",
+ "_raw_data_table",
+ "_raw_data_id",
+ "_raw_data_remark",
+ },
+ )
+
+ // verify issue conversion
+ dataflowTester.FlushTabler(&ticket.Issue{})
+ dataflowTester.FlushTabler(&ticket.BoardIssue{})
+ dataflowTester.Subtask(tasks.ConvertIssuesMeta, taskData)
+ dataflowTester.VerifyTable(
+ ticket.Issue{},
+ fmt.Sprintf("./snapshot_tables/%s.csv", ticket.Issue{}.TableName()),
+ []string{
+ "id",
+ "_raw_data_params",
+ "_raw_data_table",
+ "_raw_data_id",
+ "_raw_data_remark",
+ "url",
+ "icon_url",
+ "issue_key",
+ "title",
+ "description",
+ "epic_key",
+ "type",
+ "status",
+ "original_status",
+ "story_point",
+ "resolution_date",
+ "created_date",
+ "updated_date",
+ "lead_time_minutes",
+ "parent_issue_id",
+ "priority",
+ "original_estimate_minutes",
+ "time_spent_minutes",
+ "time_remaining_minutes",
+ "creator_id",
+ "creator_name",
+ "assignee_id",
+ "assignee_name",
+ "severity",
+ "component",
+ },
+ )
+ dataflowTester.VerifyTable(
+ ticket.BoardIssue{},
+ fmt.Sprintf("./snapshot_tables/%s.csv", ticket.BoardIssue{}.TableName()),
+ []string{"board_id", "issue_id"},
+ )
+
+}
diff --git a/plugins/bitbucket/e2e/pr_test.go b/plugins/bitbucket/e2e/pr_test.go
new file mode 100644
index 00000000..9daea3bd
--- /dev/null
+++ b/plugins/bitbucket/e2e/pr_test.go
@@ -0,0 +1,149 @@
+/*
+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 e2e
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/apache/incubator-devlake/models/domainlayer/code"
+ "github.com/apache/incubator-devlake/plugins/bitbucket/models"
+
+ "github.com/apache/incubator-devlake/helpers/e2ehelper"
+ "github.com/apache/incubator-devlake/plugins/bitbucket/impl"
+ "github.com/apache/incubator-devlake/plugins/bitbucket/tasks"
+)
+
+func TestPrDataFlow(t *testing.T) {
+ var plugin impl.Bitbucket
+ dataflowTester := e2ehelper.NewDataFlowTester(t, "bitbucket", plugin)
+
+ bitbucketRepository := &models.BitbucketRepo{
+ BitbucketId: "panjf2000/ants",
+ }
+ taskData := &tasks.BitbucketTaskData{
+ Options: &tasks.BitbucketOptions{
+ ConnectionId: 1,
+ Owner: "panjf2000",
+ Repo: "ants",
+ TransformationRules: models.TransformationRules{
+ PrType: "type/(.*)$",
+ PrComponent: "component/(.*)$",
+ PrBodyClosePattern: "(?mi)(fix|close|resolve|fixes|closes|resolves|fixed|closed|resolved)[\\s]*.*(((and )?(#|https:\\/\\/api.bitbucket.org\\/2.0\\/%s\\/%s\\/issues\\/)\\d+[ ]*)+)",
+ },
+ },
+ Repo: bitbucketRepository,
+ }
+
+ // import raw data table
+ dataflowTester.ImportCsvIntoRawTable("./raw_tables/_raw_bitbucket_api_pull_requests.csv", "_raw_bitbucket_api_pull_requests")
+
+ // verify pr extraction
+ dataflowTester.FlushTabler(&models.BitbucketPullRequest{})
+ dataflowTester.FlushTabler(&models.BitbucketAccount{})
+ dataflowTester.Subtask(tasks.ExtractApiPullRequestsMeta, taskData)
+ dataflowTester.VerifyTable(
+ models.BitbucketPullRequest{},
+ fmt.Sprintf("./snapshot_tables/%s.csv", models.BitbucketPullRequest{}.TableName()),
+ []string{
+ "connection_id",
+ "bitbucket_id",
+ "repo_id",
+ "number",
+ "state",
+ "title",
+ "bitbucket_created_at",
+ "bitbucket_updated_at",
+ "closed_at",
+ "comment_count",
+ "commits",
+ "merged_at",
+ "body",
+ "type",
+ "component",
+ "merge_commit_sha",
+ "head_ref",
+ "base_ref",
+ "base_commit_sha",
+ "head_commit_sha",
+ "url",
+ "author_name",
+ "author_id",
+ "_raw_data_params",
+ "_raw_data_table",
+ "_raw_data_id",
+ "_raw_data_remark",
+ },
+ )
+
+ dataflowTester.VerifyTable(
+ models.BitbucketAccount{},
+ "./snapshot_tables/_tool_bitbucket_accounts_in_pr.csv",
+ []string{
+ "connection_id",
+ "user_name",
+ "account_id",
+ "account_status",
+ "display_name",
+ "avatar_url",
+ "html_url",
+ "uuid",
+ "has2_fa_enabled",
+ "_raw_data_params",
+ "_raw_data_table",
+ "_raw_data_id",
+ "_raw_data_remark",
+ },
+ )
+
+ // verify pr conversion
+ dataflowTester.FlushTabler(&code.PullRequest{})
+ dataflowTester.Subtask(tasks.ConvertPullRequestsMeta, taskData)
+ dataflowTester.VerifyTable(
+ code.PullRequest{},
+ fmt.Sprintf("./snapshot_tables/%s.csv", code.PullRequest{}.TableName()),
+ []string{
+ "id",
+ "_raw_data_params",
+ "_raw_data_table",
+ "_raw_data_id",
+ "_raw_data_remark",
+ "base_repo_id",
+ "head_repo_id",
+ "status",
+ "title",
+ "description",
+ "url",
+ "author_name",
+ "author_id",
+ "parent_pr_id",
+ "pull_request_key",
+ "created_date",
+ "merged_date",
+ "closed_date",
+ "type",
+ "component",
+ "merge_commit_sha",
+ "head_ref",
+ "base_ref",
+ "base_commit_sha",
+ "head_commit_sha",
+ },
+ )
+
+}
diff --git a/plugins/bitbucket/e2e/raw_tables/_raw_bitbucket_api_issue_comments.csv b/plugins/bitbucket/e2e/raw_tables/_raw_bitbucket_api_issue_comments.csv
new file mode 100644
index 00000000..44b43ef9
--- /dev/null
+++ b/plugins/bitbucket/e2e/raw_tables/_raw_bitbucket_api_issue_comments.csv
@@ -0,0 +1,29 @@
+id,params,data,url,input,created_at
+1,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue_comment"", ""id"": 63860152, ""created_on"": ""2022-07-17T07:16:30.352685+00:00"", ""updated_on"": null, ""content"": {""type"": ""rendered"", ""raw"": null, ""markup"": ""markdown"", ""html"": """"}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D""}, ""avatar"": {""href"": ""https://secure.grav [...]
+2,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue_comment"", ""id"": 63860153, ""created_on"": ""2022-07-17T07:17:01.991973+00:00"", ""updated_on"": null, ""content"": {""type"": ""rendered"", ""raw"": ""this is a comment test for issue"", ""markup"": ""markdown"", ""html"": ""<p>this is a comment test for issue</p>""}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-429 [...]
+3,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue_comment"", ""id"": 63860241, ""created_on"": ""2022-07-17T09:11:42.759529+00:00"", ""updated_on"": null, ""content"": {""type"": ""rendered"", ""raw"": null, ""markup"": ""markdown"", ""html"": """"}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D""}, ""avatar"": {""href"": ""https://secure.grav [...]
+4,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue_comment"", ""id"": 63961973, ""created_on"": ""2022-08-12T13:51:25.882209+00:00"", ""updated_on"": null, ""content"": {""type"": ""rendered"", ""raw"": null, ""markup"": ""markdown"", ""html"": """"}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D""}, ""avatar"": {""href"": ""https://secure.grav [...]
+5,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue_comment"", ""id"": 63961995, ""created_on"": ""2022-08-12T13:55:58.125226+00:00"", ""updated_on"": null, ""content"": {""type"": ""rendered"", ""raw"": null, ""markup"": ""markdown"", ""html"": """"}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D""}, ""avatar"": {""href"": ""https://secure.grav [...]
+6,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue_comment"", ""id"": 63963442, ""created_on"": ""2022-08-13T06:17:23.165014+00:00"", ""updated_on"": null, ""content"": {""type"": ""rendered"", ""raw"": ""gvoeaoivae"", ""markup"": ""markdown"", ""html"": ""<p>gvoeaoivae</p>""}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D""}, ""avatar"": {""hr [...]
+7,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue_comment"", ""id"": 63963443, ""created_on"": ""2022-08-13T06:17:28.683792+00:00"", ""updated_on"": null, ""content"": {""type"": ""rendered"", ""raw"": ""vboeaorboaneo"", ""markup"": ""markdown"", ""html"": ""<p>vboeaorboaneo</p>""}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D""}, ""avatar"": [...]
+8,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue_comment"", ""id"": 63963444, ""created_on"": ""2022-08-13T06:17:36.685718+00:00"", ""updated_on"": null, ""content"": {""type"": ""rendered"", ""raw"": ""beoiroierbieiaoboere"", ""markup"": ""markdown"", ""html"": ""<p>beoiroierbieiaoboere</p>""}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D"" [...]
+9,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue_comment"", ""id"": 63963445, ""created_on"": ""2022-08-13T06:17:42.086856+00:00"", ""updated_on"": null, ""content"": {""type"": ""rendered"", ""raw"": ""boetboenavnin"", ""markup"": ""markdown"", ""html"": ""<p>boetboenavnin</p>""}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D""}, ""avatar"": [...]
+10,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue_comment"", ""id"": 63963446, ""created_on"": ""2022-08-13T06:17:47.634459+00:00"", ""updated_on"": null, ""content"": {""type"": ""rendered"", ""raw"": ""bbenoirbnavbeboa"", ""markup"": ""markdown"", ""html"": ""<p>bbenoirbnavbeboa</p>""}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D""}, ""av [...]
+11,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue_comment"", ""id"": 63963447, ""created_on"": ""2022-08-13T06:17:55.851366+00:00"", ""updated_on"": null, ""content"": {""type"": ""rendered"", ""raw"": ""lboehoraebow"", ""markup"": ""markdown"", ""html"": ""<p>lboehoraebow</p>""}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D""}, ""avatar"": [...]
+12,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue_comment"", ""id"": 63963449, ""created_on"": ""2022-08-13T06:18:01.927576+00:00"", ""updated_on"": null, ""content"": {""type"": ""rendered"", ""raw"": ""ebeobnoerioaeiow"", ""markup"": ""markdown"", ""html"": ""<p>ebeobnoerioaeiow</p>""}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D""}, ""av [...]
+13,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue_comment"", ""id"": 63963450, ""created_on"": ""2022-08-13T06:18:11.146999+00:00"", ""updated_on"": null, ""content"": {""type"": ""rendered"", ""raw"": ""uttrberoiea"", ""markup"": ""markdown"", ""html"": ""<p>uttrberoiea</p>""}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D""}, ""avatar"": {" [...]
+14,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue_comment"", ""id"": 63963451, ""created_on"": ""2022-08-13T06:18:19.376928+00:00"", ""updated_on"": null, ""content"": {""type"": ""rendered"", ""raw"": ""obfbdfonwea"", ""markup"": ""markdown"", ""html"": ""<p>obfbdfonwea</p>""}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D""}, ""avatar"": {" [...]
+15,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue_comment"", ""id"": 63963452, ""created_on"": ""2022-08-13T06:18:26.505035+00:00"", ""updated_on"": null, ""content"": {""type"": ""rendered"", ""raw"": ""bjeorjboewaonwea"", ""markup"": ""markdown"", ""html"": ""<p>bjeorjboewaonwea</p>""}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D""}, ""av [...]
+16,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue_comment"", ""id"": 63963453, ""created_on"": ""2022-08-13T06:18:33.109491+00:00"", ""updated_on"": null, ""content"": {""type"": ""rendered"", ""raw"": ""brtoeorbnqqe"", ""markup"": ""markdown"", ""html"": ""<p>brtoeorbnqqe</p>""}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D""}, ""avatar"": [...]
+17,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue_comment"", ""id"": 63963454, ""created_on"": ""2022-08-13T06:18:39.542749+00:00"", ""updated_on"": null, ""content"": {""type"": ""rendered"", ""raw"": ""mmboeroi"", ""markup"": ""markdown"", ""html"": ""<p>mmboeroi</p>""}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D""}, ""avatar"": {""href" [...]
+18,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue_comment"", ""id"": 63963455, ""created_on"": ""2022-08-13T06:18:46.158468+00:00"", ""updated_on"": null, ""content"": {""type"": ""rendered"", ""raw"": ""lqooaqjoe"", ""markup"": ""markdown"", ""html"": ""<p>lqooaqjoe</p>""}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D""}, ""avatar"": {""hre [...]
+19,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue_comment"", ""id"": 63963456, ""created_on"": ""2022-08-13T06:18:52.858029+00:00"", ""updated_on"": null, ""content"": {""type"": ""rendered"", ""raw"": ""vajoieiowaeovw"", ""markup"": ""markdown"", ""html"": ""<p>vajoieiowaeovw</p>""}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D""}, ""avatar [...]
+20,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue_comment"", ""id"": 63963457, ""created_on"": ""2022-08-13T06:18:58.979282+00:00"", ""updated_on"": null, ""content"": {""type"": ""rendered"", ""raw"": ""ieiojiwea"", ""markup"": ""markdown"", ""html"": ""<p>ieiojiwea</p>""}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D""}, ""avatar"": {""hre [...]
+21,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue_comment"", ""id"": 63963458, ""created_on"": ""2022-08-13T06:19:05.666753+00:00"", ""updated_on"": null, ""content"": {""type"": ""rendered"", ""raw"": ""beraowheuovhaewa"", ""markup"": ""markdown"", ""html"": ""<p>beraowheuovhaewa</p>""}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D""}, ""av [...]
+22,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue_comment"", ""id"": 63963459, ""created_on"": ""2022-08-13T06:19:15.956726+00:00"", ""updated_on"": null, ""content"": {""type"": ""rendered"", ""raw"": ""oeorjeorb"", ""markup"": ""markdown"", ""html"": ""<p>oeorjeorb</p>""}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D""}, ""avatar"": {""hre [...]
+23,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue_comment"", ""id"": 63963460, ""created_on"": ""2022-08-13T06:19:23.294249+00:00"", ""updated_on"": null, ""content"": {""type"": ""rendered"", ""raw"": ""iocowevoanewww"", ""markup"": ""markdown"", ""html"": ""<p>iocowevoanewww</p>""}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D""}, ""avatar [...]
+24,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue_comment"", ""id"": 63963461, ""created_on"": ""2022-08-13T06:19:31.457341+00:00"", ""updated_on"": null, ""content"": {""type"": ""rendered"", ""raw"": ""cowoehvowevaobnebiejaiweaw"", ""markup"": ""markdown"", ""html"": ""<p>cowoehvowevaobnebiejaiweaw</p>""}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-2182 [...]
+25,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue_comment"", ""id"": 63963462, ""created_on"": ""2022-08-13T06:19:38.433512+00:00"", ""updated_on"": null, ""content"": {""type"": ""rendered"", ""raw"": ""vbleklawi"", ""markup"": ""markdown"", ""html"": ""<p>vbleklawi</p>""}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D""}, ""avatar"": {""hre [...]
+26,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue_comment"", ""id"": 63963463, ""created_on"": ""2022-08-13T06:19:45.160531+00:00"", ""updated_on"": null, ""content"": {""type"": ""rendered"", ""raw"": ""qowjoveawev"", ""markup"": ""markdown"", ""html"": ""<p>qowjoveawev</p>""}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D""}, ""avatar"": {" [...]
+27,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue_comment"", ""id"": 63963464, ""created_on"": ""2022-08-13T06:19:54.365641+00:00"", ""updated_on"": null, ""content"": {""type"": ""rendered"", ""raw"": ""ciojboieria"", ""markup"": ""markdown"", ""html"": ""<p>ciojboieria</p>""}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D""}, ""avatar"": {" [...]
+28,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue_comment"", ""id"": 63963465, ""created_on"": ""2022-08-13T06:20:01.329125+00:00"", ""updated_on"": null, ""content"": {""type"": ""rendered"", ""raw"": ""cwiwhoervowwwqe"", ""markup"": ""markdown"", ""html"": ""<p>cwiwhoervowwwqe</p>""}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D""}, ""avat [...]
diff --git a/plugins/bitbucket/e2e/raw_tables/_raw_bitbucket_api_issues.csv b/plugins/bitbucket/e2e/raw_tables/_raw_bitbucket_api_issues.csv
new file mode 100644
index 00000000..c8cf4d14
--- /dev/null
+++ b/plugins/bitbucket/e2e/raw_tables/_raw_bitbucket_api_issues.csv
@@ -0,0 +1,31 @@
+id,params,data,url,input,created_at
+31,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue"", ""id"": 27, ""repository"": {""type"": ""repository"", ""full_name"": ""panjf2000/ants"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/repositories/panjf2000/ants""}, ""html"": {""href"": ""https://bitbucket.org/panjf2000/ants""}, ""avatar"": {""href"": ""https://bytebucket.org/ravatar/%7Be762bff4-f795-49c6-b9f9-6ea334b6d964%7D?ts=default""}}, ""name"": ""ants"", ""uuid"": ""{e7 [...]
+32,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue"", ""id"": 30, ""repository"": {""type"": ""repository"", ""full_name"": ""panjf2000/ants"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/repositories/panjf2000/ants""}, ""html"": {""href"": ""https://bitbucket.org/panjf2000/ants""}, ""avatar"": {""href"": ""https://bytebucket.org/ravatar/%7Be762bff4-f795-49c6-b9f9-6ea334b6d964%7D?ts=default""}}, ""name"": ""ants"", ""uuid"": ""{e7 [...]
+33,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue"", ""id"": 29, ""repository"": {""type"": ""repository"", ""full_name"": ""panjf2000/ants"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/repositories/panjf2000/ants""}, ""html"": {""href"": ""https://bitbucket.org/panjf2000/ants""}, ""avatar"": {""href"": ""https://bytebucket.org/ravatar/%7Be762bff4-f795-49c6-b9f9-6ea334b6d964%7D?ts=default""}}, ""name"": ""ants"", ""uuid"": ""{e7 [...]
+34,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue"", ""id"": 28, ""repository"": {""type"": ""repository"", ""full_name"": ""panjf2000/ants"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/repositories/panjf2000/ants""}, ""html"": {""href"": ""https://bitbucket.org/panjf2000/ants""}, ""avatar"": {""href"": ""https://bytebucket.org/ravatar/%7Be762bff4-f795-49c6-b9f9-6ea334b6d964%7D?ts=default""}}, ""name"": ""ants"", ""uuid"": ""{e7 [...]
+35,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue"", ""id"": 26, ""repository"": {""type"": ""repository"", ""full_name"": ""panjf2000/ants"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/repositories/panjf2000/ants""}, ""html"": {""href"": ""https://bitbucket.org/panjf2000/ants""}, ""avatar"": {""href"": ""https://bytebucket.org/ravatar/%7Be762bff4-f795-49c6-b9f9-6ea334b6d964%7D?ts=default""}}, ""name"": ""ants"", ""uuid"": ""{e7 [...]
+36,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue"", ""id"": 25, ""repository"": {""type"": ""repository"", ""full_name"": ""panjf2000/ants"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/repositories/panjf2000/ants""}, ""html"": {""href"": ""https://bitbucket.org/panjf2000/ants""}, ""avatar"": {""href"": ""https://bytebucket.org/ravatar/%7Be762bff4-f795-49c6-b9f9-6ea334b6d964%7D?ts=default""}}, ""name"": ""ants"", ""uuid"": ""{e7 [...]
+37,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue"", ""id"": 6, ""repository"": {""type"": ""repository"", ""full_name"": ""panjf2000/ants"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/repositories/panjf2000/ants""}, ""html"": {""href"": ""https://bitbucket.org/panjf2000/ants""}, ""avatar"": {""href"": ""https://bytebucket.org/ravatar/%7Be762bff4-f795-49c6-b9f9-6ea334b6d964%7D?ts=default""}}, ""name"": ""ants"", ""uuid"": ""{e76 [...]
+38,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue"", ""id"": 24, ""repository"": {""type"": ""repository"", ""full_name"": ""panjf2000/ants"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/repositories/panjf2000/ants""}, ""html"": {""href"": ""https://bitbucket.org/panjf2000/ants""}, ""avatar"": {""href"": ""https://bytebucket.org/ravatar/%7Be762bff4-f795-49c6-b9f9-6ea334b6d964%7D?ts=default""}}, ""name"": ""ants"", ""uuid"": ""{e7 [...]
+39,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue"", ""id"": 23, ""repository"": {""type"": ""repository"", ""full_name"": ""panjf2000/ants"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/repositories/panjf2000/ants""}, ""html"": {""href"": ""https://bitbucket.org/panjf2000/ants""}, ""avatar"": {""href"": ""https://bytebucket.org/ravatar/%7Be762bff4-f795-49c6-b9f9-6ea334b6d964%7D?ts=default""}}, ""name"": ""ants"", ""uuid"": ""{e7 [...]
+40,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue"", ""id"": 22, ""repository"": {""type"": ""repository"", ""full_name"": ""panjf2000/ants"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/repositories/panjf2000/ants""}, ""html"": {""href"": ""https://bitbucket.org/panjf2000/ants""}, ""avatar"": {""href"": ""https://bytebucket.org/ravatar/%7Be762bff4-f795-49c6-b9f9-6ea334b6d964%7D?ts=default""}}, ""name"": ""ants"", ""uuid"": ""{e7 [...]
+41,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue"", ""id"": 21, ""repository"": {""type"": ""repository"", ""full_name"": ""panjf2000/ants"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/repositories/panjf2000/ants""}, ""html"": {""href"": ""https://bitbucket.org/panjf2000/ants""}, ""avatar"": {""href"": ""https://bytebucket.org/ravatar/%7Be762bff4-f795-49c6-b9f9-6ea334b6d964%7D?ts=default""}}, ""name"": ""ants"", ""uuid"": ""{e7 [...]
+42,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue"", ""id"": 20, ""repository"": {""type"": ""repository"", ""full_name"": ""panjf2000/ants"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/repositories/panjf2000/ants""}, ""html"": {""href"": ""https://bitbucket.org/panjf2000/ants""}, ""avatar"": {""href"": ""https://bytebucket.org/ravatar/%7Be762bff4-f795-49c6-b9f9-6ea334b6d964%7D?ts=default""}}, ""name"": ""ants"", ""uuid"": ""{e7 [...]
+43,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue"", ""id"": 19, ""repository"": {""type"": ""repository"", ""full_name"": ""panjf2000/ants"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/repositories/panjf2000/ants""}, ""html"": {""href"": ""https://bitbucket.org/panjf2000/ants""}, ""avatar"": {""href"": ""https://bytebucket.org/ravatar/%7Be762bff4-f795-49c6-b9f9-6ea334b6d964%7D?ts=default""}}, ""name"": ""ants"", ""uuid"": ""{e7 [...]
+44,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue"", ""id"": 18, ""repository"": {""type"": ""repository"", ""full_name"": ""panjf2000/ants"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/repositories/panjf2000/ants""}, ""html"": {""href"": ""https://bitbucket.org/panjf2000/ants""}, ""avatar"": {""href"": ""https://bytebucket.org/ravatar/%7Be762bff4-f795-49c6-b9f9-6ea334b6d964%7D?ts=default""}}, ""name"": ""ants"", ""uuid"": ""{e7 [...]
+45,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue"", ""id"": 17, ""repository"": {""type"": ""repository"", ""full_name"": ""panjf2000/ants"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/repositories/panjf2000/ants""}, ""html"": {""href"": ""https://bitbucket.org/panjf2000/ants""}, ""avatar"": {""href"": ""https://bytebucket.org/ravatar/%7Be762bff4-f795-49c6-b9f9-6ea334b6d964%7D?ts=default""}}, ""name"": ""ants"", ""uuid"": ""{e7 [...]
+46,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue"", ""id"": 16, ""repository"": {""type"": ""repository"", ""full_name"": ""panjf2000/ants"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/repositories/panjf2000/ants""}, ""html"": {""href"": ""https://bitbucket.org/panjf2000/ants""}, ""avatar"": {""href"": ""https://bytebucket.org/ravatar/%7Be762bff4-f795-49c6-b9f9-6ea334b6d964%7D?ts=default""}}, ""name"": ""ants"", ""uuid"": ""{e7 [...]
+47,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue"", ""id"": 15, ""repository"": {""type"": ""repository"", ""full_name"": ""panjf2000/ants"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/repositories/panjf2000/ants""}, ""html"": {""href"": ""https://bitbucket.org/panjf2000/ants""}, ""avatar"": {""href"": ""https://bytebucket.org/ravatar/%7Be762bff4-f795-49c6-b9f9-6ea334b6d964%7D?ts=default""}}, ""name"": ""ants"", ""uuid"": ""{e7 [...]
+48,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue"", ""id"": 14, ""repository"": {""type"": ""repository"", ""full_name"": ""panjf2000/ants"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/repositories/panjf2000/ants""}, ""html"": {""href"": ""https://bitbucket.org/panjf2000/ants""}, ""avatar"": {""href"": ""https://bytebucket.org/ravatar/%7Be762bff4-f795-49c6-b9f9-6ea334b6d964%7D?ts=default""}}, ""name"": ""ants"", ""uuid"": ""{e7 [...]
+49,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue"", ""id"": 13, ""repository"": {""type"": ""repository"", ""full_name"": ""panjf2000/ants"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/repositories/panjf2000/ants""}, ""html"": {""href"": ""https://bitbucket.org/panjf2000/ants""}, ""avatar"": {""href"": ""https://bytebucket.org/ravatar/%7Be762bff4-f795-49c6-b9f9-6ea334b6d964%7D?ts=default""}}, ""name"": ""ants"", ""uuid"": ""{e7 [...]
+50,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue"", ""id"": 12, ""repository"": {""type"": ""repository"", ""full_name"": ""panjf2000/ants"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/repositories/panjf2000/ants""}, ""html"": {""href"": ""https://bitbucket.org/panjf2000/ants""}, ""avatar"": {""href"": ""https://bytebucket.org/ravatar/%7Be762bff4-f795-49c6-b9f9-6ea334b6d964%7D?ts=default""}}, ""name"": ""ants"", ""uuid"": ""{e7 [...]
+51,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue"", ""id"": 11, ""repository"": {""type"": ""repository"", ""full_name"": ""panjf2000/ants"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/repositories/panjf2000/ants""}, ""html"": {""href"": ""https://bitbucket.org/panjf2000/ants""}, ""avatar"": {""href"": ""https://bytebucket.org/ravatar/%7Be762bff4-f795-49c6-b9f9-6ea334b6d964%7D?ts=default""}}, ""name"": ""ants"", ""uuid"": ""{e7 [...]
+52,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue"", ""id"": 10, ""repository"": {""type"": ""repository"", ""full_name"": ""panjf2000/ants"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/repositories/panjf2000/ants""}, ""html"": {""href"": ""https://bitbucket.org/panjf2000/ants""}, ""avatar"": {""href"": ""https://bytebucket.org/ravatar/%7Be762bff4-f795-49c6-b9f9-6ea334b6d964%7D?ts=default""}}, ""name"": ""ants"", ""uuid"": ""{e7 [...]
+53,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue"", ""id"": 9, ""repository"": {""type"": ""repository"", ""full_name"": ""panjf2000/ants"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/repositories/panjf2000/ants""}, ""html"": {""href"": ""https://bitbucket.org/panjf2000/ants""}, ""avatar"": {""href"": ""https://bytebucket.org/ravatar/%7Be762bff4-f795-49c6-b9f9-6ea334b6d964%7D?ts=default""}}, ""name"": ""ants"", ""uuid"": ""{e76 [...]
+54,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue"", ""id"": 8, ""repository"": {""type"": ""repository"", ""full_name"": ""panjf2000/ants"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/repositories/panjf2000/ants""}, ""html"": {""href"": ""https://bitbucket.org/panjf2000/ants""}, ""avatar"": {""href"": ""https://bytebucket.org/ravatar/%7Be762bff4-f795-49c6-b9f9-6ea334b6d964%7D?ts=default""}}, ""name"": ""ants"", ""uuid"": ""{e76 [...]
+55,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue"", ""id"": 7, ""repository"": {""type"": ""repository"", ""full_name"": ""panjf2000/ants"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/repositories/panjf2000/ants""}, ""html"": {""href"": ""https://bitbucket.org/panjf2000/ants""}, ""avatar"": {""href"": ""https://bytebucket.org/ravatar/%7Be762bff4-f795-49c6-b9f9-6ea334b6d964%7D?ts=default""}}, ""name"": ""ants"", ""uuid"": ""{e76 [...]
+56,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue"", ""id"": 5, ""repository"": {""type"": ""repository"", ""full_name"": ""panjf2000/ants"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/repositories/panjf2000/ants""}, ""html"": {""href"": ""https://bitbucket.org/panjf2000/ants""}, ""avatar"": {""href"": ""https://bytebucket.org/ravatar/%7Be762bff4-f795-49c6-b9f9-6ea334b6d964%7D?ts=default""}}, ""name"": ""ants"", ""uuid"": ""{e76 [...]
+57,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue"", ""id"": 4, ""repository"": {""type"": ""repository"", ""full_name"": ""panjf2000/ants"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/repositories/panjf2000/ants""}, ""html"": {""href"": ""https://bitbucket.org/panjf2000/ants""}, ""avatar"": {""href"": ""https://bytebucket.org/ravatar/%7Be762bff4-f795-49c6-b9f9-6ea334b6d964%7D?ts=default""}}, ""name"": ""ants"", ""uuid"": ""{e76 [...]
+58,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue"", ""id"": 3, ""repository"": {""type"": ""repository"", ""full_name"": ""panjf2000/ants"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/repositories/panjf2000/ants""}, ""html"": {""href"": ""https://bitbucket.org/panjf2000/ants""}, ""avatar"": {""href"": ""https://bytebucket.org/ravatar/%7Be762bff4-f795-49c6-b9f9-6ea334b6d964%7D?ts=default""}}, ""name"": ""ants"", ""uuid"": ""{e76 [...]
+59,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue"", ""id"": 2, ""repository"": {""type"": ""repository"", ""full_name"": ""panjf2000/ants"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/repositories/panjf2000/ants""}, ""html"": {""href"": ""https://bitbucket.org/panjf2000/ants""}, ""avatar"": {""href"": ""https://bytebucket.org/ravatar/%7Be762bff4-f795-49c6-b9f9-6ea334b6d964%7D?ts=default""}}, ""name"": ""ants"", ""uuid"": ""{e76 [...]
+60,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""issue"", ""id"": 1, ""repository"": {""type"": ""repository"", ""full_name"": ""panjf2000/ants"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/repositories/panjf2000/ants""}, ""html"": {""href"": ""https://bitbucket.org/panjf2000/ants""}, ""avatar"": {""href"": ""https://bytebucket.org/ravatar/%7Be762bff4-f795-49c6-b9f9-6ea334b6d964%7D?ts=default""}}, ""name"": ""ants"", ""uuid"": ""{e76 [...]
diff --git a/plugins/bitbucket/e2e/raw_tables/_raw_bitbucket_api_pull_requests.csv b/plugins/bitbucket/e2e/raw_tables/_raw_bitbucket_api_pull_requests.csv
new file mode 100644
index 00000000..45ad9fe3
--- /dev/null
+++ b/plugins/bitbucket/e2e/raw_tables/_raw_bitbucket_api_pull_requests.csv
@@ -0,0 +1,58 @@
+id,params,data,url,input,created_at
+115,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 2, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 40, ""title"": ""Test016"", ""description"": ""* feat: add test01\n* feat: add test02\n* feat: add test03\n* feat: add test04\n* feat: add test05\n* feat: add test06\n* feat: add test07\n* feat: add test08\n* feat: add test09\n* feat: add test010\n* feat: add test011\n* feat: add test012\n* feat: add test013\n* feat: add test014\n* feat: add t [...]
+116,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 4, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 54, ""title"": ""Bitbuckettest19"", ""description"": ""* feat: add test01\n* feat: add test02\n* feat: add test03\n* feat: add test04\n* feat: add test05\n* feat: add test06\n* feat: add test07\n* feat: add test08\n* feat: add test09\n* feat: add test010\n* feat: add test011\n* feat: add test012\n* feat: add test013\n* feat: add test014\n* fea [...]
+117,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 4, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 42, ""title"": ""Test019"", ""description"": ""* feat: add test01\n* feat: add test02\n* feat: add test03\n* feat: add test04\n* feat: add test05\n* feat: add test06\n* feat: add test07\n* feat: add test08\n* feat: add test09\n* feat: add test010\n* feat: add test011\n* feat: add test012\n* feat: add test013\n* feat: add test014\n* feat: add t [...]
+118,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 3, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 31, ""title"": ""Test020"", ""description"": ""* feat: add test01\n* feat: add test02\n* feat: add test03\n* feat: add test04\n* feat: add test05\n* feat: add test06\n* feat: add test07\n* feat: add test08\n* feat: add test09\n* feat: add test010\n* feat: add test011\n* feat: add test012\n* feat: add test013\n* feat: add test014\n* feat: add t [...]
+119,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 1, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 41, ""title"": ""Test018"", ""description"": ""* feat: add test01\n* feat: add test02\n* feat: add test03\n* feat: add test04\n* feat: add test05\n* feat: add test06\n* feat: add test07\n* feat: add test08\n* feat: add test09\n* feat: add test010\n* feat: add test011\n* feat: add test012\n* feat: add test013\n* feat: add test014\n* feat: add t [...]
+120,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 2, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 44, ""title"": ""Test017"", ""description"": ""* feat: add test01\n* feat: add test02\n* feat: add test03\n* feat: add test04\n* feat: add test05\n* feat: add test06\n* feat: add test07\n* feat: add test08\n* feat: add test09\n* feat: add test010\n* feat: add test011\n* feat: add test012\n* feat: add test013\n* feat: add test014\n* feat: add t [...]
+121,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 2, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 38, ""title"": ""Test029"", ""description"": ""* feat: add test01\n* feat: add test02\n* feat: add test03\n* feat: add test04\n* feat: add test05\n* feat: add test06\n* feat: add test07\n* feat: add test08\n* feat: add test09\n* feat: add test010\n* feat: add test011\n* feat: add test012\n* feat: add test013\n* feat: add test014\n* feat: add t [...]
+122,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 2, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 51, ""title"": ""Bitbuckettest15"", ""description"": ""* feat: add test01\n* feat: add test02\n* feat: add test03\n* feat: add test04\n* feat: add test05\n* feat: add test06\n* feat: add test07\n* feat: add test08\n* feat: add test09\n* feat: add test010\n* feat: add test011\n* feat: add test012\n* feat: add test013\n* feat: add test014\n* fea [...]
+123,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 2, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 57, ""title"": ""Bitbuckettest22"", ""description"": ""* feat: add test01\n* feat: add test02\n* feat: add test03\n* feat: add test04\n* feat: add test05\n* feat: add test06\n* feat: add test07\n* feat: add test08\n* feat: add test09\n* feat: add test010\n* feat: add test011\n* feat: add test012\n* feat: add test013\n* feat: add test014\n* fea [...]
+124,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 56, ""title"": ""Bitbuckettest25"", ""description"": ""* feat: add test01\n* feat: add test02\n* feat: add test03\n* feat: add test04\n* feat: add test05\n* feat: add test06\n* feat: add test07\n* feat: add test08\n* feat: add test09\n* feat: add test010\n* feat: add test011\n* feat: add test012\n* feat: add test013\n* feat: add test014\n* fea [...]
+125,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 53, ""title"": ""Bitbuckettest17"", ""description"": ""* feat: add test01\n* feat: add test02\n* feat: add test03\n* feat: add test04\n* feat: add test05\n* feat: add test06\n* feat: add test07\n* feat: add test08\n* feat: add test09\n* feat: add test010\n* feat: add test011\n* feat: add test012\n* feat: add test013\n* feat: add test014\n* fea [...]
+126,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 55, ""title"": ""Bitbuckettest13"", ""description"": ""* feat: add test01\n* feat: add test02\n* feat: add test03\n* feat: add test04\n* feat: add test05\n* feat: add test06\n* feat: add test07\n* feat: add test08\n* feat: add test09\n* feat: add test010\n* feat: add test011\n* feat: add test012\n* feat: add test013\n* feat: add test014\n* fea [...]
+127,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 52, ""title"": ""Bitbuckettest16"", ""description"": ""* feat: add test01\n* feat: add test02\n* feat: add test03\n* feat: add test04\n* feat: add test05\n* feat: add test06\n* feat: add test07\n* feat: add test08\n* feat: add test09\n* feat: add test010\n* feat: add test011\n* feat: add test012\n* feat: add test013\n* feat: add test014\n* fea [...]
+128,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 50, ""title"": ""Bitbuckettest12"", ""description"": ""* feat: add test01\n* feat: add test02\n* feat: add test03\n* feat: add test04\n* feat: add test05\n* feat: add test06\n* feat: add test07\n* feat: add test08\n* feat: add test09\n* feat: add test010\n* feat: add test011\n* feat: add test012\n* feat: add test013\n* feat: add test014\n* fea [...]
+129,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 49, ""title"": ""Test08"", ""description"": ""* feat: add test01\n* feat: add test02\n* feat: add test03\n* feat: add test04\n* feat: add test05\n* feat: add test06\n* feat: add test07\n* feat: add test08\n\n‌"", ""state"": ""OPEN"", ""merge_commit"": null, ""close_source_branch"": false, ""closed_by"": null, ""author"": {""display_name"": ""t [...]
+130,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 48, ""title"": ""Test07"", ""description"": ""* feat: add test01\n* feat: add test02\n* feat: add test03\n* feat: add test04\n* feat: add test05\n* feat: add test06\n* feat: add test07\n\n‌"", ""state"": ""OPEN"", ""merge_commit"": null, ""close_source_branch"": false, ""closed_by"": null, ""author"": {""display_name"": ""teoiaoe"", ""links"": [...]
+131,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 47, ""title"": ""Test06"", ""description"": ""* feat: add test01\n* feat: add test02\n* feat: add test03\n* feat: add test04\n* feat: add test05\n* feat: add test06\n\n‌"", ""state"": ""OPEN"", ""merge_commit"": null, ""close_source_branch"": false, ""closed_by"": null, ""author"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href" [...]
+132,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 29, ""title"": ""Test03"", ""description"": ""* feat: add test01\n* feat: add test02\n* feat: add test03\n\n‌"", ""state"": ""OPEN"", ""merge_commit"": null, ""close_source_branch"": false, ""closed_by"": null, ""author"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-42 [...]
+133,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 46, ""title"": ""Test04"", ""description"": ""* feat: add test01\n* feat: add test02\n* feat: add test03\n* feat: add test04\n\n‌"", ""state"": ""OPEN"", ""merge_commit"": null, ""close_source_branch"": false, ""closed_by"": null, ""author"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users [...]
+134,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 45, ""title"": ""feat: add test01"", ""description"": """", ""state"": ""OPEN"", ""merge_commit"": null, ""close_source_branch"": false, ""closed_by"": null, ""author"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D""}, ""avatar"": {""href"": "" [...]
+135,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 43, ""title"": ""Test015"", ""description"": ""* feat: add test01\n* feat: add test02\n* feat: add test03\n* feat: add test04\n* feat: add test05\n* feat: add test06\n* feat: add test07\n* feat: add test08\n* feat: add test09\n* feat: add test010\n* feat: add test011\n* feat: add test012\n* feat: add test013\n* feat: add test014\n* feat: add t [...]
+136,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 39, ""title"": ""Test030"", ""description"": ""* feat: add test01\n* feat: add test02\n* feat: add test03\n* feat: add test04\n* feat: add test05\n* feat: add test06\n* feat: add test07\n* feat: add test08\n* feat: add test09\n* feat: add test010\n* feat: add test011\n* feat: add test012\n* feat: add test013\n* feat: add test014\n* feat: add t [...]
+137,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 37, ""title"": ""Test028"", ""description"": ""* feat: add test01\n* feat: add test02\n* feat: add test03\n* feat: add test04\n* feat: add test05\n* feat: add test06\n* feat: add test07\n* feat: add test08\n* feat: add test09\n* feat: add test010\n* feat: add test011\n* feat: add test012\n* feat: add test013\n* feat: add test014\n* feat: add t [...]
+138,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 36, ""title"": ""Test027"", ""description"": ""* feat: add test01\n* feat: add test02\n* feat: add test03\n* feat: add test04\n* feat: add test05\n* feat: add test06\n* feat: add test07\n* feat: add test08\n* feat: add test09\n* feat: add test010\n* feat: add test011\n* feat: add test012\n* feat: add test013\n* feat: add test014\n* feat: add t [...]
+139,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 35, ""title"": ""Test025"", ""description"": ""* feat: add test01\n* feat: add test02\n* feat: add test03\n* feat: add test04\n* feat: add test05\n* feat: add test06\n* feat: add test07\n* feat: add test08\n* feat: add test09\n* feat: add test010\n* feat: add test011\n* feat: add test012\n* feat: add test013\n* feat: add test014\n* feat: add t [...]
+140,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 34, ""title"": ""Test024"", ""description"": ""* feat: add test01\n* feat: add test02\n* feat: add test03\n* feat: add test04\n* feat: add test05\n* feat: add test06\n* feat: add test07\n* feat: add test08\n* feat: add test09\n* feat: add test010\n* feat: add test011\n* feat: add test012\n* feat: add test013\n* feat: add test014\n* feat: add t [...]
+141,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 33, ""title"": ""Test023"", ""description"": ""* feat: add test01\n* feat: add test02\n* feat: add test03\n* feat: add test04\n* feat: add test05\n* feat: add test06\n* feat: add test07\n* feat: add test08\n* feat: add test09\n* feat: add test010\n* feat: add test011\n* feat: add test012\n* feat: add test013\n* feat: add test014\n* feat: add t [...]
+142,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 32, ""title"": ""Test022"", ""description"": ""* feat: add test01\n* feat: add test02\n* feat: add test03\n* feat: add test04\n* feat: add test05\n* feat: add test06\n* feat: add test07\n* feat: add test08\n* feat: add test09\n* feat: add test010\n* feat: add test011\n* feat: add test012\n* feat: add test013\n* feat: add test014\n* feat: add t [...]
+143,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 30, ""title"": ""Test05"", ""description"": ""* feat: add test01\n* feat: add test02\n* feat: add test03\n* feat: add test04\n* feat: add test05\n\n‌"", ""state"": ""OPEN"", ""merge_commit"": null, ""close_source_branch"": false, ""closed_by"": null, ""author"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bit [...]
+144,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 24, ""title"": ""Test17"", ""description"": ""* feat: add git.sh\n* feat: add test001\n* feat: test002\n* feat: add test003\n* feat: add test004\n* feat: add test005\n* feat: add test006\n* feat: add test007\n* feat: add test008\n* feat: add test009\n* feat: add test010\n* feat: add test011\n* feat: add test012\n* feat: add test013\n* feat: ad [...]
+145,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 23, ""title"": ""Test20"", ""description"": ""* feat: add git.sh\n* feat: add test001\n* feat: test002\n* feat: add test003\n* feat: add test004\n* feat: add test005\n* feat: add test006\n* feat: add test007\n* feat: add test008\n* feat: add test009\n* feat: add test010\n* feat: add test011\n* feat: add test012\n* feat: add test013\n* feat: ad [...]
+146,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 22, ""title"": ""Test19"", ""description"": ""* feat: add git.sh\n* feat: add test001\n* feat: test002\n* feat: add test003\n* feat: add test004\n* feat: add test005\n* feat: add test006\n* feat: add test007\n* feat: add test008\n* feat: add test009\n* feat: add test010\n* feat: add test011\n* feat: add test012\n* feat: add test013\n* feat: ad [...]
+147,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 21, ""title"": ""Test18"", ""description"": ""* feat: add git.sh\n* feat: add test001\n* feat: test002\n* feat: add test003\n* feat: add test004\n* feat: add test005\n* feat: add test006\n* feat: add test007\n* feat: add test008\n* feat: add test009\n* feat: add test010\n* feat: add test011\n* feat: add test012\n* feat: add test013\n* feat: ad [...]
+148,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 20, ""title"": ""Test16"", ""description"": ""* feat: add git.sh\n* feat: add test001\n* feat: test002\n* feat: add test003\n* feat: add test004\n* feat: add test005\n* feat: add test006\n* feat: add test007\n* feat: add test008\n* feat: add test009\n* feat: add test010\n* feat: add test011\n* feat: add test012\n* feat: add test013\n* feat: ad [...]
+149,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 19, ""title"": ""Test15"", ""description"": ""* feat: add git.sh\n* feat: add test001\n* feat: test002\n* feat: add test003\n* feat: add test004\n* feat: add test005\n* feat: add test006\n* feat: add test007\n* feat: add test008\n* feat: add test009\n* feat: add test010\n* feat: add test011\n* feat: add test012\n* feat: add test013\n* feat: ad [...]
+150,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 18, ""title"": ""Test14"", ""description"": ""* feat: add git.sh\n* feat: add test001\n* feat: test002\n* feat: add test003\n* feat: add test004\n* feat: add test005\n* feat: add test006\n* feat: add test007\n* feat: add test008\n* feat: add test009\n* feat: add test010\n* feat: add test011\n* feat: add test012\n* feat: add test013\n* feat: ad [...]
+151,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 17, ""title"": ""Test13"", ""description"": ""* feat: add git.sh\n* feat: add test001\n* feat: test002\n* feat: add test003\n* feat: add test004\n* feat: add test005\n* feat: add test006\n* feat: add test007\n* feat: add test008\n* feat: add test009\n* feat: add test010\n* feat: add test011\n* feat: add test012\n* feat: add test013\n* feat: ad [...]
+152,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 28, ""title"": ""mergev1"", ""description"": ""merge all v1"", ""state"": ""MERGED"", ""merge_commit"": {""type"": ""commit"", ""hash"": ""c0e12b61e44b"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/repositories/panjf2000/ants/commit/c0e12b61e44b""}, ""html"": {""href"": ""https://bitbucket.org/panjf2000/ants/commits/c0e12 [...]
+153,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 27, ""title"": ""4"", ""description"": ""* feat: add git.sh\n* feat: add test001\n* feat: test002\n* feat: add test003\n* feat: add test004\n* feat: add test005\n* feat: add test006\n* feat: add test007\n* feat: add test008\n* feat: add test009\n* feat: add test010\n* feat: add test011\n* feat: add test012\n* feat: add test013\n* feat: add bra [...]
+154,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 26, ""title"": ""2"", ""description"": ""* feat: add git.sh\n* feat: add test001\n* feat: test002\n* feat: add test003\n* feat: add test004\n* feat: add test005\n* feat: add test006\n* feat: add test007\n* feat: add test008\n* feat: add test009\n* feat: add test010\n* feat: add test011\n* feat: add test012\n* feat: add test013\n* feat: add bra [...]
+155,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 25, ""title"": ""1"", ""description"": ""* feat: add git.sh\n* feat: add test001\n* feat: test002\n* feat: add test003\n* feat: add test004\n* feat: add test005\n* feat: add test006\n* feat: add test007\n* feat: add test008\n* feat: add test009\n* feat: add test010\n* feat: add test011\n* feat: add test012\n* feat: add test013\n* feat: add bra [...]
+156,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 15, ""title"": ""Test011"", ""description"": ""* feat: add git.sh\n* feat: add test001\n* feat: test002\n* feat: add test003\n* feat: add test004\n* feat: add test005\n* feat: add test006\n* feat: add test007\n* feat: add test008\n* feat: add test009\n* feat: add test010\n* feat: add test011\n\n‌"", ""state"": ""MERGED"", ""merge_commit"": {"" [...]
+157,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 14, ""title"": ""Test10"", ""description"": ""* feat: add git.sh\n* feat: add test001\n* feat: test002\n* feat: add test003\n* feat: add test004\n* feat: add test005\n* feat: add test006\n* feat: add test007\n* feat: add test008\n* feat: add test009\n* feat: add test010\n* feat: add test011\n* feat: add test012\n* feat: add test013\n* feat: ad [...]
+158,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 13, ""title"": ""Test8"", ""description"": ""* feat: add git.sh\n* feat: add test001\n* feat: test002\n* feat: add test003\n* feat: add test004\n* feat: add test005\n* feat: add test006\n* feat: add test007\n* feat: add test008\n* feat: add test009\n* feat: add test010\n* feat: add test011\n* feat: add test012\n* feat: add test013\n* feat: add [...]
+159,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 12, ""title"": ""Test7"", ""description"": ""* feat: add git.sh\n* feat: add test001\n* feat: test002\n* feat: add test003\n* feat: add test004\n* feat: add test005\n* feat: add test006\n* feat: add test007\n* feat: add test008\n* feat: add test009\n* feat: add test010\n* feat: add test011\n* feat: add test012\n* feat: add test013\n* feat: add [...]
+160,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 11, ""title"": ""Test6"", ""description"": ""* feat: add git.sh\n* feat: add test001\n* feat: test002\n* feat: add test003\n* feat: add test004\n* feat: add test005\n* feat: add test006\n* feat: add test007\n* feat: add test008\n* feat: add test009\n* feat: add test010\n* feat: add test011\n* feat: add test012\n* feat: add test013\n* feat: add [...]
+161,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 10, ""title"": ""Test007"", ""description"": ""* feat: add git.sh\n* feat: add test001\n* feat: test002\n* feat: add test003\n* feat: add test004\n* feat: add test005\n* feat: add test006\n* feat: add test007\n\n‌"", ""state"": ""MERGED"", ""merge_commit"": {""type"": ""commit"", ""hash"": ""983edbf06740"", ""links"": {""self"": {""href"": ""h [...]
+162,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 9, ""title"": ""Test006"", ""description"": ""* feat: add git.sh\n* feat: add test001\n* feat: test002\n* feat: add test003\n* feat: add test004\n* feat: add test005\n* feat: add test006\n\n‌"", ""state"": ""MERGED"", ""merge_commit"": {""type"": ""commit"", ""hash"": ""983edbf06740"", ""links"": {""self"": {""href"": ""https://api.bitbucket.o [...]
+163,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 8, ""title"": ""Test005"", ""description"": ""* feat: add git.sh\n* feat: add test001\n* feat: test002\n* feat: add test003\n* feat: add test004\n* feat: add test005\n\n‌"", ""state"": ""MERGED"", ""merge_commit"": {""type"": ""commit"", ""hash"": ""983edbf06740"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/repositories/p [...]
+164,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 7, ""title"": ""Test004"", ""description"": ""* feat: add git.sh\n* feat: add test001\n* feat: test002\n* feat: add test003\n* feat: add test004\n\n‌"", ""state"": ""MERGED"", ""merge_commit"": {""type"": ""commit"", ""hash"": ""983edbf06740"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/repositories/panjf2000/ants/commit/ [...]
+165,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 6, ""title"": ""Test003"", ""description"": ""* feat: add git.sh\n* feat: add test001\n* feat: test002\n* feat: add test003\n\n‌"", ""state"": ""MERGED"", ""merge_commit"": {""type"": ""commit"", ""hash"": ""983edbf06740"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/repositories/panjf2000/ants/commit/983edbf06740""}, ""ht [...]
+166,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 5, ""title"": ""Test002"", ""description"": ""* feat: add git.sh\n* feat: add test001\n* feat: test002\n\n‌"", ""state"": ""MERGED"", ""merge_commit"": {""type"": ""commit"", ""hash"": ""983edbf06740"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/repositories/panjf2000/ants/commit/983edbf06740""}, ""html"": {""href"": ""ht [...]
+167,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 4, ""title"": ""Test001"", ""description"": ""* feat: add git.sh\n* feat: add test001\n\n‌"", ""state"": ""MERGED"", ""merge_commit"": {""type"": ""commit"", ""hash"": ""983edbf06740"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/repositories/panjf2000/ants/commit/983edbf06740""}, ""html"": {""href"": ""https://bitbucket.o [...]
+168,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 3, ""title"": ""Test9"", ""description"": ""* feat: add git.sh\n* feat: add test001\n* feat: test002\n* feat: add test003\n* feat: add test004\n* feat: add test005\n* feat: add test006\n* feat: add test007\n* feat: add test008\n* feat: add test009\n* feat: add test010\n* feat: add test011\n* feat: add test012\n* feat: add test013\n* feat: add [...]
+169,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 2, ""title"": ""feat: add git.sh"", ""description"": ""help to git commit faster"", ""state"": ""MERGED"", ""merge_commit"": {""type"": ""commit"", ""hash"": ""983edbf06740"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/repositories/panjf2000/ants/commit/983edbf06740""}, ""html"": {""href"": ""https://bitbucket.org/panjf20 [...]
+170,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 0, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 16, ""title"": ""Test12"", ""description"": ""* feat: add git.sh\n* feat: add test001\n* feat: test002\n* feat: add test003\n* feat: add test004\n* feat: add test005\n* feat: add test006\n* feat: add test007\n* feat: add test008\n* feat: add test009\n* feat: add test010\n* feat: add test011\n* feat: add test012\n* feat: add test013\n* feat: ad [...]
+171,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""comment_count"": 2, ""task_count"": 0, ""type"": ""pullrequest"", ""id"": 1, ""title"": ""feat: add pr test"", ""description"": ""this is a pr test for Bitbucket plugin"", ""state"": ""OPEN"", ""merge_commit"": null, ""close_source_branch"": false, ""closed_by"": null, ""author"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0 [...]
\ No newline at end of file
diff --git a/plugins/bitbucket/e2e/raw_tables/_raw_bitbucket_api_pullrequest_comments.csv b/plugins/bitbucket/e2e/raw_tables/_raw_bitbucket_api_pullrequest_comments.csv
new file mode 100644
index 00000000..c88e22af
--- /dev/null
+++ b/plugins/bitbucket/e2e/raw_tables/_raw_bitbucket_api_pullrequest_comments.csv
@@ -0,0 +1,19 @@
+id,params,data,url,input,created_at
+1,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""id"": 323120278, ""created_on"": ""2022-08-12T15:40:01.755214+00:00"", ""updated_on"": ""2022-08-12T15:40:01.755276+00:00"", ""content"": {""type"": ""rendered"", ""raw"": ""baroboerhba"", ""markup"": ""markdown"", ""html"": ""<p>baroboerhba</p>""}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D""}, ""avatar"": [...]
+2,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""id"": 323120296, ""created_on"": ""2022-08-12T15:40:07.757075+00:00"", ""updated_on"": ""2022-08-12T15:40:07.757120+00:00"", ""content"": {""type"": ""rendered"", ""raw"": ""bweiwbeanb"", ""markup"": ""markdown"", ""html"": ""<p>bweiwbeanb</p>""}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D""}, ""avatar"": { [...]
+3,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""id"": 323120311, ""created_on"": ""2022-08-12T15:40:12.401207+00:00"", ""updated_on"": ""2022-08-12T15:40:12.401282+00:00"", ""content"": {""type"": ""rendered"", ""raw"": ""jbirobsore"", ""markup"": ""markdown"", ""html"": ""<p>jbirobsore</p>""}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D""}, ""avatar"": { [...]
+4,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""id"": 323119960, ""created_on"": ""2022-08-12T15:38:26.895025+00:00"", ""updated_on"": ""2022-08-12T15:38:26.895092+00:00"", ""content"": {""type"": ""rendered"", ""raw"": ""regrioteothro"", ""markup"": ""markdown"", ""html"": ""<p>regrioteothro</p>""}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D""}, ""avata [...]
+5,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""id"": 323119980, ""created_on"": ""2022-08-12T15:38:31.110923+00:00"", ""updated_on"": ""2022-08-12T15:38:31.110990+00:00"", ""content"": {""type"": ""rendered"", ""raw"": ""goeotbioebhotn"", ""markup"": ""markdown"", ""html"": ""<p>goeotbioebhotn</p>""}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D""}, ""ava [...]
+6,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""id"": 323120140, ""created_on"": ""2022-08-12T15:39:27.438543+00:00"", ""updated_on"": ""2022-08-12T15:39:27.438603+00:00"", ""content"": {""type"": ""rendered"", ""raw"": ""evbnoera"", ""markup"": ""markdown"", ""html"": ""<p>evbnoera</p>""}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D""}, ""avatar"": {""hr [...]
+7,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""id"": 323120196, ""created_on"": ""2022-08-12T15:39:41.750361+00:00"", ""updated_on"": ""2022-08-12T15:39:41.750414+00:00"", ""content"": {""type"": ""rendered"", ""raw"": ""vobreia"", ""markup"": ""markdown"", ""html"": ""<p>vobreia</p>""}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D""}, ""avatar"": {""href [...]
+8,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""id"": 323119842, ""created_on"": ""2022-08-12T15:37:51.356670+00:00"", ""updated_on"": ""2022-08-12T15:37:51.356716+00:00"", ""content"": {""type"": ""rendered"", ""raw"": ""feafoegoaweoir"", ""markup"": ""markdown"", ""html"": ""<p>feafoegoaweoir</p>""}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D""}, ""ava [...]
+9,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""id"": 323119856, ""created_on"": ""2022-08-12T15:37:56.276961+00:00"", ""updated_on"": ""2022-08-12T15:37:56.277012+00:00"", ""content"": {""type"": ""rendered"", ""raw"": ""boroehruheorefe"", ""markup"": ""markdown"", ""html"": ""<p>boroehruheorefe</p>""}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D""}, ""a [...]
+10,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""id"": 323120091, ""created_on"": ""2022-08-12T15:39:11.612223+00:00"", ""updated_on"": ""2022-08-12T15:39:11.612271+00:00"", ""content"": {""type"": ""rendered"", ""raw"": ""feawrjoiw"", ""markup"": ""markdown"", ""html"": ""<p>feawrjoiw</p>""}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D""}, ""avatar"": {" [...]
+11,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""id"": 323120101, ""created_on"": ""2022-08-12T15:39:15.632407+00:00"", ""updated_on"": ""2022-08-12T15:39:15.632461+00:00"", ""content"": {""type"": ""rendered"", ""raw"": ""bbeirobeoiahoa"", ""markup"": ""markdown"", ""html"": ""<p>bbeirobeoiahoa</p>""}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D""}, ""av [...]
+12,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""id"": 323119784, ""created_on"": ""2022-08-12T15:37:36.308815+00:00"", ""updated_on"": ""2022-08-12T15:37:36.308866+00:00"", ""content"": {""type"": ""rendered"", ""raw"": ""fejoafefewa"", ""markup"": ""markdown"", ""html"": ""<p>fejoafefewa</p>""}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D""}, ""avatar"" [...]
+13,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""id"": 323119790, ""created_on"": ""2022-08-12T15:37:40.769308+00:00"", ""updated_on"": ""2022-08-12T15:37:40.769367+00:00"", ""content"": {""type"": ""rendered"", ""raw"": ""feafeogioehrw"", ""markup"": ""markdown"", ""html"": ""<p>feafeogioehrw</p>""}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D""}, ""avat [...]
+14,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""id"": 323119643, ""created_on"": ""2022-08-12T15:36:49.030226+00:00"", ""updated_on"": ""2022-08-12T15:36:49.030284+00:00"", ""content"": {""type"": ""rendered"", ""raw"": ""this is test014"", ""markup"": ""markdown"", ""html"": ""<p>this is test014</p>""}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D""}, "" [...]
+15,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""id"": 323119662, ""created_on"": ""2022-08-12T15:36:53.393567+00:00"", ""updated_on"": ""2022-08-12T15:36:53.393609+00:00"", ""content"": {""type"": ""rendered"", ""raw"": ""feafjae"", ""markup"": ""markdown"", ""html"": ""<p>feafjae</p>""}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D""}, ""avatar"": {""hre [...]
+16,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""id"": 323119897, ""created_on"": ""2022-08-12T15:38:11.179587+00:00"", ""updated_on"": ""2022-08-12T15:38:11.179637+00:00"", ""content"": {""type"": ""rendered"", ""raw"": ""feajfweofjwvgrea"", ""markup"": ""markdown"", ""html"": ""<p>feajfweofjwvgrea</p>""}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D""}, [...]
+17,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""id"": 323119730, ""created_on"": ""2022-08-12T15:37:16.879523+00:00"", ""updated_on"": ""2022-08-12T15:37:16.879578+00:00"", ""content"": {""type"": ""rendered"", ""raw"": ""fejoiafjoej\n\n‌"", ""markup"": ""markdown"", ""html"": ""<p>fejoiafjoej</p>\n<p>‌</p>""}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D [...]
+18,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""id"": 323119743, ""created_on"": ""2022-08-12T15:37:21.558078+00:00"", ""updated_on"": ""2022-08-12T15:37:21.558120+00:00"", ""content"": {""type"": ""rendered"", ""raw"": ""fjeafojeoirjgoar"", ""markup"": ""markdown"", ""html"": ""<p>fjeafojeoirjgoar</p>""}, ""user"": {""display_name"": ""teoiaoe"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/users/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D""}, [...]
diff --git a/plugins/bitbucket/e2e/raw_tables/_raw_bitbucket_api_repositories.csv b/plugins/bitbucket/e2e/raw_tables/_raw_bitbucket_api_repositories.csv
new file mode 100644
index 00000000..9ff1bbbf
--- /dev/null
+++ b/plugins/bitbucket/e2e/raw_tables/_raw_bitbucket_api_repositories.csv
@@ -0,0 +1,3 @@
+id,params,data,url,input,created_at
+2,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""type"": ""repository"", ""full_name"": ""panjf2000/ants"", ""links"": {""self"": {""href"": ""https://api.bitbucket.org/2.0/repositories/panjf2000/ants""}, ""html"": {""href"": ""https://bitbucket.org/panjf2000/ants""}, ""avatar"": {""href"": ""https://bytebucket.org/ravatar/%7Be762bff4-f795-49c6-b9f9-6ea334b6d964%7D?ts=default""}, ""pullrequests"": {""href"": ""https://api.bitbucket.org/2.0/repositories/panjf2000/ant [...]
+
diff --git a/plugins/bitbucket/e2e/repo_test.go b/plugins/bitbucket/e2e/repo_test.go
new file mode 100644
index 00000000..2de4bc32
--- /dev/null
+++ b/plugins/bitbucket/e2e/repo_test.go
@@ -0,0 +1,138 @@
+/*
+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 e2e
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/apache/incubator-devlake/models/domainlayer/code"
+ "github.com/apache/incubator-devlake/models/domainlayer/ticket"
+ "github.com/apache/incubator-devlake/plugins/bitbucket/models"
+
+ "github.com/apache/incubator-devlake/helpers/e2ehelper"
+ "github.com/apache/incubator-devlake/plugins/bitbucket/impl"
+ "github.com/apache/incubator-devlake/plugins/bitbucket/tasks"
+)
+
+func TestRepoDataFlow(t *testing.T) {
+ var plugin impl.Bitbucket
+ dataflowTester := e2ehelper.NewDataFlowTester(t, "bitbucket", plugin)
+
+ bitbucketRepository := &models.BitbucketRepo{
+ BitbucketId: "panjf2000/ants",
+ }
+ taskData := &tasks.BitbucketTaskData{
+ Options: &tasks.BitbucketOptions{
+ ConnectionId: 1,
+ Owner: "panjf2000",
+ Repo: "ants",
+ TransformationRules: models.TransformationRules{
+ PrType: "type/(.*)$",
+ PrComponent: "component/(.*)$",
+ },
+ },
+ Repo: bitbucketRepository,
+ }
+
+ // import raw data table
+ dataflowTester.ImportCsvIntoRawTable("./raw_tables/_raw_bitbucket_api_repositories.csv", "_raw_bitbucket_api_repositories")
+
+ // verify extraction
+ dataflowTester.FlushTabler(&models.BitbucketRepo{})
+ dataflowTester.FlushTabler(&models.BitbucketAccount{})
+ dataflowTester.Subtask(tasks.ExtractApiRepoMeta, taskData)
+ dataflowTester.VerifyTable(
+ models.BitbucketRepo{},
+ fmt.Sprintf("./snapshot_tables/%s.csv", models.BitbucketRepo{}.TableName()),
+ []string{
+ "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",
+ },
+ )
+ dataflowTester.VerifyTable(
+ models.BitbucketAccount{},
+ fmt.Sprintf("./snapshot_tables/%s.csv", models.BitbucketAccount{}.TableName()),
+ []string{
+ "connection_id",
+ "user_name",
+ "account_id",
+ "account_status",
+ "display_name",
+ "avatar_url",
+ "html_url",
+ "uuid",
+ "has2_fa_enabled",
+ "_raw_data_params",
+ "_raw_data_table",
+ "_raw_data_id",
+ "_raw_data_remark",
+ },
+ )
+
+ // verify extraction
+ dataflowTester.FlushTabler(&code.Repo{})
+ dataflowTester.FlushTabler(&ticket.Board{})
+ dataflowTester.Subtask(tasks.ConvertRepoMeta, taskData)
+ dataflowTester.VerifyTable(
+ code.Repo{},
+ fmt.Sprintf("./snapshot_tables/%s.csv", code.Repo{}.TableName()),
+ []string{
+ "id",
+ "_raw_data_params",
+ "_raw_data_table",
+ "_raw_data_id",
+ "_raw_data_remark",
+ "name",
+ "url",
+ "description",
+ "owner_id",
+ "language",
+ "forked_from",
+ "created_date",
+ "updated_date",
+ "deleted",
+ },
+ )
+ dataflowTester.VerifyTable(
+ ticket.Board{},
+ fmt.Sprintf("./snapshot_tables/%s.csv", ticket.Board{}.TableName()),
+ []string{
+ "id",
+ "_raw_data_params",
+ "_raw_data_table",
+ "_raw_data_id",
+ "_raw_data_remark",
+ "name",
+ "description",
+ "url",
+ "created_date",
+ },
+ )
+}
diff --git a/plugins/bitbucket/e2e/snapshot_tables/_tool_bitbucket_accounts.csv b/plugins/bitbucket/e2e/snapshot_tables/_tool_bitbucket_accounts.csv
new file mode 100644
index 00000000..6665d105
--- /dev/null
+++ b/plugins/bitbucket/e2e/snapshot_tables/_tool_bitbucket_accounts.csv
@@ -0,0 +1,2 @@
+connection_id,account_id,user_name,account_status,display_name,avatar_url,html_url,uuid,has2_fa_enabled,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+1,62abf394192edb006fa0e8cf,,,teoiaoe,https://secure.gravatar.com/avatar/d37e5c64cd007c5c20654af064c3f410?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FT-0.png,https://bitbucket.org/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D/,,0,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,171,
diff --git a/plugins/bitbucket/e2e/snapshot_tables/_tool_bitbucket_accounts_in_comments.csv b/plugins/bitbucket/e2e/snapshot_tables/_tool_bitbucket_accounts_in_comments.csv
new file mode 100644
index 00000000..72cc7408
--- /dev/null
+++ b/plugins/bitbucket/e2e/snapshot_tables/_tool_bitbucket_accounts_in_comments.csv
@@ -0,0 +1,2 @@
+connection_id,account_id,user_name,account_status,display_name,avatar_url,html_url,uuid,has2_fa_enabled,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+1,62abf394192edb006fa0e8cf,,,teoiaoe,https://secure.gravatar.com/avatar/d37e5c64cd007c5c20654af064c3f410?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FT-0.png,https://bitbucket.org/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D/,,0,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_request_comments,18,
diff --git a/plugins/bitbucket/e2e/snapshot_tables/_tool_bitbucket_accounts_in_issue.csv b/plugins/bitbucket/e2e/snapshot_tables/_tool_bitbucket_accounts_in_issue.csv
new file mode 100644
index 00000000..78ff1746
--- /dev/null
+++ b/plugins/bitbucket/e2e/snapshot_tables/_tool_bitbucket_accounts_in_issue.csv
@@ -0,0 +1,2 @@
+connection_id,account_id,user_name,account_status,display_name,avatar_url,html_url,uuid,has2_fa_enabled,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+1,62abf394192edb006fa0e8cf,,,teoiaoe,https://secure.gravatar.com/avatar/d37e5c64cd007c5c20654af064c3f410?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FT-0.png,https://bitbucket.org/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D/,,0,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,60,
diff --git a/plugins/bitbucket/e2e/snapshot_tables/_tool_bitbucket_accounts_in_pr.csv b/plugins/bitbucket/e2e/snapshot_tables/_tool_bitbucket_accounts_in_pr.csv
new file mode 100644
index 00000000..6665d105
--- /dev/null
+++ b/plugins/bitbucket/e2e/snapshot_tables/_tool_bitbucket_accounts_in_pr.csv
@@ -0,0 +1,2 @@
+connection_id,account_id,user_name,account_status,display_name,avatar_url,html_url,uuid,has2_fa_enabled,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+1,62abf394192edb006fa0e8cf,,,teoiaoe,https://secure.gravatar.com/avatar/d37e5c64cd007c5c20654af064c3f410?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FT-0.png,https://bitbucket.org/%7B64135f6a-3978-4297-99b1-21827e0faf0b%7D/,,0,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,171,
diff --git a/plugins/bitbucket/e2e/snapshot_tables/_tool_bitbucket_issue_comments.csv b/plugins/bitbucket/e2e/snapshot_tables/_tool_bitbucket_issue_comments.csv
new file mode 100644
index 00000000..400fb799
--- /dev/null
+++ b/plugins/bitbucket/e2e/snapshot_tables/_tool_bitbucket_issue_comments.csv
@@ -0,0 +1,29 @@
+connection_id,bitbucket_id,issue_id,author_name,author_id,bitbucket_created_at,bitbucket_updated_at,type,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+1,63860152,1,teoiaoe,62abf394192edb006fa0e8cf,2022-07-17T07:16:30.353+00:00,,issue_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,1,
+1,63860153,1,teoiaoe,62abf394192edb006fa0e8cf,2022-07-17T07:17:01.992+00:00,,issue_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,2,
+1,63860241,1,teoiaoe,62abf394192edb006fa0e8cf,2022-07-17T09:11:42.760+00:00,,issue_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,3,
+1,63961973,6,teoiaoe,62abf394192edb006fa0e8cf,2022-08-12T13:51:25.882+00:00,,issue_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,4,
+1,63961995,28,teoiaoe,62abf394192edb006fa0e8cf,2022-08-12T13:55:58.125+00:00,,issue_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,5,
+1,63963442,27,teoiaoe,62abf394192edb006fa0e8cf,2022-08-13T06:17:23.165+00:00,,issue_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,6,
+1,63963443,27,teoiaoe,62abf394192edb006fa0e8cf,2022-08-13T06:17:28.684+00:00,,issue_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,7,
+1,63963444,27,teoiaoe,62abf394192edb006fa0e8cf,2022-08-13T06:17:36.686+00:00,,issue_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,8,
+1,63963445,27,teoiaoe,62abf394192edb006fa0e8cf,2022-08-13T06:17:42.087+00:00,,issue_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,9,
+1,63963446,27,teoiaoe,62abf394192edb006fa0e8cf,2022-08-13T06:17:47.634+00:00,,issue_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,10,
+1,63963447,27,teoiaoe,62abf394192edb006fa0e8cf,2022-08-13T06:17:55.851+00:00,,issue_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,11,
+1,63963449,27,teoiaoe,62abf394192edb006fa0e8cf,2022-08-13T06:18:01.928+00:00,,issue_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,12,
+1,63963450,27,teoiaoe,62abf394192edb006fa0e8cf,2022-08-13T06:18:11.147+00:00,,issue_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,13,
+1,63963451,27,teoiaoe,62abf394192edb006fa0e8cf,2022-08-13T06:18:19.377+00:00,,issue_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,14,
+1,63963452,27,teoiaoe,62abf394192edb006fa0e8cf,2022-08-13T06:18:26.505+00:00,,issue_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,15,
+1,63963453,27,teoiaoe,62abf394192edb006fa0e8cf,2022-08-13T06:18:33.109+00:00,,issue_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,16,
+1,63963454,27,teoiaoe,62abf394192edb006fa0e8cf,2022-08-13T06:18:39.543+00:00,,issue_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,17,
+1,63963455,27,teoiaoe,62abf394192edb006fa0e8cf,2022-08-13T06:18:46.158+00:00,,issue_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,18,
+1,63963456,27,teoiaoe,62abf394192edb006fa0e8cf,2022-08-13T06:18:52.858+00:00,,issue_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,19,
+1,63963457,27,teoiaoe,62abf394192edb006fa0e8cf,2022-08-13T06:18:58.979+00:00,,issue_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,20,
+1,63963458,27,teoiaoe,62abf394192edb006fa0e8cf,2022-08-13T06:19:05.667+00:00,,issue_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,21,
+1,63963459,27,teoiaoe,62abf394192edb006fa0e8cf,2022-08-13T06:19:15.957+00:00,,issue_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,22,
+1,63963460,27,teoiaoe,62abf394192edb006fa0e8cf,2022-08-13T06:19:23.294+00:00,,issue_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,23,
+1,63963461,27,teoiaoe,62abf394192edb006fa0e8cf,2022-08-13T06:19:31.457+00:00,,issue_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,24,
+1,63963462,27,teoiaoe,62abf394192edb006fa0e8cf,2022-08-13T06:19:38.434+00:00,,issue_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,25,
+1,63963463,27,teoiaoe,62abf394192edb006fa0e8cf,2022-08-13T06:19:45.161+00:00,,issue_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,26,
+1,63963464,27,teoiaoe,62abf394192edb006fa0e8cf,2022-08-13T06:19:54.366+00:00,,issue_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,27,
+1,63963465,27,teoiaoe,62abf394192edb006fa0e8cf,2022-08-13T06:20:01.329+00:00,,issue_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,28,
diff --git a/plugins/bitbucket/e2e/snapshot_tables/_tool_bitbucket_issue_labels.csv b/plugins/bitbucket/e2e/snapshot_tables/_tool_bitbucket_issue_labels.csv
new file mode 100644
index 00000000..fa39feb0
--- /dev/null
+++ b/plugins/bitbucket/e2e/snapshot_tables/_tool_bitbucket_issue_labels.csv
@@ -0,0 +1 @@
+connection_id,issue_id,label_name,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
diff --git a/plugins/bitbucket/e2e/snapshot_tables/_tool_bitbucket_issues.csv b/plugins/bitbucket/e2e/snapshot_tables/_tool_bitbucket_issues.csv
new file mode 100644
index 00000000..ce4a736f
--- /dev/null
+++ b/plugins/bitbucket/e2e/snapshot_tables/_tool_bitbucket_issues.csv
@@ -0,0 +1,55 @@
+connection_id,bitbucket_id,repo_id,number,state,title,body,priority,type,author_id,author_name,assignee_id,assignee_name,milestone_id,lead_time_minutes,url,closed_at,bitbucket_created_at,bitbucket_updated_at,severity,component,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+1,1,panjf2000/ants,1,new,issue test,bitbucket issues test for devants,major,issue,62abf394192edb006fa0e8cf,teoiaoe,62abf394192edb006fa0e8cf,teoiaoe,0,0,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/1,,2022-07-17T07:15:55.959+00:00,2022-07-17T09:11:42.656+00:00,,,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,60,
+1,2,panjf2000/ants,2,new,add bitbucket issue,feafejo,major,issue,62abf394192edb006fa0e8cf,teoiaoe,,,0,0,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/2,,2022-07-28T06:40:08.187+00:00,2022-07-28T06:40:08.187+00:00,,,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,59,
+1,3,panjf2000/ants,3,new,bitbucket test,"efaegjeoaijefioaegrjoeior,af enfaoiee vioea.,,.wew",major,issue,62abf394192edb006fa0e8cf,teoiaoe,,,0,0,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/3,,2022-07-28T06:41:23.334+00:00,2022-07-28T06:41:23.334+00:00,,,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,58,
+1,4,panjf2000/ants,4,new,issue test001,Bitbucket issue test001,major,issue,62abf394192edb006fa0e8cf,teoiaoe,,,0,0,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/4,,2022-08-12T13:39:52.109+00:00,2022-08-12T13:39:52.109+00:00,,,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,57,
+1,5,panjf2000/ants,5,new,issue test002,issue test002,major,issue,62abf394192edb006fa0e8cf,teoiaoe,,,0,0,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/5,,2022-08-12T13:40:32.843+00:00,2022-08-12T13:40:32.843+00:00,,,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,56,
+1,6,panjf2000/ants,6,new,issue test003,issue test 003,major,issue,62abf394192edb006fa0e8cf,teoiaoe,62abf394192edb006fa0e8cf,teoiaoe,0,0,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/6,,2022-08-12T13:40:55.635+00:00,2022-08-12T13:51:25.769+00:00,,,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,37,
+1,7,panjf2000/ants,7,new,issue test004,issue test004,major,issue,62abf394192edb006fa0e8cf,teoiaoe,,,0,0,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/7,,2022-08-12T13:41:21.859+00:00,2022-08-12T13:41:21.859+00:00,,,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,55,
+1,8,panjf2000/ants,8,new,issue test005,issue test005,critical,issue,62abf394192edb006fa0e8cf,teoiaoe,,,0,0,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/8,,2022-08-12T13:41:58.511+00:00,2022-08-12T13:41:58.511+00:00,,,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,54,
+1,9,panjf2000/ants,9,new,issue test006,issue test006,blocker,issue,62abf394192edb006fa0e8cf,teoiaoe,62abf394192edb006fa0e8cf,teoiaoe,0,0,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/9,,2022-08-12T13:42:29.072+00:00,2022-08-12T13:42:29.072+00:00,,,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,53,
+1,10,panjf2000/ants,10,new,issue test007,issue test007,trivial,issue,62abf394192edb006fa0e8cf,teoiaoe,62abf394192edb006fa0e8cf,teoiaoe,0,0,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/10,,2022-08-12T13:43:00.783+00:00,2022-08-12T13:43:00.783+00:00,,,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,52,
+1,11,panjf2000/ants,11,new,issue test008,issue test008,major,issue,62abf394192edb006fa0e8cf,teoiaoe,62abf394192edb006fa0e8cf,teoiaoe,0,0,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/11,,2022-08-12T13:43:26.414+00:00,2022-08-12T13:43:26.414+00:00,,,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,51,
+1,12,panjf2000/ants,12,new,issue test009,issue test009,minor,issue,62abf394192edb006fa0e8cf,teoiaoe,,,0,0,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/12,,2022-08-12T13:44:04.502+00:00,2022-08-12T13:44:04.502+00:00,,,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,50,
+1,13,panjf2000/ants,13,new,issue test010,issue test010,critical,issue,62abf394192edb006fa0e8cf,teoiaoe,,,0,0,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/13,,2022-08-12T13:44:46.508+00:00,2022-08-12T13:44:46.508+00:00,,,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,49,
+1,14,panjf2000/ants,14,new,issue test011,issue test011,blocker,issue,62abf394192edb006fa0e8cf,teoiaoe,62abf394192edb006fa0e8cf,teoiaoe,0,0,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/14,,2022-08-12T13:45:12.810+00:00,2022-08-12T13:45:12.810+00:00,,,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,48,
+1,15,panjf2000/ants,15,new,issue test012,issue test012,minor,issue,62abf394192edb006fa0e8cf,teoiaoe,,,0,0,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/15,,2022-08-12T13:45:41.640+00:00,2022-08-12T13:45:41.640+00:00,,,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,47,
+1,16,panjf2000/ants,16,new,issue test013,issue test013,trivial,issue,62abf394192edb006fa0e8cf,teoiaoe,,,0,0,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/16,,2022-08-12T13:46:06.169+00:00,2022-08-12T13:46:06.169+00:00,,,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,46,
+1,17,panjf2000/ants,17,new,issue test014,issue test014,trivial,issue,62abf394192edb006fa0e8cf,teoiaoe,,,0,0,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/17,,2022-08-12T13:46:39.426+00:00,2022-08-12T13:46:39.426+00:00,,,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,45,
+1,18,panjf2000/ants,18,new,issue test015,issue test015,minor,issue,62abf394192edb006fa0e8cf,teoiaoe,,,0,0,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/18,,2022-08-12T13:47:01.892+00:00,2022-08-12T13:47:01.892+00:00,,,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,44,
+1,19,panjf2000/ants,19,new,issue test016,issue test016,critical,issue,62abf394192edb006fa0e8cf,teoiaoe,62abf394192edb006fa0e8cf,teoiaoe,0,0,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/19,,2022-08-12T13:47:26.928+00:00,2022-08-12T13:47:26.928+00:00,,,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,43,
+1,20,panjf2000/ants,20,new,issue test017,issue test017,blocker,issue,62abf394192edb006fa0e8cf,teoiaoe,62abf394192edb006fa0e8cf,teoiaoe,0,0,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/20,,2022-08-12T13:47:54.126+00:00,2022-08-12T13:47:54.126+00:00,,,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,42,
+1,21,panjf2000/ants,21,new,issue test018,issue test018,trivial,issue,62abf394192edb006fa0e8cf,teoiaoe,,,0,0,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/21,,2022-08-12T13:48:50.020+00:00,2022-08-12T13:48:50.020+00:00,,,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,41,
+1,22,panjf2000/ants,22,new,issue test019,issue test019,minor,issue,62abf394192edb006fa0e8cf,teoiaoe,,,0,0,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/22,,2022-08-12T13:49:23.252+00:00,2022-08-12T13:49:23.252+00:00,,,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,40,
+1,23,panjf2000/ants,23,new,issue test020,issue test020,critical,issue,62abf394192edb006fa0e8cf,teoiaoe,62abf394192edb006fa0e8cf,teoiaoe,0,0,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/23,,2022-08-12T13:49:51.630+00:00,2022-08-12T13:49:51.630+00:00,,,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,39,
+1,24,panjf2000/ants,24,new,issue test021,"issue test021 ijeiawgoeive/faveevaeviaevfejaofejfioejaiofe_veavejiovajgiorejoifjrogiorejieafajejaojoejvgioriovioraivjairobnrnoivaiorjbiorjiojaeiorjvioejroivjaoijeriojiaojioeefjafioejfiojeiofawefwefoiwefiwoiefweefwoefuwhufirfrw._
+
+```
+``eveaeaeae`jiovjeiojoa oi2j3oij23jovnw3
+```
+
+‌",trivial,issue,62abf394192edb006fa0e8cf,teoiaoe,62abf394192edb006fa0e8cf,teoiaoe,0,0,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/24,,2022-08-12T13:50:57.290+00:00,2022-08-12T13:50:57.290+00:00,,,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,38,
+1,25,panjf2000/ants,25,new,issue 023,"issue test023 ejoafehoafe
+
+q
+
+```c++
+feafefoa
+feafoiehfe
+rvvgeaioehr
+feaofe
+```
+
+| **fefae** | | |
+| --- | --- | --- |
+| | | |
+| | | |
+
+‌",critical,issue,62abf394192edb006fa0e8cf,teoiaoe,,,0,0,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/25,,2022-08-12T13:53:49.232+00:00,2022-08-12T13:53:49.232+00:00,,,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,36,
+1,26,panjf2000/ants,26,new,issue test022,issue test022,blocker,issue,62abf394192edb006fa0e8cf,teoiaoe,,,0,0,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/26,,2022-08-12T13:54:28.911+00:00,2022-08-12T13:54:28.911+00:00,,,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,35,
+1,27,panjf2000/ants,27,new,issue test024,"issue test024v aejnoafoeiogoiae
+
+qwofjeoiwjf",trivial,issue,62abf394192edb006fa0e8cf,teoiaoe,,,0,0,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/27,,2022-08-12T13:55:09.734+00:00,2022-08-13T06:20:01.410+00:00,,,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,31,
+1,28,panjf2000/ants,28,new,issue test025,issue test025,minor,issue,62abf394192edb006fa0e8cf,teoiaoe,,,0,0,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/28,,2022-08-12T13:55:32.410+00:00,2022-08-12T13:55:58.035+00:00,,,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,34,
+1,29,panjf2000/ants,29,new,issue test026,issue test026,critical,issue,62abf394192edb006fa0e8cf,teoiaoe,62abf394192edb006fa0e8cf,teoiaoe,0,0,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/29,,2022-08-12T13:56:26.434+00:00,2022-08-12T13:56:26.434+00:00,,,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,33,
+1,30,panjf2000/ants,30,new,issue test027,issue test027,critical,issue,62abf394192edb006fa0e8cf,teoiaoe,62abf394192edb006fa0e8cf,teoiaoe,0,0,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/30,,2022-08-12T13:56:51.480+00:00,2022-08-12T13:56:51.480+00:00,,,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,32,
diff --git a/plugins/bitbucket/e2e/snapshot_tables/_tool_bitbucket_pull_request_comments.csv b/plugins/bitbucket/e2e/snapshot_tables/_tool_bitbucket_pull_request_comments.csv
new file mode 100644
index 00000000..a4e5de43
--- /dev/null
+++ b/plugins/bitbucket/e2e/snapshot_tables/_tool_bitbucket_pull_request_comments.csv
@@ -0,0 +1,19 @@
+connection_id,bitbucket_id,pull_request_id,author_name,author_id,bitbucket_created_at,bitbucket_updated_at,type,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+1,323119643,54,,62abf394192edb006fa0e8cf,2022-08-12T15:36:49.030+00:00,2022-08-12T15:36:49.030+00:00,pullrequest_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_request_comments,14,
+1,323119662,54,,62abf394192edb006fa0e8cf,2022-08-12T15:36:53.394+00:00,2022-08-12T15:36:53.394+00:00,pullrequest_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_request_comments,15,
+1,323119730,57,,62abf394192edb006fa0e8cf,2022-08-12T15:37:16.880+00:00,2022-08-12T15:37:16.880+00:00,pullrequest_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_request_comments,17,
+1,323119743,57,,62abf394192edb006fa0e8cf,2022-08-12T15:37:21.558+00:00,2022-08-12T15:37:21.558+00:00,pullrequest_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_request_comments,18,
+1,323119784,51,,62abf394192edb006fa0e8cf,2022-08-12T15:37:36.309+00:00,2022-08-12T15:37:36.309+00:00,pullrequest_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_request_comments,12,
+1,323119790,51,,62abf394192edb006fa0e8cf,2022-08-12T15:37:40.769+00:00,2022-08-12T15:37:40.769+00:00,pullrequest_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_request_comments,13,
+1,323119842,42,,62abf394192edb006fa0e8cf,2022-08-12T15:37:51.357+00:00,2022-08-12T15:37:51.357+00:00,pullrequest_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_request_comments,8,
+1,323119856,42,,62abf394192edb006fa0e8cf,2022-08-12T15:37:56.277+00:00,2022-08-12T15:37:56.277+00:00,pullrequest_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_request_comments,9,
+1,323119897,54,,62abf394192edb006fa0e8cf,2022-08-12T15:38:11.180+00:00,2022-08-12T15:38:11.180+00:00,pullrequest_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_request_comments,16,
+1,323119960,38,,62abf394192edb006fa0e8cf,2022-08-12T15:38:26.895+00:00,2022-08-12T15:38:26.895+00:00,pullrequest_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_request_comments,4,
+1,323119980,38,,62abf394192edb006fa0e8cf,2022-08-12T15:38:31.111+00:00,2022-08-12T15:38:31.111+00:00,pullrequest_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_request_comments,5,
+1,323120091,44,,62abf394192edb006fa0e8cf,2022-08-12T15:39:11.612+00:00,2022-08-12T15:39:11.612+00:00,pullrequest_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_request_comments,10,
+1,323120101,44,,62abf394192edb006fa0e8cf,2022-08-12T15:39:15.632+00:00,2022-08-12T15:39:15.632+00:00,pullrequest_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_request_comments,11,
+1,323120140,40,,62abf394192edb006fa0e8cf,2022-08-12T15:39:27.439+00:00,2022-08-12T15:39:27.439+00:00,pullrequest_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_request_comments,6,
+1,323120196,41,,62abf394192edb006fa0e8cf,2022-08-12T15:39:41.750+00:00,2022-08-12T15:39:41.750+00:00,pullrequest_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_request_comments,7,
+1,323120278,31,,62abf394192edb006fa0e8cf,2022-08-12T15:40:01.755+00:00,2022-08-12T15:40:01.755+00:00,pullrequest_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_request_comments,1,
+1,323120296,31,,62abf394192edb006fa0e8cf,2022-08-12T15:40:07.757+00:00,2022-08-12T15:40:07.757+00:00,pullrequest_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_request_comments,2,
+1,323120311,31,,62abf394192edb006fa0e8cf,2022-08-12T15:40:12.401+00:00,2022-08-12T15:40:12.401+00:00,pullrequest_comment,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_request_comments,3,
diff --git a/plugins/bitbucket/e2e/snapshot_tables/_tool_bitbucket_pull_requests.csv b/plugins/bitbucket/e2e/snapshot_tables/_tool_bitbucket_pull_requests.csv
new file mode 100644
index 00000000..65160009
--- /dev/null
+++ b/plugins/bitbucket/e2e/snapshot_tables/_tool_bitbucket_pull_requests.csv
@@ -0,0 +1,58 @@
+connection_id,bitbucket_id,repo_id,number,state,title,bitbucket_created_at,bitbucket_updated_at,closed_at,comment_count,commits,merged_at,body,type,component,merge_commit_sha,head_ref,base_ref,base_commit_sha,head_commit_sha,url,author_name,author_id,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+1,1,panjf2000/ants,0,OPEN,feat: add pr test,2022-07-12T04:17:03.055+00:00,2022-07-18T05:39:20.730+00:00,,2,0,,,pullrequest,,,test,master,c0e12b61e44b,5e5bccfcc656,https://bitbucket.org/panjf2000/ants/pull-requests/1,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,171,
+1,2,panjf2000/ants,0,MERGED,feat: add git.sh,2022-08-12T13:18:39.132+00:00,2022-08-12T15:03:37.272+00:00,,0,0,,,pullrequest,,983edbf06740,vowehwo,master,0bd83caaf9c7,d4a6789a3751,https://bitbucket.org/panjf2000/ants/pull-requests/2,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,169,
+1,3,panjf2000/ants,0,MERGED,Test9,2022-08-12T14:53:19.796+00:00,2022-08-12T15:03:37.475+00:00,,0,0,,,pullrequest,,983edbf06740,test9,master,0bd83caaf9c7,6557208f4aa6,https://bitbucket.org/panjf2000/ants/pull-requests/3,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,168,
+1,4,panjf2000/ants,0,MERGED,Test001,2022-08-12T14:53:43.845+00:00,2022-08-12T15:03:37.622+00:00,,0,0,,,pullrequest,,983edbf06740,test001,master,0bd83caaf9c7,3bd3b34b2397,https://bitbucket.org/panjf2000/ants/pull-requests/4,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,167,
+1,5,panjf2000/ants,0,MERGED,Test002,2022-08-12T14:53:59.654+00:00,2022-08-12T15:03:37.766+00:00,,0,0,,,pullrequest,,983edbf06740,test002,master,0bd83caaf9c7,904df6924f1e,https://bitbucket.org/panjf2000/ants/pull-requests/5,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,166,
+1,6,panjf2000/ants,0,MERGED,Test003,2022-08-12T14:54:20.308+00:00,2022-08-12T15:03:37.916+00:00,,0,0,,,pullrequest,,983edbf06740,test003,master,0bd83caaf9c7,494b97e9d0b2,https://bitbucket.org/panjf2000/ants/pull-requests/6,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,165,
+1,7,panjf2000/ants,0,MERGED,Test004,2022-08-12T14:54:40.056+00:00,2022-08-12T15:03:38.066+00:00,,0,0,,,pullrequest,,983edbf06740,test004,master,0bd83caaf9c7,db921ce6537c,https://bitbucket.org/panjf2000/ants/pull-requests/7,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,164,
+1,8,panjf2000/ants,0,MERGED,Test005,2022-08-12T14:54:56.506+00:00,2022-08-12T15:03:38.297+00:00,,0,0,,,pullrequest,,983edbf06740,test005,master,0bd83caaf9c7,8a224cc0eaae,https://bitbucket.org/panjf2000/ants/pull-requests/8,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,163,
+1,9,panjf2000/ants,0,MERGED,Test006,2022-08-12T14:55:13.971+00:00,2022-08-12T15:03:38.479+00:00,,0,0,,,pullrequest,,983edbf06740,test006,master,0bd83caaf9c7,8a8f44b997a8,https://bitbucket.org/panjf2000/ants/pull-requests/9,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,162,
+1,10,panjf2000/ants,0,MERGED,Test007,2022-08-12T14:55:28.082+00:00,2022-08-12T15:03:38.628+00:00,,0,0,,,pullrequest,,983edbf06740,test007,master,0bd83caaf9c7,15a1fcb51f0e,https://bitbucket.org/panjf2000/ants/pull-requests/10,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,161,
+1,11,panjf2000/ants,0,MERGED,Test6,2022-08-12T14:55:48.770+00:00,2022-08-12T15:03:38.774+00:00,,0,0,,,pullrequest,,983edbf06740,test6,master,0bd83caaf9c7,bc9a3ddd47ea,https://bitbucket.org/panjf2000/ants/pull-requests/11,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,160,
+1,12,panjf2000/ants,0,MERGED,Test7,2022-08-12T14:56:07.092+00:00,2022-08-12T15:03:38.926+00:00,,0,0,,,pullrequest,,983edbf06740,test7,master,0bd83caaf9c7,dd2cd76d7098,https://bitbucket.org/panjf2000/ants/pull-requests/12,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,159,
+1,13,panjf2000/ants,0,MERGED,Test8,2022-08-12T14:56:30.570+00:00,2022-08-12T15:03:39.074+00:00,,0,0,,,pullrequest,,983edbf06740,test8,master,0bd83caaf9c7,94056215cf46,https://bitbucket.org/panjf2000/ants/pull-requests/13,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,158,
+1,14,panjf2000/ants,0,MERGED,Test10,2022-08-12T14:56:52.550+00:00,2022-08-12T15:03:39.228+00:00,,0,0,,,pullrequest,,983edbf06740,test10,master,0bd83caaf9c7,bc2cf25142e3,https://bitbucket.org/panjf2000/ants/pull-requests/14,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,157,
+1,15,panjf2000/ants,0,MERGED,Test011,2022-08-12T14:57:15.254+00:00,2022-08-12T15:03:39.381+00:00,,0,0,,,pullrequest,,983edbf06740,test011,master,0bd83caaf9c7,5db6b1a9e60c,https://bitbucket.org/panjf2000/ants/pull-requests/15,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,156,
+1,16,panjf2000/ants,0,MERGED,Test12,2022-08-12T14:57:41.230+00:00,2022-08-12T15:03:36.526+00:00,,0,0,,,pullrequest,,983edbf06740,test12,master,0bd83caaf9c7,d1321cb5e4fb,https://bitbucket.org/panjf2000/ants/pull-requests/16,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,170,
+1,17,panjf2000/ants,0,MERGED,Test13,2022-08-12T14:57:59.501+00:00,2022-08-12T15:10:43.032+00:00,,0,0,,,pullrequest,,c0e12b61e44b,test13,master,983edbf06740,3d6d96659c4d,https://bitbucket.org/panjf2000/ants/pull-requests/17,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,151,
+1,18,panjf2000/ants,0,MERGED,Test14,2022-08-12T14:58:16.105+00:00,2022-08-12T15:10:43.206+00:00,,0,0,,,pullrequest,,c0e12b61e44b,test14,master,983edbf06740,8d4896eef3ed,https://bitbucket.org/panjf2000/ants/pull-requests/18,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,150,
+1,19,panjf2000/ants,0,MERGED,Test15,2022-08-12T14:58:33.361+00:00,2022-08-12T15:10:43.322+00:00,,0,0,,,pullrequest,,c0e12b61e44b,test15,master,983edbf06740,daa1cfa7ec0c,https://bitbucket.org/panjf2000/ants/pull-requests/19,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,149,
+1,20,panjf2000/ants,0,MERGED,Test16,2022-08-12T14:59:07.791+00:00,2022-08-12T15:10:43.437+00:00,,0,0,,,pullrequest,,c0e12b61e44b,test16,master,983edbf06740,11f6c86580e2,https://bitbucket.org/panjf2000/ants/pull-requests/20,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,148,
+1,21,panjf2000/ants,0,MERGED,Test18,2022-08-12T14:59:22.874+00:00,2022-08-12T15:10:43.562+00:00,,0,0,,,pullrequest,,c0e12b61e44b,test18,master,983edbf06740,0b958bbfed04,https://bitbucket.org/panjf2000/ants/pull-requests/21,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,147,
+1,22,panjf2000/ants,0,MERGED,Test19,2022-08-12T14:59:39.610+00:00,2022-08-12T15:10:43.684+00:00,,0,0,,,pullrequest,,c0e12b61e44b,test19,master,983edbf06740,d1bb3677810e,https://bitbucket.org/panjf2000/ants/pull-requests/22,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,146,
+1,23,panjf2000/ants,0,MERGED,Test20,2022-08-12T14:59:57.869+00:00,2022-08-12T15:10:43.807+00:00,,0,0,,,pullrequest,,c0e12b61e44b,test20,master,983edbf06740,9d16a2368acd,https://bitbucket.org/panjf2000/ants/pull-requests/23,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,145,
+1,24,panjf2000/ants,0,MERGED,Test17,2022-08-12T15:00:34.399+00:00,2022-08-12T15:10:43.931+00:00,,0,0,,,pullrequest,,c0e12b61e44b,test17,master,983edbf06740,2bfd29a17665,https://bitbucket.org/panjf2000/ants/pull-requests/24,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,144,
+1,25,panjf2000/ants,0,MERGED,1,2022-08-12T15:00:59.503+00:00,2022-08-12T15:03:40.499+00:00,,0,0,,,pullrequest,,983edbf06740,1,master,0bd83caaf9c7,592612c47ff5,https://bitbucket.org/panjf2000/ants/pull-requests/25,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,155,
+1,26,panjf2000/ants,0,MERGED,2,2022-08-12T15:01:18.980+00:00,2022-08-12T15:03:40.646+00:00,,0,0,,,pullrequest,,983edbf06740,2,master,0bd83caaf9c7,05b97cf92b4b,https://bitbucket.org/panjf2000/ants/pull-requests/26,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,154,
+1,27,panjf2000/ants,0,MERGED,4,2022-08-12T15:02:16.840+00:00,2022-08-12T15:03:40.792+00:00,,0,0,,,pullrequest,,983edbf06740,4,master,0bd83caaf9c7,761fdb9244ef,https://bitbucket.org/panjf2000/ants/pull-requests/27,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,153,
+1,28,panjf2000/ants,0,MERGED,mergev1,2022-08-12T15:10:16.367+00:00,2022-08-12T15:10:42.415+00:00,,0,0,,,pullrequest,,c0e12b61e44b,mergev1,master,983edbf06740,581a89006076,https://bitbucket.org/panjf2000/ants/pull-requests/28,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,152,
+1,29,panjf2000/ants,0,OPEN,Test03,2022-08-12T15:21:47.437+00:00,2022-08-12T15:29:13.392+00:00,,0,0,,,pullrequest,,,test03,master,c0e12b61e44b,a3768d182d18,https://bitbucket.org/panjf2000/ants/pull-requests/29,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,132,
+1,30,panjf2000/ants,0,OPEN,Test05,2022-08-12T15:22:17.954+00:00,2022-08-12T15:22:18.488+00:00,,0,0,,,pullrequest,,,test05,master,c0e12b61e44b,91e7a1db9ae0,https://bitbucket.org/panjf2000/ants/pull-requests/30,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,143,
+1,31,panjf2000/ants,0,OPEN,Test020,2022-08-12T15:22:51.303+00:00,2022-08-12T15:40:12.401+00:00,,3,0,,,pullrequest,,,test020,master,c0e12b61e44b,0342bf5351f8,https://bitbucket.org/panjf2000/ants/pull-requests/31,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,118,
+1,32,panjf2000/ants,0,OPEN,Test022,2022-08-12T15:23:06.858+00:00,2022-08-12T15:23:07.539+00:00,,0,0,,,pullrequest,,,test022,master,c0e12b61e44b,d72c46a722d7,https://bitbucket.org/panjf2000/ants/pull-requests/32,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,142,
+1,33,panjf2000/ants,0,OPEN,Test023,2022-08-12T15:23:27.568+00:00,2022-08-12T15:23:28.341+00:00,,0,0,,,pullrequest,,,test023,master,c0e12b61e44b,0a8011c8c0ac,https://bitbucket.org/panjf2000/ants/pull-requests/33,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,141,
+1,34,panjf2000/ants,0,OPEN,Test024,2022-08-12T15:23:42.883+00:00,2022-08-12T15:23:43.529+00:00,,0,0,,,pullrequest,,,test024,master,c0e12b61e44b,bd7e81a581f1,https://bitbucket.org/panjf2000/ants/pull-requests/34,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,140,
+1,35,panjf2000/ants,0,OPEN,Test025,2022-08-12T15:23:58.227+00:00,2022-08-12T15:23:59.023+00:00,,0,0,,,pullrequest,,,test025,master,c0e12b61e44b,a15cb5876414,https://bitbucket.org/panjf2000/ants/pull-requests/35,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,139,
+1,36,panjf2000/ants,0,OPEN,Test027,2022-08-12T15:24:16.607+00:00,2022-08-12T15:24:17.311+00:00,,0,0,,,pullrequest,,,test027,master,c0e12b61e44b,200665f463ff,https://bitbucket.org/panjf2000/ants/pull-requests/36,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,138,
+1,37,panjf2000/ants,0,OPEN,Test028,2022-08-12T15:24:44.626+00:00,2022-08-12T15:24:45.191+00:00,,0,0,,,pullrequest,,,test028,master,c0e12b61e44b,8a9052ac97f4,https://bitbucket.org/panjf2000/ants/pull-requests/37,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,137,
+1,38,panjf2000/ants,0,OPEN,Test029,2022-08-12T15:24:59.520+00:00,2022-08-12T15:38:31.111+00:00,,2,0,,,pullrequest,,,test029,master,c0e12b61e44b,74bddc0541c6,https://bitbucket.org/panjf2000/ants/pull-requests/38,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,121,
+1,39,panjf2000/ants,0,OPEN,Test030,2022-08-12T15:25:14.361+00:00,2022-08-12T15:25:15.090+00:00,,0,0,,,pullrequest,,,test030,master,c0e12b61e44b,f332a9855de1,https://bitbucket.org/panjf2000/ants/pull-requests/39,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,136,
+1,40,panjf2000/ants,0,OPEN,Test016,2022-08-12T15:25:31.596+00:00,2022-08-14T07:38:28.303+00:00,,2,0,,,pullrequest,,,test016,master,c0e12b61e44b,a000a2a9fc11,https://bitbucket.org/panjf2000/ants/pull-requests/40,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,115,
+1,41,panjf2000/ants,0,OPEN,Test018,2022-08-12T15:25:57.607+00:00,2022-08-12T15:39:41.750+00:00,,1,0,,,pullrequest,,,test018,master,c0e12b61e44b,561e4e3ca4cc,https://bitbucket.org/panjf2000/ants/pull-requests/41,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,119,
+1,42,panjf2000/ants,0,OPEN,Test019,2022-08-12T15:26:15.843+00:00,2022-08-14T07:37:37.798+00:00,,4,0,,,pullrequest,,,test019,master,c0e12b61e44b,8597a99eebb3,https://bitbucket.org/panjf2000/ants/pull-requests/42,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,117,
+1,43,panjf2000/ants,0,OPEN,Test015,2022-08-12T15:26:50.905+00:00,2022-08-12T15:27:32.304+00:00,,0,0,,,pullrequest,,,test015,master,c0e12b61e44b,5af2c361263a,https://bitbucket.org/panjf2000/ants/pull-requests/43,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,135,
+1,44,panjf2000/ants,0,OPEN,Test017,2022-08-12T15:27:54.969+00:00,2022-08-12T15:39:15.632+00:00,,2,0,,,pullrequest,,,test017,master,c0e12b61e44b,ee87b0380472,https://bitbucket.org/panjf2000/ants/pull-requests/44,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,120,
+1,45,panjf2000/ants,0,OPEN,feat: add test01,2022-08-12T15:28:43.059+00:00,2022-08-12T15:28:43.627+00:00,,0,0,,,pullrequest,,,test01,master,c0e12b61e44b,63e6acbb60a9,https://bitbucket.org/panjf2000/ants/pull-requests/45,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,134,
+1,46,panjf2000/ants,0,OPEN,Test04,2022-08-12T15:28:57.787+00:00,2022-08-12T15:28:58.392+00:00,,0,0,,,pullrequest,,,test04,master,c0e12b61e44b,33419e166d75,https://bitbucket.org/panjf2000/ants/pull-requests/46,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,133,
+1,47,panjf2000/ants,0,OPEN,Test06,2022-08-12T15:29:31.360+00:00,2022-08-12T15:29:31.922+00:00,,0,0,,,pullrequest,,,test06,master,c0e12b61e44b,f8f9ef467e3a,https://bitbucket.org/panjf2000/ants/pull-requests/47,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,131,
+1,48,panjf2000/ants,0,OPEN,Test07,2022-08-12T15:29:49.045+00:00,2022-08-12T15:29:49.646+00:00,,0,0,,,pullrequest,,,test07,master,c0e12b61e44b,c28f34dde568,https://bitbucket.org/panjf2000/ants/pull-requests/48,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,130,
+1,49,panjf2000/ants,0,OPEN,Test08,2022-08-12T15:30:04.329+00:00,2022-08-12T15:30:04.827+00:00,,0,0,,,pullrequest,,,test08,master,c0e12b61e44b,6b59c8607581,https://bitbucket.org/panjf2000/ants/pull-requests/49,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,129,
+1,50,panjf2000/ants,0,OPEN,Bitbuckettest12,2022-08-12T15:32:46.725+00:00,2022-08-12T15:32:47.488+00:00,,0,0,,,pullrequest,,,bitbuckettest12,master,c0e12b61e44b,deff6927812f,https://bitbucket.org/panjf2000/ants/pull-requests/50,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,128,
+1,51,panjf2000/ants,0,OPEN,Bitbuckettest15,2022-08-12T15:32:59.604+00:00,2022-08-12T15:37:40.769+00:00,,2,0,,,pullrequest,,,bitbuckettest15,master,c0e12b61e44b,5c2c9af70da2,https://bitbucket.org/panjf2000/ants/pull-requests/51,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,122,
+1,52,panjf2000/ants,0,OPEN,Bitbuckettest16,2022-08-12T15:33:13.806+00:00,2022-08-12T15:33:14.346+00:00,,0,0,,,pullrequest,,,bitbuckettest16,master,c0e12b61e44b,7c91d40ffaa3,https://bitbucket.org/panjf2000/ants/pull-requests/52,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,127,
+1,53,panjf2000/ants,0,OPEN,Bitbuckettest17,2022-08-12T15:33:29.075+00:00,2022-08-12T15:34:20.065+00:00,,0,0,,,pullrequest,,,bitbuckettest17,master,c0e12b61e44b,765ab2594451,https://bitbucket.org/panjf2000/ants/pull-requests/53,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,125,
+1,54,panjf2000/ants,0,OPEN,Bitbuckettest19,2022-08-12T15:33:43.689+00:00,2022-08-14T07:38:08.195+00:00,,4,0,,,pullrequest,,,bitbuckettest19,master,c0e12b61e44b,2493b9212fd4,https://bitbucket.org/panjf2000/ants/pull-requests/54,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,116,
+1,55,panjf2000/ants,0,OPEN,Bitbuckettest13,2022-08-12T15:34:03.617+00:00,2022-08-12T15:34:04.159+00:00,,0,0,,,pullrequest,,,bitbuckettest13,master,c0e12b61e44b,9dd8c98115ca,https://bitbucket.org/panjf2000/ants/pull-requests/55,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,126,
+1,56,panjf2000/ants,0,OPEN,Bitbuckettest25,2022-08-12T15:34:37.513+00:00,2022-08-12T15:35:11.960+00:00,,0,0,,,pullrequest,,,bitbuckettest25,master,c0e12b61e44b,d69faf8c90c4,https://bitbucket.org/panjf2000/ants/pull-requests/56,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,124,
+1,57,panjf2000/ants,0,OPEN,Bitbuckettest22,2022-08-12T15:34:55.738+00:00,2022-08-12T15:37:21.558+00:00,,2,0,,,pullrequest,,,bitbuckettest22,master,c0e12b61e44b,2c23b3babf27,https://bitbucket.org/panjf2000/ants/pull-requests/57,teoiaoe,62abf394192edb006fa0e8cf,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,123,
diff --git a/plugins/bitbucket/e2e/snapshot_tables/_tool_bitbucket_repos.csv b/plugins/bitbucket/e2e/snapshot_tables/_tool_bitbucket_repos.csv
new file mode 100644
index 00000000..cd993327
--- /dev/null
+++ b/plugins/bitbucket/e2e/snapshot_tables/_tool_bitbucket_repos.csv
@@ -0,0 +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,,62abf394192edb006fa0e8cf,,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/accounts.csv b/plugins/bitbucket/e2e/snapshot_tables/accounts.csv
new file mode 100644
index 00000000..8ff11005
--- /dev/null
+++ b/plugins/bitbucket/e2e/snapshot_tables/accounts.csv
@@ -0,0 +1,2 @@
+id,email,full_name,user_name,avatar_url,organization,created_date,status
+bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,teoiaoe,,https://secure.gravatar.com/avatar/d37e5c64cd007c5c20654af064c3f410?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FT-0.png,,,0
diff --git a/plugins/bitbucket/e2e/snapshot_tables/board_issues.csv b/plugins/bitbucket/e2e/snapshot_tables/board_issues.csv
new file mode 100644
index 00000000..35c52148
--- /dev/null
+++ b/plugins/bitbucket/e2e/snapshot_tables/board_issues.csv
@@ -0,0 +1,31 @@
+board_id,issue_id
+bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketIssue:1:1
+bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketIssue:1:10
+bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketIssue:1:11
+bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketIssue:1:12
+bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketIssue:1:13
+bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketIssue:1:14
+bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketIssue:1:15
+bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketIssue:1:16
+bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketIssue:1:17
+bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketIssue:1:18
+bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketIssue:1:19
+bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketIssue:1:2
+bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketIssue:1:20
+bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketIssue:1:21
+bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketIssue:1:22
+bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketIssue:1:23
+bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketIssue:1:24
+bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketIssue:1:25
+bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketIssue:1:26
+bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketIssue:1:27
+bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketIssue:1:28
+bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketIssue:1:29
+bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketIssue:1:3
+bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketIssue:1:30
+bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketIssue:1:4
+bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketIssue:1:5
+bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketIssue:1:6
+bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketIssue:1:7
+bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketIssue:1:8
+bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketIssue:1:9
diff --git a/plugins/bitbucket/e2e/snapshot_tables/boards.csv b/plugins/bitbucket/e2e/snapshot_tables/boards.csv
new file mode 100644
index 00000000..d4cbb227
--- /dev/null
+++ b/plugins/bitbucket/e2e/snapshot_tables/boards.csv
@@ -0,0 +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
diff --git a/plugins/bitbucket/e2e/snapshot_tables/issue_comments.csv b/plugins/bitbucket/e2e/snapshot_tables/issue_comments.csv
new file mode 100644
index 00000000..c2090b12
--- /dev/null
+++ b/plugins/bitbucket/e2e/snapshot_tables/issue_comments.csv
@@ -0,0 +1,29 @@
+id,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark,issue_id,body,user_id
+bitbucket:BitbucketIssue:1:63860152,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,1,,bitbucket:BitbucketIssue:1:1,,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf
+bitbucket:BitbucketIssue:1:63860153,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,2,,bitbucket:BitbucketIssue:1:1,this is a comment test for issue,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf
+bitbucket:BitbucketIssue:1:63860241,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,3,,bitbucket:BitbucketIssue:1:1,,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf
+bitbucket:BitbucketIssue:1:63961973,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,4,,bitbucket:BitbucketIssue:1:6,,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf
+bitbucket:BitbucketIssue:1:63961995,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,5,,bitbucket:BitbucketIssue:1:28,,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf
+bitbucket:BitbucketIssue:1:63963442,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,6,,bitbucket:BitbucketIssue:1:27,gvoeaoivae,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf
+bitbucket:BitbucketIssue:1:63963443,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,7,,bitbucket:BitbucketIssue:1:27,vboeaorboaneo,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf
+bitbucket:BitbucketIssue:1:63963444,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,8,,bitbucket:BitbucketIssue:1:27,beoiroierbieiaoboere,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf
+bitbucket:BitbucketIssue:1:63963445,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,9,,bitbucket:BitbucketIssue:1:27,boetboenavnin,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf
+bitbucket:BitbucketIssue:1:63963446,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,10,,bitbucket:BitbucketIssue:1:27,bbenoirbnavbeboa,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf
+bitbucket:BitbucketIssue:1:63963447,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,11,,bitbucket:BitbucketIssue:1:27,lboehoraebow,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf
+bitbucket:BitbucketIssue:1:63963449,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,12,,bitbucket:BitbucketIssue:1:27,ebeobnoerioaeiow,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf
+bitbucket:BitbucketIssue:1:63963450,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,13,,bitbucket:BitbucketIssue:1:27,uttrberoiea,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf
+bitbucket:BitbucketIssue:1:63963451,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,14,,bitbucket:BitbucketIssue:1:27,obfbdfonwea,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf
+bitbucket:BitbucketIssue:1:63963452,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,15,,bitbucket:BitbucketIssue:1:27,bjeorjboewaonwea,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf
+bitbucket:BitbucketIssue:1:63963453,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,16,,bitbucket:BitbucketIssue:1:27,brtoeorbnqqe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf
+bitbucket:BitbucketIssue:1:63963454,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,17,,bitbucket:BitbucketIssue:1:27,mmboeroi,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf
+bitbucket:BitbucketIssue:1:63963455,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,18,,bitbucket:BitbucketIssue:1:27,lqooaqjoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf
+bitbucket:BitbucketIssue:1:63963456,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,19,,bitbucket:BitbucketIssue:1:27,vajoieiowaeovw,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf
+bitbucket:BitbucketIssue:1:63963457,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,20,,bitbucket:BitbucketIssue:1:27,ieiojiwea,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf
+bitbucket:BitbucketIssue:1:63963458,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,21,,bitbucket:BitbucketIssue:1:27,beraowheuovhaewa,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf
+bitbucket:BitbucketIssue:1:63963459,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,22,,bitbucket:BitbucketIssue:1:27,oeorjeorb,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf
+bitbucket:BitbucketIssue:1:63963460,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,23,,bitbucket:BitbucketIssue:1:27,iocowevoanewww,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf
+bitbucket:BitbucketIssue:1:63963461,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,24,,bitbucket:BitbucketIssue:1:27,cowoehvowevaobnebiejaiweaw,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf
+bitbucket:BitbucketIssue:1:63963462,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,25,,bitbucket:BitbucketIssue:1:27,vbleklawi,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf
+bitbucket:BitbucketIssue:1:63963463,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,26,,bitbucket:BitbucketIssue:1:27,qowjoveawev,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf
+bitbucket:BitbucketIssue:1:63963464,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,27,,bitbucket:BitbucketIssue:1:27,ciojboieria,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf
+bitbucket:BitbucketIssue:1:63963465,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issue_comments,28,,bitbucket:BitbucketIssue:1:27,cwiwhoervowwwqe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf
diff --git a/plugins/bitbucket/e2e/snapshot_tables/issues.csv b/plugins/bitbucket/e2e/snapshot_tables/issues.csv
new file mode 100644
index 00000000..f8a5e130
--- /dev/null
+++ b/plugins/bitbucket/e2e/snapshot_tables/issues.csv
@@ -0,0 +1,55 @@
+id,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark,url,icon_url,issue_key,title,description,epic_key,type,status,original_status,story_point,resolution_date,created_date,updated_date,lead_time_minutes,parent_issue_id,priority,original_estimate_minutes,time_spent_minutes,time_remaining_minutes,creator_id,creator_name,assignee_id,assignee_name,severity,component
+bitbucket:BitbucketIssue:1:1,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,60,,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/1,,1,issue test,bitbucket issues test for devants,,issue,TODO,new,0,,2022-07-17T07:15:55.959+00:00,2022-07-17T09:11:42.656+00:00,0,,major,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,
+bitbucket:BitbucketIssue:1:10,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,52,,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/10,,10,issue test007,issue test007,,issue,TODO,new,0,,2022-08-12T13:43:00.783+00:00,2022-08-12T13:43:00.783+00:00,0,,trivial,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,
+bitbucket:BitbucketIssue:1:11,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,51,,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/11,,11,issue test008,issue test008,,issue,TODO,new,0,,2022-08-12T13:43:26.414+00:00,2022-08-12T13:43:26.414+00:00,0,,major,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,
+bitbucket:BitbucketIssue:1:12,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,50,,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/12,,12,issue test009,issue test009,,issue,TODO,new,0,,2022-08-12T13:44:04.502+00:00,2022-08-12T13:44:04.502+00:00,0,,minor,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,,
+bitbucket:BitbucketIssue:1:13,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,49,,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/13,,13,issue test010,issue test010,,issue,TODO,new,0,,2022-08-12T13:44:46.508+00:00,2022-08-12T13:44:46.508+00:00,0,,critical,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,,
+bitbucket:BitbucketIssue:1:14,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,48,,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/14,,14,issue test011,issue test011,,issue,TODO,new,0,,2022-08-12T13:45:12.810+00:00,2022-08-12T13:45:12.810+00:00,0,,blocker,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,
+bitbucket:BitbucketIssue:1:15,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,47,,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/15,,15,issue test012,issue test012,,issue,TODO,new,0,,2022-08-12T13:45:41.640+00:00,2022-08-12T13:45:41.640+00:00,0,,minor,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,,
+bitbucket:BitbucketIssue:1:16,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,46,,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/16,,16,issue test013,issue test013,,issue,TODO,new,0,,2022-08-12T13:46:06.169+00:00,2022-08-12T13:46:06.169+00:00,0,,trivial,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,,
+bitbucket:BitbucketIssue:1:17,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,45,,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/17,,17,issue test014,issue test014,,issue,TODO,new,0,,2022-08-12T13:46:39.426+00:00,2022-08-12T13:46:39.426+00:00,0,,trivial,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,,
+bitbucket:BitbucketIssue:1:18,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,44,,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/18,,18,issue test015,issue test015,,issue,TODO,new,0,,2022-08-12T13:47:01.892+00:00,2022-08-12T13:47:01.892+00:00,0,,minor,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,,
+bitbucket:BitbucketIssue:1:19,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,43,,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/19,,19,issue test016,issue test016,,issue,TODO,new,0,,2022-08-12T13:47:26.928+00:00,2022-08-12T13:47:26.928+00:00,0,,critical,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,
+bitbucket:BitbucketIssue:1:2,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,59,,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/2,,2,add bitbucket issue,feafejo,,issue,TODO,new,0,,2022-07-28T06:40:08.187+00:00,2022-07-28T06:40:08.187+00:00,0,,major,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,,
+bitbucket:BitbucketIssue:1:20,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,42,,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/20,,20,issue test017,issue test017,,issue,TODO,new,0,,2022-08-12T13:47:54.126+00:00,2022-08-12T13:47:54.126+00:00,0,,blocker,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,
+bitbucket:BitbucketIssue:1:21,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,41,,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/21,,21,issue test018,issue test018,,issue,TODO,new,0,,2022-08-12T13:48:50.020+00:00,2022-08-12T13:48:50.020+00:00,0,,trivial,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,,
+bitbucket:BitbucketIssue:1:22,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,40,,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/22,,22,issue test019,issue test019,,issue,TODO,new,0,,2022-08-12T13:49:23.252+00:00,2022-08-12T13:49:23.252+00:00,0,,minor,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,,
+bitbucket:BitbucketIssue:1:23,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,39,,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/23,,23,issue test020,issue test020,,issue,TODO,new,0,,2022-08-12T13:49:51.630+00:00,2022-08-12T13:49:51.630+00:00,0,,critical,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,
+bitbucket:BitbucketIssue:1:24,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,38,,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/24,,24,issue test021,"issue test021 ijeiawgoeive/faveevaeviaevfejaofejfioejaiofe_veavejiovajgiorejoifjrogiorejieafajejaojoejvgioriovioraivjairobnrnoivaiorjbiorjiojaeiorjvioejroivjaoijeriojiaojioeefjafioejfiojeiofawefwefoiwefiwoiefweefwoefuwhufirfrw._
+
+```
+``eveaeaeae`jiovjeiojoa oi2j3oij23jovnw3
+```
+
+‌",,issue,TODO,new,0,,2022-08-12T13:50:57.290+00:00,2022-08-12T13:50:57.290+00:00,0,,trivial,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,
+bitbucket:BitbucketIssue:1:25,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,36,,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/25,,25,issue 023,"issue test023 ejoafehoafe
+
+q
+
+```c++
+feafefoa
+feafoiehfe
+rvvgeaioehr
+feaofe
+```
+
+| **fefae** | | |
+| --- | --- | --- |
+| | | |
+| | | |
+
+‌",,issue,TODO,new,0,,2022-08-12T13:53:49.232+00:00,2022-08-12T13:53:49.232+00:00,0,,critical,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,,
+bitbucket:BitbucketIssue:1:26,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,35,,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/26,,26,issue test022,issue test022,,issue,TODO,new,0,,2022-08-12T13:54:28.911+00:00,2022-08-12T13:54:28.911+00:00,0,,blocker,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,,
+bitbucket:BitbucketIssue:1:27,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,31,,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/27,,27,issue test024,"issue test024v aejnoafoeiogoiae
+
+qwofjeoiwjf",,issue,TODO,new,0,,2022-08-12T13:55:09.734+00:00,2022-08-13T06:20:01.410+00:00,0,,trivial,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,,
+bitbucket:BitbucketIssue:1:28,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,34,,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/28,,28,issue test025,issue test025,,issue,TODO,new,0,,2022-08-12T13:55:32.410+00:00,2022-08-12T13:55:58.035+00:00,0,,minor,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,,
+bitbucket:BitbucketIssue:1:29,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,33,,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/29,,29,issue test026,issue test026,,issue,TODO,new,0,,2022-08-12T13:56:26.434+00:00,2022-08-12T13:56:26.434+00:00,0,,critical,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,
+bitbucket:BitbucketIssue:1:3,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,58,,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/3,,3,bitbucket test,"efaegjeoaijefioaegrjoeior,af enfaoiee vioea.,,.wew",,issue,TODO,new,0,,2022-07-28T06:41:23.334+00:00,2022-07-28T06:41:23.334+00:00,0,,major,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,,
+bitbucket:BitbucketIssue:1:30,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,32,,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/30,,30,issue test027,issue test027,,issue,TODO,new,0,,2022-08-12T13:56:51.480+00:00,2022-08-12T13:56:51.480+00:00,0,,critical,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,
+bitbucket:BitbucketIssue:1:4,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,57,,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/4,,4,issue test001,Bitbucket issue test001,,issue,TODO,new,0,,2022-08-12T13:39:52.109+00:00,2022-08-12T13:39:52.109+00:00,0,,major,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,,
+bitbucket:BitbucketIssue:1:5,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,56,,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/5,,5,issue test002,issue test002,,issue,TODO,new,0,,2022-08-12T13:40:32.843+00:00,2022-08-12T13:40:32.843+00:00,0,,major,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,,
+bitbucket:BitbucketIssue:1:6,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,37,,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/6,,6,issue test003,issue test 003,,issue,TODO,new,0,,2022-08-12T13:40:55.635+00:00,2022-08-12T13:51:25.769+00:00,0,,major,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,
+bitbucket:BitbucketIssue:1:7,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,55,,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/7,,7,issue test004,issue test004,,issue,TODO,new,0,,2022-08-12T13:41:21.859+00:00,2022-08-12T13:41:21.859+00:00,0,,major,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,,
+bitbucket:BitbucketIssue:1:8,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,54,,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/8,,8,issue test005,issue test005,,issue,TODO,new,0,,2022-08-12T13:41:58.511+00:00,2022-08-12T13:41:58.511+00:00,0,,critical,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,,
+bitbucket:BitbucketIssue:1:9,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_issues,53,,https://api.bitbucket.org/2.0/repositories/panjf2000/ants/issues/9,,9,issue test006,issue test006,,issue,TODO,new,0,,2022-08-12T13:42:29.072+00:00,2022-08-12T13:42:29.072+00:00,0,,blocker,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,
diff --git a/plugins/bitbucket/e2e/snapshot_tables/pull_request_comments.csv b/plugins/bitbucket/e2e/snapshot_tables/pull_request_comments.csv
new file mode 100644
index 00000000..ae71f7c1
--- /dev/null
+++ b/plugins/bitbucket/e2e/snapshot_tables/pull_request_comments.csv
@@ -0,0 +1,21 @@
+id,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark,pull_request_id,body,user_id,commit_sha,position,type,review_id,status
+bitbucket:BitbucketPrComment:1:323119643,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_request_comments,14,,bitbucket:BitbucketPullRequest:1:54,this is test014,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,pullrequest_comment,,
+bitbucket:BitbucketPrComment:1:323119662,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_request_comments,15,,bitbucket:BitbucketPullRequest:1:54,feafjae,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,pullrequest_comment,,
+bitbucket:BitbucketPrComment:1:323119730,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_request_comments,17,,bitbucket:BitbucketPullRequest:1:57,"fejoiafjoej
+
+‌",bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,pullrequest_comment,,
+bitbucket:BitbucketPrComment:1:323119743,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_request_comments,18,,bitbucket:BitbucketPullRequest:1:57,fjeafojeoirjgoar,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,pullrequest_comment,,
+bitbucket:BitbucketPrComment:1:323119784,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_request_comments,12,,bitbucket:BitbucketPullRequest:1:51,fejoafefewa,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,pullrequest_comment,,
+bitbucket:BitbucketPrComment:1:323119790,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_request_comments,13,,bitbucket:BitbucketPullRequest:1:51,feafeogioehrw,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,pullrequest_comment,,
+bitbucket:BitbucketPrComment:1:323119842,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_request_comments,8,,bitbucket:BitbucketPullRequest:1:42,feafoegoaweoir,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,pullrequest_comment,,
+bitbucket:BitbucketPrComment:1:323119856,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_request_comments,9,,bitbucket:BitbucketPullRequest:1:42,boroehruheorefe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,pullrequest_comment,,
+bitbucket:BitbucketPrComment:1:323119897,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_request_comments,16,,bitbucket:BitbucketPullRequest:1:54,feajfweofjwvgrea,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,pullrequest_comment,,
+bitbucket:BitbucketPrComment:1:323119960,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_request_comments,4,,bitbucket:BitbucketPullRequest:1:38,regrioteothro,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,pullrequest_comment,,
+bitbucket:BitbucketPrComment:1:323119980,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_request_comments,5,,bitbucket:BitbucketPullRequest:1:38,goeotbioebhotn,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,pullrequest_comment,,
+bitbucket:BitbucketPrComment:1:323120091,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_request_comments,10,,bitbucket:BitbucketPullRequest:1:44,feawrjoiw,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,pullrequest_comment,,
+bitbucket:BitbucketPrComment:1:323120101,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_request_comments,11,,bitbucket:BitbucketPullRequest:1:44,bbeirobeoiahoa,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,pullrequest_comment,,
+bitbucket:BitbucketPrComment:1:323120140,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_request_comments,6,,bitbucket:BitbucketPullRequest:1:40,evbnoera,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,pullrequest_comment,,
+bitbucket:BitbucketPrComment:1:323120196,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_request_comments,7,,bitbucket:BitbucketPullRequest:1:41,vobreia,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,pullrequest_comment,,
+bitbucket:BitbucketPrComment:1:323120278,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_request_comments,1,,bitbucket:BitbucketPullRequest:1:31,baroboerhba,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,pullrequest_comment,,
+bitbucket:BitbucketPrComment:1:323120296,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_request_comments,2,,bitbucket:BitbucketPullRequest:1:31,bweiwbeanb,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,pullrequest_comment,,
+bitbucket:BitbucketPrComment:1:323120311,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_request_comments,3,,bitbucket:BitbucketPullRequest:1:31,jbirobsore,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,pullrequest_comment,,
diff --git a/plugins/bitbucket/e2e/snapshot_tables/pull_requests.csv b/plugins/bitbucket/e2e/snapshot_tables/pull_requests.csv
new file mode 100644
index 00000000..3b479109
--- /dev/null
+++ b/plugins/bitbucket/e2e/snapshot_tables/pull_requests.csv
@@ -0,0 +1,1257 @@
+id,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark,base_repo_id,head_repo_id,status,title,description,url,author_name,author_id,parent_pr_id,pull_request_key,created_date,merged_date,closed_date,type,component,merge_commit_sha,head_ref,base_ref,base_commit_sha,head_commit_sha
+bitbucket:BitbucketPullRequest:1:1,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,171,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,OPEN,feat: add pr test,this is a pr test for Bitbucket plugin,https://bitbucket.org/panjf2000/ants/pull-requests/1,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-07-12T04:17:03.055+00:00,,,pullrequest,,,test,master,c0e12b61e44b,5e5bccfcc656
+bitbucket:BitbucketPullRequest:1:10,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,161,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,MERGED,Test007,"* feat: add git.sh
+* feat: add test001
+* feat: test002
+* feat: add test003
+* feat: add test004
+* feat: add test005
+* feat: add test006
+* feat: add test007
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/10,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T14:55:28.082+00:00,,,pullrequest,,983edbf06740,test007,master,0bd83caaf9c7,15a1fcb51f0e
+bitbucket:BitbucketPullRequest:1:11,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,160,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,MERGED,Test6,"* feat: add git.sh
+* feat: add test001
+* feat: test002
+* feat: add test003
+* feat: add test004
+* feat: add test005
+* feat: add test006
+* feat: add test007
+* feat: add test008
+* feat: add test009
+* feat: add test010
+* feat: add test011
+* feat: add test012
+* feat: add test013
+* feat: add branchname
+* feat: add 1
+* feat: add 2
+* feat: add 3
+* feat: add 4
+* feat: add 5
+* feat: add test6
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/11,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T14:55:48.770+00:00,,,pullrequest,,983edbf06740,test6,master,0bd83caaf9c7,bc9a3ddd47ea
+bitbucket:BitbucketPullRequest:1:12,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,159,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,MERGED,Test7,"* feat: add git.sh
+* feat: add test001
+* feat: test002
+* feat: add test003
+* feat: add test004
+* feat: add test005
+* feat: add test006
+* feat: add test007
+* feat: add test008
+* feat: add test009
+* feat: add test010
+* feat: add test011
+* feat: add test012
+* feat: add test013
+* feat: add branchname
+* feat: add 1
+* feat: add 2
+* feat: add 3
+* feat: add 4
+* feat: add 5
+* feat: add test6
+* feat: add test7
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/12,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T14:56:07.092+00:00,,,pullrequest,,983edbf06740,test7,master,0bd83caaf9c7,dd2cd76d7098
+bitbucket:BitbucketPullRequest:1:13,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,158,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,MERGED,Test8,"* feat: add git.sh
+* feat: add test001
+* feat: test002
+* feat: add test003
+* feat: add test004
+* feat: add test005
+* feat: add test006
+* feat: add test007
+* feat: add test008
+* feat: add test009
+* feat: add test010
+* feat: add test011
+* feat: add test012
+* feat: add test013
+* feat: add branchname
+* feat: add 1
+* feat: add 2
+* feat: add 3
+* feat: add 4
+* feat: add 5
+* feat: add test6
+* feat: add test7
+* feat: add test8
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/13,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T14:56:30.570+00:00,,,pullrequest,,983edbf06740,test8,master,0bd83caaf9c7,94056215cf46
+bitbucket:BitbucketPullRequest:1:14,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,157,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,MERGED,Test10,"* feat: add git.sh
+* feat: add test001
+* feat: test002
+* feat: add test003
+* feat: add test004
+* feat: add test005
+* feat: add test006
+* feat: add test007
+* feat: add test008
+* feat: add test009
+* feat: add test010
+* feat: add test011
+* feat: add test012
+* feat: add test013
+* feat: add branchname
+* feat: add 1
+* feat: add 2
+* feat: add 3
+* feat: add 4
+* feat: add 5
+* feat: add test6
+* feat: add test7
+* feat: add test8
+* feat: add test9
+* feat: add test10
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/14,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T14:56:52.550+00:00,,,pullrequest,,983edbf06740,test10,master,0bd83caaf9c7,bc2cf25142e3
+bitbucket:BitbucketPullRequest:1:15,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,156,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,MERGED,Test011,"* feat: add git.sh
+* feat: add test001
+* feat: test002
+* feat: add test003
+* feat: add test004
+* feat: add test005
+* feat: add test006
+* feat: add test007
+* feat: add test008
+* feat: add test009
+* feat: add test010
+* feat: add test011
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/15,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T14:57:15.254+00:00,,,pullrequest,,983edbf06740,test011,master,0bd83caaf9c7,5db6b1a9e60c
+bitbucket:BitbucketPullRequest:1:16,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,170,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,MERGED,Test12,"* feat: add git.sh
+* feat: add test001
+* feat: test002
+* feat: add test003
+* feat: add test004
+* feat: add test005
+* feat: add test006
+* feat: add test007
+* feat: add test008
+* feat: add test009
+* feat: add test010
+* feat: add test011
+* feat: add test012
+* feat: add test013
+* feat: add branchname
+* feat: add 1
+* feat: add 2
+* feat: add 3
+* feat: add 4
+* feat: add 5
+* feat: add test6
+* feat: add test7
+* feat: add test8
+* feat: add test9
+* feat: add test10
+* feat: add test11
+* feat: add test12
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/16,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T14:57:41.230+00:00,,,pullrequest,,983edbf06740,test12,master,0bd83caaf9c7,d1321cb5e4fb
+bitbucket:BitbucketPullRequest:1:17,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,151,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,MERGED,Test13,"* feat: add git.sh
+* feat: add test001
+* feat: test002
+* feat: add test003
+* feat: add test004
+* feat: add test005
+* feat: add test006
+* feat: add test007
+* feat: add test008
+* feat: add test009
+* feat: add test010
+* feat: add test011
+* feat: add test012
+* feat: add test013
+* feat: add branchname
+* feat: add 1
+* feat: add 2
+* feat: add 3
+* feat: add 4
+* feat: add 5
+* feat: add test6
+* feat: add test7
+* feat: add test8
+* feat: add test9
+* feat: add test10
+* feat: add test11
+* feat: add test12
+* feat: add test13
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/17,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T14:57:59.501+00:00,,,pullrequest,,c0e12b61e44b,test13,master,983edbf06740,3d6d96659c4d
+bitbucket:BitbucketPullRequest:1:18,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,150,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,MERGED,Test14,"* feat: add git.sh
+* feat: add test001
+* feat: test002
+* feat: add test003
+* feat: add test004
+* feat: add test005
+* feat: add test006
+* feat: add test007
+* feat: add test008
+* feat: add test009
+* feat: add test010
+* feat: add test011
+* feat: add test012
+* feat: add test013
+* feat: add branchname
+* feat: add 1
+* feat: add 2
+* feat: add 3
+* feat: add 4
+* feat: add 5
+* feat: add test6
+* feat: add test7
+* feat: add test8
+* feat: add test9
+* feat: add test10
+* feat: add test11
+* feat: add test12
+* feat: add test13
+* feat: add test14
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/18,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T14:58:16.105+00:00,,,pullrequest,,c0e12b61e44b,test14,master,983edbf06740,8d4896eef3ed
+bitbucket:BitbucketPullRequest:1:19,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,149,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,MERGED,Test15,"* feat: add git.sh
+* feat: add test001
+* feat: test002
+* feat: add test003
+* feat: add test004
+* feat: add test005
+* feat: add test006
+* feat: add test007
+* feat: add test008
+* feat: add test009
+* feat: add test010
+* feat: add test011
+* feat: add test012
+* feat: add test013
+* feat: add branchname
+* feat: add 1
+* feat: add 2
+* feat: add 3
+* feat: add 4
+* feat: add 5
+* feat: add test6
+* feat: add test7
+* feat: add test8
+* feat: add test9
+* feat: add test10
+* feat: add test11
+* feat: add test12
+* feat: add test13
+* feat: add test14
+* feat: add test15
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/19,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T14:58:33.361+00:00,,,pullrequest,,c0e12b61e44b,test15,master,983edbf06740,daa1cfa7ec0c
+bitbucket:BitbucketPullRequest:1:2,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,169,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,MERGED,feat: add git.sh,help to git commit faster,https://bitbucket.org/panjf2000/ants/pull-requests/2,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T13:18:39.132+00:00,,,pullrequest,,983edbf06740,vowehwo,master,0bd83caaf9c7,d4a6789a3751
+bitbucket:BitbucketPullRequest:1:20,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,148,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,MERGED,Test16,"* feat: add git.sh
+* feat: add test001
+* feat: test002
+* feat: add test003
+* feat: add test004
+* feat: add test005
+* feat: add test006
+* feat: add test007
+* feat: add test008
+* feat: add test009
+* feat: add test010
+* feat: add test011
+* feat: add test012
+* feat: add test013
+* feat: add branchname
+* feat: add 1
+* feat: add 2
+* feat: add 3
+* feat: add 4
+* feat: add 5
+* feat: add test6
+* feat: add test7
+* feat: add test8
+* feat: add test9
+* feat: add test10
+* feat: add test11
+* feat: add test12
+* feat: add test13
+* feat: add test14
+* feat: add test15
+* feat: add test16
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/20,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T14:59:07.791+00:00,,,pullrequest,,c0e12b61e44b,test16,master,983edbf06740,11f6c86580e2
+bitbucket:BitbucketPullRequest:1:21,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,147,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,MERGED,Test18,"* feat: add git.sh
+* feat: add test001
+* feat: test002
+* feat: add test003
+* feat: add test004
+* feat: add test005
+* feat: add test006
+* feat: add test007
+* feat: add test008
+* feat: add test009
+* feat: add test010
+* feat: add test011
+* feat: add test012
+* feat: add test013
+* feat: add branchname
+* feat: add 1
+* feat: add 2
+* feat: add 3
+* feat: add 4
+* feat: add 5
+* feat: add test6
+* feat: add test7
+* feat: add test8
+* feat: add test9
+* feat: add test10
+* feat: add test11
+* feat: add test12
+* feat: add test13
+* feat: add test14
+* feat: add test15
+* feat: add test16
+* feat: add test17
+* feat: add test18
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/21,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T14:59:22.874+00:00,,,pullrequest,,c0e12b61e44b,test18,master,983edbf06740,0b958bbfed04
+bitbucket:BitbucketPullRequest:1:22,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,146,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,MERGED,Test19,"* feat: add git.sh
+* feat: add test001
+* feat: test002
+* feat: add test003
+* feat: add test004
+* feat: add test005
+* feat: add test006
+* feat: add test007
+* feat: add test008
+* feat: add test009
+* feat: add test010
+* feat: add test011
+* feat: add test012
+* feat: add test013
+* feat: add branchname
+* feat: add 1
+* feat: add 2
+* feat: add 3
+* feat: add 4
+* feat: add 5
+* feat: add test6
+* feat: add test7
+* feat: add test8
+* feat: add test9
+* feat: add test10
+* feat: add test11
+* feat: add test12
+* feat: add test13
+* feat: add test14
+* feat: add test15
+* feat: add test16
+* feat: add test17
+* feat: add test18
+* feat: add test19
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/22,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T14:59:39.610+00:00,,,pullrequest,,c0e12b61e44b,test19,master,983edbf06740,d1bb3677810e
+bitbucket:BitbucketPullRequest:1:23,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,145,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,MERGED,Test20,"* feat: add git.sh
+* feat: add test001
+* feat: test002
+* feat: add test003
+* feat: add test004
+* feat: add test005
+* feat: add test006
+* feat: add test007
+* feat: add test008
+* feat: add test009
+* feat: add test010
+* feat: add test011
+* feat: add test012
+* feat: add test013
+* feat: add branchname
+* feat: add 1
+* feat: add 2
+* feat: add 3
+* feat: add 4
+* feat: add 5
+* feat: add test6
+* feat: add test7
+* feat: add test8
+* feat: add test9
+* feat: add test10
+* feat: add test11
+* feat: add test12
+* feat: add test13
+* feat: add test14
+* feat: add test15
+* feat: add test16
+* feat: add test17
+* feat: add test18
+* feat: add test19
+* feat: add test20
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/23,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T14:59:57.869+00:00,,,pullrequest,,c0e12b61e44b,test20,master,983edbf06740,9d16a2368acd
+bitbucket:BitbucketPullRequest:1:24,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,144,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,MERGED,Test17,"* feat: add git.sh
+* feat: add test001
+* feat: test002
+* feat: add test003
+* feat: add test004
+* feat: add test005
+* feat: add test006
+* feat: add test007
+* feat: add test008
+* feat: add test009
+* feat: add test010
+* feat: add test011
+* feat: add test012
+* feat: add test013
+* feat: add branchname
+* feat: add 1
+* feat: add 2
+* feat: add 3
+* feat: add 4
+* feat: add 5
+* feat: add test6
+* feat: add test7
+* feat: add test8
+* feat: add test9
+* feat: add test10
+* feat: add test11
+* feat: add test12
+* feat: add test13
+* feat: add test14
+* feat: add test15
+* feat: add test16
+* feat: add test17
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/24,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T15:00:34.399+00:00,,,pullrequest,,c0e12b61e44b,test17,master,983edbf06740,2bfd29a17665
+bitbucket:BitbucketPullRequest:1:25,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,155,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,MERGED,1,"* feat: add git.sh
+* feat: add test001
+* feat: test002
+* feat: add test003
+* feat: add test004
+* feat: add test005
+* feat: add test006
+* feat: add test007
+* feat: add test008
+* feat: add test009
+* feat: add test010
+* feat: add test011
+* feat: add test012
+* feat: add test013
+* feat: add branchname
+* feat: add 1
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/25,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T15:00:59.503+00:00,,,pullrequest,,983edbf06740,1,master,0bd83caaf9c7,592612c47ff5
+bitbucket:BitbucketPullRequest:1:26,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,154,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,MERGED,2,"* feat: add git.sh
+* feat: add test001
+* feat: test002
+* feat: add test003
+* feat: add test004
+* feat: add test005
+* feat: add test006
+* feat: add test007
+* feat: add test008
+* feat: add test009
+* feat: add test010
+* feat: add test011
+* feat: add test012
+* feat: add test013
+* feat: add branchname
+* feat: add 1
+* feat: add 2
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/26,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T15:01:18.980+00:00,,,pullrequest,,983edbf06740,2,master,0bd83caaf9c7,05b97cf92b4b
+bitbucket:BitbucketPullRequest:1:27,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,153,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,MERGED,4,"* feat: add git.sh
+* feat: add test001
+* feat: test002
+* feat: add test003
+* feat: add test004
+* feat: add test005
+* feat: add test006
+* feat: add test007
+* feat: add test008
+* feat: add test009
+* feat: add test010
+* feat: add test011
+* feat: add test012
+* feat: add test013
+* feat: add branchname
+* feat: add 1
+* feat: add 2
+* feat: add 3
+* feat: add 4
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/27,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T15:02:16.840+00:00,,,pullrequest,,983edbf06740,4,master,0bd83caaf9c7,761fdb9244ef
+bitbucket:BitbucketPullRequest:1:28,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,152,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,MERGED,mergev1,merge all v1,https://bitbucket.org/panjf2000/ants/pull-requests/28,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T15:10:16.367+00:00,,,pullrequest,,c0e12b61e44b,mergev1,master,983edbf06740,581a89006076
+bitbucket:BitbucketPullRequest:1:29,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,132,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,OPEN,Test03,"* feat: add test01
+* feat: add test02
+* feat: add test03
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/29,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T15:21:47.437+00:00,,,pullrequest,,,test03,master,c0e12b61e44b,a3768d182d18
+bitbucket:BitbucketPullRequest:1:3,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,168,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,MERGED,Test9,"* feat: add git.sh
+* feat: add test001
+* feat: test002
+* feat: add test003
+* feat: add test004
+* feat: add test005
+* feat: add test006
+* feat: add test007
+* feat: add test008
+* feat: add test009
+* feat: add test010
+* feat: add test011
+* feat: add test012
+* feat: add test013
+* feat: add branchname
+* feat: add 1
+* feat: add 2
+* feat: add 3
+* feat: add 4
+* feat: add 5
+* feat: add test6
+* feat: add test7
+* feat: add test8
+* feat: add test9
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/3,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T14:53:19.796+00:00,,,pullrequest,,983edbf06740,test9,master,0bd83caaf9c7,6557208f4aa6
+bitbucket:BitbucketPullRequest:1:30,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,143,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,OPEN,Test05,"* feat: add test01
+* feat: add test02
+* feat: add test03
+* feat: add test04
+* feat: add test05
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/30,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T15:22:17.954+00:00,,,pullrequest,,,test05,master,c0e12b61e44b,91e7a1db9ae0
+bitbucket:BitbucketPullRequest:1:31,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,118,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,OPEN,Test020,"* feat: add test01
+* feat: add test02
+* feat: add test03
+* feat: add test04
+* feat: add test05
+* feat: add test06
+* feat: add test07
+* feat: add test08
+* feat: add test09
+* feat: add test010
+* feat: add test011
+* feat: add test012
+* feat: add test013
+* feat: add test014
+* feat: add test015
+* feat: add test016
+* feat: add test017
+* feat: add test018
+* feat: add test019
+* feat: add test020
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/31,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T15:22:51.303+00:00,,,pullrequest,,,test020,master,c0e12b61e44b,0342bf5351f8
+bitbucket:BitbucketPullRequest:1:32,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,142,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,OPEN,Test022,"* feat: add test01
+* feat: add test02
+* feat: add test03
+* feat: add test04
+* feat: add test05
+* feat: add test06
+* feat: add test07
+* feat: add test08
+* feat: add test09
+* feat: add test010
+* feat: add test011
+* feat: add test012
+* feat: add test013
+* feat: add test014
+* feat: add test015
+* feat: add test016
+* feat: add test017
+* feat: add test018
+* feat: add test019
+* feat: add test020
+* feat: add test021
+* feat: add test022
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/32,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T15:23:06.858+00:00,,,pullrequest,,,test022,master,c0e12b61e44b,d72c46a722d7
+bitbucket:BitbucketPullRequest:1:33,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,141,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,OPEN,Test023,"* feat: add test01
+* feat: add test02
+* feat: add test03
+* feat: add test04
+* feat: add test05
+* feat: add test06
+* feat: add test07
+* feat: add test08
+* feat: add test09
+* feat: add test010
+* feat: add test011
+* feat: add test012
+* feat: add test013
+* feat: add test014
+* feat: add test015
+* feat: add test016
+* feat: add test017
+* feat: add test018
+* feat: add test019
+* feat: add test020
+* feat: add test021
+* feat: add test022
+* feat: add test023
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/33,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T15:23:27.568+00:00,,,pullrequest,,,test023,master,c0e12b61e44b,0a8011c8c0ac
+bitbucket:BitbucketPullRequest:1:34,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,140,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,OPEN,Test024,"* feat: add test01
+* feat: add test02
+* feat: add test03
+* feat: add test04
+* feat: add test05
+* feat: add test06
+* feat: add test07
+* feat: add test08
+* feat: add test09
+* feat: add test010
+* feat: add test011
+* feat: add test012
+* feat: add test013
+* feat: add test014
+* feat: add test015
+* feat: add test016
+* feat: add test017
+* feat: add test018
+* feat: add test019
+* feat: add test020
+* feat: add test021
+* feat: add test022
+* feat: add test023
+* feat: add test024
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/34,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T15:23:42.883+00:00,,,pullrequest,,,test024,master,c0e12b61e44b,bd7e81a581f1
+bitbucket:BitbucketPullRequest:1:35,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,139,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,OPEN,Test025,"* feat: add test01
+* feat: add test02
+* feat: add test03
+* feat: add test04
+* feat: add test05
+* feat: add test06
+* feat: add test07
+* feat: add test08
+* feat: add test09
+* feat: add test010
+* feat: add test011
+* feat: add test012
+* feat: add test013
+* feat: add test014
+* feat: add test015
+* feat: add test016
+* feat: add test017
+* feat: add test018
+* feat: add test019
+* feat: add test020
+* feat: add test021
+* feat: add test022
+* feat: add test023
+* feat: add test024
+* feat: add test025
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/35,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T15:23:58.227+00:00,,,pullrequest,,,test025,master,c0e12b61e44b,a15cb5876414
+bitbucket:BitbucketPullRequest:1:36,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,138,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,OPEN,Test027,"* feat: add test01
+* feat: add test02
+* feat: add test03
+* feat: add test04
+* feat: add test05
+* feat: add test06
+* feat: add test07
+* feat: add test08
+* feat: add test09
+* feat: add test010
+* feat: add test011
+* feat: add test012
+* feat: add test013
+* feat: add test014
+* feat: add test015
+* feat: add test016
+* feat: add test017
+* feat: add test018
+* feat: add test019
+* feat: add test020
+* feat: add test021
+* feat: add test022
+* feat: add test023
+* feat: add test024
+* feat: add test025
+* feat: add test026
+* feat: add test027
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/36,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T15:24:16.607+00:00,,,pullrequest,,,test027,master,c0e12b61e44b,200665f463ff
+bitbucket:BitbucketPullRequest:1:37,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,137,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,OPEN,Test028,"* feat: add test01
+* feat: add test02
+* feat: add test03
+* feat: add test04
+* feat: add test05
+* feat: add test06
+* feat: add test07
+* feat: add test08
+* feat: add test09
+* feat: add test010
+* feat: add test011
+* feat: add test012
+* feat: add test013
+* feat: add test014
+* feat: add test015
+* feat: add test016
+* feat: add test017
+* feat: add test018
+* feat: add test019
+* feat: add test020
+* feat: add test021
+* feat: add test022
+* feat: add test023
+* feat: add test024
+* feat: add test025
+* feat: add test026
+* feat: add test027
+* feat: add test028
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/37,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T15:24:44.626+00:00,,,pullrequest,,,test028,master,c0e12b61e44b,8a9052ac97f4
+bitbucket:BitbucketPullRequest:1:38,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,121,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,OPEN,Test029,"* feat: add test01
+* feat: add test02
+* feat: add test03
+* feat: add test04
+* feat: add test05
+* feat: add test06
+* feat: add test07
+* feat: add test08
+* feat: add test09
+* feat: add test010
+* feat: add test011
+* feat: add test012
+* feat: add test013
+* feat: add test014
+* feat: add test015
+* feat: add test016
+* feat: add test017
+* feat: add test018
+* feat: add test019
+* feat: add test020
+* feat: add test021
+* feat: add test022
+* feat: add test023
+* feat: add test024
+* feat: add test025
+* feat: add test026
+* feat: add test027
+* feat: add test028
+* feat: add test029
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/38,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T15:24:59.520+00:00,,,pullrequest,,,test029,master,c0e12b61e44b,74bddc0541c6
+bitbucket:BitbucketPullRequest:1:39,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,136,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,OPEN,Test030,"* feat: add test01
+* feat: add test02
+* feat: add test03
+* feat: add test04
+* feat: add test05
+* feat: add test06
+* feat: add test07
+* feat: add test08
+* feat: add test09
+* feat: add test010
+* feat: add test011
+* feat: add test012
+* feat: add test013
+* feat: add test014
+* feat: add test015
+* feat: add test016
+* feat: add test017
+* feat: add test018
+* feat: add test019
+* feat: add test020
+* feat: add test021
+* feat: add test022
+* feat: add test023
+* feat: add test024
+* feat: add test025
+* feat: add test026
+* feat: add test027
+* feat: add test028
+* feat: add test029
+* feat: add test030
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/39,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T15:25:14.361+00:00,,,pullrequest,,,test030,master,c0e12b61e44b,f332a9855de1
+bitbucket:BitbucketPullRequest:1:4,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,167,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,MERGED,Test001,"* feat: add git.sh
+* feat: add test001
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/4,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T14:53:43.845+00:00,,,pullrequest,,983edbf06740,test001,master,0bd83caaf9c7,3bd3b34b2397
+bitbucket:BitbucketPullRequest:1:40,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,115,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,OPEN,Test016,"* feat: add test01
+* feat: add test02
+* feat: add test03
+* feat: add test04
+* feat: add test05
+* feat: add test06
+* feat: add test07
+* feat: add test08
+* feat: add test09
+* feat: add test010
+* feat: add test011
+* feat: add test012
+* feat: add test013
+* feat: add test014
+* feat: add test015
+* feat: add test016
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/40,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T15:25:31.596+00:00,,,pullrequest,,,test016,master,c0e12b61e44b,a000a2a9fc11
+bitbucket:BitbucketPullRequest:1:41,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,119,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,OPEN,Test018,"* feat: add test01
+* feat: add test02
+* feat: add test03
+* feat: add test04
+* feat: add test05
+* feat: add test06
+* feat: add test07
+* feat: add test08
+* feat: add test09
+* feat: add test010
+* feat: add test011
+* feat: add test012
+* feat: add test013
+* feat: add test014
+* feat: add test015
+* feat: add test016
+* feat: add test017
+* feat: add test018
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/41,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T15:25:57.607+00:00,,,pullrequest,,,test018,master,c0e12b61e44b,561e4e3ca4cc
+bitbucket:BitbucketPullRequest:1:42,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,117,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,OPEN,Test019,"* feat: add test01
+* feat: add test02
+* feat: add test03
+* feat: add test04
+* feat: add test05
+* feat: add test06
+* feat: add test07
+* feat: add test08
+* feat: add test09
+* feat: add test010
+* feat: add test011
+* feat: add test012
+* feat: add test013
+* feat: add test014
+* feat: add test015
+* feat: add test016
+* feat: add test017
+* feat: add test018
+* feat: add test019
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/42,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T15:26:15.843+00:00,,,pullrequest,,,test019,master,c0e12b61e44b,8597a99eebb3
+bitbucket:BitbucketPullRequest:1:43,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,135,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,OPEN,Test015,"* feat: add test01
+* feat: add test02
+* feat: add test03
+* feat: add test04
+* feat: add test05
+* feat: add test06
+* feat: add test07
+* feat: add test08
+* feat: add test09
+* feat: add test010
+* feat: add test011
+* feat: add test012
+* feat: add test013
+* feat: add test014
+* feat: add test015
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/43,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T15:26:50.905+00:00,,,pullrequest,,,test015,master,c0e12b61e44b,5af2c361263a
+bitbucket:BitbucketPullRequest:1:44,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,120,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,OPEN,Test017,"* feat: add test01
+* feat: add test02
+* feat: add test03
+* feat: add test04
+* feat: add test05
+* feat: add test06
+* feat: add test07
+* feat: add test08
+* feat: add test09
+* feat: add test010
+* feat: add test011
+* feat: add test012
+* feat: add test013
+* feat: add test014
+* feat: add test015
+* feat: add test016
+* feat: add test017
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/44,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T15:27:54.969+00:00,,,pullrequest,,,test017,master,c0e12b61e44b,ee87b0380472
+bitbucket:BitbucketPullRequest:1:45,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,134,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,OPEN,feat: add test01,,https://bitbucket.org/panjf2000/ants/pull-requests/45,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T15:28:43.059+00:00,,,pullrequest,,,test01,master,c0e12b61e44b,63e6acbb60a9
+bitbucket:BitbucketPullRequest:1:46,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,133,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,OPEN,Test04,"* feat: add test01
+* feat: add test02
+* feat: add test03
+* feat: add test04
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/46,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T15:28:57.787+00:00,,,pullrequest,,,test04,master,c0e12b61e44b,33419e166d75
+bitbucket:BitbucketPullRequest:1:47,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,131,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,OPEN,Test06,"* feat: add test01
+* feat: add test02
+* feat: add test03
+* feat: add test04
+* feat: add test05
+* feat: add test06
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/47,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T15:29:31.360+00:00,,,pullrequest,,,test06,master,c0e12b61e44b,f8f9ef467e3a
+bitbucket:BitbucketPullRequest:1:48,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,130,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,OPEN,Test07,"* feat: add test01
+* feat: add test02
+* feat: add test03
+* feat: add test04
+* feat: add test05
+* feat: add test06
+* feat: add test07
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/48,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T15:29:49.045+00:00,,,pullrequest,,,test07,master,c0e12b61e44b,c28f34dde568
+bitbucket:BitbucketPullRequest:1:49,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,129,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,OPEN,Test08,"* feat: add test01
+* feat: add test02
+* feat: add test03
+* feat: add test04
+* feat: add test05
+* feat: add test06
+* feat: add test07
+* feat: add test08
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/49,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T15:30:04.329+00:00,,,pullrequest,,,test08,master,c0e12b61e44b,6b59c8607581
+bitbucket:BitbucketPullRequest:1:5,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,166,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,MERGED,Test002,"* feat: add git.sh
+* feat: add test001
+* feat: test002
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/5,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T14:53:59.654+00:00,,,pullrequest,,983edbf06740,test002,master,0bd83caaf9c7,904df6924f1e
+bitbucket:BitbucketPullRequest:1:50,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,128,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,OPEN,Bitbuckettest12,"* feat: add test01
+* feat: add test02
+* feat: add test03
+* feat: add test04
+* feat: add test05
+* feat: add test06
+* feat: add test07
+* feat: add test08
+* feat: add test09
+* feat: add test010
+* feat: add test011
+* feat: add test012
+* feat: add test013
+* feat: add test014
+* feat: add test015
+* feat: add test016
+* feat: add test017
+* feat: add test018
+* feat: add test019
+* feat: add test020
+* feat: add test021
+* feat: add test022
+* feat: add test023
+* feat: add test024
+* feat: add test025
+* feat: add test026
+* feat: add test027
+* feat: add test028
+* feat: add test029
+* feat: add test030
+* feat: add bitbucket
+* feat: add bitbucket
+* feat: add bitbuckettest12
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/50,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T15:32:46.725+00:00,,,pullrequest,,,bitbuckettest12,master,c0e12b61e44b,deff6927812f
+bitbucket:BitbucketPullRequest:1:51,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,122,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,OPEN,Bitbuckettest15,"* feat: add test01
+* feat: add test02
+* feat: add test03
+* feat: add test04
+* feat: add test05
+* feat: add test06
+* feat: add test07
+* feat: add test08
+* feat: add test09
+* feat: add test010
+* feat: add test011
+* feat: add test012
+* feat: add test013
+* feat: add test014
+* feat: add test015
+* feat: add test016
+* feat: add test017
+* feat: add test018
+* feat: add test019
+* feat: add test020
+* feat: add test021
+* feat: add test022
+* feat: add test023
+* feat: add test024
+* feat: add test025
+* feat: add test026
+* feat: add test027
+* feat: add test028
+* feat: add test029
+* feat: add test030
+* feat: add bitbucket
+* feat: add bitbucket
+* feat: add bitbuckettest12
+* feat: add bitbuckettest13
+* feat: add bitbuckettest14
+* feat: add bitbuckettest15
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/51,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T15:32:59.604+00:00,,,pullrequest,,,bitbuckettest15,master,c0e12b61e44b,5c2c9af70da2
+bitbucket:BitbucketPullRequest:1:52,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,127,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,OPEN,Bitbuckettest16,"* feat: add test01
+* feat: add test02
+* feat: add test03
+* feat: add test04
+* feat: add test05
+* feat: add test06
+* feat: add test07
+* feat: add test08
+* feat: add test09
+* feat: add test010
+* feat: add test011
+* feat: add test012
+* feat: add test013
+* feat: add test014
+* feat: add test015
+* feat: add test016
+* feat: add test017
+* feat: add test018
+* feat: add test019
+* feat: add test020
+* feat: add test021
+* feat: add test022
+* feat: add test023
+* feat: add test024
+* feat: add test025
+* feat: add test026
+* feat: add test027
+* feat: add test028
+* feat: add test029
+* feat: add test030
+* feat: add bitbucket
+* feat: add bitbucket
+* feat: add bitbuckettest12
+* feat: add bitbuckettest13
+* feat: add bitbuckettest14
+* feat: add bitbuckettest15
+* feat: add bitbuckettest16
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/52,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T15:33:13.806+00:00,,,pullrequest,,,bitbuckettest16,master,c0e12b61e44b,7c91d40ffaa3
+bitbucket:BitbucketPullRequest:1:53,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,125,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,OPEN,Bitbuckettest17,"* feat: add test01
+* feat: add test02
+* feat: add test03
+* feat: add test04
+* feat: add test05
+* feat: add test06
+* feat: add test07
+* feat: add test08
+* feat: add test09
+* feat: add test010
+* feat: add test011
+* feat: add test012
+* feat: add test013
+* feat: add test014
+* feat: add test015
+* feat: add test016
+* feat: add test017
+* feat: add test018
+* feat: add test019
+* feat: add test020
+* feat: add test021
+* feat: add test022
+* feat: add test023
+* feat: add test024
+* feat: add test025
+* feat: add test026
+* feat: add test027
+* feat: add test028
+* feat: add test029
+* feat: add test030
+* feat: add bitbucket
+* feat: add bitbucket
+* feat: add bitbuckettest12
+* feat: add bitbuckettest13
+* feat: add bitbuckettest14
+* feat: add bitbuckettest15
+* feat: add bitbuckettest16
+* feat: add bitbuckettest17
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/53,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T15:33:29.075+00:00,,,pullrequest,,,bitbuckettest17,master,c0e12b61e44b,765ab2594451
+bitbucket:BitbucketPullRequest:1:54,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,116,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,OPEN,Bitbuckettest19,"* feat: add test01
+* feat: add test02
+* feat: add test03
+* feat: add test04
+* feat: add test05
+* feat: add test06
+* feat: add test07
+* feat: add test08
+* feat: add test09
+* feat: add test010
+* feat: add test011
+* feat: add test012
+* feat: add test013
+* feat: add test014
+* feat: add test015
+* feat: add test016
+* feat: add test017
+* feat: add test018
+* feat: add test019
+* feat: add test020
+* feat: add test021
+* feat: add test022
+* feat: add test023
+* feat: add test024
+* feat: add test025
+* feat: add test026
+* feat: add test027
+* feat: add test028
+* feat: add test029
+* feat: add test030
+* feat: add bitbucket
+* feat: add bitbucket
+* feat: add bitbuckettest12
+* feat: add bitbuckettest13
+* feat: add bitbuckettest14
+* feat: add bitbuckettest15
+* feat: add bitbuckettest16
+* feat: add bitbuckettest17
+* feat: add bitbuckettest18
+* feat: add bitbuckettest19
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/54,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T15:33:43.689+00:00,,,pullrequest,,,bitbuckettest19,master,c0e12b61e44b,2493b9212fd4
+bitbucket:BitbucketPullRequest:1:55,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,126,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,OPEN,Bitbuckettest13,"* feat: add test01
+* feat: add test02
+* feat: add test03
+* feat: add test04
+* feat: add test05
+* feat: add test06
+* feat: add test07
+* feat: add test08
+* feat: add test09
+* feat: add test010
+* feat: add test011
+* feat: add test012
+* feat: add test013
+* feat: add test014
+* feat: add test015
+* feat: add test016
+* feat: add test017
+* feat: add test018
+* feat: add test019
+* feat: add test020
+* feat: add test021
+* feat: add test022
+* feat: add test023
+* feat: add test024
+* feat: add test025
+* feat: add test026
+* feat: add test027
+* feat: add test028
+* feat: add test029
+* feat: add test030
+* feat: add bitbucket
+* feat: add bitbucket
+* feat: add bitbuckettest12
+* feat: add bitbuckettest13
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/55,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T15:34:03.617+00:00,,,pullrequest,,,bitbuckettest13,master,c0e12b61e44b,9dd8c98115ca
+bitbucket:BitbucketPullRequest:1:56,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,124,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,OPEN,Bitbuckettest25,"* feat: add test01
+* feat: add test02
+* feat: add test03
+* feat: add test04
+* feat: add test05
+* feat: add test06
+* feat: add test07
+* feat: add test08
+* feat: add test09
+* feat: add test010
+* feat: add test011
+* feat: add test012
+* feat: add test013
+* feat: add test014
+* feat: add test015
+* feat: add test016
+* feat: add test017
+* feat: add test018
+* feat: add test019
+* feat: add test020
+* feat: add test021
+* feat: add test022
+* feat: add test023
+* feat: add test024
+* feat: add test025
+* feat: add test026
+* feat: add test027
+* feat: add test028
+* feat: add test029
+* feat: add test030
+* feat: add bitbucket
+* feat: add bitbucket
+* feat: add bitbuckettest12
+* feat: add bitbuckettest13
+* feat: add bitbuckettest14
+* feat: add bitbuckettest15
+* feat: add bitbuckettest16
+* feat: add bitbuckettest17
+* feat: add bitbuckettest18
+* feat: add bitbuckettest19
+* feat: add bitbuckettest20
+* feat: add bitbuckettest21
+* feat: add bitbuckettest22
+* feat: add bitbuckettest23
+* feat: add bitbuckettest24
+* feat: add bitbuckettest25
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/56,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T15:34:37.513+00:00,,,pullrequest,,,bitbuckettest25,master,c0e12b61e44b,d69faf8c90c4
+bitbucket:BitbucketPullRequest:1:57,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,123,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,OPEN,Bitbuckettest22,"* feat: add test01
+* feat: add test02
+* feat: add test03
+* feat: add test04
+* feat: add test05
+* feat: add test06
+* feat: add test07
+* feat: add test08
+* feat: add test09
+* feat: add test010
+* feat: add test011
+* feat: add test012
+* feat: add test013
+* feat: add test014
+* feat: add test015
+* feat: add test016
+* feat: add test017
+* feat: add test018
+* feat: add test019
+* feat: add test020
+* feat: add test021
+* feat: add test022
+* feat: add test023
+* feat: add test024
+* feat: add test025
+* feat: add test026
+* feat: add test027
+* feat: add test028
+* feat: add test029
+* feat: add test030
+* feat: add bitbucket
+* feat: add bitbucket
+* feat: add bitbuckettest12
+* feat: add bitbuckettest13
+* feat: add bitbuckettest14
+* feat: add bitbuckettest15
+* feat: add bitbuckettest16
+* feat: add bitbuckettest17
+* feat: add bitbuckettest18
+* feat: add bitbuckettest19
+* feat: add bitbuckettest20
+* feat: add bitbuckettest21
+* feat: add bitbuckettest22
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/57,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T15:34:55.738+00:00,,,pullrequest,,,bitbuckettest22,master,c0e12b61e44b,2c23b3babf27
+bitbucket:BitbucketPullRequest:1:6,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,165,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,MERGED,Test003,"* feat: add git.sh
+* feat: add test001
+* feat: test002
+* feat: add test003
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/6,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T14:54:20.308+00:00,,,pullrequest,,983edbf06740,test003,master,0bd83caaf9c7,494b97e9d0b2
+bitbucket:BitbucketPullRequest:1:7,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,164,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,MERGED,Test004,"* feat: add git.sh
+* feat: add test001
+* feat: test002
+* feat: add test003
+* feat: add test004
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/7,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T14:54:40.056+00:00,,,pullrequest,,983edbf06740,test004,master,0bd83caaf9c7,db921ce6537c
+bitbucket:BitbucketPullRequest:1:8,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,163,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,MERGED,Test005,"* feat: add git.sh
+* feat: add test001
+* feat: test002
+* feat: add test003
+* feat: add test004
+* feat: add test005
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/8,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T14:54:56.506+00:00,,,pullrequest,,983edbf06740,test005,master,0bd83caaf9c7,8a224cc0eaae
+bitbucket:BitbucketPullRequest:1:9,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}",_raw_bitbucket_api_pull_requests,162,,bitbucket:BitbucketRepo:1:panjf2000/ants,bitbucket:BitbucketRepo:1:panjf2000/ants,MERGED,Test006,"* feat: add git.sh
+* feat: add test001
+* feat: test002
+* feat: add test003
+* feat: add test004
+* feat: add test005
+* feat: add test006
+
+‌",https://bitbucket.org/panjf2000/ants/pull-requests/9,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,,0,2022-08-12T14:55:13.971+00:00,,,pullrequest,,983edbf06740,test006,master,0bd83caaf9c7,8a8f44b997a8
diff --git a/plugins/bitbucket/e2e/snapshot_tables/repos.csv b/plugins/bitbucket/e2e/snapshot_tables/repos.csv
new file mode 100644
index 00000000..694cfb95
--- /dev/null
+++ b/plugins/bitbucket/e2e/snapshot_tables/repos.csv
@@ -0,0 +1,2 @@
+id,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark,name,url,description,owner_id,language,forked_from,created_date,updated_date,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,,,,,2022-06-17T03:27:18.865+00:00,2022-08-12T15:40:12.409+00:00,0
diff --git a/plugins/bitbucket/impl/impl.go b/plugins/bitbucket/impl/impl.go
new file mode 100644
index 00000000..1c702576
--- /dev/null
+++ b/plugins/bitbucket/impl/impl.go
@@ -0,0 +1,125 @@
+/*
+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 impl
+
+import (
+ "fmt"
+ "github.com/apache/incubator-devlake/migration"
+ "github.com/apache/incubator-devlake/plugins/bitbucket/api"
+ "github.com/apache/incubator-devlake/plugins/bitbucket/models"
+ "github.com/apache/incubator-devlake/plugins/bitbucket/models/migrationscripts"
+ "github.com/apache/incubator-devlake/plugins/bitbucket/tasks"
+ "github.com/apache/incubator-devlake/plugins/core"
+ "github.com/apache/incubator-devlake/plugins/helper"
+ "github.com/spf13/viper"
+ "gorm.io/gorm"
+)
+
+var _ core.PluginMeta = (*Bitbucket)(nil)
+var _ core.PluginInit = (*Bitbucket)(nil)
+var _ core.PluginTask = (*Bitbucket)(nil)
+var _ core.PluginApi = (*Bitbucket)(nil)
+var _ core.Migratable = (*Bitbucket)(nil)
+var _ core.PluginBlueprintV100 = (*Bitbucket)(nil)
+
+type Bitbucket string
+
+func (plugin Bitbucket) Init(config *viper.Viper, logger core.Logger, db *gorm.DB) error {
+ api.Init(config, logger, db)
+ return nil
+}
+
+func (plugin Bitbucket) Description() string {
+ return "To collect and enrich data from Bitbucket"
+}
+
+func (plugin Bitbucket) SubTaskMetas() []core.SubTaskMeta {
+ return []core.SubTaskMeta{
+ tasks.CollectApiRepoMeta,
+ tasks.ExtractApiRepoMeta,
+ tasks.CollectApiPullRequestsMeta,
+ tasks.ExtractApiPullRequestsMeta,
+ tasks.CollectApiIssuesMeta,
+ tasks.ExtractApiIssuesMeta,
+ tasks.CollectApiPrCommentsMeta,
+ tasks.ExtractApiPrCommentsMeta,
+ tasks.CollectApiIssueCommentsMeta,
+ tasks.ExtractApiIssueCommentsMeta,
+ tasks.ConvertRepoMeta,
+ tasks.ConvertAccountsMeta,
+ tasks.ConvertPullRequestsMeta,
+ tasks.ConvertPrCommentsMeta,
+ tasks.ConvertIssuesMeta,
+ tasks.ConvertIssueCommentsMeta,
+ }
+}
+
+func (plugin Bitbucket) PrepareTaskData(taskCtx core.TaskContext, options map[string]interface{}) (interface{}, error) {
+ op, err := tasks.DecodeAndValidateTaskOptions(options)
+ if err != nil {
+ return nil, err
+ }
+ connectionHelper := helper.NewConnectionHelper(
+ taskCtx,
+ nil,
+ )
+ connection := &models.BitbucketConnection{}
+ err = connectionHelper.FirstById(connection, op.ConnectionId)
+ if err != nil {
+ return nil, fmt.Errorf("unable to get bitbucket connection by the given connection ID: %v", err)
+ }
+
+ apiClient, err := tasks.CreateApiClient(taskCtx, connection)
+ if err != nil {
+ return nil, fmt.Errorf("unable to get bitbucket API client instance: %v", err)
+ }
+
+ return &tasks.BitbucketTaskData{
+ Options: op,
+ ApiClient: apiClient,
+ }, nil
+}
+
+func (plugin Bitbucket) RootPkgPath() string {
+ return "github.com/apache/incubator-devlake/plugins/bitbucket"
+}
+
+func (plugin Bitbucket) MigrationScripts() []migration.Script {
+ return migrationscripts.All()
+}
+
+func (plugin Bitbucket) MakePipelinePlan(connectionId uint64, scope []*core.BlueprintScopeV100) (core.PipelinePlan, error) {
+ return api.MakePipelinePlan(plugin.SubTaskMetas(), connectionId, scope)
+}
+
+func (plugin Bitbucket) ApiResources() map[string]map[string]core.ApiResourceHandler {
+ return map[string]map[string]core.ApiResourceHandler{
+ "test": {
+ "POST": api.TestConnection,
+ },
+ "connections": {
+ "POST": api.PostConnections,
+ "GET": api.ListConnections,
+ },
+ "connections/:connectionId": {
+ "PATCH": api.PatchConnection,
+ "DELETE": api.DeleteConnection,
+ "GET": api.GetConnection,
+ },
+ }
+}
diff --git a/plugins/bitbucket/models/account.go b/plugins/bitbucket/models/account.go
new file mode 100644
index 00000000..83cbaef3
--- /dev/null
+++ b/plugins/bitbucket/models/account.go
@@ -0,0 +1,39 @@
+/*
+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 models
+
+import (
+ "github.com/apache/incubator-devlake/models/common"
+)
+
+type BitbucketAccount struct {
+ ConnectionId uint64 `gorm:"primaryKey"`
+ AccountId string `gorm:"primaryKey;type:varchar(255)"`
+ UserName string `gorm:"type:varchar(255)"`
+ AccountStatus string `gorm:"type:varchar(255)"`
+ DisplayName string `gorm:"type:varchar(255)"`
+ AvatarUrl string `gorm:"type:varchar(255)"`
+ HtmlUrl string `gorm:"type:varchar(255)"`
+ Uuid string `gorm:"type:varchar(255)"`
+ Has2FaEnabled bool
+ common.NoPKModel
+}
+
+func (BitbucketAccount) TableName() string {
+ return "_tool_bitbucket_accounts"
+}
diff --git a/plugins/bitbucket/models/commit.go b/plugins/bitbucket/models/commit.go
new file mode 100644
index 00000000..4f61c219
--- /dev/null
+++ b/plugins/bitbucket/models/commit.go
@@ -0,0 +1,42 @@
+/*
+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 models
+
+import (
+ "time"
+
+ "github.com/apache/incubator-devlake/models/common"
+)
+
+type BitbucketCommit struct {
+ Sha string `gorm:"primaryKey;type:varchar(40)"`
+ AuthorId string `gorm:"type:varchar(255)"`
+ AuthorName string `gorm:"type:varchar(255)"`
+ AuthorEmail string `gorm:"type:varchar(255)"`
+ AuthoredDate time.Time
+ CommittedDate time.Time
+ Message string
+ Url string `gorm:"type:varchar(255)"`
+ Additions int `gorm:"comment:Added lines of code"`
+ Deletions int `gorm:"comment:Deleted lines of code"`
+ common.NoPKModel
+}
+
+func (BitbucketCommit) TableName() string {
+ return "_tool_bitbucket_commits"
+}
diff --git a/plugins/bitbucket/models/connection.go b/plugins/bitbucket/models/connection.go
new file mode 100644
index 00000000..70266995
--- /dev/null
+++ b/plugins/bitbucket/models/connection.go
@@ -0,0 +1,69 @@
+/*
+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 models
+
+import (
+ "github.com/apache/incubator-devlake/plugins/helper"
+)
+
+type EpicResponse struct {
+ Id int
+ Title string
+ Value string
+}
+
+type TestConnectionRequest struct {
+ Endpoint string `json:"endpoint"`
+ Proxy string `json:"proxy"`
+ helper.BasicAuth `mapstructure:",squash"`
+}
+
+type BoardResponse struct {
+ Id int
+ Title string
+ Value string
+}
+type TransformationRules struct {
+ PrType string `mapstructure:"prType" json:"prType"`
+ PrComponent string `mapstructure:"prComponent" json:"prComponent"`
+ PrBodyClosePattern string `mapstructure:"prBodyClosePattern" json:"prBodyClosePattern"`
+ IssueSeverity string `mapstructure:"issueSeverity" json:"issueSeverity"`
+ IssuePriority string `mapstructure:"issuePriority" json:"issuePriority"`
+ IssueComponent string `mapstructure:"issueComponent" json:"issueComponent"`
+ IssueTypeBug string `mapstructure:"issueTypeBug" json:"issueTypeBug"`
+ IssueTypeIncident string `mapstructure:"issueTypeIncident" json:"issueTypeIncident"`
+ IssueTypeRequirement string `mapstructure:"issueTypeRequirement" json:"issueTypeRequirement"`
+}
+
+type ApiUserResponse struct {
+ Username string `json:"username"`
+ DisplayName string `json:"display_name"`
+ AccountId int `json:"account_id"`
+ Uuid string `json:"uuid"`
+ AccountStatus string `json:"account_status"`
+}
+
+type BitbucketConnection struct {
+ helper.RestConnection `mapstructure:",squash"`
+ helper.BasicAuth `mapstructure:",squash"`
+ RemotelinkCommitShaPattern string `gorm:"type:varchar(255);comment='golang regexp, the first group will be recognized as commit sha, ref https://github.com/google/re2/wiki/Syntax'" json:"remotelinkCommitShaPattern"`
+}
+
+func (BitbucketConnection) TableName() string {
+ return "_tool_bitbucket_connections"
+}
diff --git a/plugins/bitbucket/models/issue.go b/plugins/bitbucket/models/issue.go
new file mode 100644
index 00000000..676c75cd
--- /dev/null
+++ b/plugins/bitbucket/models/issue.go
@@ -0,0 +1,52 @@
+/*
+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 models
+
+import (
+ "github.com/apache/incubator-devlake/models/common"
+ "time"
+)
+
+type BitbucketIssue struct {
+ ConnectionId uint64 `gorm:"primaryKey"`
+ BitbucketId int `gorm:"primaryKey"`
+ RepoId string `gorm:"index;type:varchar(255)"`
+ Number int `gorm:"index;comment:Used in API requests ex. api/issues/<THIS_NUMBER>"`
+ State string `gorm:"type:varchar(255)"`
+ Title string `gorm:"type:varchar(255)"`
+ Body string
+ Priority string `gorm:"type:varchar(255)"`
+ Type string `gorm:"type:varchar(100)"`
+ AuthorId string `gorm:"type:varchar(255)"`
+ AuthorName string `gorm:"type:varchar(255)"`
+ AssigneeId string `gorm:"type:varchar(255)"`
+ AssigneeName string `gorm:"type:varchar(255)"`
+ MilestoneId int `gorm:"index"`
+ LeadTimeMinutes uint
+ Url string `gorm:"type:varchar(255)"`
+ ClosedAt *time.Time
+ BitbucketCreatedAt time.Time
+ BitbucketUpdatedAt time.Time `gorm:"index"`
+ Severity string `gorm:"type:varchar(255)"`
+ Component string `gorm:"type:varchar(255)"`
+ common.NoPKModel
+}
+
+func (BitbucketIssue) TableName() string {
+ return "_tool_bitbucket_issues"
+}
diff --git a/plugins/bitbucket/models/issue_comment.go b/plugins/bitbucket/models/issue_comment.go
new file mode 100644
index 00000000..99c93e67
--- /dev/null
+++ b/plugins/bitbucket/models/issue_comment.go
@@ -0,0 +1,40 @@
+/*
+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 models
+
+import (
+ "github.com/apache/incubator-devlake/models/common"
+ "time"
+)
+
+type BitbucketIssueComment struct {
+ ConnectionId uint64 `gorm:"primaryKey"`
+ BitbucketId int `gorm:"primaryKey"`
+ IssueId int `gorm:"index;comment:References the Issue"`
+ AuthorName string `gorm:"type:varchar(255)"`
+ AuthorId string `gorm:"type:varchar(255)"`
+ BitbucketCreatedAt time.Time
+ BitbucketUpdatedAt *time.Time
+ Type string
+ Body string
+ common.NoPKModel
+}
+
+func (BitbucketIssueComment) TableName() string {
+ return "_tool_bitbucket_issue_comments"
+}
diff --git a/plugins/bitbucket/models/issue_event.go b/plugins/bitbucket/models/issue_event.go
new file mode 100644
index 00000000..112baa37
--- /dev/null
+++ b/plugins/bitbucket/models/issue_event.go
@@ -0,0 +1,37 @@
+/*
+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 models
+
+import (
+ "github.com/apache/incubator-devlake/models/common"
+ "time"
+)
+
+type BitbucketIssueEvent struct {
+ ConnectionId uint64 `gorm:"primaryKey"`
+ BitbucketId int `gorm:"primaryKey"`
+ IssueId int `gorm:"index;comment:References the Issue"`
+ Type string `gorm:"type:varchar(255);comment:Events that can occur to an issue, ex. assigned, closed, labeled, etc."`
+ AuthorUsername string `gorm:"type:varchar(255)"`
+ BitbucketCreatedAt time.Time `gorm:"index"`
+ common.NoPKModel
+}
+
+func (BitbucketIssueEvent) TableName() string {
+ return "_tool_bitbucket_issue_events"
+}
diff --git a/plugins/bitbucket/models/issue_label.go b/plugins/bitbucket/models/issue_label.go
new file mode 100644
index 00000000..1533579e
--- /dev/null
+++ b/plugins/bitbucket/models/issue_label.go
@@ -0,0 +1,36 @@
+/*
+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 models
+
+import (
+ "github.com/apache/incubator-devlake/models/common"
+)
+
+// Please note that Issue Labels can also apply to Pull Requests.
+// Pull Requests are considered Issues in Bitbucket.
+
+type BitbucketIssueLabel struct {
+ ConnectionId uint64 `gorm:"primaryKey"`
+ IssueId int `gorm:"primaryKey;autoIncrement:false"`
+ LabelName string `gorm:"primaryKey;type:varchar(255)"`
+ common.NoPKModel
+}
+
+func (BitbucketIssueLabel) TableName() string {
+ return "_tool_bitbucket_issue_labels"
+}
diff --git a/plugins/bitbucket/models/migrationscripts/20220803_add_init_tables.go b/plugins/bitbucket/models/migrationscripts/20220803_add_init_tables.go
new file mode 100644
index 00000000..039542c7
--- /dev/null
+++ b/plugins/bitbucket/models/migrationscripts/20220803_add_init_tables.go
@@ -0,0 +1,101 @@
+/*
+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 (
+ "context"
+ "github.com/apache/incubator-devlake/config"
+ "github.com/apache/incubator-devlake/plugins/bitbucket/models/migrationscripts/archived"
+ "gorm.io/gorm"
+ "gorm.io/gorm/clause"
+)
+
+type addInitTables struct{}
+
+func (*addInitTables) Up(ctx context.Context, db *gorm.DB) error {
+ err := db.Migrator().DropTable(
+ &archived.BitbucketUser{},
+ &archived.BitbucketRepo{},
+ &archived.BitbucketRepoCommit{},
+ //&archived.BitbucketConnection{},
+ &archived.BitbucketAccount{},
+ &archived.BitbucketCommit{},
+ &archived.BitbucketPullRequest{},
+ &archived.BitbucketIssue{},
+ &archived.BitbucketPrComment{},
+ &archived.BitbucketIssueComment{},
+ )
+
+ if err != nil {
+ return err
+ }
+
+ err = db.Migrator().AutoMigrate(
+ &archived.BitbucketUser{},
+ &archived.BitbucketRepo{},
+ &archived.BitbucketRepoCommit{},
+ &archived.BitbucketConnection{},
+ &archived.BitbucketAccount{},
+ &archived.BitbucketCommit{},
+ &archived.BitbucketPullRequest{},
+ &archived.BitbucketIssue{},
+ &archived.BitbucketPrComment{},
+ &archived.BitbucketIssueComment{},
+ )
+
+ if err != nil {
+ return err
+ }
+
+ v := config.GetConfig()
+ encKey := v.GetString("ENCODE_KEY")
+ endPoint := v.GetString("BITBUCKET_ENDPOINT")
+ bitbucketUsername := v.GetString("BITBUCKET_AUTH_USERNAME")
+ bitbucketAppPassword := v.GetString("BITBUCKET_AUTH_PASSWORD")
+ if encKey == "" || endPoint == "" || bitbucketUsername == "" || bitbucketAppPassword == "" {
+ return nil
+ } else {
+ conn := &archived.BitbucketConnection{}
+ conn.Name = "init bitbucket connection"
+ conn.ID = 1
+ conn.Endpoint = endPoint
+ conn.BasicAuth.Username = bitbucketUsername
+ conn.BasicAuth.Password = bitbucketAppPassword
+ if err != nil {
+ return err
+ }
+ conn.Proxy = v.GetString("BITBUCKET_PROXY")
+ conn.RateLimitPerHour = v.GetInt("BITBUCKET_API_REQUESTS_PER_HOUR")
+
+ err = db.Clauses(clause.OnConflict{DoNothing: true}).Create(conn).Error
+
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (*addInitTables) Version() uint64 {
+ return 20220803220824
+}
+
+func (*addInitTables) Name() string {
+ return "Bitbucket init schema 20220803"
+}
diff --git a/plugins/bitbucket/models/migrationscripts/archived/account.go b/plugins/bitbucket/models/migrationscripts/archived/account.go
new file mode 100644
index 00000000..5d4cfa40
--- /dev/null
+++ b/plugins/bitbucket/models/migrationscripts/archived/account.go
@@ -0,0 +1,39 @@
+/*
+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 archived
+
+import (
+ "github.com/apache/incubator-devlake/models/migrationscripts/archived"
+)
+
+type BitbucketAccount struct {
+ ConnectionId uint64 `gorm:"primaryKey"`
+ UserName string `gorm:"type:varchar(255)"`
+ AccountId string `gorm:"primaryKey;type:varchar(255)"`
+ AccountStatus string `gorm:"type:varchar(255)"`
+ DisplayName string `gorm:"type:varchar(255)"`
+ AvatarUrl string `gorm:"type:varchar(255)"`
+ HtmlUrl string `gorm:"type:varchar(255)"`
+ Uuid string `gorm:"type:varchar(255)"`
+ Has2FaEnabled bool
+ archived.NoPKModel
+}
+
+func (BitbucketAccount) TableName() string {
+ return "_tool_bitbucket_accounts"
+}
diff --git a/plugins/bitbucket/models/migrationscripts/archived/commit.go b/plugins/bitbucket/models/migrationscripts/archived/commit.go
new file mode 100644
index 00000000..710f432e
--- /dev/null
+++ b/plugins/bitbucket/models/migrationscripts/archived/commit.go
@@ -0,0 +1,42 @@
+/*
+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 archived
+
+import (
+ "time"
+
+ "github.com/apache/incubator-devlake/models/migrationscripts/archived"
+)
+
+type BitbucketCommit struct {
+ Sha string `gorm:"primaryKey;type:varchar(40)"`
+ AuthorId string `gorm:"type:varchar(255)"`
+ AuthorName string `gorm:"type:varchar(255)"`
+ AuthorEmail string `gorm:"type:varchar(255)"`
+ AuthoredDate time.Time
+ CommittedDate time.Time
+ Message string
+ Url string `gorm:"type:varchar(255)"`
+ Additions int `gorm:"comment:Added lines of code"`
+ Deletions int `gorm:"comment:Deleted lines of code"`
+ archived.NoPKModel
+}
+
+func (BitbucketCommit) TableName() string {
+ return "_tool_bitbucket_commits"
+}
diff --git a/plugins/bitbucket/models/migrationscripts/archived/connection.go b/plugins/bitbucket/models/migrationscripts/archived/connection.go
new file mode 100644
index 00000000..dae6374d
--- /dev/null
+++ b/plugins/bitbucket/models/migrationscripts/archived/connection.go
@@ -0,0 +1,69 @@
+/*
+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 archived
+
+import (
+ "github.com/apache/incubator-devlake/plugins/helper"
+)
+
+type EpicResponse struct {
+ Id int
+ Title string
+ Value string
+}
+
+type TestConnectionRequest struct {
+ Endpoint string `json:"endpoint"`
+ Proxy string `json:"proxy"`
+ helper.BasicAuth `mapstructure:",squash"`
+}
+
+type BoardResponse struct {
+ Id int
+ Title string
+ Value string
+}
+type TransformationRules struct {
+ PrType string `mapstructure:"prType" json:"prType"`
+ PrComponent string `mapstructure:"prComponent" json:"prComponent"`
+ PrBodyClosePattern string `mapstructure:"prBodyClosePattern" json:"prBodyClosePattern"`
+ IssueSeverity string `mapstructure:"issueSeverity" json:"issueSeverity"`
+ IssuePriority string `mapstructure:"issuePriority" json:"issuePriority"`
+ IssueComponent string `mapstructure:"issueComponent" json:"issueComponent"`
+ IssueTypeBug string `mapstructure:"issueTypeBug" json:"issueTypeBug"`
+ IssueTypeIncident string `mapstructure:"issueTypeIncident" json:"issueTypeIncident"`
+ IssueTypeRequirement string `mapstructure:"issueTypeRequirement" json:"issueTypeRequirement"`
+}
+
+type ApiUserResponse struct {
+ Username string `json:"username"`
+ DisplayName string `json:"display_name"`
+ AccountId int `json:"account_id"`
+ Uuid string `json:"uuid"`
+ AccountStatus string `json:"account_status"`
+}
+
+type BitbucketConnection struct {
+ helper.RestConnection `mapstructure:",squash"`
+ helper.BasicAuth `mapstructure:",squash"`
+ RemotelinkCommitShaPattern string `gorm:"type:varchar(255);comment='golang regexp, the first group will be recognized as commit sha, ref https://github.com/google/re2/wiki/Syntax'" json:"remotelinkCommitShaPattern"`
+}
+
+func (BitbucketConnection) TableName() string {
+ return "_tool_bitbucket_connections"
+}
diff --git a/plugins/bitbucket/models/migrationscripts/archived/issue.go b/plugins/bitbucket/models/migrationscripts/archived/issue.go
new file mode 100644
index 00000000..b24c0bdf
--- /dev/null
+++ b/plugins/bitbucket/models/migrationscripts/archived/issue.go
@@ -0,0 +1,53 @@
+/*
+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 archived
+
+import (
+ "github.com/apache/incubator-devlake/models/migrationscripts/archived"
+ "time"
+)
+
+type BitbucketIssue struct {
+ ConnectionId uint64 `gorm:"primaryKey"`
+ BitbucketId int `gorm:"primaryKey"`
+ RepoId string `gorm:"index;type:varchar(255)"`
+ Number int `gorm:"index;comment:Used in API requests ex. api/issues/<THIS_NUMBER>"`
+ State string `gorm:"type:varchar(255)"`
+ Title string `gorm:"type:varchar(255)"`
+ Body string
+ Priority string `gorm:"type:varchar(255)"`
+ Type string `gorm:"type:varchar(100)"`
+ Status string `gorm:"type:varchar(255)"`
+ AuthorId string `gorm:"type:varchar(255)"`
+ AuthorName string `gorm:"type:varchar(255)"`
+ AssigneeId string `gorm:"type:varchar(255)"`
+ AssigneeName string `gorm:"type:varchar(255)"`
+ MilestoneId int `gorm:"index"`
+ LeadTimeMinutes uint
+ Url string `gorm:"type:varchar(255)"`
+ ClosedAt *time.Time
+ BitbucketCreatedAt time.Time
+ BitbucketUpdatedAt time.Time `gorm:"index"`
+ Severity string `gorm:"type:varchar(255)"`
+ Component string `gorm:"type:varchar(255)"`
+ archived.NoPKModel
+}
+
+func (BitbucketIssue) TableName() string {
+ return "_tool_bitbucket_issues"
+}
diff --git a/plugins/bitbucket/models/migrationscripts/archived/issue_comment.go b/plugins/bitbucket/models/migrationscripts/archived/issue_comment.go
new file mode 100644
index 00000000..0112cf74
--- /dev/null
+++ b/plugins/bitbucket/models/migrationscripts/archived/issue_comment.go
@@ -0,0 +1,40 @@
+/*
+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 archived
+
+import (
+ "github.com/apache/incubator-devlake/models/migrationscripts/archived"
+ "time"
+)
+
+type BitbucketIssueComment struct {
+ ConnectionId uint64 `gorm:"primaryKey"`
+ BitbucketId int `gorm:"primaryKey"`
+ IssueId int `gorm:"index;comment:References the Issue"`
+ AuthorName string `gorm:"type:varchar(255)"`
+ AuthorId string `gorm:"type:varchar(255)"`
+ BitbucketCreatedAt time.Time
+ BitbucketUpdatedAt *time.Time
+ Type string
+ Body string
+ archived.NoPKModel
+}
+
+func (BitbucketIssueComment) TableName() string {
+ return "_tool_bitbucket_issue_comments"
+}
diff --git a/plugins/bitbucket/models/migrationscripts/archived/issue_event.go b/plugins/bitbucket/models/migrationscripts/archived/issue_event.go
new file mode 100644
index 00000000..249a62ac
--- /dev/null
+++ b/plugins/bitbucket/models/migrationscripts/archived/issue_event.go
@@ -0,0 +1,37 @@
+/*
+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 archived
+
+import (
+ "github.com/apache/incubator-devlake/models/migrationscripts/archived"
+ "time"
+)
+
+type BitbucketIssueEvent struct {
+ ConnectionId uint64 `gorm:"primaryKey"`
+ BitbucketId int `gorm:"primaryKey"`
+ IssueId int `gorm:"index;comment:References the Issue"`
+ Type string `gorm:"type:varchar(255);comment:Events that can occur to an issue, ex. assigned, closed, labeled, etc."`
+ AuthorUsername string `gorm:"type:varchar(255)"`
+ BitbucketCreatedAt time.Time `gorm:"index"`
+ archived.NoPKModel
+}
+
+func (BitbucketIssueEvent) TableName() string {
+ return "_tool_bitbucket_issue_events"
+}
diff --git a/plugins/bitbucket/models/migrationscripts/archived/issue_label.go b/plugins/bitbucket/models/migrationscripts/archived/issue_label.go
new file mode 100644
index 00000000..518d0051
--- /dev/null
+++ b/plugins/bitbucket/models/migrationscripts/archived/issue_label.go
@@ -0,0 +1,36 @@
+/*
+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 archived
+
+import (
+ "github.com/apache/incubator-devlake/models/migrationscripts/archived"
+)
+
+// Please note that Issue Labels can also apply to Pull Requests.
+// Pull Requests are considered Issues in Bitbucket.
+
+type BitbucketIssueLabel struct {
+ ConnectionId uint64 `gorm:"primaryKey"`
+ IssueId int `gorm:"primaryKey;autoIncrement:false"`
+ LabelName string `gorm:"primaryKey;type:varchar(255)"`
+ archived.NoPKModel
+}
+
+func (BitbucketIssueLabel) TableName() string {
+ return "_tool_bitbucket_issue_labels"
+}
diff --git a/plugins/bitbucket/models/migrationscripts/archived/pr.go b/plugins/bitbucket/models/migrationscripts/archived/pr.go
new file mode 100644
index 00000000..42796cf8
--- /dev/null
+++ b/plugins/bitbucket/models/migrationscripts/archived/pr.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 archived
+
+import (
+ "github.com/apache/incubator-devlake/models/migrationscripts/archived"
+ "time"
+)
+
+type BitbucketPullRequest struct {
+ ConnectionId uint64 `gorm:"primaryKey"`
+ BitbucketId int `gorm:"primaryKey"`
+ RepoId string `gorm:"index;type:varchar(255)"`
+ Number int `gorm:"index"` // This number is used in GET requests to the API associated to reviewers / comments / etc.
+ BaseRepoId string
+ HeadRepoId string
+ State string `gorm:"type:varchar(255)"`
+ Title string
+ Description string
+ BitbucketCreatedAt time.Time
+ BitbucketUpdatedAt time.Time `gorm:"index"`
+ ClosedAt *time.Time
+ CommentCount int
+ Commits int
+ MergedAt *time.Time
+ Body string
+ Type string `gorm:"type:varchar(255)"`
+ Component string `gorm:"type:varchar(255)"`
+ MergeCommitSha string `gorm:"type:varchar(40)"`
+ HeadRef string `gorm:"type:varchar(255)"`
+ BaseRef string `gorm:"type:varchar(255)"`
+ BaseCommitSha string `gorm:"type:varchar(255)"`
+ HeadCommitSha string `gorm:"type:varchar(255)"`
+ Url string `gorm:"type:varchar(255)"`
+ AuthorName string `gorm:"type:varchar(255)"`
+ AuthorId string `gorm:"type:varchar(255)"`
+ archived.NoPKModel
+}
+
+func (BitbucketPullRequest) TableName() string {
+ return "_tool_bitbucket_pull_requests"
+}
diff --git a/plugins/bitbucket/models/migrationscripts/archived/pr_comment.go b/plugins/bitbucket/models/migrationscripts/archived/pr_comment.go
new file mode 100644
index 00000000..47d89134
--- /dev/null
+++ b/plugins/bitbucket/models/migrationscripts/archived/pr_comment.go
@@ -0,0 +1,41 @@
+/*
+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 archived
+
+import (
+ "time"
+
+ "github.com/apache/incubator-devlake/models/migrationscripts/archived"
+)
+
+type BitbucketPrComment struct {
+ ConnectionId uint64 `gorm:"primaryKey"`
+ BitbucketId int `gorm:"primaryKey"`
+ PullRequestId int `gorm:"index"`
+ AuthorId string `gorm:"type:varchar(255)"`
+ AuthorName string `gorm:"type:varchar(255)"`
+ BitbucketCreatedAt time.Time
+ BitbucketUpdatedAt *time.Time
+ Type string `gorm:"comment:if type=null, it is normal comment,if type=diffNote,it is diff comment"`
+ Body string
+ archived.NoPKModel
+}
+
+func (BitbucketPrComment) TableName() string {
+ return "_tool_bitbucket_pull_request_comments"
+}
diff --git a/plugins/bitbucket/models/migrationscripts/archived/repo.go b/plugins/bitbucket/models/migrationscripts/archived/repo.go
new file mode 100644
index 00000000..9b13e2d7
--- /dev/null
+++ b/plugins/bitbucket/models/migrationscripts/archived/repo.go
@@ -0,0 +1,40 @@
+/*
+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 archived
+
+import (
+ "github.com/apache/incubator-devlake/models/migrationscripts/archived"
+ "time"
+)
+
+type BitbucketRepo struct {
+ ConnectionId uint64 `gorm:"primaryKey"`
+ BitbucketId string `gorm:"primaryKey;type:varchar(255)"`
+ Name string `gorm:"type:varchar(255)"`
+ HTMLUrl string `gorm:"type:varchar(255)"`
+ Description string
+ OwnerId string `json:"ownerId"`
+ Language string `json:"language" gorm:"type:varchar(255)"`
+ CreatedDate time.Time `json:"createdDate"`
+ UpdatedDate *time.Time `json:"updatedDate"`
+ archived.NoPKModel
+}
+
+func (BitbucketRepo) TableName() string {
+ return "_tool_bitbucket_repos"
+}
diff --git a/plugins/bitbucket/models/migrationscripts/archived/repo_commit.go b/plugins/bitbucket/models/migrationscripts/archived/repo_commit.go
new file mode 100644
index 00000000..956e0e64
--- /dev/null
+++ b/plugins/bitbucket/models/migrationscripts/archived/repo_commit.go
@@ -0,0 +1,31 @@
+/*
+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 archived
+
+import "github.com/apache/incubator-devlake/models/migrationscripts/archived"
+
+type BitbucketRepoCommit struct {
+ ConnectionId uint64 `gorm:"primaryKey"`
+ RepoId string `gorm:"primaryKey;type:varchar(255)"`
+ CommitSha string `gorm:"primaryKey;type:varchar(40)"`
+ archived.NoPKModel
+}
+
+func (BitbucketRepoCommit) TableName() string {
+ return "_tool_bitbucket_repo_commits"
+}
diff --git a/plugins/bitbucket/models/migrationscripts/archived/user.go b/plugins/bitbucket/models/migrationscripts/archived/user.go
new file mode 100644
index 00000000..bf8e3125
--- /dev/null
+++ b/plugins/bitbucket/models/migrationscripts/archived/user.go
@@ -0,0 +1,33 @@
+/*
+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 archived
+
+import "github.com/apache/incubator-devlake/models/migrationscripts/archived"
+
+type BitbucketUser struct {
+ ConnectionId uint64 `gorm:"primaryKey"`
+ UserName string `json:"username"`
+ DisplayName string `json:"display_name"`
+ AccountId string `json:"account_id"`
+
+ archived.NoPKModel
+}
+
+func (BitbucketUser) TableName() string {
+ return "_tool_bitbucket_users"
+}
diff --git a/plugins/bitbucket/models/migrationscripts/register.go b/plugins/bitbucket/models/migrationscripts/register.go
new file mode 100644
index 00000000..c1365f7d
--- /dev/null
+++ b/plugins/bitbucket/models/migrationscripts/register.go
@@ -0,0 +1,29 @@
+/*
+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 (
+ "github.com/apache/incubator-devlake/migration"
+)
+
+// All return all the migration scripts
+func All() []migration.Script {
+ return []migration.Script{
+ new(addInitTables),
+ }
+}
diff --git a/plugins/bitbucket/models/pr.go b/plugins/bitbucket/models/pr.go
new file mode 100644
index 00000000..c30dcaa1
--- /dev/null
+++ b/plugins/bitbucket/models/pr.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 models
+
+import (
+ "github.com/apache/incubator-devlake/models/common"
+ "time"
+)
+
+type BitbucketPullRequest struct {
+ ConnectionId uint64 `gorm:"primaryKey"`
+ BitbucketId int `gorm:"primaryKey"`
+ RepoId string `gorm:"index;type:varchar(255)"`
+ Number int `gorm:"index"` // This number is used in GET requests to the API associated to reviewers / comments / etc.
+ BaseRepoId string
+ HeadRepoId string
+ State string `gorm:"type:varchar(255)"`
+ Title string
+ Description string
+ BitbucketCreatedAt time.Time
+ BitbucketUpdatedAt time.Time `gorm:"index"`
+ ClosedAt *time.Time
+ CommentCount int
+ Commits int
+ MergedAt *time.Time
+ Body string
+ Type string `gorm:"type:varchar(255)"`
+ Component string `gorm:"type:varchar(255)"`
+ MergeCommitSha string `gorm:"type:varchar(40)"`
+ HeadRef string `gorm:"type:varchar(255)"`
+ BaseRef string `gorm:"type:varchar(255)"`
+ BaseCommitSha string `gorm:"type:varchar(255)"`
+ HeadCommitSha string `gorm:"type:varchar(255)"`
+ Url string `gorm:"type:varchar(255)"`
+ AuthorName string `gorm:"type:varchar(255)"`
+ AuthorId string `gorm:"type:varchar(255)"`
+ common.NoPKModel
+}
+
+func (BitbucketPullRequest) TableName() string {
+ return "_tool_bitbucket_pull_requests"
+}
diff --git a/plugins/bitbucket/models/pr_comment.go b/plugins/bitbucket/models/pr_comment.go
new file mode 100644
index 00000000..e783df3c
--- /dev/null
+++ b/plugins/bitbucket/models/pr_comment.go
@@ -0,0 +1,41 @@
+/*
+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 models
+
+import (
+ "time"
+
+ "github.com/apache/incubator-devlake/models/common"
+)
+
+type BitbucketPrComment struct {
+ ConnectionId uint64 `gorm:"primaryKey"`
+ BitbucketId int `gorm:"primaryKey"`
+ PullRequestId int `gorm:"index"`
+ AuthorId string `gorm:"type:varchar(255)"`
+ AuthorName string `gorm:"type:varchar(255)"`
+ BitbucketCreatedAt time.Time
+ BitbucketUpdatedAt *time.Time
+ Type string `gorm:"comment:if type=null, it is normal comment,if type=diffNote,it is diff comment"`
+ Body string
+ common.NoPKModel
+}
+
+func (BitbucketPrComment) TableName() string {
+ return "_tool_bitbucket_pull_request_comments"
+}
diff --git a/plugins/bitbucket/models/repo.go b/plugins/bitbucket/models/repo.go
new file mode 100644
index 00000000..3dd6c463
--- /dev/null
+++ b/plugins/bitbucket/models/repo.go
@@ -0,0 +1,40 @@
+/*
+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 models
+
+import (
+ "github.com/apache/incubator-devlake/models/common"
+ "time"
+)
+
+type BitbucketRepo struct {
+ ConnectionId uint64 `gorm:"primaryKey"`
+ BitbucketId string `gorm:"primaryKey;type:varchar(255)"`
+ Name string `gorm:"type:varchar(255)"`
+ HTMLUrl string `gorm:"type:varchar(255)"`
+ Description string
+ OwnerId string `json:"ownerId"`
+ Language string `json:"language" gorm:"type:varchar(255)"`
+ CreatedDate time.Time `json:"createdDate"`
+ UpdatedDate *time.Time `json:"updatedDate"`
+ common.NoPKModel
+}
+
+func (BitbucketRepo) TableName() string {
+ return "_tool_bitbucket_repos"
+}
diff --git a/plugins/bitbucket/models/repo_commit.go b/plugins/bitbucket/models/repo_commit.go
new file mode 100644
index 00000000..9979b06b
--- /dev/null
+++ b/plugins/bitbucket/models/repo_commit.go
@@ -0,0 +1,31 @@
+/*
+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 models
+
+import "github.com/apache/incubator-devlake/models/common"
+
+type BitbucketRepoCommit struct {
+ ConnectionId uint64 `gorm:"primaryKey"`
+ RepoId string `gorm:"primaryKey;type:varchar(255)"`
+ CommitSha string `gorm:"primaryKey;type:varchar(40)"`
+ common.NoPKModel
+}
+
+func (BitbucketRepoCommit) TableName() string {
+ return "_tool_bitbucket_repo_commits"
+}
diff --git a/plugins/bitbucket/models/user.go b/plugins/bitbucket/models/user.go
new file mode 100644
index 00000000..129dfc65
--- /dev/null
+++ b/plugins/bitbucket/models/user.go
@@ -0,0 +1,35 @@
+/*
+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 models
+
+import (
+ "github.com/apache/incubator-devlake/models/common"
+)
+
+type BitbucketUser struct {
+ ConnectionId uint64 `gorm:"primaryKey"`
+ UserName string `json:"username"`
+ DisplayName string `json:"display_name"`
+ AccountId string `json:"account_id"`
+
+ common.NoPKModel
+}
+
+func (BitbucketUser) TableName() string {
+ return "_tool_bitbucket_users"
+}
diff --git a/plugins/bitbucket/tasks/account_convertor.go b/plugins/bitbucket/tasks/account_convertor.go
new file mode 100644
index 00000000..82642764
--- /dev/null
+++ b/plugins/bitbucket/tasks/account_convertor.go
@@ -0,0 +1,87 @@
+/*
+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 tasks
+
+import (
+ "reflect"
+
+ "github.com/apache/incubator-devlake/models/domainlayer/crossdomain"
+
+ "github.com/apache/incubator-devlake/plugins/core/dal"
+
+ "github.com/apache/incubator-devlake/models/domainlayer"
+ "github.com/apache/incubator-devlake/models/domainlayer/didgen"
+ bitbucketModels "github.com/apache/incubator-devlake/plugins/bitbucket/models"
+ "github.com/apache/incubator-devlake/plugins/core"
+ "github.com/apache/incubator-devlake/plugins/helper"
+)
+
+const RAW_ACCOUNT_TABLE = "bitbucket_api_accounts"
+
+var ConvertAccountsMeta = core.SubTaskMeta{
+ Name: "convertAccounts",
+ EntryPoint: ConvertAccounts,
+ EnabledByDefault: true,
+ Required: true,
+ Description: "Convert tool layer table bitbucket_accounts into domain layer table accounts",
+ DomainTypes: []string{core.DOMAIN_TYPE_CROSS},
+}
+
+func ConvertAccounts(taskCtx core.SubTaskContext) error {
+ db := taskCtx.GetDal()
+ data := taskCtx.GetData().(*BitbucketTaskData)
+
+ cursor, err := db.Cursor(dal.From(&bitbucketModels.BitbucketAccount{}))
+ if err != nil {
+ return err
+ }
+ defer cursor.Close()
+
+ accountIdGen := didgen.NewDomainIdGenerator(&bitbucketModels.BitbucketAccount{})
+
+ converter, err := helper.NewDataConverter(helper.DataConverterArgs{
+ InputRowType: reflect.TypeOf(bitbucketModels.BitbucketAccount{}),
+ Input: cursor,
+ RawDataSubTaskArgs: helper.RawDataSubTaskArgs{
+ Ctx: taskCtx,
+ Params: BitbucketApiParams{
+ ConnectionId: data.Options.ConnectionId,
+ Owner: data.Options.Owner,
+ Repo: data.Options.Repo,
+ },
+ Table: RAW_ACCOUNT_TABLE,
+ },
+ Convert: func(inputRow interface{}) ([]interface{}, error) {
+ bitbucketUser := inputRow.(*bitbucketModels.BitbucketAccount)
+ domainUser := &crossdomain.Account{
+ DomainEntity: domainlayer.DomainEntity{Id: accountIdGen.Generate(data.Options.ConnectionId, bitbucketUser.AccountId)},
+ UserName: bitbucketUser.UserName,
+ FullName: bitbucketUser.DisplayName,
+ AvatarUrl: bitbucketUser.AvatarUrl,
+ }
+ return []interface{}{
+ domainUser,
+ }, nil
+ },
+ })
+ if err != nil {
+ return err
+ }
+
+ return converter.Execute()
+}
diff --git a/plugins/bitbucket/tasks/account_extractor.go b/plugins/bitbucket/tasks/account_extractor.go
new file mode 100644
index 00000000..10442e33
--- /dev/null
+++ b/plugins/bitbucket/tasks/account_extractor.go
@@ -0,0 +1,52 @@
+/*
+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 tasks
+
+import (
+ "github.com/apache/incubator-devlake/plugins/bitbucket/models"
+ "time"
+)
+
+type BitbucketAccountResponse struct {
+ UserName string `json:"username"`
+ DisplayName string `json:"display_name"`
+ AccountId string `json:"account_id"`
+ AccountStatus string `json:"account_status"`
+ CreateOn time.Time `json:"create_on"`
+ Links struct {
+ Self struct{ Href string } `json:"self"`
+ Html struct{ Href string } `json:"html"`
+ Avatar struct{ Href string } `json:"avatar"`
+ Followers struct{ Href string } `json:"followers"`
+ Following struct{ Href string } `json:"following"`
+ Repository struct{ Href string } `json:"repository"`
+ }
+}
+
+func convertAccount(res *BitbucketAccountResponse, connId uint64) (*models.BitbucketAccount, error) {
+ bitbucketAccount := &models.BitbucketAccount{
+ ConnectionId: connId,
+ UserName: res.UserName,
+ DisplayName: res.DisplayName,
+ AccountId: res.AccountId,
+ AccountStatus: res.AccountStatus,
+ AvatarUrl: res.Links.Avatar.Href,
+ HtmlUrl: res.Links.Html.Href,
+ }
+ return bitbucketAccount, nil
+}
diff --git a/plugins/bitbucket/tasks/api_client.go b/plugins/bitbucket/tasks/api_client.go
new file mode 100644
index 00000000..53c9b706
--- /dev/null
+++ b/plugins/bitbucket/tasks/api_client.go
@@ -0,0 +1,86 @@
+/*
+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 tasks
+
+import (
+ "fmt"
+ "github.com/apache/incubator-devlake/plugins/bitbucket/models"
+ "net/http"
+ "strconv"
+ "time"
+
+ "github.com/apache/incubator-devlake/plugins/core"
+ "github.com/apache/incubator-devlake/plugins/helper"
+)
+
+func CreateApiClient(taskCtx core.TaskContext, connection *models.BitbucketConnection) (*helper.ApiAsyncClient, error) {
+ // load configuration
+ token := connection.GetEncodedToken()
+ // create synchronize api client so we can calculate api rate limit dynamically
+ apiClient, err := helper.NewApiClient(taskCtx.GetContext(), connection.Endpoint, nil, 0, connection.Proxy, taskCtx)
+ if err != nil {
+ return nil, err
+ }
+ // Rotates token on each request.
+ apiClient.SetBeforeFunction(func(req *http.Request) error {
+ req.Header.Set("Authorization", fmt.Sprintf("Basic %v", token))
+ return nil
+ })
+ apiClient.SetAfterFunction(func(res *http.Response) error {
+ if res.StatusCode == http.StatusUnauthorized {
+ return fmt.Errorf("authentication failed, please check your Basic Auth configuration")
+ }
+ return nil
+ })
+
+ // create rate limit calculator
+ rateLimiter := &helper.ApiRateLimitCalculator{
+ UserRateLimitPerHour: connection.RateLimitPerHour,
+ Method: http.MethodGet,
+ DynamicRateLimit: func(res *http.Response) (int, time.Duration, error) {
+ rateLimitHeader := res.Header.Get("X-Request-Count")
+ if rateLimitHeader == "" {
+ // use default
+ return 0, 0, nil
+ }
+ rateLimit, err := strconv.Atoi(rateLimitHeader)
+ if err != nil {
+ return 0, 0, fmt.Errorf("failed to parse X-Request-Count header: %w", err)
+ }
+
+ return rateLimit, 1 * time.Minute, nil
+ },
+ }
+ asyncApiClient, err := helper.CreateAsyncApiClient(
+ taskCtx,
+ apiClient,
+ rateLimiter,
+ )
+ if err != nil {
+ return nil, err
+ }
+ return asyncApiClient, nil
+}
+
+type BitbucketPagination struct {
+ Values []interface{} `json:"values"`
+ PageLen int `json:"pagelen"`
+ Size int `json:"size"`
+ Page int `json:"page"`
+ Next string `json:"next"`
+}
diff --git a/plugins/bitbucket/tasks/api_common.go b/plugins/bitbucket/tasks/api_common.go
new file mode 100644
index 00000000..a626239f
--- /dev/null
+++ b/plugins/bitbucket/tasks/api_common.go
@@ -0,0 +1,137 @@
+/*
+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 tasks
+
+import (
+ "encoding/json"
+ "fmt"
+ "github.com/apache/incubator-devlake/plugins/core"
+ "github.com/apache/incubator-devlake/plugins/core/dal"
+ "github.com/apache/incubator-devlake/plugins/helper"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+ "reflect"
+)
+
+type BitbucketApiParams struct {
+ ConnectionId uint64
+ Owner string
+ Repo string
+}
+
+type BitbucketInput struct {
+ BitbucketId int
+}
+
+func CreateRawDataSubTaskArgs(taskCtx core.SubTaskContext, Table string) (*helper.RawDataSubTaskArgs, *BitbucketTaskData) {
+ data := taskCtx.GetData().(*BitbucketTaskData)
+ RawDataSubTaskArgs := &helper.RawDataSubTaskArgs{
+ Ctx: taskCtx,
+ Params: BitbucketApiParams{
+ ConnectionId: data.Options.ConnectionId,
+ Owner: data.Options.Owner,
+ Repo: data.Options.Repo,
+ },
+ Table: Table,
+ }
+ return RawDataSubTaskArgs, data
+}
+
+func GetQuery(reqData *helper.RequestData) (url.Values, error) {
+ query := url.Values{}
+ query.Set("state", "all")
+ query.Set("page", fmt.Sprintf("%v", reqData.Pager.Page))
+ query.Set("pagelen", fmt.Sprintf("%v", reqData.Pager.Size))
+
+ return query, nil
+}
+
+func GetTotalPagesFromResponse(res *http.Response, args *helper.ApiCollectorArgs) (int, error) {
+ body := &BitbucketPagination{}
+ err := helper.UnmarshalResponse(res, body)
+ if err != nil {
+ return 0, err
+ }
+ pages := body.Size / args.PageSize
+ if body.Size%args.PageSize > 0 {
+ pages++
+ }
+ return pages, nil
+}
+
+func GetRawMessageFromResponse(res *http.Response) ([]json.RawMessage, error) {
+ var rawMessages struct {
+ Values []json.RawMessage `json:"values"`
+ }
+ if res == nil {
+ return nil, fmt.Errorf("res is nil")
+ }
+ defer res.Body.Close()
+ resBody, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ return nil, fmt.Errorf("%w %s", err, res.Request.URL.String())
+ }
+
+ err = json.Unmarshal(resBody, &rawMessages)
+ if err != nil {
+ return nil, fmt.Errorf("%w %s %s", err, res.Request.URL.String(), string(resBody))
+ }
+
+ return rawMessages.Values, nil
+}
+
+func GetPullRequestsIterator(taskCtx core.SubTaskContext) (*helper.DalCursorIterator, error) {
+ db := taskCtx.GetDal()
+ data := taskCtx.GetData().(*BitbucketTaskData)
+ clauses := []dal.Clause{
+ dal.Select("bpr.bitbucket_id"),
+ 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,
+ ),
+ }
+ // construct the input iterator
+ cursor, err := db.Cursor(clauses...)
+ if err != nil {
+ return nil, err
+ }
+
+ return helper.NewDalCursorIterator(db, cursor, reflect.TypeOf(BitbucketInput{}))
+}
+
+func GetIssuesIterator(taskCtx core.SubTaskContext) (*helper.DalCursorIterator, error) {
+ db := taskCtx.GetDal()
+ data := taskCtx.GetData().(*BitbucketTaskData)
+ clauses := []dal.Clause{
+ dal.Select("bpr.bitbucket_id"),
+ 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,
+ ),
+ }
+ // construct the input iterator
+ cursor, err := db.Cursor(clauses...)
+ if err != nil {
+ return nil, err
+ }
+
+ return helper.NewDalCursorIterator(db, cursor, reflect.TypeOf(BitbucketInput{}))
+}
diff --git a/plugins/bitbucket/tasks/commit_collector.go b/plugins/bitbucket/tasks/commit_collector.go
new file mode 100644
index 00000000..3182115f
--- /dev/null
+++ b/plugins/bitbucket/tasks/commit_collector.go
@@ -0,0 +1,56 @@
+/*
+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 tasks
+
+import (
+ "github.com/apache/incubator-devlake/plugins/helper"
+
+ "github.com/apache/incubator-devlake/plugins/core"
+)
+
+const RAW_COMMIT_TABLE = "bitbucket_api_commits"
+
+var CollectApiCommitsMeta = core.SubTaskMeta{
+ Name: "collectApiCommits",
+ EntryPoint: CollectApiCommits,
+ EnabledByDefault: false,
+ Required: false,
+ Description: "Collect commits data from Bitbucket api",
+ DomainTypes: []string{core.DOMAIN_TYPE_CODE},
+}
+
+func CollectApiCommits(taskCtx core.SubTaskContext) error {
+ rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, RAW_COMMIT_TABLE)
+
+ collector, err := helper.NewApiCollector(helper.ApiCollectorArgs{
+ RawDataSubTaskArgs: *rawDataSubTaskArgs,
+ ApiClient: data.ApiClient,
+ PageSize: 100,
+ Incremental: false,
+ UrlTemplate: "repositories/{{ .Params.Owner }}/{{ .Params.Repo }}/commits",
+ Query: GetQuery,
+ GetTotalPages: GetTotalPagesFromResponse,
+ ResponseParser: GetRawMessageFromResponse,
+ })
+
+ if err != nil {
+ return err
+ }
+
+ return collector.Execute()
+}
diff --git a/plugins/bitbucket/tasks/commit_convertor.go b/plugins/bitbucket/tasks/commit_convertor.go
new file mode 100644
index 00000000..5900e395
--- /dev/null
+++ b/plugins/bitbucket/tasks/commit_convertor.go
@@ -0,0 +1,103 @@
+/*
+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 tasks
+
+import (
+ "reflect"
+
+ "github.com/apache/incubator-devlake/plugins/core/dal"
+
+ "github.com/apache/incubator-devlake/models/domainlayer/code"
+ "github.com/apache/incubator-devlake/models/domainlayer/didgen"
+ bitbucketModels "github.com/apache/incubator-devlake/plugins/bitbucket/models"
+ "github.com/apache/incubator-devlake/plugins/core"
+ "github.com/apache/incubator-devlake/plugins/helper"
+)
+
+var ConvertCommitsMeta = core.SubTaskMeta{
+ Name: "convertCommits",
+ EntryPoint: ConvertCommits,
+ EnabledByDefault: false,
+ Required: false,
+ Description: "Convert tool layer table bitbucket_commits into domain layer table commits",
+ DomainTypes: []string{core.DOMAIN_TYPE_CODE},
+}
+
+func ConvertCommits(taskCtx core.SubTaskContext) error {
+ db := taskCtx.GetDal()
+ data := taskCtx.GetData().(*BitbucketTaskData)
+ repoId := data.Repo.BitbucketId
+
+ cursor, err := db.Cursor(
+ dal.From("_tool_bitbucket_commits gc"),
+ dal.Join(`left join _tool_bitbucket_repo_commits grc on (
+ grc.commit_sha = gc.sha
+ )`),
+ dal.Select("gc.*"),
+ dal.Where("grc.repo_id = ? AND grc.connection_id = ?", repoId, data.Options.ConnectionId),
+ )
+ if err != nil {
+ return err
+ }
+ defer cursor.Close()
+
+ repoDidGen := didgen.NewDomainIdGenerator(&bitbucketModels.BitbucketRepo{})
+ domainRepoId := repoDidGen.Generate(data.Options.ConnectionId, repoId)
+
+ converter, err := helper.NewDataConverter(helper.DataConverterArgs{
+ RawDataSubTaskArgs: helper.RawDataSubTaskArgs{
+ Ctx: taskCtx,
+ Params: BitbucketApiParams{
+ ConnectionId: data.Options.ConnectionId,
+ Owner: data.Options.Owner,
+ Repo: data.Options.Repo,
+ },
+ Table: RAW_COMMIT_TABLE,
+ },
+ InputRowType: reflect.TypeOf(bitbucketModels.BitbucketCommit{}),
+ Input: cursor,
+
+ Convert: func(inputRow interface{}) ([]interface{}, error) {
+ bitbucketCommit := inputRow.(*bitbucketModels.BitbucketCommit)
+ domainCommit := &code.Commit{
+ Sha: bitbucketCommit.Sha,
+ Message: bitbucketCommit.Message,
+ Additions: bitbucketCommit.Additions,
+ Deletions: bitbucketCommit.Deletions,
+ AuthorId: bitbucketCommit.AuthorEmail,
+ AuthorName: bitbucketCommit.AuthorName,
+ AuthorEmail: bitbucketCommit.AuthorEmail,
+ AuthoredDate: bitbucketCommit.AuthoredDate,
+ CommittedDate: bitbucketCommit.CommittedDate,
+ }
+ repoCommit := &code.RepoCommit{
+ RepoId: domainRepoId,
+ CommitSha: domainCommit.Sha,
+ }
+ return []interface{}{
+ domainCommit,
+ repoCommit,
+ }, nil
+ },
+ })
+ if err != nil {
+ return err
+ }
+
+ return converter.Execute()
+}
diff --git a/plugins/bitbucket/tasks/commit_extractor.go b/plugins/bitbucket/tasks/commit_extractor.go
new file mode 100644
index 00000000..d209a4d4
--- /dev/null
+++ b/plugins/bitbucket/tasks/commit_extractor.go
@@ -0,0 +1,123 @@
+/*
+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 tasks
+
+import (
+ "encoding/json"
+ "time"
+
+ "github.com/apache/incubator-devlake/plugins/bitbucket/models"
+ "github.com/apache/incubator-devlake/plugins/core"
+ "github.com/apache/incubator-devlake/plugins/helper"
+)
+
+var ExtractApiCommitsMeta = core.SubTaskMeta{
+ Name: "extractApiCommits",
+ EntryPoint: ExtractApiCommits,
+ EnabledByDefault: false,
+ Required: false,
+ Description: "Extract raw commit data into tool layer table bitbucket_commits",
+ DomainTypes: []string{core.DOMAIN_TYPE_CODE},
+}
+
+type CommitsResponse struct {
+ Type string `json:"type"`
+ Sha string `json:"hash"`
+ Date time.Time `json:"date"`
+ //Author *models.BitbucketAccount
+ Message string `json:"message"`
+ Links struct {
+ Self struct{ Href string }
+ Html struct{ Href string }
+ Diff struct{ Href string }
+ Approve struct{ Href string }
+ Comments struct{ Href string }
+ } `json:"links"`
+ Parents []struct {
+ Type string
+ Hash string
+ Links struct {
+ Self struct{ Href string }
+ Html struct{ Href string }
+ }
+ } `json:"parents"`
+}
+
+func ExtractApiCommits(taskCtx core.SubTaskContext) error {
+ data := taskCtx.GetData().(*BitbucketTaskData)
+
+ extractor, err := helper.NewApiExtractor(helper.ApiExtractorArgs{
+ RawDataSubTaskArgs: helper.RawDataSubTaskArgs{
+ Ctx: taskCtx,
+ /*
+ This struct will be JSONEncoded and stored into database along with raw data itself, to identity minimal
+ set of data to be process, for example, we process JiraCommits by Board
+ */
+ Params: BitbucketApiParams{
+ ConnectionId: data.Options.ConnectionId,
+ Owner: data.Options.Owner,
+ Repo: data.Options.Repo,
+ },
+ /*
+ Table store raw data
+ */
+ Table: RAW_COMMIT_TABLE,
+ },
+ Extract: func(row *helper.RawData) ([]interface{}, error) {
+ commit := &CommitsResponse{}
+ err := json.Unmarshal(row.Data, commit)
+ if err != nil {
+ return nil, err
+ }
+ if commit.Sha == "" {
+ return nil, nil
+ }
+ results := make([]interface{}, 0, 4)
+
+ bitbucketCommit := &models.BitbucketCommit{
+ Sha: commit.Sha,
+ Message: commit.Message,
+ AuthoredDate: commit.Date,
+ Url: commit.Links.Self.Href,
+ CommittedDate: commit.Date,
+ }
+
+ //if commit.Author != nil {
+ // bitbucketCommit.AuthorName = commit.Author.User.DisplayName
+ // bitbucketCommit.AuthorId = commit.Author.User.AccountId
+ // results = append(results, commit.Author)
+ //}
+
+ bitbucketRepoCommit := &models.BitbucketRepoCommit{
+ ConnectionId: data.Options.ConnectionId,
+ RepoId: data.Repo.BitbucketId,
+ CommitSha: commit.Sha,
+ }
+
+ results = append(results, bitbucketCommit)
+ results = append(results, bitbucketRepoCommit)
+ return results, nil
+ },
+ })
+
+ if err != nil {
+ return err
+ }
+
+ return extractor.Execute()
+}
diff --git a/plugins/bitbucket/tasks/issue_collector.go b/plugins/bitbucket/tasks/issue_collector.go
new file mode 100644
index 00000000..dd08b1ba
--- /dev/null
+++ b/plugins/bitbucket/tasks/issue_collector.go
@@ -0,0 +1,55 @@
+/*
+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 tasks
+
+import (
+ "github.com/apache/incubator-devlake/plugins/helper"
+
+ "github.com/apache/incubator-devlake/plugins/core"
+)
+
+const RAW_ISSUE_TABLE = "bitbucket_api_issues"
+
+var CollectApiIssuesMeta = core.SubTaskMeta{
+ Name: "collectApiIssues",
+ EntryPoint: CollectApiIssues,
+ EnabledByDefault: true,
+ Description: "Collect issues data from Bitbucket api",
+ DomainTypes: []string{core.DOMAIN_TYPE_TICKET},
+}
+
+func CollectApiIssues(taskCtx core.SubTaskContext) error {
+ rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, RAW_ISSUE_TABLE)
+
+ collector, err := helper.NewApiCollector(helper.ApiCollectorArgs{
+ RawDataSubTaskArgs: *rawDataSubTaskArgs,
+ ApiClient: data.ApiClient,
+ PageSize: 100,
+ Incremental: false,
+ UrlTemplate: "repositories/{{ .Params.Owner }}/{{ .Params.Repo }}/issues",
+ Query: GetQuery,
+ GetTotalPages: GetTotalPagesFromResponse,
+ ResponseParser: GetRawMessageFromResponse,
+ })
+
+ if err != nil {
+ return err
+ }
+
+ return collector.Execute()
+}
diff --git a/plugins/bitbucket/tasks/issue_comment_collector.go b/plugins/bitbucket/tasks/issue_comment_collector.go
new file mode 100644
index 00000000..c8f40132
--- /dev/null
+++ b/plugins/bitbucket/tasks/issue_comment_collector.go
@@ -0,0 +1,61 @@
+/*
+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 tasks
+
+import (
+ "github.com/apache/incubator-devlake/plugins/core"
+ "github.com/apache/incubator-devlake/plugins/helper"
+)
+
+const RAW_ISSUE_COMMENTS_TABLE = "bitbucket_api_issue_comments"
+
+var CollectApiIssueCommentsMeta = core.SubTaskMeta{
+ Name: "collectApiIssueComments",
+ EntryPoint: CollectApiIssueComments,
+ EnabledByDefault: true,
+ Required: true,
+ Description: "Collect issue comments data from bitbucket api",
+ DomainTypes: []string{core.DOMAIN_TYPE_CODE},
+}
+
+func CollectApiIssueComments(taskCtx core.SubTaskContext) error {
+ rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, RAW_ISSUE_COMMENTS_TABLE)
+
+ iterator, err := GetIssuesIterator(taskCtx)
+ if err != nil {
+ return err
+ }
+ defer iterator.Close()
+
+ collector, err := helper.NewApiCollector(helper.ApiCollectorArgs{
+ RawDataSubTaskArgs: *rawDataSubTaskArgs,
+ ApiClient: data.ApiClient,
+ PageSize: 100,
+ Incremental: false,
+ Input: iterator,
+ UrlTemplate: "repositories/{{ .Params.Owner }}/{{ .Params.Repo }}/issues/{{ .Input.BitbucketId }}/comments",
+ Query: GetQuery,
+ GetTotalPages: GetTotalPagesFromResponse,
+ ResponseParser: GetRawMessageFromResponse,
+ })
+ if err != nil {
+ return err
+ }
+
+ return collector.Execute()
+}
diff --git a/plugins/bitbucket/tasks/issue_comment_convertor.go b/plugins/bitbucket/tasks/issue_comment_convertor.go
new file mode 100644
index 00000000..97dc0627
--- /dev/null
+++ b/plugins/bitbucket/tasks/issue_comment_convertor.go
@@ -0,0 +1,92 @@
+/*
+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 tasks
+
+import (
+ "github.com/apache/incubator-devlake/models/domainlayer/ticket"
+ "reflect"
+
+ "github.com/apache/incubator-devlake/models/domainlayer"
+ "github.com/apache/incubator-devlake/models/domainlayer/didgen"
+ bitbucketModels "github.com/apache/incubator-devlake/plugins/bitbucket/models"
+ "github.com/apache/incubator-devlake/plugins/core"
+ "github.com/apache/incubator-devlake/plugins/core/dal"
+ "github.com/apache/incubator-devlake/plugins/helper"
+)
+
+var ConvertIssueCommentsMeta = core.SubTaskMeta{
+ Name: "convertIssueComments",
+ EntryPoint: ConvertIssueComments,
+ EnabledByDefault: true,
+ Description: "ConvertIssueComments data from Bitbucket api",
+ DomainTypes: []string{core.DOMAIN_TYPE_CODE},
+}
+
+func ConvertIssueComments(taskCtx core.SubTaskContext) error {
+ db := taskCtx.GetDal()
+ data := taskCtx.GetData().(*BitbucketTaskData)
+ repoId := data.Repo.BitbucketId
+
+ cursor, err := db.Cursor(
+ dal.From(&bitbucketModels.BitbucketIssueComment{}),
+ dal.Join("left join _tool_bitbucket_issues "+
+ "on _tool_bitbucket_issues.bitbucket_id = _tool_bitbucket_issue_comments.issue_id"),
+ dal.Where("repo_id = ? and _tool_bitbucket_issues.connection_id = ?", repoId, data.Options.ConnectionId),
+ )
+ if err != nil {
+ return err
+ }
+ defer cursor.Close()
+
+ issueIdGen := didgen.NewDomainIdGenerator(&bitbucketModels.BitbucketIssue{})
+ accountIdGen := didgen.NewDomainIdGenerator(&bitbucketModels.BitbucketAccount{})
+
+ converter, err := helper.NewDataConverter(helper.DataConverterArgs{
+ InputRowType: reflect.TypeOf(bitbucketModels.BitbucketIssueComment{}),
+ Input: cursor,
+ RawDataSubTaskArgs: helper.RawDataSubTaskArgs{
+ Ctx: taskCtx,
+ Params: BitbucketApiParams{
+ ConnectionId: data.Options.ConnectionId,
+ Owner: data.Options.Owner,
+ Repo: data.Options.Repo,
+ },
+ Table: RAW_ISSUE_COMMENTS_TABLE,
+ },
+ Convert: func(inputRow interface{}) ([]interface{}, error) {
+ bitbucketIssueComment := inputRow.(*bitbucketModels.BitbucketIssueComment)
+ domainIssueComment := &ticket.IssueComment{
+ DomainEntity: domainlayer.DomainEntity{
+ Id: issueIdGen.Generate(data.Options.ConnectionId, bitbucketIssueComment.BitbucketId),
+ },
+ IssueId: issueIdGen.Generate(data.Options.ConnectionId, bitbucketIssueComment.IssueId),
+ UserId: accountIdGen.Generate(data.Options.ConnectionId, bitbucketIssueComment.AuthorId),
+ CreatedDate: bitbucketIssueComment.CreatedAt,
+ Body: bitbucketIssueComment.Body,
+ }
+ return []interface{}{
+ domainIssueComment,
+ }, nil
+ },
+ })
+ if err != nil {
+ return err
+ }
+
+ return converter.Execute()
+}
diff --git a/plugins/bitbucket/tasks/issue_comment_extractor.go b/plugins/bitbucket/tasks/issue_comment_extractor.go
new file mode 100644
index 00000000..35042461
--- /dev/null
+++ b/plugins/bitbucket/tasks/issue_comment_extractor.go
@@ -0,0 +1,122 @@
+/*
+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 tasks
+
+import (
+ "encoding/json"
+ "github.com/apache/incubator-devlake/plugins/bitbucket/models"
+ "github.com/apache/incubator-devlake/plugins/core"
+ "github.com/apache/incubator-devlake/plugins/helper"
+ "time"
+)
+
+type BitbucketIssueCommentsResponse struct {
+ Type string `json:"type"`
+ BitbucketId int `json:"id"`
+ CreatedOn time.Time `json:"created_on"`
+ UpdatedOn *time.Time `json:"updated_on"`
+ Content struct {
+ Type string
+ Raw string `json:"raw"`
+ } `json:"content"`
+ User *BitbucketAccountResponse
+ Issue struct {
+ Type string
+ Id int
+ Repository *BitbucketApiRepo
+ Links struct {
+ Self struct {
+ Href string
+ }
+ }
+ Title string
+ }
+ Links struct {
+ Self struct {
+ Href string
+ } `json:"self"`
+ Html struct {
+ Href string
+ } `json:"html"`
+ } `json:"links"`
+}
+
+var ExtractApiIssueCommentsMeta = core.SubTaskMeta{
+ Name: "extractApiIssueComments",
+ EntryPoint: ExtractApiIssueComments,
+ EnabledByDefault: true,
+ Required: true,
+ Description: "Extract raw issue comments data into tool layer table BitbucketIssueComments",
+ DomainTypes: []string{core.DOMAIN_TYPE_CODE},
+}
+
+func ExtractApiIssueComments(taskCtx core.SubTaskContext) error {
+ rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, RAW_ISSUE_COMMENTS_TABLE)
+
+ extractor, err := helper.NewApiExtractor(helper.ApiExtractorArgs{
+ RawDataSubTaskArgs: *rawDataSubTaskArgs,
+ Extract: func(row *helper.RawData) ([]interface{}, error) {
+ issueComment := &BitbucketIssueCommentsResponse{}
+ err := json.Unmarshal(row.Data, issueComment)
+ if err != nil {
+ return nil, err
+ }
+
+ toolIssueComment, err := convertIssueComment(issueComment)
+ toolIssueComment.ConnectionId = data.Options.ConnectionId
+ if err != nil {
+ return nil, err
+ }
+ results := make([]interface{}, 0, 2)
+
+ if issueComment.User != nil {
+ bitbucketUser, err := convertAccount(issueComment.User, data.Options.ConnectionId)
+ if err != nil {
+ return nil, err
+ }
+ toolIssueComment.AuthorId = bitbucketUser.AccountId
+ toolIssueComment.AuthorName = bitbucketUser.DisplayName
+ results = append(results, bitbucketUser)
+ }
+
+ results = append(results, toolIssueComment)
+
+ return results, nil
+ },
+ })
+
+ if err != nil {
+ return err
+ }
+
+ return extractor.Execute()
+}
+
+func convertIssueComment(issueComment *BitbucketIssueCommentsResponse) (*models.BitbucketIssueComment, error) {
+ bitbucketIssueComment := &models.BitbucketIssueComment{
+ BitbucketId: issueComment.BitbucketId,
+ AuthorId: issueComment.User.AccountId,
+ IssueId: issueComment.Issue.Id,
+ AuthorName: issueComment.User.DisplayName,
+ BitbucketCreatedAt: issueComment.CreatedOn,
+ BitbucketUpdatedAt: issueComment.UpdatedOn,
+ Type: issueComment.Type,
+ Body: issueComment.Content.Raw,
+ }
+ return bitbucketIssueComment, nil
+}
diff --git a/plugins/bitbucket/tasks/issue_convertor.go b/plugins/bitbucket/tasks/issue_convertor.go
new file mode 100644
index 00000000..cb928bfb
--- /dev/null
+++ b/plugins/bitbucket/tasks/issue_convertor.go
@@ -0,0 +1,120 @@
+/*
+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 tasks
+
+import (
+ "reflect"
+ "strconv"
+
+ "github.com/apache/incubator-devlake/plugins/core/dal"
+
+ "github.com/apache/incubator-devlake/plugins/core"
+ "github.com/apache/incubator-devlake/plugins/helper"
+
+ "github.com/apache/incubator-devlake/models/domainlayer"
+ "github.com/apache/incubator-devlake/models/domainlayer/didgen"
+ "github.com/apache/incubator-devlake/models/domainlayer/ticket"
+ bitbucketModels "github.com/apache/incubator-devlake/plugins/bitbucket/models"
+)
+
+var ConvertIssuesMeta = core.SubTaskMeta{
+ Name: "convertIssues",
+ EntryPoint: ConvertIssues,
+ EnabledByDefault: true,
+ Description: "Convert tool layer table bitbucket_issues into domain layer table issues",
+ DomainTypes: []string{core.DOMAIN_TYPE_TICKET},
+}
+
+func ConvertIssues(taskCtx core.SubTaskContext) error {
+ db := taskCtx.GetDal()
+ data := taskCtx.GetData().(*BitbucketTaskData)
+ repoId := data.Repo.BitbucketId
+
+ issue := &bitbucketModels.BitbucketIssue{}
+ cursor, err := db.Cursor(
+ dal.From(issue),
+ dal.Where("repo_id = ? and connection_id=?", repoId, data.Options.ConnectionId),
+ )
+ if err != nil {
+ return err
+ }
+ defer cursor.Close()
+
+ issueIdGen := didgen.NewDomainIdGenerator(&bitbucketModels.BitbucketIssue{})
+ boardIdGen := didgen.NewDomainIdGenerator(&bitbucketModels.BitbucketRepo{})
+ accountIdGen := didgen.NewDomainIdGenerator(&bitbucketModels.BitbucketAccount{})
+
+ converter, err := helper.NewDataConverter(helper.DataConverterArgs{
+ RawDataSubTaskArgs: helper.RawDataSubTaskArgs{
+ Ctx: taskCtx,
+ Params: BitbucketApiParams{
+ ConnectionId: data.Options.ConnectionId,
+ Owner: data.Options.Owner,
+ Repo: data.Options.Repo,
+ },
+ Table: RAW_ISSUE_TABLE,
+ },
+ InputRowType: reflect.TypeOf(bitbucketModels.BitbucketIssue{}),
+ Input: cursor,
+ Convert: func(inputRow interface{}) ([]interface{}, error) {
+ issue := inputRow.(*bitbucketModels.BitbucketIssue)
+ domainIssue := &ticket.Issue{
+ DomainEntity: domainlayer.DomainEntity{Id: issueIdGen.Generate(data.Options.ConnectionId, issue.BitbucketId)},
+ IssueKey: strconv.Itoa(issue.Number),
+ Title: issue.Title,
+ Description: issue.Body,
+ Priority: issue.Priority,
+ Type: issue.Type,
+ OriginalStatus: issue.State,
+ LeadTimeMinutes: issue.LeadTimeMinutes,
+ Url: issue.Url,
+ CreatedDate: &issue.BitbucketCreatedAt,
+ UpdatedDate: &issue.BitbucketUpdatedAt,
+ ResolutionDate: issue.ClosedAt,
+ Severity: issue.Severity,
+ Component: issue.Component,
+ }
+ if issue.State == "closed" {
+ domainIssue.Status = ticket.DONE
+ } else {
+ domainIssue.Status = ticket.TODO
+ }
+ if issue.AssigneeName != "" {
+ domainIssue.AssigneeName = issue.AssigneeName
+ domainIssue.AssigneeId = accountIdGen.Generate(data.Options.ConnectionId, issue.AssigneeId)
+ }
+ if issue.AuthorName != "" {
+ domainIssue.CreatorName = issue.AuthorName
+ domainIssue.CreatorId = accountIdGen.Generate(data.Options.ConnectionId, issue.AuthorId)
+ }
+ boardIssue := &ticket.BoardIssue{
+ BoardId: boardIdGen.Generate(data.Options.ConnectionId, repoId),
+ IssueId: domainIssue.Id,
+ }
+ return []interface{}{
+ domainIssue,
+ boardIssue,
+ }, nil
+ },
+ })
+ if err != nil {
+ return err
+ }
+
+ return converter.Execute()
+}
diff --git a/plugins/bitbucket/tasks/issue_extractor.go b/plugins/bitbucket/tasks/issue_extractor.go
new file mode 100644
index 00000000..9d1cd5dc
--- /dev/null
+++ b/plugins/bitbucket/tasks/issue_extractor.go
@@ -0,0 +1,179 @@
+/*
+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 tasks
+
+import (
+ "encoding/json"
+ "regexp"
+ "time"
+
+ "github.com/apache/incubator-devlake/plugins/bitbucket/models"
+ "github.com/apache/incubator-devlake/plugins/core"
+ "github.com/apache/incubator-devlake/plugins/helper"
+)
+
+type IssuesResponse struct {
+ Type string `json:"type"`
+ BitbucketId int `json:"id"`
+ Repository *BitbucketApiRepo
+ Links struct {
+ Self struct {
+ Href string
+ } `json:"self"`
+ Html struct {
+ Href string
+ } `json:"html"`
+ } `json:"links"`
+ Title string `json:"title"`
+ Content struct {
+ Type string
+ Raw string
+ } `json:"content"`
+ Reporter *BitbucketAccountResponse
+ Assignee *BitbucketAccountResponse
+ State string `json:"state"`
+ Kind string `json:"kind"`
+ Milestone *struct {
+ Id int
+ } `json:"milestone"`
+ Component string `json:"component"`
+ Priority string `json:"priority"`
+ Votes int `json:"votes"`
+ Watches int `json:"watches"`
+ BitbucketCreatedAt time.Time `json:"created_on"`
+ BitbucketUpdatedAt time.Time `json:"updated_on"`
+}
+
+type IssueRegexes struct {
+ SeverityRegex *regexp.Regexp
+ ComponentRegex *regexp.Regexp
+ PriorityRegex *regexp.Regexp
+ TypeBugRegex *regexp.Regexp
+ TypeRequirementRegex *regexp.Regexp
+ TypeIncidentRegex *regexp.Regexp
+}
+
+var ExtractApiIssuesMeta = core.SubTaskMeta{
+ Name: "extractApiIssues",
+ EntryPoint: ExtractApiIssues,
+ EnabledByDefault: true,
+ Description: "Extract raw Issues data into tool layer table bitbucket_issues",
+ DomainTypes: []string{core.DOMAIN_TYPE_TICKET},
+}
+
+func ExtractApiIssues(taskCtx core.SubTaskContext) error {
+ data := taskCtx.GetData().(*BitbucketTaskData)
+
+ extractor, err := helper.NewApiExtractor(helper.ApiExtractorArgs{
+ RawDataSubTaskArgs: helper.RawDataSubTaskArgs{
+ Ctx: taskCtx,
+ /*
+ This struct will be JSONEncoded and stored into database along with raw data itself, to identity minimal
+ set of data to be process, for example, we process JiraIssues by Board
+ */
+ Params: BitbucketApiParams{
+ ConnectionId: data.Options.ConnectionId,
+ Owner: data.Options.Owner,
+ Repo: data.Options.Repo,
+ },
+ /*
+ Table store raw data
+ */
+ Table: RAW_ISSUE_TABLE,
+ },
+ Extract: func(row *helper.RawData) ([]interface{}, error) {
+ body := &IssuesResponse{}
+ err := json.Unmarshal(row.Data, body)
+ if err != nil {
+ return nil, err
+ }
+ // need to extract 2 kinds of entities here
+ if body.BitbucketId == 0 {
+ return nil, nil
+ }
+ //If this is not an issue, ignore
+ if body.Type != "issue" {
+ return nil, nil
+ }
+ results := make([]interface{}, 0, 2)
+
+ bitbucketIssue, err := convertBitbucketIssue(body, data.Options.ConnectionId, data.Repo.BitbucketId)
+ if err != nil {
+ return nil, err
+ }
+
+ results = append(results, bitbucketIssue)
+ if body.Assignee != nil {
+ bitbucketIssue.AssigneeId = body.Assignee.AccountId
+ bitbucketIssue.AssigneeName = body.Assignee.DisplayName
+ relatedUser, err := convertAccount(body.Assignee, data.Options.ConnectionId)
+ if err != nil {
+ return nil, err
+ }
+ results = append(results, relatedUser)
+ }
+ if body.Reporter != nil {
+ bitbucketIssue.AuthorId = body.Reporter.AccountId
+ bitbucketIssue.AuthorName = body.Reporter.DisplayName
+ relatedUser, err := convertAccount(body.Reporter, data.Options.ConnectionId)
+ if err != nil {
+ return nil, err
+ }
+ results = append(results, relatedUser)
+ }
+ return results, nil
+ },
+ })
+ if err != nil {
+ return err
+ }
+
+ return extractor.Execute()
+}
+
+func convertBitbucketIssue(issue *IssuesResponse, connectionId uint64, repositoryId string) (*models.BitbucketIssue, error) {
+ bitbucketIssue := &models.BitbucketIssue{
+ ConnectionId: connectionId,
+ BitbucketId: issue.BitbucketId,
+ RepoId: repositoryId,
+ Number: issue.BitbucketId,
+ State: issue.State,
+ Title: issue.Title,
+ Type: issue.Type,
+ Body: issue.Content.Raw,
+ Url: issue.Links.Self.Href,
+ Priority: issue.Priority,
+ Component: issue.Component,
+ BitbucketCreatedAt: issue.BitbucketCreatedAt,
+ BitbucketUpdatedAt: issue.BitbucketUpdatedAt,
+ }
+
+ if issue.Milestone != nil {
+ bitbucketIssue.MilestoneId = issue.Milestone.Id
+ }
+ if issue.Assignee != nil {
+ bitbucketIssue.AssigneeId = issue.Assignee.AccountId
+ bitbucketIssue.AssigneeName = issue.Assignee.DisplayName
+ }
+ if issue.Reporter != nil {
+ bitbucketIssue.AuthorId = issue.Reporter.AccountId
+ bitbucketIssue.AuthorName = issue.Reporter.DisplayName
+ }
+
+ return bitbucketIssue, nil
+}
diff --git a/plugins/bitbucket/tasks/pr_collector.go b/plugins/bitbucket/tasks/pr_collector.go
new file mode 100644
index 00000000..eb0a81b3
--- /dev/null
+++ b/plugins/bitbucket/tasks/pr_collector.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 tasks
+
+import (
+ "github.com/apache/incubator-devlake/plugins/helper"
+
+ "github.com/apache/incubator-devlake/plugins/core"
+)
+
+const RAW_PULL_REQUEST_TABLE = "bitbucket_api_pull_requests"
+
+// this struct should be moved to `bitbucket_api_common.go`
+
+var CollectApiPullRequestsMeta = core.SubTaskMeta{
+ Name: "collectApiPullRequests",
+ EntryPoint: CollectApiPullRequests,
+ EnabledByDefault: true,
+ Required: true,
+ Description: "Collect PullRequests data from Bitbucket api",
+ DomainTypes: []string{core.DOMAIN_TYPE_CODE},
+}
+
+func CollectApiPullRequests(taskCtx core.SubTaskContext) error {
+ rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, RAW_PULL_REQUEST_TABLE)
+
+ collector, err := helper.NewApiCollector(helper.ApiCollectorArgs{
+ RawDataSubTaskArgs: *rawDataSubTaskArgs,
+ ApiClient: data.ApiClient,
+ PageSize: 50,
+ Incremental: false,
+ UrlTemplate: "repositories/{{ .Params.Owner }}/{{ .Params.Repo }}/pullrequests",
+ Query: GetQuery,
+ GetTotalPages: GetTotalPagesFromResponse,
+ ResponseParser: GetRawMessageFromResponse,
+ })
+
+ if err != nil {
+ return err
+ }
+
+ return collector.Execute()
+}
diff --git a/plugins/bitbucket/tasks/pr_comment_collector.go b/plugins/bitbucket/tasks/pr_comment_collector.go
new file mode 100644
index 00000000..33bba48c
--- /dev/null
+++ b/plugins/bitbucket/tasks/pr_comment_collector.go
@@ -0,0 +1,61 @@
+/*
+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 tasks
+
+import (
+ "github.com/apache/incubator-devlake/plugins/core"
+ "github.com/apache/incubator-devlake/plugins/helper"
+)
+
+const RAW_PULL_REQUEST_COMMENTS_TABLE = "bitbucket_api_pull_request_comments"
+
+var CollectApiPrCommentsMeta = core.SubTaskMeta{
+ Name: "collectApiPullRequestsComments",
+ EntryPoint: CollectApiPullRequestsComments,
+ EnabledByDefault: true,
+ Required: true,
+ Description: "Collect pull requests comments data from bitbucket api",
+ DomainTypes: []string{core.DOMAIN_TYPE_CODE},
+}
+
+func CollectApiPullRequestsComments(taskCtx core.SubTaskContext) error {
+ rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, RAW_PULL_REQUEST_COMMENTS_TABLE)
+
+ iterator, err := GetPullRequestsIterator(taskCtx)
+ if err != nil {
+ return err
+ }
+ defer iterator.Close()
+
+ collector, err := helper.NewApiCollector(helper.ApiCollectorArgs{
+ RawDataSubTaskArgs: *rawDataSubTaskArgs,
+ ApiClient: data.ApiClient,
+ PageSize: 100,
+ Incremental: false,
+ Input: iterator,
+ UrlTemplate: "repositories/{{ .Params.Owner }}/{{ .Params.Repo }}/pullrequests/{{ .Input.BitbucketId }}/comments",
+ Query: GetQuery,
+ GetTotalPages: GetTotalPagesFromResponse,
+ ResponseParser: GetRawMessageFromResponse,
+ })
+ if err != nil {
+ return err
+ }
+
+ return collector.Execute()
+}
diff --git a/plugins/bitbucket/tasks/pr_comment_convertor.go b/plugins/bitbucket/tasks/pr_comment_convertor.go
new file mode 100644
index 00000000..467869ce
--- /dev/null
+++ b/plugins/bitbucket/tasks/pr_comment_convertor.go
@@ -0,0 +1,96 @@
+/*
+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 tasks
+
+import (
+ "reflect"
+
+ "github.com/apache/incubator-devlake/models/domainlayer"
+ "github.com/apache/incubator-devlake/models/domainlayer/code"
+ "github.com/apache/incubator-devlake/models/domainlayer/didgen"
+ bitbucketModels "github.com/apache/incubator-devlake/plugins/bitbucket/models"
+ "github.com/apache/incubator-devlake/plugins/core"
+ "github.com/apache/incubator-devlake/plugins/core/dal"
+ "github.com/apache/incubator-devlake/plugins/helper"
+)
+
+var ConvertPrCommentsMeta = core.SubTaskMeta{
+ Name: "convertPullRequestComments",
+ EntryPoint: ConvertPullRequestComments,
+ EnabledByDefault: true,
+ Description: "ConvertPullRequestComments data from Bitbucket api",
+ DomainTypes: []string{core.DOMAIN_TYPE_CODE},
+}
+
+func ConvertPullRequestComments(taskCtx core.SubTaskContext) error {
+ db := taskCtx.GetDal()
+ data := taskCtx.GetData().(*BitbucketTaskData)
+ repoId := data.Repo.BitbucketId
+
+ cursor, err := db.Cursor(
+ dal.From(&bitbucketModels.BitbucketPrComment{}),
+ dal.Join("left join _tool_bitbucket_pull_requests "+
+ "on _tool_bitbucket_pull_requests.bitbucket_id = _tool_bitbucket_pull_request_comments.pull_request_id"),
+ dal.Where("repo_id = ? and _tool_bitbucket_pull_requests.connection_id = ?", repoId, data.Options.ConnectionId),
+ )
+ if err != nil {
+ return err
+ }
+ defer cursor.Close()
+
+ domainIdGeneratorComment := didgen.NewDomainIdGenerator(&bitbucketModels.BitbucketPrComment{})
+ prIdGen := didgen.NewDomainIdGenerator(&bitbucketModels.BitbucketPullRequest{})
+ accountIdGen := didgen.NewDomainIdGenerator(&bitbucketModels.BitbucketAccount{})
+
+ converter, err := helper.NewDataConverter(helper.DataConverterArgs{
+ InputRowType: reflect.TypeOf(bitbucketModels.BitbucketPrComment{}),
+ Input: cursor,
+ RawDataSubTaskArgs: helper.RawDataSubTaskArgs{
+ Ctx: taskCtx,
+ Params: BitbucketApiParams{
+ ConnectionId: data.Options.ConnectionId,
+ Owner: data.Options.Owner,
+ Repo: data.Options.Repo,
+ },
+ Table: RAW_PULL_REQUEST_COMMENTS_TABLE,
+ },
+ Convert: func(inputRow interface{}) ([]interface{}, error) {
+ bitbucketPullRequestComment := inputRow.(*bitbucketModels.BitbucketPrComment)
+ domainPrComment := &code.PullRequestComment{
+ DomainEntity: domainlayer.DomainEntity{
+ Id: domainIdGeneratorComment.Generate(data.Options.ConnectionId, bitbucketPullRequestComment.BitbucketId),
+ },
+ PullRequestId: prIdGen.Generate(data.Options.ConnectionId, bitbucketPullRequestComment.PullRequestId),
+ UserId: accountIdGen.Generate(data.Options.ConnectionId, bitbucketPullRequestComment.AuthorId),
+ CreatedDate: bitbucketPullRequestComment.CreatedAt,
+ Body: bitbucketPullRequestComment.Body,
+ Type: bitbucketPullRequestComment.Type,
+ CommitSha: "",
+ Position: 0,
+ }
+ return []interface{}{
+ domainPrComment,
+ }, nil
+ },
+ })
+ if err != nil {
+ return err
+ }
+
+ return converter.Execute()
+}
diff --git a/plugins/bitbucket/tasks/pr_comment_extractor.go b/plugins/bitbucket/tasks/pr_comment_extractor.go
new file mode 100644
index 00000000..9acc5100
--- /dev/null
+++ b/plugins/bitbucket/tasks/pr_comment_extractor.go
@@ -0,0 +1,124 @@
+/*
+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 tasks
+
+import (
+ "encoding/json"
+ "github.com/apache/incubator-devlake/plugins/bitbucket/models"
+ "github.com/apache/incubator-devlake/plugins/core"
+ "github.com/apache/incubator-devlake/plugins/helper"
+ "time"
+)
+
+var ExtractApiPrCommentsMeta = core.SubTaskMeta{
+ Name: "extractApiPullRequestsComments",
+ EntryPoint: ExtractApiPullRequestsComments,
+ EnabledByDefault: true,
+ Required: true,
+ Description: "Extract raw pull requests comments data into tool layer table BitbucketPrComments",
+ DomainTypes: []string{core.DOMAIN_TYPE_CODE},
+}
+
+type BitbucketPrCommentsResponse struct {
+ BitbucketId int `json:"id"`
+ CreatedOn time.Time `json:"created_on"`
+ UpdatedOn *time.Time `json:"updated_on"`
+ Content struct {
+ Type string
+ Raw string
+ } `json:"content"`
+ User *BitbucketAccountResponse
+ Deleted bool
+ Type string `json:"type"`
+ Links struct {
+ Self struct {
+ Href string
+ } `json:"self"`
+ Html struct {
+ Href string
+ } `json:"html"`
+ } `json:"links"`
+ PullRequest struct {
+ Type string
+ Id int
+ Title string
+ Links struct {
+ Self struct {
+ Href string
+ }
+ Html struct {
+ Href string
+ }
+ }
+ }
+}
+
+func ExtractApiPullRequestsComments(taskCtx core.SubTaskContext) error {
+ rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, RAW_PULL_REQUEST_COMMENTS_TABLE)
+
+ extractor, err := helper.NewApiExtractor(helper.ApiExtractorArgs{
+ RawDataSubTaskArgs: *rawDataSubTaskArgs,
+ Extract: func(row *helper.RawData) ([]interface{}, error) {
+ prComment := &BitbucketPrCommentsResponse{}
+ err := json.Unmarshal(row.Data, prComment)
+ if err != nil {
+ return nil, err
+ }
+
+ toolprComment, err := convertPullRequestComment(prComment)
+ toolprComment.ConnectionId = data.Options.ConnectionId
+ if err != nil {
+ return nil, err
+ }
+ results := make([]interface{}, 0, 2)
+
+ if prComment.User != nil {
+ bitbucketUser, err := convertAccount(prComment.User, data.Options.ConnectionId)
+ if err != nil {
+ return nil, err
+ }
+ toolprComment.AuthorId = bitbucketUser.AccountId
+ toolprComment.AuthorName = bitbucketUser.UserName
+ results = append(results, bitbucketUser)
+ }
+ results = append(results, toolprComment)
+
+ return results, nil
+ },
+ })
+
+ if err != nil {
+ return err
+ }
+
+ return extractor.Execute()
+}
+
+func convertPullRequestComment(prComment *BitbucketPrCommentsResponse) (*models.BitbucketPrComment, error) {
+ bitbucketPrComment := &models.BitbucketPrComment{
+ BitbucketId: prComment.BitbucketId,
+ AuthorId: prComment.User.AccountId,
+ PullRequestId: prComment.PullRequest.Id,
+ AuthorName: prComment.User.DisplayName,
+ BitbucketCreatedAt: prComment.CreatedOn,
+ BitbucketUpdatedAt: prComment.UpdatedOn,
+ Type: prComment.Type,
+ Body: prComment.Content.Raw,
+ }
+ return bitbucketPrComment, nil
+}
diff --git a/plugins/bitbucket/tasks/pr_convertor.go b/plugins/bitbucket/tasks/pr_convertor.go
new file mode 100644
index 00000000..48107f85
--- /dev/null
+++ b/plugins/bitbucket/tasks/pr_convertor.go
@@ -0,0 +1,108 @@
+/*
+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 tasks
+
+import (
+ "reflect"
+
+ "github.com/apache/incubator-devlake/plugins/core/dal"
+
+ "github.com/apache/incubator-devlake/models/domainlayer"
+ "github.com/apache/incubator-devlake/models/domainlayer/code"
+ "github.com/apache/incubator-devlake/models/domainlayer/didgen"
+ "github.com/apache/incubator-devlake/plugins/bitbucket/models"
+ "github.com/apache/incubator-devlake/plugins/core"
+ "github.com/apache/incubator-devlake/plugins/helper"
+)
+
+var ConvertPullRequestsMeta = core.SubTaskMeta{
+ Name: "convertPullRequests",
+ EntryPoint: ConvertPullRequests,
+ EnabledByDefault: true,
+ Required: true,
+ Description: "ConvertPullRequests data from Bitbucket api",
+ DomainTypes: []string{core.DOMAIN_TYPE_CODE},
+}
+
+func ConvertPullRequests(taskCtx core.SubTaskContext) error {
+ db := taskCtx.GetDal()
+ data := taskCtx.GetData().(*BitbucketTaskData)
+ repoId := data.Repo.BitbucketId
+
+ cursor, err := db.Cursor(
+ dal.From(&models.BitbucketPullRequest{}),
+ dal.Where("repo_id = ? and connection_id = ?", repoId, data.Options.ConnectionId),
+ )
+ if err != nil {
+ return err
+ }
+ defer cursor.Close()
+
+ prIdGen := didgen.NewDomainIdGenerator(&models.BitbucketPullRequest{})
+ repoIdGen := didgen.NewDomainIdGenerator(&models.BitbucketRepo{})
+ domainUserIdGen := didgen.NewDomainIdGenerator(&models.BitbucketAccount{})
+
+ converter, err := helper.NewDataConverter(helper.DataConverterArgs{
+ InputRowType: reflect.TypeOf(models.BitbucketPullRequest{}),
+ Input: cursor,
+ RawDataSubTaskArgs: helper.RawDataSubTaskArgs{
+ Ctx: taskCtx,
+ Params: BitbucketApiParams{
+ ConnectionId: data.Options.ConnectionId,
+ Owner: data.Options.Owner,
+ Repo: data.Options.Repo,
+ },
+ Table: RAW_PULL_REQUEST_TABLE,
+ },
+ Convert: func(inputRow interface{}) ([]interface{}, error) {
+ pr := inputRow.(*models.BitbucketPullRequest)
+ domainPr := &code.PullRequest{
+ DomainEntity: domainlayer.DomainEntity{
+ Id: prIdGen.Generate(data.Options.ConnectionId, pr.BitbucketId),
+ },
+ BaseRepoId: repoIdGen.Generate(data.Options.ConnectionId, pr.BaseRepoId),
+ HeadRepoId: repoIdGen.Generate(data.Options.ConnectionId, pr.HeadRepoId),
+ Status: pr.State,
+ Title: pr.Title,
+ Url: pr.Url,
+ AuthorId: domainUserIdGen.Generate(data.Options.ConnectionId, pr.AuthorId),
+ AuthorName: pr.AuthorName,
+ Description: pr.Description,
+ CreatedDate: pr.BitbucketCreatedAt,
+ MergedDate: pr.MergedAt,
+ ClosedDate: pr.ClosedAt,
+ PullRequestKey: pr.Number,
+ Type: pr.Type,
+ Component: pr.Component,
+ MergeCommitSha: pr.MergeCommitSha,
+ BaseRef: pr.BaseRef,
+ BaseCommitSha: pr.BaseCommitSha,
+ HeadRef: pr.HeadRef,
+ HeadCommitSha: pr.HeadCommitSha,
+ }
+ return []interface{}{
+ domainPr,
+ }, nil
+ },
+ })
+ if err != nil {
+ return err
+ }
+
+ return converter.Execute()
+}
diff --git a/plugins/bitbucket/tasks/pr_extractor.go b/plugins/bitbucket/tasks/pr_extractor.go
new file mode 100644
index 00000000..360739a1
--- /dev/null
+++ b/plugins/bitbucket/tasks/pr_extractor.go
@@ -0,0 +1,166 @@
+/*
+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 tasks
+
+import (
+ "encoding/json"
+ "github.com/apache/incubator-devlake/plugins/bitbucket/models"
+ "github.com/apache/incubator-devlake/plugins/core"
+ "github.com/apache/incubator-devlake/plugins/helper"
+ "time"
+)
+
+var ExtractApiPullRequestsMeta = core.SubTaskMeta{
+ Name: "extractApiPullRequests",
+ EntryPoint: ExtractApiPullRequests,
+ EnabledByDefault: true,
+ Required: true,
+ Description: "Extract raw PullRequests data into tool layer table bitbucket_pull_requests",
+ DomainTypes: []string{core.DOMAIN_TYPE_CODE},
+}
+
+type BitbucketApiPullRequest struct {
+ BitbucketId int `json:"id"`
+ CommentCount int `json:"comment_count"`
+ TaskCount int `json:"task_count"`
+ Type string `json:"type"`
+ State string `json:"state"`
+ Title string `json:"title"`
+ Description string `json:"description"`
+ MergeCommit *struct {
+ Type string
+ Hash string `json:"hash"`
+ Links *struct {
+ Self struct{ Href string }
+ Html struct{ Href string }
+ }
+ } `json:"merge_commit"`
+ Links *struct {
+ Self struct{ Href string }
+ Html struct{ Href string }
+ }
+ ClosedBy *BitbucketAccountResponse `json:"closed_by"`
+ Author *BitbucketAccountResponse `json:"author"`
+ BitbucketCreatedAt time.Time `json:"created_on"`
+ BitbucketUpdatedAt time.Time `json:"updated_on"`
+ BaseRef *struct {
+ Branch struct {
+ Name string
+ } `json:"branch"`
+ Commit struct {
+ Type string
+ Hash string
+ } `json:"commit"`
+ Repo *BitbucketApiRepo `json:"repository"`
+ } `json:"destination"`
+ HeadRef *struct {
+ Branch struct {
+ Name string
+ } `json:"branch"`
+ Commit struct {
+ Type string
+ Hash string
+ } `json:"commit"`
+ Repo *BitbucketApiRepo `json:"repository"`
+ } `json:"source"`
+ Reviewers []BitbucketAccountResponse `json:"reviewers"`
+ Participants []BitbucketAccountResponse `json:"participants"`
+}
+
+func ExtractApiPullRequests(taskCtx core.SubTaskContext) error {
+ data := taskCtx.GetData().(*BitbucketTaskData)
+ var err error
+ extractor, err := helper.NewApiExtractor(helper.ApiExtractorArgs{
+ RawDataSubTaskArgs: helper.RawDataSubTaskArgs{
+ Ctx: taskCtx,
+ /*
+ This struct will be JSONEncoded and stored into database along with raw data itself, to identity minimal
+ set of data to be process, for example, we process JiraIssues by Board
+ */
+ Params: BitbucketApiParams{
+ ConnectionId: data.Options.ConnectionId,
+ Owner: data.Options.Owner,
+ Repo: data.Options.Repo,
+ },
+ /*
+ Table store raw data
+ */
+ Table: RAW_PULL_REQUEST_TABLE,
+ },
+ Extract: func(row *helper.RawData) ([]interface{}, error) {
+ rawL := &BitbucketApiPullRequest{}
+ err := json.Unmarshal(row.Data, rawL)
+ if err != nil {
+ return nil, err
+ }
+ // need to extract 2 kinds of entities here
+ results := make([]interface{}, 0, 1)
+ if rawL.BitbucketId == 0 {
+ return nil, nil
+ }
+
+ bitbucketPr, err := convertBitbucketPullRequest(rawL, data.Options.ConnectionId, data.Repo.BitbucketId)
+ if err != nil {
+ return nil, err
+ }
+ if rawL.Author != nil {
+ bitbucketUser, err := convertAccount(rawL.Author, data.Options.ConnectionId)
+ if err != nil {
+ return nil, err
+ }
+ results = append(results, bitbucketUser)
+ bitbucketPr.AuthorName = bitbucketUser.DisplayName
+ bitbucketPr.AuthorId = bitbucketUser.AccountId
+ }
+ if rawL.MergeCommit != nil {
+ bitbucketPr.MergeCommitSha = rawL.MergeCommit.Hash
+ }
+ results = append(results, bitbucketPr)
+
+ return results, nil
+ },
+ })
+
+ if err != nil {
+ return err
+ }
+
+ return extractor.Execute()
+}
+func convertBitbucketPullRequest(pull *BitbucketApiPullRequest, connId uint64, repoId string) (*models.BitbucketPullRequest, error) {
+ bitbucketPull := &models.BitbucketPullRequest{
+ ConnectionId: connId,
+ BitbucketId: pull.BitbucketId,
+ RepoId: repoId,
+ BaseRepoId: pull.BaseRef.Repo.FullName,
+ HeadRepoId: pull.HeadRef.Repo.FullName,
+ State: pull.State,
+ Title: pull.Title,
+ Description: pull.Description,
+ Url: pull.Links.Html.Href,
+ Type: pull.Type,
+ CommentCount: pull.CommentCount,
+ BitbucketCreatedAt: pull.BitbucketCreatedAt,
+ BitbucketUpdatedAt: pull.BitbucketUpdatedAt,
+ BaseRef: pull.BaseRef.Branch.Name,
+ BaseCommitSha: pull.BaseRef.Commit.Hash,
+ HeadRef: pull.HeadRef.Branch.Name,
+ HeadCommitSha: pull.HeadRef.Commit.Hash,
+ }
+ return bitbucketPull, nil
+}
diff --git a/plugins/bitbucket/tasks/repo_collector.go b/plugins/bitbucket/tasks/repo_collector.go
new file mode 100644
index 00000000..efa7b8e7
--- /dev/null
+++ b/plugins/bitbucket/tasks/repo_collector.go
@@ -0,0 +1,63 @@
+/*
+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 tasks
+
+import (
+ "encoding/json"
+ "github.com/apache/incubator-devlake/plugins/helper"
+ "io/ioutil"
+ "net/http"
+
+ "github.com/apache/incubator-devlake/plugins/core"
+)
+
+const RAW_REPOSITORIES_TABLE = "bitbucket_api_repositories"
+
+var CollectApiRepoMeta = core.SubTaskMeta{
+ Name: "collectApiRepo",
+ EntryPoint: CollectApiRepositories,
+ Required: true,
+ Description: "Collect repositories data from Bitbucket api",
+ DomainTypes: core.DOMAIN_TYPES,
+}
+
+func CollectApiRepositories(taskCtx core.SubTaskContext) error {
+ rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, RAW_REPOSITORIES_TABLE)
+
+ collector, err := helper.NewApiCollector(helper.ApiCollectorArgs{
+ RawDataSubTaskArgs: *rawDataSubTaskArgs,
+ ApiClient: data.ApiClient,
+
+ UrlTemplate: "repositories/{{ .Params.Owner }}/{{ .Params.Repo }}",
+ Query: GetQuery,
+ ResponseParser: func(res *http.Response) ([]json.RawMessage, error) {
+ body, err := ioutil.ReadAll(res.Body)
+ res.Body.Close()
+ if err != nil {
+ return nil, err
+ }
+ return []json.RawMessage{body}, nil
+ },
+ })
+
+ if err != nil {
+ return err
+ }
+
+ return collector.Execute()
+}
diff --git a/plugins/bitbucket/tasks/repo_convertor.go b/plugins/bitbucket/tasks/repo_convertor.go
new file mode 100644
index 00000000..1890e8d2
--- /dev/null
+++ b/plugins/bitbucket/tasks/repo_convertor.go
@@ -0,0 +1,106 @@
+/*
+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 tasks
+
+import (
+ "fmt"
+ "reflect"
+
+ "github.com/apache/incubator-devlake/plugins/core/dal"
+
+ "github.com/apache/incubator-devlake/models/domainlayer"
+ "github.com/apache/incubator-devlake/models/domainlayer/code"
+ "github.com/apache/incubator-devlake/models/domainlayer/didgen"
+ "github.com/apache/incubator-devlake/models/domainlayer/ticket"
+ "github.com/apache/incubator-devlake/plugins/bitbucket/models"
+ "github.com/apache/incubator-devlake/plugins/core"
+ "github.com/apache/incubator-devlake/plugins/helper"
+)
+
+var ConvertRepoMeta = core.SubTaskMeta{
+ Name: "convertRepo",
+ EntryPoint: ConvertRepo,
+ EnabledByDefault: true,
+ Description: "Convert tool layer table bitbucket_repos into domain layer table repos and boards",
+ DomainTypes: core.DOMAIN_TYPES,
+}
+
+func ConvertRepo(taskCtx core.SubTaskContext) error {
+ db := taskCtx.GetDal()
+ data := taskCtx.GetData().(*BitbucketTaskData)
+ repoId := data.Repo.BitbucketId
+
+ cursor, err := db.Cursor(
+ dal.From(&models.BitbucketRepo{}),
+ dal.Where("bitbucket_id = ?", repoId),
+ )
+ if err != nil {
+ return err
+ }
+ defer cursor.Close()
+
+ repoIdGen := didgen.NewDomainIdGenerator(&models.BitbucketRepo{})
+
+ converter, err := helper.NewDataConverter(helper.DataConverterArgs{
+ InputRowType: reflect.TypeOf(models.BitbucketRepo{}),
+ Input: cursor,
+ RawDataSubTaskArgs: helper.RawDataSubTaskArgs{
+ Ctx: taskCtx,
+ Params: BitbucketApiParams{
+ ConnectionId: data.Options.ConnectionId,
+ Owner: data.Options.Owner,
+ Repo: data.Options.Repo,
+ },
+ Table: RAW_REPOSITORIES_TABLE,
+ },
+ Convert: func(inputRow interface{}) ([]interface{}, error) {
+ repository := inputRow.(*models.BitbucketRepo)
+ domainRepository := &code.Repo{
+ DomainEntity: domainlayer.DomainEntity{
+ Id: repoIdGen.Generate(data.Options.ConnectionId, repository.BitbucketId),
+ },
+ Name: repository.Name,
+ Url: repository.HTMLUrl,
+ Description: repository.Description,
+ Language: repository.Language,
+ CreatedDate: repository.CreatedDate,
+ UpdatedDate: repository.UpdatedDate,
+ }
+
+ domainBoard := &ticket.Board{
+ DomainEntity: domainlayer.DomainEntity{
+ Id: repoIdGen.Generate(data.Options.ConnectionId, repository.BitbucketId),
+ },
+ Name: repository.Name,
+ Url: fmt.Sprintf("%s/%s", repository.HTMLUrl, "issues"),
+ Description: repository.Description,
+ CreatedDate: &repository.CreatedDate,
+ }
+
+ return []interface{}{
+ domainRepository,
+ domainBoard,
+ }, nil
+ },
+ })
+ if err != nil {
+ return err
+ }
+
+ return converter.Execute()
+}
diff --git a/plugins/bitbucket/tasks/repo_extractor.go b/plugins/bitbucket/tasks/repo_extractor.go
new file mode 100644
index 00000000..40f94112
--- /dev/null
+++ b/plugins/bitbucket/tasks/repo_extractor.go
@@ -0,0 +1,126 @@
+/*
+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 tasks
+
+import (
+ "encoding/json"
+ "fmt"
+ "time"
+
+ "github.com/apache/incubator-devlake/plugins/bitbucket/models"
+ "github.com/apache/incubator-devlake/plugins/core"
+ "github.com/apache/incubator-devlake/plugins/helper"
+)
+
+var ExtractApiRepoMeta = core.SubTaskMeta{
+ Name: "extractApiRepo",
+ EntryPoint: ExtractApiRepositories,
+ Required: true,
+ Description: "Extract raw Repositories data into tool layer table bitbucket_repos",
+ DomainTypes: core.DOMAIN_TYPES,
+}
+
+type ApiRepoResponse BitbucketApiRepo
+
+type BitbucketApiRepo struct {
+ BitbucketId string
+ Scm string `json:"scm"`
+ HasWiki bool `json:"has_wiki"`
+ Links struct {
+ Clone []struct {
+ Href string
+ Name string
+ } `json:"clone"`
+ Self struct {
+ Href string
+ } `json:"self"`
+
+ Html struct {
+ Href string
+ } `json:"html"`
+ } `json:"links"`
+ Uuid string `json:"uuid"`
+ FullName string `json:"full_name"`
+ Language string `json:"language"`
+ Description string `json:"description"`
+ Type string `json:"type"`
+ HasIssue bool `json:"has_issue"`
+ ForkPolicy string `json:"fork_policy"`
+ Owner models.BitbucketUser
+ CreatedAt time.Time `json:"created_on"`
+ UpdatedAt *time.Time `json:"updated_on"`
+}
+
+func ExtractApiRepositories(taskCtx core.SubTaskContext) error {
+ data := taskCtx.GetData().(*BitbucketTaskData)
+ extractor, err := helper.NewApiExtractor(helper.ApiExtractorArgs{
+ RawDataSubTaskArgs: helper.RawDataSubTaskArgs{
+ Ctx: taskCtx,
+ /*
+ This struct will be JSONEncoded and stored into database along with raw data itself, to identity minimal
+ set of data to be process, for example, we process JiraIssues by Board
+ */
+ Params: BitbucketApiParams{
+ ConnectionId: data.Options.ConnectionId,
+ Owner: data.Options.Owner,
+ Repo: data.Options.Repo,
+ },
+ /*
+ Table store raw data
+ */
+ Table: RAW_REPOSITORIES_TABLE,
+ },
+ Extract: func(row *helper.RawData) ([]interface{}, error) {
+ body := &ApiRepoResponse{}
+ err := json.Unmarshal(row.Data, body)
+ if err != nil {
+ return nil, err
+ }
+ if body.FullName == "" {
+ return nil, fmt.Errorf("repo %s/%s not found", data.Options.Owner, data.Options.Repo)
+ }
+ results := make([]interface{}, 0, 1)
+ bitbucketRepository := &models.BitbucketRepo{
+ ConnectionId: data.Options.ConnectionId,
+ BitbucketId: "repositories/" + body.FullName,
+ Name: body.FullName,
+ HTMLUrl: body.Links.Html.Href,
+ Description: body.Description,
+ OwnerId: body.Owner.AccountId,
+ Language: body.Language,
+ CreatedDate: body.CreatedAt,
+ UpdatedDate: body.UpdatedAt,
+ }
+ data.Repo = bitbucketRepository
+
+ results = append(results, bitbucketRepository)
+
+ parentTaskContext := taskCtx.TaskContext()
+ if parentTaskContext != nil {
+ parentTaskContext.GetData().(*BitbucketTaskData).Repo = bitbucketRepository
+ }
+ return results, nil
+ },
+ })
+
+ if err != nil {
+ return err
+ }
+
+ return extractor.Execute()
+}
diff --git a/plugins/bitbucket/tasks/task_data.go b/plugins/bitbucket/tasks/task_data.go
new file mode 100644
index 00000000..e28968b6
--- /dev/null
+++ b/plugins/bitbucket/tasks/task_data.go
@@ -0,0 +1,90 @@
+/*
+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 tasks
+
+import (
+ "fmt"
+ "time"
+
+ "github.com/apache/incubator-devlake/plugins/bitbucket/models"
+ "github.com/apache/incubator-devlake/plugins/helper"
+ "github.com/mitchellh/mapstructure"
+)
+
+type BitbucketOptions struct {
+ ConnectionId uint64 `json:"connectionId"`
+ Tasks []string `json:"tasks,omitempty"`
+ Since string
+ Owner string
+ Repo string
+ models.TransformationRules `mapstructure:"transformationRules" json:"transformationRules"`
+}
+
+type BitbucketTaskData struct {
+ Options *BitbucketOptions
+ ApiClient *helper.ApiAsyncClient
+ Since *time.Time
+ Repo *models.BitbucketRepo
+}
+
+func DecodeAndValidateTaskOptions(options map[string]interface{}) (*BitbucketOptions, error) {
+ var op BitbucketOptions
+ err := mapstructure.Decode(options, &op)
+ if err != nil {
+ return nil, err
+ }
+ if op.Owner == "" {
+ return nil, fmt.Errorf("owner is required for Bitbucket execution")
+ }
+ if op.Repo == "" {
+ return nil, fmt.Errorf("repo is required for Bitbucket execution")
+ }
+ if op.PrType == "" {
+ op.PrType = "type/(.*)$"
+ }
+ if op.PrComponent == "" {
+ op.PrComponent = "component/(.*)$"
+ }
+ if op.PrBodyClosePattern == "" {
+ op.PrBodyClosePattern = "(?mi)(fix|close|resolve|fixes|closes|resolves|fixed|closed|resolved)[\\s]*.*(((and )?(#|https:\\/\\/api.bitbucket.org\\/2.0\\/%s\\/%s\\/issues\\/)\\d+[ ]*)+)"
+ }
+ if op.IssueSeverity == "" {
+ op.IssueSeverity = "severity/(.*)$"
+ }
+ if op.IssuePriority == "" {
+ op.IssuePriority = "^(highest|high|medium|low)$"
+ }
+ if op.IssueComponent == "" {
+ op.IssueComponent = "component/(.*)$"
+ }
+ if op.IssueTypeBug == "" {
+ op.IssueTypeBug = "^(bug|failure|error)$"
+ }
+ if op.IssueTypeIncident == "" {
+ op.IssueTypeIncident = ""
+ }
+ if op.IssueTypeRequirement == "" {
+ op.IssueTypeRequirement = "^(feat|feature|proposal|requirement)$"
+ }
+
+ // find the needed Bitbucket now
+ if op.ConnectionId == 0 {
+ return nil, fmt.Errorf("connectionId is invalid")
+ }
+ return &op, nil
+}
diff --git a/plugins/bitbucket/utils/utils.go b/plugins/bitbucket/utils/utils.go
new file mode 100644
index 00000000..14675b1c
--- /dev/null
+++ b/plugins/bitbucket/utils/utils.go
@@ -0,0 +1,152 @@
+/*
+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 utils
+
+import (
+ "errors"
+ "github.com/apache/incubator-devlake/plugins/helper"
+ "net/http"
+ "regexp"
+ "strconv"
+ "strings"
+ "time"
+)
+
+type PagingInfo struct {
+ Next int
+ Last int
+ First int
+ Prev int
+}
+
+type RateLimitInfo struct {
+ Date time.Time
+ ResetTime time.Time
+ Remaining int
+}
+
+func GetTotalPagesFromResponse(res *http.Response, args *helper.ApiCollectorArgs) (int, error) {
+ link := res.Header.Get("link")
+ pageInfo, err := GetPagingFromLinkHeader(link)
+ if err != nil {
+ return 0, nil
+ }
+ return pageInfo.Last, nil
+}
+
+func ConvertRateLimitInfo(date string, resetTime string, remaining string) (RateLimitInfo, error) {
+ var rateLimitInfo RateLimitInfo
+ var err error
+ if date != "" {
+ rateLimitInfo.Date, err = http.ParseTime(date)
+ if err != nil {
+ return rateLimitInfo, err
+ }
+ } else {
+ return rateLimitInfo, errors.New("rate limit date was an empty string")
+ }
+ if resetTime != "" {
+ resetInt, err := strconv.ParseInt(resetTime, 10, 64)
+ if err != nil {
+ return rateLimitInfo, err
+ }
+ rateLimitInfo.ResetTime = time.Unix(resetInt, 0)
+ } else {
+ return rateLimitInfo, errors.New("rate limit reset time was an empty string")
+ }
+ if remaining != "" {
+ rateLimitInfo.Remaining, err = strconv.Atoi(remaining)
+ if err != nil {
+ return rateLimitInfo, err
+ }
+ } else {
+ return rateLimitInfo, errors.New("rate remaining was an empty string")
+ }
+ return rateLimitInfo, nil
+}
+
+func GetRateLimitPerSecond(info RateLimitInfo) int {
+ unixResetTime := info.ResetTime.Unix()
+ unixNow := info.Date.Unix()
+ timeBetweenNowAndReset := unixResetTime - unixNow
+ // Adjust the remaining to be less then actual to avoid hitting the limit exactly.
+ multiplier := 0.98
+ adjustedRemaining := float64(info.Remaining) * multiplier
+ return int(adjustedRemaining / float64(timeBetweenNowAndReset)) //* multiplier
+}
+func ConvertStringToInt(input string) (int, error) {
+ return strconv.Atoi(input)
+}
+func GetPagingFromLinkHeader(link string) (PagingInfo, error) {
+ result := PagingInfo{
+ Next: 1,
+ Last: 1,
+ Prev: 1,
+ First: 1,
+ }
+ linksArray := strings.Split(link, ",")
+ pattern1 := regexp.MustCompile(`page=*[0-9]+`)
+ pattern2 := regexp.MustCompile(`rel="*[a-z]+`)
+ if len(linksArray) >= 2 {
+ for i := 0; i < len(linksArray); i++ {
+ content := []byte(linksArray[i])
+ loc1 := pattern1.FindIndex(content)
+ loc2 := pattern2.FindIndex(content)
+ if len(loc1) >= 2 && len(loc2) >= 2 {
+ pageNumberSubstring := string(content[loc1[0]:loc1[1]])
+ pageNumberString := strings.Replace(pageNumberSubstring, `page=`, ``, 1)
+ pageNameSubstring := string(content[loc2[0]:loc2[1]])
+ pageNameString := strings.Replace(pageNameSubstring, `rel="`, ``, 1)
+
+ pageNumberInt, convertErr := ConvertStringToInt(pageNumberString)
+ if convertErr != nil {
+ return result, convertErr
+ }
+ switch pageNameString {
+ case "next":
+ result.Next = pageNumberInt
+
+ case "first":
+ result.First = pageNumberInt
+
+ case "last":
+ result.Last = pageNumberInt
+
+ case "prev":
+ result.Prev = pageNumberInt
+ }
+
+ } else {
+ return result, errors.New("parsed string values aren't long enough")
+ }
+ }
+ return result, nil
+ } else {
+ return result, errors.New("the link string provided is invalid. There is likely no next page of data to fetch")
+ }
+}
+
+func GetIssueIdByIssueUrl(s string) (int, error) {
+ regex := regexp.MustCompile(`.*/issues/(\d+)`)
+ groups := regex.FindStringSubmatch(s)
+ if len(groups) > 0 {
+ return strconv.Atoi(groups[1])
+ } else {
+ return 0, errors.New("invalid issue url")
+ }
+}