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

klesh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git


The following commit(s) were added to refs/heads/main by this push:
     new 264fb6b1 add e2e raw table generator and change the e2e comparison way 
(#2356)
264fb6b1 is described below

commit 264fb6b14ef2fa40eae8d88a56470b617271907d
Author: likyh <[email protected]>
AuthorDate: Mon Jun 27 11:54:36 2022 +0800

    add e2e raw table generator and change the e2e comparison way (#2356)
    
    * add raw generator and update e2e test compare way
    
    * fix old Verify call
    
    * append
    
    * fix ci
    
    * finish create-e2e-raw generator
    
    Co-authored-by: linyh <[email protected]>
---
 generator/README.md                                |   7 +
 generator/cmd/create_migration.go                  |  10 +-
 generator/cmd/create_plugin.go                     |  17 +++
 generator/cmd/e2e_raw_create.go                    | 104 +++++++++++++++
 generator/cmd/init_migration.go                    |  11 +-
 generator/docs/generator.md                        |   5 +-
 generator/docs/generator_create-e2e-raw.md         |  31 +++++
 generator/docs/generator_init-migration.md         |   6 +-
 helpers/e2ehelper/data_flow_tester.go              | 144 ++++++++++++++-------
 helpers/e2ehelper/data_flow_tester_test.go         |   2 +-
 plugins/ae/e2e/commit_test.go                      |   4 +-
 plugins/ae/e2e/project_test.go                     |   3 +-
 plugins/github/e2e/comment_test.go                 |  10 +-
 plugins/github/e2e/event_test.go                   |   3 +-
 plugins/github/e2e/issue_test.go                   |  12 +-
 plugins/github/e2e/pr_commit_test.go               |   6 +-
 plugins/github/e2e/pr_enrich_issue_test.go         |   7 +-
 plugins/github/e2e/pr_reviewer_test.go             |   4 +-
 plugins/github/e2e/pr_test.go                      |   8 +-
 .../e2e/raw_tables/_raw_github_api_issues.csv      |  84 ++++++------
 plugins/github/e2e/repo_test.go                    |   7 +-
 plugins/gitlab/e2e/issues_test.go                  |  14 +-
 plugins/gitlab/e2e/mr_commits_test.go              |  23 ++--
 plugins/gitlab/e2e/mr_notes_test.go                |  13 +-
 plugins/gitlab/e2e/mr_test.go                      |  11 +-
 plugins/gitlab/e2e/pipelines_test.go               |   3 +-
 plugins/gitlab/e2e/project_test.go                 |   5 +-
 plugins/jenkins/e2e/builds_test.go                 |   5 +-
 plugins/jenkins/e2e/jobs_test.go                   |   2 -
 .../e2e/snapshot_tables/_tool_jenkins_jobs.csv     |  24 ++--
 plugins/jenkins/e2e/snapshot_tables/builds.csv     |  42 +++---
 plugins/jenkins/e2e/snapshot_tables/jobs.csv       |  24 ++--
 plugins/jira/e2e/board_test.go                     |   2 -
 plugins/jira/e2e/issue_test.go                     |   5 +-
 plugins/jira/e2e/project_test.go                   |   1 -
 plugins/tapd/e2e/bug_changelog_test.go             |   8 +-
 plugins/tapd/e2e/bug_custom_field_test.go          |   3 +-
 plugins/tapd/e2e/bugs_commit_test.go               |   5 +-
 plugins/tapd/e2e/bugs_test.go                      |  21 +--
 plugins/tapd/e2e/company_test.go                   |   3 +-
 plugins/tapd/e2e/iterations_test.go                |   9 +-
 plugins/tapd/e2e/stories_commit_test.go            |   5 +-
 plugins/tapd/e2e/stories_test.go                   |  21 +--
 plugins/tapd/e2e/story_and_bug_status_test.go      |   8 +-
 plugins/tapd/e2e/story_bug_test.go                 |   2 -
 plugins/tapd/e2e/story_category_test.go            |   3 +-
 plugins/tapd/e2e/story_changelog_test.go           |   7 +-
 plugins/tapd/e2e/story_custom_field_test.go        |   3 +-
 plugins/tapd/e2e/sub_workspace_test.go             |   5 +-
 plugins/tapd/e2e/task_custom_field_test.go         |   3 +-
 plugins/tapd/e2e/tasks_commit_test.go              |   5 +-
 plugins/tapd/e2e/tasks_test.go                     |  17 +--
 plugins/tapd/e2e/user_test.go                      |   6 +-
 plugins/tapd/e2e/worklog_test.go                   |   5 +-
 54 files changed, 496 insertions(+), 302 deletions(-)

diff --git a/generator/README.md b/generator/README.md
index 0f1894d4..775d99ac 100644
--- a/generator/README.md
+++ b/generator/README.md
@@ -29,6 +29,13 @@ Usage Gif:
 Usage Gif:
 
![usage](https://user-images.githubusercontent.com/3294100/175537358-083809ce-9862-41f1-86e9-41448a44eaae.gif)
 
+## E2E Test Related
+
+* [create-e2e-raw](./docs/generator_create-e2e-raw.md)      - Export raw table 
to csv in E2E test
+
+Usage Gif:
+![usage](https://user-images.githubusercontent.com/3294100/175849225-12af5251-6181-4cd9-ba72-26087b05ee73.gif)
+
 ## Others
 
 * [completion](./docs/generator_completion.md)               - Generate the 
autocompletion script for the specified shell
diff --git a/generator/cmd/create_migration.go 
b/generator/cmd/create_migration.go
index 07f10b5a..5ec3cc7c 100644
--- a/generator/cmd/create_migration.go
+++ b/generator/cmd/create_migration.go
@@ -45,19 +45,13 @@ If framework passed, generator will create a new migration 
in models/migrationsc
                var purpose string
                var err error
 
-               // try to get plugin name and extractor name
+               // try to get plugin name
                if len(args) > 0 {
                        pluginName = args[0]
                }
                if pluginName == `` {
-                       files, err := ioutil.ReadDir(`plugins`)
+                       pluginItems, err := pluginNames(false)
                        cobra.CheckErr(err)
-                       pluginItems := []string{"framework"}
-                       for _, file := range files {
-                               if file.IsDir() {
-                                       pluginItems = append(pluginItems, 
file.Name())
-                               }
-                       }
                        prompt := promptui.Select{
                                Label: "plugin_name",
                                Items: pluginItems,
diff --git a/generator/cmd/create_plugin.go b/generator/cmd/create_plugin.go
index 5fce2803..aebfcefd 100644
--- a/generator/cmd/create_plugin.go
+++ b/generator/cmd/create_plugin.go
@@ -23,6 +23,7 @@ import (
        "github.com/apache/incubator-devlake/generator/util"
        "github.com/manifoldco/promptui"
        "github.com/spf13/cobra"
+       "io/ioutil"
        "os"
        "regexp"
        "strings"
@@ -61,6 +62,22 @@ func pluginNameExistValidate(input string) error {
        return err
 }
 
+func pluginNames(withFramework bool) (pluginItems []string, err error) {
+       files, err := ioutil.ReadDir(`plugins`)
+       if err != nil {
+               return nil, err
+       }
+       if withFramework {
+               pluginItems = append(pluginItems, `framework`)
+       }
+       for _, file := range files {
+               if file.IsDir() {
+                       pluginItems = append(pluginItems, file.Name())
+               }
+       }
+       return pluginItems, nil
+}
+
 var createPluginCmd = &cobra.Command{
        Use:   "create-plugin [plugin_name]",
        Short: "Create a new plugin",
diff --git a/generator/cmd/e2e_raw_create.go b/generator/cmd/e2e_raw_create.go
new file mode 100644
index 00000000..9102fde7
--- /dev/null
+++ b/generator/cmd/e2e_raw_create.go
@@ -0,0 +1,104 @@
+/*
+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 cmd
+
+import (
+       "errors"
+       "github.com/apache/incubator-devlake/helpers/e2ehelper"
+       "github.com/manifoldco/promptui"
+       "github.com/spf13/cobra"
+       "path/filepath"
+       "strings"
+)
+
+func init() {
+       rootCmd.AddCommand(createE2eRawCmd)
+}
+
+var createE2eRawCmd = &cobra.Command{
+       Use:   "create-e2e-raw [plugin_name] [raw_table_name]",
+       Short: "Create _raw_table.csv for e2e test",
+       Long: `Create _raw_table.csv for e2e test
+Type in what the raw_table is, then generator will export and save in 
plugins/$plugin_name/e2e/_raw_$raw_name.csv for you.`,
+       Run: func(cmd *cobra.Command, args []string) {
+               var pluginName, rawTableName, csvFileName string
+               var err error
+
+               // try to get plugin name
+               if len(args) > 0 {
+                       pluginName = args[0]
+               }
+               if pluginName == `` {
+                       pluginItems, err := pluginNames(false)
+                       cobra.CheckErr(err)
+                       prompt := promptui.Select{
+                               Label: "plugin_name",
+                               Items: pluginItems,
+                       }
+                       _, pluginName, err = prompt.Run()
+                       cobra.CheckErr(err)
+               }
+
+               // try to get rawTableName
+               if len(args) > 1 {
+                       rawTableName = args[1]
+               }
+               if rawTableName == `` {
+                       prompt := promptui.Prompt{
+                               Label:   "raw_table_name",
+                               Default: `_raw_`,
+                               Validate: func(input string) error {
+                                       if input == `` {
+                                               return 
errors.New("raw_table_name requite")
+                                       }
+                                       if !strings.HasPrefix(input, `_raw_`) {
+                                               return 
errors.New("raw_table_name should start with `_raw_`")
+                                       }
+                                       return nil
+                               },
+                       }
+                       rawTableName, err = prompt.Run()
+                       cobra.CheckErr(err)
+               }
+
+               // try to get rawTableName
+               prompt := promptui.Prompt{
+                       Label:   "csv_file_name",
+                       Default: rawTableName + `.csv`,
+                       Validate: func(input string) error {
+                               if input == `` {
+                                       return errors.New("csv_file_name 
requite")
+                               }
+                               if !strings.HasSuffix(input, `.csv`) {
+                                       return errors.New("csv_file_name should 
end with `.csv`")
+                               }
+                               return nil
+                       },
+               }
+               csvFileName, err = prompt.Run()
+               cobra.CheckErr(err)
+
+               rawTablesPath := filepath.Join(`plugins`, pluginName, `e2e`, 
`raw_tables`)
+               dataflowTester := e2ehelper.NewDataFlowTester(nil, "gitlab", 
nil)
+               dataflowTester.ExportRawTable(
+                       rawTableName,
+                       filepath.Join(rawTablesPath, csvFileName),
+               )
+               println(csvFileName, ` generated in `, rawTablesPath)
+       },
+}
diff --git a/generator/cmd/init_migration.go b/generator/cmd/init_migration.go
index 309e087a..8bc735bc 100644
--- a/generator/cmd/init_migration.go
+++ b/generator/cmd/init_migration.go
@@ -24,7 +24,6 @@ import (
        "github.com/manifoldco/promptui"
        "github.com/spf13/cobra"
        "github.com/stoewer/go-strcase"
-       "io/ioutil"
        "os"
        "path/filepath"
        "time"
@@ -42,19 +41,13 @@ Type in which plugin do you want init migrations in, then 
generator will create
        Run: func(cmd *cobra.Command, args []string) {
                var pluginName string
 
-               // try to get plugin name and extractor name
+               // try to get plugin name
                if len(args) > 0 {
                        pluginName = args[0]
                }
                if pluginName == `` {
-                       files, err := ioutil.ReadDir(`plugins`)
+                       pluginItems, err := pluginNames(false)
                        cobra.CheckErr(err)
-                       var pluginItems []string
-                       for _, file := range files {
-                               if file.IsDir() {
-                                       pluginItems = append(pluginItems, 
file.Name())
-                               }
-                       }
                        prompt := promptui.Select{
                                Label: "plugin_name",
                                Items: pluginItems,
diff --git a/generator/docs/generator.md b/generator/docs/generator.md
index 3e3bb0b1..e0f3e22e 100644
--- a/generator/docs/generator.md
+++ b/generator/docs/generator.md
@@ -14,10 +14,11 @@ Apache DevLake Cli Tool -- Code Generator
 
 * [generator completion](generator_completion.md)       - Generate the 
autocompletion script for the specified shell
 * [generator create-collector](generator_create-collector.md)   - Create a new 
collector
+* [generator create-e2e-raw](generator_create-e2e-raw.md)       - Create 
_raw_table.csv for e2e test
 * [generator create-extractor](generator_create-extractor.md)   - Create a new 
extractor
 * [generator create-migration](generator_create-migration.md)   - Create a new 
migration
 * [generator create-plugin](generator_create-plugin.md)         - Create a new 
plugin
 * [generator generator-doc](generator_generator-doc.md)         - generate 
document for generator
-* [generator init-migration](generator_init-migration.md)       - init 
migration for plugin
+* [generator init-migration](generator_init-migration.md)       - Init 
migration for plugin
 
-###### Auto generated by spf13/cobra on 24-Jun-2022
+###### Auto generated by spf13/cobra on 27-Jun-2022
diff --git a/generator/docs/generator_create-e2e-raw.md 
b/generator/docs/generator_create-e2e-raw.md
new file mode 100644
index 00000000..0c0534cc
--- /dev/null
+++ b/generator/docs/generator_create-e2e-raw.md
@@ -0,0 +1,31 @@
+## generator create-e2e-raw
+
+Create _raw_table.csv for e2e test
+
+### Synopsis
+
+Create _raw_table.csv for e2e test
+Type in what the raw_table is, then generator will export and save in 
plugins/$plugin_name/e2e/_raw_$raw_name.csv for you.
+
+```
+generator create-e2e-raw [plugin_name] [raw_table_name] [csv_file_name] [flags]
+```
+
+### Options
+
+```
+  -h, --help   help for create-e2e-raw
+```
+
+### Options inherited from parent commands
+
+```
+      --config string     config file (default is PROJECT/.env)
+      --modifyExistCode   allow generator modify exist code (default true)
+```
+
+### SEE ALSO
+
+* [generator](generator.md)     - Apache DevLake Cli Tool -- Code Generator
+
+###### Auto generated by spf13/cobra on 27-Jun-2022
diff --git a/generator/docs/generator_init-migration.md 
b/generator/docs/generator_init-migration.md
index 011a80a4..0aefbb06 100644
--- a/generator/docs/generator_init-migration.md
+++ b/generator/docs/generator_init-migration.md
@@ -1,10 +1,10 @@
 ## generator init-migration
 
-init migration for plugin
+Init migration for plugin
 
 ### Synopsis
 
-init migration for plugin
+Init migration for plugin
 Type in which plugin do you want init migrations in, then generator will 
create a init migration in plugins/$plugin_name/models/migrationscripts/ for 
you.
 
 ```
@@ -28,4 +28,4 @@ generator init-migration [plugin_name] [flags]
 
 * [generator](generator.md)     - Apache DevLake Cli Tool -- Code Generator
 
-###### Auto generated by spf13/cobra on 24-Jun-2022
+###### Auto generated by spf13/cobra on 27-Jun-2022
diff --git a/helpers/e2ehelper/data_flow_tester.go 
b/helpers/e2ehelper/data_flow_tester.go
index 1b7dab52..2474b046 100644
--- a/helpers/e2ehelper/data_flow_tester.go
+++ b/helpers/e2ehelper/data_flow_tester.go
@@ -30,11 +30,13 @@ import (
        "github.com/apache/incubator-devlake/plugins/core/dal"
        "github.com/apache/incubator-devlake/plugins/helper"
        "github.com/apache/incubator-devlake/runner"
+       "github.com/apache/incubator-devlake/utils"
        "github.com/spf13/viper"
        "github.com/stretchr/testify/assert"
        "gorm.io/gorm"
        "gorm.io/gorm/schema"
        "os"
+       "strconv"
        "strings"
        "testing"
        "time"
@@ -171,14 +173,30 @@ func (t *DataFlowTester) Subtask(subtaskMeta 
core.SubTaskMeta, taskData interfac
        }
 }
 
+func (t *DataFlowTester) getPkFields(dst schema.Tabler) []string {
+       columnTypes, err := t.Db.Migrator().ColumnTypes(dst)
+       var pkFields []string
+       if err != nil {
+               panic(err)
+       }
+       for _, columnType := range columnTypes {
+               if isPrimaryKey, _ := columnType.PrimaryKey(); isPrimaryKey {
+                       pkFields = append(pkFields, columnType.Name())
+               }
+       }
+       return pkFields
+}
+
 // CreateSnapshot reads rows from database and write them into .csv file.
-func (t *DataFlowTester) CreateSnapshot(dst schema.Tabler, csvRelPath string, 
pkfields []string, targetfields []string) {
+func (t *DataFlowTester) CreateSnapshot(dst schema.Tabler, csvRelPath string, 
targetfields []string) {
        location, _ := time.LoadLocation(`UTC`)
-       allFields := append(pkfields, targetfields...)
+       pkFields := t.getPkFields(dst)
+       allFields := append(pkFields, targetfields...)
+       allFields = utils.StringsUniq(allFields)
        dbCursor, err := t.Dal.Cursor(
                dal.Select(strings.Join(allFields, `,`)),
                dal.From(dst.TableName()),
-               dal.Orderby(strings.Join(pkfields, `,`)),
+               dal.Orderby(strings.Join(pkFields, `,`)),
        )
        if err != nil {
                panic(err)
@@ -233,66 +251,100 @@ func (t *DataFlowTester) CreateSnapshot(dst 
schema.Tabler, csvRelPath string, pk
        }
 }
 
+// ExportRawTable reads rows from raw table and write them into .csv file.
+func (t *DataFlowTester) ExportRawTable(rawTableName string, csvRelPath 
string) {
+       location, _ := time.LoadLocation(`UTC`)
+       allFields := []string{`id`, `params`, `data`, `url`, `input`, 
`created_at`}
+       rawRows := &[]helper.RawData{}
+       err := t.Dal.All(
+               rawRows,
+               dal.Select(`id, params, data, url, input, created_at`),
+               dal.From(rawTableName),
+               dal.Orderby(`id`),
+       )
+       if err != nil {
+               panic(err)
+       }
+
+       csvWriter := pluginhelper.NewCsvFileWriter(csvRelPath, allFields)
+       defer csvWriter.Close()
+
+       for _, rawRow := range *rawRows {
+               csvWriter.Write([]string{
+                       strconv.FormatUint(rawRow.ID, 10),
+                       rawRow.Params,
+                       string(rawRow.Data),
+                       rawRow.Url,
+                       string(rawRow.Input),
+                       
rawRow.CreatedAt.In(location).Format("2006-01-02T15:04:05.000-07:00"),
+               })
+       }
+}
+
+func formatDbValue(value interface{}) string {
+       location, _ := time.LoadLocation(`UTC`)
+       switch value := value.(type) {
+       case time.Time:
+               return 
value.In(location).Format("2006-01-02T15:04:05.000-07:00")
+       case bool:
+               if value {
+                       return `1`
+               } else {
+                       return `0`
+               }
+       default:
+               if value != nil {
+                       return fmt.Sprint(value)
+               }
+       }
+       return ``
+}
+
 // VerifyTable reads rows from csv file and compare with records from database 
one by one. You must specified the
-// Primary Key Fields with `pkfields` so DataFlowTester could select the exact 
record from database, as well as which
-// fields to compare with by specifying `targetfields` parameter.
-func (t *DataFlowTester) VerifyTable(dst schema.Tabler, csvRelPath string, 
pkfields []string, targetfields []string) {
+// Primary Key Fields with `pkFields` so DataFlowTester could select the exact 
record from database, as well as which
+// fields to compare with by specifying `targetFields` parameter.
+func (t *DataFlowTester) VerifyTable(dst schema.Tabler, csvRelPath string, 
targetFields []string) {
        _, err := os.Stat(csvRelPath)
        if os.IsNotExist(err) {
-               t.CreateSnapshot(dst, csvRelPath, pkfields, targetfields)
+               t.CreateSnapshot(dst, csvRelPath, targetFields)
                return
        }
+       pkFields := t.getPkFields(dst)
 
        csvIter := pluginhelper.NewCsvFileIterator(csvRelPath)
-       location, _ := time.LoadLocation(`UTC`)
        defer csvIter.Close()
 
        expectedTotal := int64(0)
+       csvMap := map[string]map[string]interface{}{}
        for csvIter.HasNext() {
                expected := csvIter.Fetch()
-               pkvalues := make([]interface{}, 0, len(pkfields))
-               for _, pkf := range pkfields {
-                       pkvalues = append(pkvalues, expected[pkf])
-               }
-               actual := make(map[string]interface{})
-               where := []string{}
-               for _, field := range pkfields {
-                       where = append(where, fmt.Sprintf(" %s = ?", field))
+               pkValues := make([]string, 0, len(pkFields))
+               for _, pkf := range pkFields {
+                       pkValues = append(pkValues, expected[pkf].(string))
                }
+               pkValueStr := strings.Join(pkValues, `-`)
+               _, ok := csvMap[pkValueStr]
+               assert.False(t.T, ok, fmt.Sprintf(`%s duplicated in csv (with 
params from csv %s)`, dst.TableName(), pkValues))
+               csvMap[pkValueStr] = expected
+       }
 
-               var actualCount int64
-               err := t.Db.Table(dst.TableName()).Where(strings.Join(where, ` 
AND `), pkvalues...).Count(&actualCount).Error
-               if err != nil {
-                       panic(err)
+       dbRows := &[]map[string]interface{}{}
+       err = t.Db.Table(dst.TableName()).Find(dbRows).Error
+       if err != nil {
+               panic(err)
+       }
+       for _, actual := range *dbRows {
+               pkValues := make([]string, 0, len(pkFields))
+               for _, pkf := range pkFields {
+                       pkValues = append(pkValues, formatDbValue(actual[pkf]))
                }
-               assert.Equal(t.T, int64(1), actualCount, fmt.Sprintf(`%s found 
not eq 1 but %d (with params from csv %s)`, dst.TableName(), actualCount, 
pkvalues))
-               if actualCount != 1 {
+               expected, ok := csvMap[strings.Join(pkValues, `-`)]
+               assert.True(t.T, ok, fmt.Sprintf(`%s not found (with params 
from csv %s)`, dst.TableName(), pkValues))
+               if !ok {
                        continue
                }
-
-               err = t.Db.Table(dst.TableName()).Where(strings.Join(where, ` 
AND `), pkvalues...).Find(actual).Error
-               if err != nil {
-                       panic(err)
-               }
-               for _, field := range targetfields {
-                       actualValue := ""
-                       switch actual[field].(type) {
-                       case time.Time:
-                               if actual[field] != nil {
-                                       actualValue = 
actual[field].(time.Time).In(location).Format("2006-01-02T15:04:05.000-07:00")
-                               }
-                       case bool:
-                               if actual[field].(bool) {
-                                       actualValue = `1`
-                               } else {
-                                       actualValue = `0`
-                               }
-                       default:
-                               if actual[field] != nil {
-                                       actualValue = fmt.Sprint(actual[field])
-                               }
-                       }
-                       assert.Equal(t.T, expected[field], actualValue, 
fmt.Sprintf(`%s.%s not match (with params from csv %s)`, dst.TableName(), 
field, pkvalues))
+               for _, field := range targetFields {
+                       assert.Equal(t.T, expected[field], 
formatDbValue(actual[field]), fmt.Sprintf(`%s.%s not match (with params from 
csv %s)`, dst.TableName(), field, pkValues))
                }
                expectedTotal++
        }
diff --git a/helpers/e2ehelper/data_flow_tester_test.go 
b/helpers/e2ehelper/data_flow_tester_test.go
index dc7c7de8..8c0b5c8d 100644
--- a/helpers/e2ehelper/data_flow_tester_test.go
+++ b/helpers/e2ehelper/data_flow_tester_test.go
@@ -46,8 +46,8 @@ func ExampleDataFlowTester() {
        dataflowTester.VerifyTable(
                gitlabModels.GitlabProject{},
                "tables/_tool_gitlab_projects.csv",
-               []string{"gitlab_id"},
                []string{
+                       "gitlab_id",
                        "name",
                        "description",
                        "default_branch",
diff --git a/plugins/ae/e2e/commit_test.go b/plugins/ae/e2e/commit_test.go
index 4b84f6fa..31330240 100644
--- a/plugins/ae/e2e/commit_test.go
+++ b/plugins/ae/e2e/commit_test.go
@@ -48,8 +48,8 @@ func TestAECommitDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.AECommit{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
models.AECommit{}.TableName()),
-               []string{"hex_sha"},
                []string{
+                       "hex_sha",
                        "analysis_id",
                        "author_email",
                        "dev_eq",
@@ -69,8 +69,8 @@ func TestAECommitDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                code.Commit{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
code.Commit{}.TableName()),
-               []string{"sha"},
                []string{
+                       "sha",
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
diff --git a/plugins/ae/e2e/project_test.go b/plugins/ae/e2e/project_test.go
index 19632ec6..149ad58a 100644
--- a/plugins/ae/e2e/project_test.go
+++ b/plugins/ae/e2e/project_test.go
@@ -47,8 +47,9 @@ func TestAEProjectDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.AEProject{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
models.AEProject{}.TableName()),
-               []string{"connection_id", "id"},
                []string{
+                       "connection_id",
+                       "id",
                        "git_url",
                        "priority",
                        "ae_create_time",
diff --git a/plugins/github/e2e/comment_test.go 
b/plugins/github/e2e/comment_test.go
index 8b27a9fa..3b0093c1 100644
--- a/plugins/github/e2e/comment_test.go
+++ b/plugins/github/e2e/comment_test.go
@@ -69,8 +69,9 @@ func TestCommentDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.GithubIssueComment{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
models.GithubIssueComment{}.TableName()),
-               []string{"connection_id", "github_id"},
                []string{
+                       "connection_id",
+                       "github_id",
                        "issue_id",
                        "body",
                        "author_username",
@@ -86,8 +87,9 @@ func TestCommentDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.GithubPullRequestComment{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
models.GithubPullRequestComment{}.TableName()),
-               []string{"connection_id", "github_id"},
                []string{
+                       "connection_id",
+                       "github_id",
                        "pull_request_id",
                        "body",
                        "author_username",
@@ -107,8 +109,8 @@ func TestCommentDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                ticket.IssueComment{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
ticket.IssueComment{}.TableName()),
-               []string{"id"},
                []string{
+                       "id",
                        "issue_id",
                        "body",
                        "user_id",
@@ -122,8 +124,8 @@ func TestCommentDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                code.PullRequestComment{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
code.PullRequestComment{}.TableName()),
-               []string{"id"},
                []string{
+                       "id",
                        "pull_request_id",
                        "body",
                        "user_id",
diff --git a/plugins/github/e2e/event_test.go b/plugins/github/e2e/event_test.go
index 69b5ad9f..fc895684 100644
--- a/plugins/github/e2e/event_test.go
+++ b/plugins/github/e2e/event_test.go
@@ -51,8 +51,9 @@ func TestEventDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.GithubIssueEvent{},
                "./snapshot_tables/_tool_github_issue_events.csv",
-               []string{"connection_id", "github_id"},
                []string{
+                       "connection_id",
+                       "github_id",
                        "issue_id",
                        "type",
                        "author_username",
diff --git a/plugins/github/e2e/issue_test.go b/plugins/github/e2e/issue_test.go
index b045ea55..2cb56140 100644
--- a/plugins/github/e2e/issue_test.go
+++ b/plugins/github/e2e/issue_test.go
@@ -64,8 +64,10 @@ func TestIssueDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.GithubIssue{},
                "./snapshot_tables/_tool_github_issues.csv",
-               []string{"connection_id", "github_id", "repo_id"},
                []string{
+                       "connection_id",
+                       "github_id",
+                       "repo_id",
                        "number",
                        "state",
                        "title",
@@ -93,8 +95,10 @@ func TestIssueDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.GithubIssueLabel{},
                "./snapshot_tables/_tool_github_issue_labels.csv",
-               []string{"connection_id", "issue_id", "label_name"},
                []string{
+                       "connection_id",
+                       "issue_id",
+                       "label_name",
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
@@ -109,8 +113,8 @@ func TestIssueDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                ticket.Issue{},
                "./snapshot_tables/issues.csv",
-               []string{"id"},
                []string{
+                       "id",
                        "url",
                        "icon_url",
                        "issue_key",
@@ -142,7 +146,6 @@ func TestIssueDataFlow(t *testing.T) {
                ticket.BoardIssue{},
                "./snapshot_tables/board_issues.csv",
                []string{"board_id", "issue_id"},
-               []string{},
        )
 
        // verify issue labels conversion
@@ -152,6 +155,5 @@ func TestIssueDataFlow(t *testing.T) {
                ticket.IssueLabel{},
                "./snapshot_tables/issue_labels.csv",
                []string{"issue_id", "label_name"},
-               []string{},
        )
 }
diff --git a/plugins/github/e2e/pr_commit_test.go 
b/plugins/github/e2e/pr_commit_test.go
index 67a95d03..75ea23f7 100644
--- a/plugins/github/e2e/pr_commit_test.go
+++ b/plugins/github/e2e/pr_commit_test.go
@@ -54,8 +54,8 @@ func TestPrCommitDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.GithubCommit{},
                "./snapshot_tables/_tool_github_commits.csv",
-               []string{"sha"},
                []string{
+                       "sha",
                        "author_id",
                        "author_name",
                        "author_email",
@@ -78,8 +78,9 @@ func TestPrCommitDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.GithubPullRequestCommit{},
                "./snapshot_tables/_tool_github_pull_request_commits.csv",
-               []string{"connection_id", "commit_sha"},
                []string{
+                       "connection_id",
+                       "commit_sha",
                        "pull_request_id",
                        "_raw_data_params",
                        "_raw_data_table",
@@ -95,6 +96,5 @@ func TestPrCommitDataFlow(t *testing.T) {
                code.PullRequestCommit{},
                "./snapshot_tables/pull_request_commits.csv",
                []string{"commit_sha", "pull_request_id"},
-               []string{},
        )
 }
diff --git a/plugins/github/e2e/pr_enrich_issue_test.go 
b/plugins/github/e2e/pr_enrich_issue_test.go
index 660ee2bf..4e9214e9 100644
--- a/plugins/github/e2e/pr_enrich_issue_test.go
+++ b/plugins/github/e2e/pr_enrich_issue_test.go
@@ -64,8 +64,10 @@ func TestPrEnrichIssueDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.GithubPullRequestIssue{},
                "./snapshot_tables/_tool_github_pull_request_issues.csv",
-               []string{"connection_id", "pull_request_id", "issue_id"},
                []string{
+                       "connection_id",
+                       "pull_request_id",
+                       "issue_id",
                        "pull_request_number",
                        "issue_number",
                        "_raw_data_params",
@@ -81,8 +83,9 @@ func TestPrEnrichIssueDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                crossdomain.PullRequestIssue{},
                "./snapshot_tables/pull_request_issues.csv",
-               []string{"pull_request_id", "issue_id"},
                []string{
+                       "pull_request_id",
+                       "issue_id",
                        "pull_request_number",
                        "issue_number",
                        "_raw_data_params",
diff --git a/plugins/github/e2e/pr_reviewer_test.go 
b/plugins/github/e2e/pr_reviewer_test.go
index fd2cea50..fec52543 100644
--- a/plugins/github/e2e/pr_reviewer_test.go
+++ b/plugins/github/e2e/pr_reviewer_test.go
@@ -52,8 +52,10 @@ func TestPrReviewerDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.GithubReviewer{},
                "./snapshot_tables/_tool_github_reviewers.csv",
-               []string{"connection_id", "github_id", "pull_request_id"},
                []string{
+                       "connection_id",
+                       "github_id",
+                       "pull_request_id",
                        "login",
                        "_raw_data_params",
                        "_raw_data_table",
diff --git a/plugins/github/e2e/pr_test.go b/plugins/github/e2e/pr_test.go
index ddac5e28..91704f89 100644
--- a/plugins/github/e2e/pr_test.go
+++ b/plugins/github/e2e/pr_test.go
@@ -58,8 +58,10 @@ func TestPrDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.GithubPullRequest{},
                "./snapshot_tables/_tool_github_pull_requests.csv",
-               []string{"connection_id", "github_id", "repo_id"},
                []string{
+                       "connection_id",
+                       "github_id",
+                       "repo_id",
                        "number",
                        "state",
                        "title",
@@ -95,7 +97,6 @@ func TestPrDataFlow(t *testing.T) {
                models.GithubPullRequestLabel{},
                "./snapshot_tables/_tool_github_pull_request_labels.csv",
                []string{"connection_id", "pull_id", "label_name"},
-               []string{},
        )
 
        // verify pr conversion
@@ -104,8 +105,8 @@ func TestPrDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                code.PullRequest{},
                "./snapshot_tables/pull_requests.csv",
-               []string{"id"},
                []string{
+                       "id",
                        "base_repo_id",
                        "head_repo_id",
                        "status",
@@ -136,6 +137,5 @@ func TestPrDataFlow(t *testing.T) {
                code.PullRequestLabel{},
                "./snapshot_tables/pull_request_labels.csv",
                []string{"pull_request_id", "label_name"},
-               []string{},
        )
 }
diff --git a/plugins/github/e2e/raw_tables/_raw_github_api_issues.csv 
b/plugins/github/e2e/raw_tables/_raw_github_api_issues.csv
index 78cf624d..1c8760f3 100644
--- a/plugins/github/e2e/raw_tables/_raw_github_api_issues.csv
+++ b/plugins/github/e2e/raw_tables/_raw_github_api_issues.csv
@@ -1,43 +1,43 @@
 id,params,data,url,input,created_at
-9,"{""ConnectionId"":2,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/4"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/4/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/4/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/4/events"",""html_url"":""https://github.com/panjf2000
 [...]
-10,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/4"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/4/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/4/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/4/events"",""html_url"":""https://github.com/panjf200
 [...]
-11,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/5"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/5/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/5/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/5/events"",""html_url"":""https://github.com/panjf200
 [...]
-12,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/6"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/6/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/6/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/6/events"",""html_url"":""https://github.com/panjf200
 [...]
-13,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/7"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/7/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/7/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/7/events"",""html_url"":""https://github.com/panjf200
 [...]
-14,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/8"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/8/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/8/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/8/events"",""html_url"":""https://github.com/panjf200
 [...]
-15,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/9"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/9/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/9/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/9/events"",""html_url"":""https://github.com/panjf200
 [...]
-16,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/10"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/10/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/10/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/10/events"",""html_url"":""https://github.com/panj
 [...]
-17,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/11"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/11/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/11/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/11/events"",""html_url"":""https://github.com/panj
 [...]
-18,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/12"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/12/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/12/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/12/events"",""html_url"":""https://github.com/panj
 [...]
-19,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/13"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/13/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/13/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/13/events"",""html_url"":""https://github.com/panj
 [...]
-20,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/14"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/14/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/14/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/14/events"",""html_url"":""https://github.com/panj
 [...]
-21,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/15"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/15/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/15/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/15/events"",""html_url"":""https://github.com/panj
 [...]
-22,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/16"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/16/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/16/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/16/events"",""html_url"":""https://github.com/panj
 [...]
-23,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/17"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/17/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/17/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/17/events"",""html_url"":""https://github.com/panj
 [...]
-24,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/18"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/18/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/18/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/18/events"",""html_url"":""https://github.com/panj
 [...]
-25,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/19"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/19/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/19/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/19/events"",""html_url"":""https://github.com/panj
 [...]
-26,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/20"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/20/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/20/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/20/events"",""html_url"":""https://github.com/panj
 [...]
-27,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/21"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/21/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/21/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/21/events"",""html_url"":""https://github.com/panj
 [...]
-28,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/22"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/22/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/22/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/22/events"",""html_url"":""https://github.com/panj
 [...]
-29,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/23"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/23/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/23/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/23/events"",""html_url"":""https://github.com/panj
 [...]
-30,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/24"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/24/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/24/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/24/events"",""html_url"":""https://github.com/panj
 [...]
-31,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/25"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/25/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/25/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/25/events"",""html_url"":""https://github.com/panj
 [...]
-32,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/26"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/26/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/26/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/26/events"",""html_url"":""https://github.com/panj
 [...]
-33,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/27"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/27/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/27/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/27/events"",""html_url"":""https://github.com/panj
 [...]
-34,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/28"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/28/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/28/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/28/events"",""html_url"":""https://github.com/panj
 [...]
-35,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/29"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/29/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/29/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/29/events"",""html_url"":""https://github.com/panj
 [...]
-36,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/30"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/30/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/30/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/30/events"",""html_url"":""https://github.com/panj
 [...]
-37,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/31"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/31/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/31/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/31/events"",""html_url"":""https://github.com/panj
 [...]
-38,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/32"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/32/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/32/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/32/events"",""html_url"":""https://github.com/panj
 [...]
-39,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/33"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/33/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/33/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/33/events"",""html_url"":""https://github.com/panj
 [...]
-40,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/34"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/34/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/34/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/34/events"",""html_url"":""https://github.com/panj
 [...]
-41,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/35"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/35/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/35/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/35/events"",""html_url"":""https://github.com/panj
 [...]
-42,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/36"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/36/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/36/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/36/events"",""html_url"":""https://github.com/panj
 [...]
-43,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/37"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/37/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/37/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/37/events"",""html_url"":""https://github.com/panj
 [...]
-44,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/38"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/38/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/38/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/38/events"",""html_url"":""https://github.com/panj
 [...]
-45,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/39"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/39/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/39/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/39/events"",""html_url"":""https://github.com/panj
 [...]
-46,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/40"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/40/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/40/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/40/events"",""html_url"":""https://github.com/panj
 [...]
-47,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/41"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/41/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/41/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/41/events"",""html_url"":""https://github.com/panj
 [...]
-48,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/42"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/42/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/42/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/42/events"",""html_url"":""https://github.com/panj
 [...]
-49,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/43"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/43/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/43/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/43/events"",""html_url"":""https://github.com/panj
 [...]
-50,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/44"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/44/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/44/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/44/events"",""html_url"":""https://github.com/panj
 [...]
+9,"{""ConnectionId"":2,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/4"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/4/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/4/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/4/events"",""html_url"":""https://github.com/panjf2000
 [...]
+10,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/4"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/4/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/4/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/4/events"",""html_url"":""https://github.com/panjf200
 [...]
+11,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/5"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/5/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/5/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/5/events"",""html_url"":""https://github.com/panjf200
 [...]
+12,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/6"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/6/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/6/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/6/events"",""html_url"":""https://github.com/panjf200
 [...]
+13,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/7"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/7/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/7/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/7/events"",""html_url"":""https://github.com/panjf200
 [...]
+14,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/8"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/8/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/8/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/8/events"",""html_url"":""https://github.com/panjf200
 [...]
+15,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/9"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/9/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/9/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/9/events"",""html_url"":""https://github.com/panjf200
 [...]
+16,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/10"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/10/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/10/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/10/events"",""html_url"":""https://github.com/panj
 [...]
+17,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/11"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/11/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/11/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/11/events"",""html_url"":""https://github.com/panj
 [...]
+18,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/12"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/12/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/12/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/12/events"",""html_url"":""https://github.com/panj
 [...]
+19,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/13"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/13/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/13/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/13/events"",""html_url"":""https://github.com/panj
 [...]
+20,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/14"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/14/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/14/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/14/events"",""html_url"":""https://github.com/panj
 [...]
+21,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/15"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/15/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/15/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/15/events"",""html_url"":""https://github.com/panj
 [...]
+22,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/16"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/16/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/16/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/16/events"",""html_url"":""https://github.com/panj
 [...]
+23,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/17"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/17/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/17/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/17/events"",""html_url"":""https://github.com/panj
 [...]
+24,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/18"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/18/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/18/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/18/events"",""html_url"":""https://github.com/panj
 [...]
+25,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/19"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/19/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/19/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/19/events"",""html_url"":""https://github.com/panj
 [...]
+26,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/20"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/20/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/20/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/20/events"",""html_url"":""https://github.com/panj
 [...]
+27,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/21"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/21/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/21/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/21/events"",""html_url"":""https://github.com/panj
 [...]
+28,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/22"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/22/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/22/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/22/events"",""html_url"":""https://github.com/panj
 [...]
+29,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/23"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/23/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/23/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/23/events"",""html_url"":""https://github.com/panj
 [...]
+30,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/24"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/24/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/24/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/24/events"",""html_url"":""https://github.com/panj
 [...]
+31,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/25"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/25/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/25/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/25/events"",""html_url"":""https://github.com/panj
 [...]
+32,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/26"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/26/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/26/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/26/events"",""html_url"":""https://github.com/panj
 [...]
+33,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/27"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/27/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/27/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/27/events"",""html_url"":""https://github.com/panj
 [...]
+34,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/28"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/28/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/28/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/28/events"",""html_url"":""https://github.com/panj
 [...]
+35,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/29"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/29/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/29/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/29/events"",""html_url"":""https://github.com/panj
 [...]
+36,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/30"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/30/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/30/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/30/events"",""html_url"":""https://github.com/panj
 [...]
+37,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/31"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/31/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/31/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/31/events"",""html_url"":""https://github.com/panj
 [...]
+38,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/32"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/32/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/32/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/32/events"",""html_url"":""https://github.com/panj
 [...]
+39,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/33"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/33/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/33/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/33/events"",""html_url"":""https://github.com/panj
 [...]
+40,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/34"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/34/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/34/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/34/events"",""html_url"":""https://github.com/panj
 [...]
+41,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/35"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/35/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/35/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/35/events"",""html_url"":""https://github.com/panj
 [...]
+42,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/36"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/36/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/36/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/36/events"",""html_url"":""https://github.com/panj
 [...]
+43,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/37"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/37/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/37/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/37/events"",""html_url"":""https://github.com/panj
 [...]
+44,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/38"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/38/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/38/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/38/events"",""html_url"":""https://github.com/panj
 [...]
+45,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/39"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/39/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/39/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/39/events"",""html_url"":""https://github.com/panj
 [...]
+46,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/40"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/40/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/40/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/40/events"",""html_url"":""https://github.com/panj
 [...]
+47,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/41"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/41/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/41/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/41/events"",""html_url"":""https://github.com/panj
 [...]
+48,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/42"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/42/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/42/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/42/events"",""html_url"":""https://github.com/panj
 [...]
+49,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/43"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/43/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/43/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/43/events"",""html_url"":""https://github.com/panj
 [...]
+50,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/issues/44"",""repository_url"":""https://api.github.com/repos/panjf2000/ants"",""labels_url"":""https://api.github.com/repos/panjf2000/ants/issues/44/labels{/name}"",""comments_url"":""https://api.github.com/repos/panjf2000/ants/issues/44/comments"",""events_url"":""https://api.github.com/repos/panjf2000/ants/issues/44/events"",""html_url"":""https://github.com/panj
 [...]
diff --git a/plugins/github/e2e/repo_test.go b/plugins/github/e2e/repo_test.go
index 4a7f8e89..a1d239e6 100644
--- a/plugins/github/e2e/repo_test.go
+++ b/plugins/github/e2e/repo_test.go
@@ -57,8 +57,9 @@ func TestRepoDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.GithubRepo{},
                "./snapshot_tables/_tool_github_repos.csv",
-               []string{"connection_id", "github_id"},
                []string{
+                       "connection_id",
+                       "github_id",
                        "name",
                        "html_url",
                        "description",
@@ -81,8 +82,8 @@ func TestRepoDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                code.Repo{},
                "./snapshot_tables/repos.csv",
-               []string{"id"},
                []string{
+                       "id",
                        "name",
                        "url",
                        "description",
@@ -97,8 +98,8 @@ func TestRepoDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                ticket.Board{},
                "./snapshot_tables/boards.csv",
-               []string{"id"},
                []string{
+                       "id",
                        "name",
                        "description",
                        "url",
diff --git a/plugins/gitlab/e2e/issues_test.go 
b/plugins/gitlab/e2e/issues_test.go
index 83c76e9b..1f6abfea 100644
--- a/plugins/gitlab/e2e/issues_test.go
+++ b/plugins/gitlab/e2e/issues_test.go
@@ -50,8 +50,9 @@ func TestGitlabIssueDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.GitlabIssue{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
models.GitlabIssue{}.TableName()),
-               []string{"connection_id", "gitlab_id"},
                []string{
+                       "connection_id",
+                       "gitlab_id",
                        "project_id",
                        "number",
                        "state",
@@ -81,8 +82,10 @@ func TestGitlabIssueDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.GitlabIssueLabel{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
models.GitlabIssueLabel{}.TableName()),
-               []string{"connection_id", "issue_id", "label_name"},
                []string{
+                       "connection_id",
+                       "issue_id",
+                       "label_name",
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
@@ -97,12 +100,12 @@ func TestGitlabIssueDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                ticket.Issue{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
ticket.Issue{}.TableName()),
-               []string{"id"},
                []string{
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
                        "_raw_data_remark",
+                       "id",
                        "url",
                        "issue_key",
                        "title",
@@ -134,8 +137,9 @@ func TestGitlabIssueDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                &ticket.BoardIssue{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
ticket.BoardIssue{}.TableName()),
-               []string{"board_id", "issue_id"},
                []string{
+                       "board_id",
+                       "issue_id",
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
@@ -151,8 +155,6 @@ func TestGitlabIssueDataFlow(t *testing.T) {
                []string{
                        "issue_id",
                        "label_name",
-               },
-               []string{
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
diff --git a/plugins/gitlab/e2e/mr_commits_test.go 
b/plugins/gitlab/e2e/mr_commits_test.go
index 6844ce7f..fbc1d07e 100644
--- a/plugins/gitlab/e2e/mr_commits_test.go
+++ b/plugins/gitlab/e2e/mr_commits_test.go
@@ -46,12 +46,14 @@ func TestGitlabMrCommitDataFlow(t *testing.T) {
                "_raw_gitlab_api_merge_requests")
        // verify extraction
        dataflowTester.FlushTabler(&models.GitlabMergeRequest{})
+       dataflowTester.FlushTabler(&models.GitlabMrLabel{})
        dataflowTester.Subtask(tasks.ExtractApiMergeRequestsMeta, taskData)
        dataflowTester.VerifyTable(
                models.GitlabMergeRequest{},
                fmt.Sprintf("./snapshot_tables/%s_for_mr_commit_test.csv", 
models.GitlabMergeRequest{}.TableName()),
-               []string{"connection_id", "gitlab_id"},
                []string{
+                       "connection_id",
+                       "gitlab_id",
                        "iid",
                        "project_id",
                        "source_project_id",
@@ -93,8 +95,8 @@ func TestGitlabMrCommitDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.GitlabCommit{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
models.GitlabCommit{}.TableName()),
-               []string{"sha"},
                []string{
+                       "sha",
                        "title",
                        "message",
                        "short_id",
@@ -117,8 +119,10 @@ func TestGitlabMrCommitDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.GitlabProjectCommit{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
models.GitlabProjectCommit{}.TableName()),
-               []string{"connection_id", "gitlab_project_id", "commit_sha"},
                []string{
+                       "connection_id",
+                       "gitlab_project_id",
+                       "commit_sha",
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
@@ -129,8 +133,10 @@ func TestGitlabMrCommitDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.GitlabMrCommit{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
models.GitlabMrCommit{}.TableName()),
-               []string{"connection_id", "merge_request_id", "commit_sha"},
                []string{
+                       "connection_id",
+                       "merge_request_id",
+                       "commit_sha",
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
@@ -144,9 +150,9 @@ func TestGitlabMrCommitDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                code.PullRequestCommit{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
code.PullRequestCommit{}.TableName()),
-               []string{"commit_sha",
-                       "pull_request_id"},
                []string{
+                       "commit_sha",
+                       "pull_request_id",
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
@@ -161,8 +167,8 @@ func TestGitlabMrCommitDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                code.Commit{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
code.Commit{}.TableName()),
-               []string{"sha"},
                []string{
+                       "sha",
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
@@ -185,8 +191,9 @@ func TestGitlabMrCommitDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                code.RepoCommit{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
code.RepoCommit{}.TableName()),
-               []string{"repo_id", "commit_sha"},
                []string{
+                       "repo_id",
+                       "commit_sha",
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
diff --git a/plugins/gitlab/e2e/mr_notes_test.go 
b/plugins/gitlab/e2e/mr_notes_test.go
index a0d463d3..940cf485 100644
--- a/plugins/gitlab/e2e/mr_notes_test.go
+++ b/plugins/gitlab/e2e/mr_notes_test.go
@@ -50,8 +50,9 @@ func TestGitlabMrNoteDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.GitlabMergeRequest{},
                fmt.Sprintf("./snapshot_tables/%s_for_mr_notes_test.csv", 
models.GitlabMergeRequest{}.TableName()),
-               []string{"connection_id", "gitlab_id"},
                []string{
+                       "connection_id",
+                       "gitlab_id",
                        "iid",
                        "project_id",
                        "source_project_id",
@@ -91,8 +92,9 @@ func TestGitlabMrNoteDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.GitlabMrNote{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
models.GitlabMrNote{}.TableName()),
-               []string{"connection_id", "gitlab_id"},
                []string{
+                       "connection_id",
+                       "gitlab_id",
                        "merge_request_id",
                        "merge_request_iid",
                        "noteable_type",
@@ -111,8 +113,9 @@ func TestGitlabMrNoteDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.GitlabMrComment{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
models.GitlabMrComment{}.TableName()),
-               []string{"connection_id", "gitlab_id"},
                []string{
+                       "connection_id",
+                       "gitlab_id",
                        "merge_request_id",
                        "merge_request_iid",
                        "body",
@@ -133,12 +136,12 @@ func TestGitlabMrNoteDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                code.Note{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
code.Note{}.TableName()),
-               []string{"id"},
                []string{
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
                        "_raw_data_remark",
+                       "id",
                        "pr_id",
                        "type",
                        "author",
@@ -155,12 +158,12 @@ func TestGitlabMrNoteDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                code.PullRequestComment{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
code.PullRequestComment{}.TableName()),
-               []string{"id"},
                []string{
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
                        "_raw_data_remark",
+                       "id",
                        "pull_request_id",
                        "body",
                        "user_id",
diff --git a/plugins/gitlab/e2e/mr_test.go b/plugins/gitlab/e2e/mr_test.go
index 101b834d..f1e66fd6 100644
--- a/plugins/gitlab/e2e/mr_test.go
+++ b/plugins/gitlab/e2e/mr_test.go
@@ -50,8 +50,9 @@ func TestGitlabMrDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.GitlabMergeRequest{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
models.GitlabMergeRequest{}.TableName()),
-               []string{"connection_id", "gitlab_id"},
                []string{
+                       "connection_id",
+                       "gitlab_id",
                        "iid",
                        "project_id",
                        "source_project_id",
@@ -83,8 +84,10 @@ func TestGitlabMrDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.GitlabMrLabel{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
models.GitlabMrLabel{}.TableName()),
-               []string{"connection_id", "mr_id", "label_name"},
                []string{
+                       "connection_id",
+                       "mr_id",
+                       "label_name",
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
@@ -98,8 +101,8 @@ func TestGitlabMrDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                code.PullRequest{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
code.PullRequest{}.TableName()),
-               []string{"id"},
                []string{
+                       "id",
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
@@ -136,8 +139,6 @@ func TestGitlabMrDataFlow(t *testing.T) {
                []string{
                        "pull_request_id",
                        "label_name",
-               },
-               []string{
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
diff --git a/plugins/gitlab/e2e/pipelines_test.go 
b/plugins/gitlab/e2e/pipelines_test.go
index ca77d285..c491714d 100644
--- a/plugins/gitlab/e2e/pipelines_test.go
+++ b/plugins/gitlab/e2e/pipelines_test.go
@@ -48,8 +48,9 @@ func TestGitlabPipelineDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.GitlabPipeline{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
models.GitlabPipeline{}.TableName()),
-               []string{"connection_id", "gitlab_id"},
                []string{
+                       "connection_id",
+                       "gitlab_id",
                        "project_id",
                        "gitlab_created_at",
                        "status",
diff --git a/plugins/gitlab/e2e/project_test.go 
b/plugins/gitlab/e2e/project_test.go
index f551a2cf..6ecc9acb 100644
--- a/plugins/gitlab/e2e/project_test.go
+++ b/plugins/gitlab/e2e/project_test.go
@@ -50,8 +50,9 @@ func TestGitlabProjectDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.GitlabProject{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
models.GitlabProject{}.TableName()),
-               []string{"connection_id", "gitlab_id"},
                []string{
+                       "connection_id",
+                       "gitlab_id",
                        "name",
                        "description",
                        "default_branch",
@@ -78,8 +79,8 @@ func TestGitlabProjectDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                code.Repo{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
code.Repo{}.TableName()),
-               []string{"id"},
                []string{
+                       "id",
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
diff --git a/plugins/jenkins/e2e/builds_test.go 
b/plugins/jenkins/e2e/builds_test.go
index d6e045dd..480ad6a5 100644
--- a/plugins/jenkins/e2e/builds_test.go
+++ b/plugins/jenkins/e2e/builds_test.go
@@ -49,8 +49,10 @@ func TestJenkinsBuildsDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.JenkinsBuild{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
models.JenkinsBuild{}.TableName()),
-               []string{"connection_id", "job_name", "number"},
                []string{
+                       "connection_id",
+                       "job_name",
+                       "number",
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
@@ -74,7 +76,6 @@ func TestJenkinsBuildsDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                devops.Build{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
devops.Build{}.TableName()),
-               []string{"id"},
                []string{
                        "id",
                        "_raw_data_params",
diff --git a/plugins/jenkins/e2e/jobs_test.go b/plugins/jenkins/e2e/jobs_test.go
index 0f6a6328..a8d1fe79 100644
--- a/plugins/jenkins/e2e/jobs_test.go
+++ b/plugins/jenkins/e2e/jobs_test.go
@@ -48,7 +48,6 @@ func TestJenkinsJobsDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.JenkinsJob{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
models.JenkinsJob{}.TableName()),
-               []string{"connection_id", "name"},
                []string{
                        "connection_id",
                        "name",
@@ -68,7 +67,6 @@ func TestJenkinsJobsDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                devops.Job{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
devops.Job{}.TableName()),
-               []string{"id"},
                []string{
                        "name",
                        "id",
diff --git a/plugins/jenkins/e2e/snapshot_tables/_tool_jenkins_jobs.csv 
b/plugins/jenkins/e2e/snapshot_tables/_tool_jenkins_jobs.csv
index da6fe645..5cbe8dda 100644
--- a/plugins/jenkins/e2e/snapshot_tables/_tool_jenkins_jobs.csv
+++ b/plugins/jenkins/e2e/snapshot_tables/_tool_jenkins_jobs.csv
@@ -1,12 +1,12 @@
-connection_id,name,connection_id,name,class,color,base,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
-1,auto_test,1,auto_test,hudson.model.FreeStyleProject,blue,,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,1,
-1,auto_test_framework,1,auto_test_framework,hudson.model.FreeStyleProject,disabled,,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,2,
-1,build_devlake,1,build_devlake,hudson.model.FreeStyleProject,blue,,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,3,
-1,Create_License,1,Create_License,hudson.model.FreeStyleProject,blue,,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,4,
-1,Create_License_new,1,Create_License_new,hudson.model.FreeStyleProject,blue,,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,5,
-1,Deploy k8s single_data,1,Deploy k8s 
single_data,hudson.model.FreeStyleProject,blue,,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,6,
-1,Deploy k8s single_new,1,Deploy k8s 
single_new,hudson.model.FreeStyleProject,blue,,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,7,
-1,Deploy k8s test,1,Deploy k8s 
test,hudson.model.FreeStyleProject,blue,,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,8,
-1,devlake_empty_build,1,devlake_empty_build,hudson.model.FreeStyleProject,blue,,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,9,
-1,test-platform-backend,1,test-platform-backend,org.jenkinsci.plugins.workflow.job.WorkflowJob,blue,,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,10,
-1,test-platform-frontend,1,test-platform-frontend,org.jenkinsci.plugins.workflow.job.WorkflowJob,blue,,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,11,
+connection_id,name,class,color,base,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+1,auto_test,hudson.model.FreeStyleProject,blue,,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,1,
+1,auto_test_framework,hudson.model.FreeStyleProject,disabled,,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,2,
+1,build_devlake,hudson.model.FreeStyleProject,blue,,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,3,
+1,Create_License,hudson.model.FreeStyleProject,blue,,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,4,
+1,Create_License_new,hudson.model.FreeStyleProject,blue,,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,5,
+1,Deploy k8s 
single_data,hudson.model.FreeStyleProject,blue,,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,6,
+1,Deploy k8s 
single_new,hudson.model.FreeStyleProject,blue,,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,7,
+1,Deploy k8s 
test,hudson.model.FreeStyleProject,blue,,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,8,
+1,devlake_empty_build,hudson.model.FreeStyleProject,blue,,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,9,
+1,test-platform-backend,org.jenkinsci.plugins.workflow.job.WorkflowJob,blue,,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,10,
+1,test-platform-frontend,org.jenkinsci.plugins.workflow.job.WorkflowJob,blue,,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,11,
diff --git a/plugins/jenkins/e2e/snapshot_tables/builds.csv 
b/plugins/jenkins/e2e/snapshot_tables/builds.csv
index 2a646b94..f3dca0fa 100644
--- a/plugins/jenkins/e2e/snapshot_tables/builds.csv
+++ b/plugins/jenkins/e2e/snapshot_tables/builds.csv
@@ -1,21 +1,21 @@
-id,id,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark,job_id,name,commit_sha,duration_sec,status,started_date
-jenkins:JenkinsBuild:1:Create_License:186,jenkins:JenkinsBuild:1:Create_License:186,"{""ConnectionId"":1}",_raw_jenkins_api_builds,20,,jenkins:JenkinsJob:1:Create_License,#186,,0,SUCCESS,2021-08-11T09:38:52.000+00:00
-jenkins:JenkinsBuild:1:Create_License:187,jenkins:JenkinsBuild:1:Create_License:187,"{""ConnectionId"":1}",_raw_jenkins_api_builds,19,,jenkins:JenkinsJob:1:Create_License,#187,,0,SUCCESS,2021-08-13T07:11:52.000+00:00
-jenkins:JenkinsBuild:1:Create_License:188,jenkins:JenkinsBuild:1:Create_License:188,"{""ConnectionId"":1}",_raw_jenkins_api_builds,18,,jenkins:JenkinsJob:1:Create_License,#188,,0,SUCCESS,2021-08-17T03:00:39.000+00:00
-jenkins:JenkinsBuild:1:Create_License:189,jenkins:JenkinsBuild:1:Create_License:189,"{""ConnectionId"":1}",_raw_jenkins_api_builds,17,,jenkins:JenkinsJob:1:Create_License,#189,,0,SUCCESS,2021-09-03T01:23:54.000+00:00
-jenkins:JenkinsBuild:1:Create_License:190,jenkins:JenkinsBuild:1:Create_License:190,"{""ConnectionId"":1}",_raw_jenkins_api_builds,16,,jenkins:JenkinsJob:1:Create_License,#190,,0,SUCCESS,2021-09-07T07:42:26.000+00:00
-jenkins:JenkinsBuild:1:Create_License:191,jenkins:JenkinsBuild:1:Create_License:191,"{""ConnectionId"":1}",_raw_jenkins_api_builds,15,,jenkins:JenkinsJob:1:Create_License,#191,,0,SUCCESS,2021-09-10T01:39:26.000+00:00
-jenkins:JenkinsBuild:1:Create_License:192,jenkins:JenkinsBuild:1:Create_License:192,"{""ConnectionId"":1}",_raw_jenkins_api_builds,14,,jenkins:JenkinsJob:1:Create_License,#192,,0,SUCCESS,2021-09-18T03:30:45.000+00:00
-jenkins:JenkinsBuild:1:Create_License:193,jenkins:JenkinsBuild:1:Create_License:193,"{""ConnectionId"":1}",_raw_jenkins_api_builds,13,,jenkins:JenkinsJob:1:Create_License,#193,,0,SUCCESS,2021-10-13T07:08:52.000+00:00
-jenkins:JenkinsBuild:1:Create_License:194,jenkins:JenkinsBuild:1:Create_License:194,"{""ConnectionId"":1}",_raw_jenkins_api_builds,12,,jenkins:JenkinsJob:1:Create_License,#194,,0,SUCCESS,2021-10-13T07:54:40.000+00:00
-jenkins:JenkinsBuild:1:Create_License:195,jenkins:JenkinsBuild:1:Create_License:195,"{""ConnectionId"":1}",_raw_jenkins_api_builds,11,,jenkins:JenkinsJob:1:Create_License,#195,,0,SUCCESS,2021-10-15T09:10:09.000+00:00
-jenkins:JenkinsBuild:1:Create_License:196,jenkins:JenkinsBuild:1:Create_License:196,"{""ConnectionId"":1}",_raw_jenkins_api_builds,10,,jenkins:JenkinsJob:1:Create_License,#196,,0,SUCCESS,2021-10-20T07:10:46.000+00:00
-jenkins:JenkinsBuild:1:Create_License:197,jenkins:JenkinsBuild:1:Create_License:197,"{""ConnectionId"":1}",_raw_jenkins_api_builds,9,,jenkins:JenkinsJob:1:Create_License,#197,,0,SUCCESS,2021-10-25T06:37:10.000+00:00
-jenkins:JenkinsBuild:1:Create_License:198,jenkins:JenkinsBuild:1:Create_License:198,"{""ConnectionId"":1}",_raw_jenkins_api_builds,8,,jenkins:JenkinsJob:1:Create_License,#198,,0,SUCCESS,2021-11-02T08:45:17.000+00:00
-jenkins:JenkinsBuild:1:Create_License:199,jenkins:JenkinsBuild:1:Create_License:199,"{""ConnectionId"":1}",_raw_jenkins_api_builds,7,,jenkins:JenkinsJob:1:Create_License,#199,,0,FAILURE,2021-11-23T03:08:24.000+00:00
-jenkins:JenkinsBuild:1:Create_License:200,jenkins:JenkinsBuild:1:Create_License:200,"{""ConnectionId"":1}",_raw_jenkins_api_builds,6,,jenkins:JenkinsJob:1:Create_License,#200,,0,SUCCESS,2021-12-02T06:14:57.000+00:00
-jenkins:JenkinsBuild:1:Create_License:201,jenkins:JenkinsBuild:1:Create_License:201,"{""ConnectionId"":1}",_raw_jenkins_api_builds,5,,jenkins:JenkinsJob:1:Create_License,#201,,0,SUCCESS,2022-01-04T09:00:09.000+00:00
-jenkins:JenkinsBuild:1:Create_License:202,jenkins:JenkinsBuild:1:Create_License:202,"{""ConnectionId"":1}",_raw_jenkins_api_builds,4,,jenkins:JenkinsJob:1:Create_License,#202,,0,SUCCESS,2022-01-07T03:00:12.000+00:00
-jenkins:JenkinsBuild:1:Create_License:203,jenkins:JenkinsBuild:1:Create_License:203,"{""ConnectionId"":1}",_raw_jenkins_api_builds,3,,jenkins:JenkinsJob:1:Create_License,#203,,0,SUCCESS,2022-01-19T02:40:20.000+00:00
-jenkins:JenkinsBuild:1:Create_License:204,jenkins:JenkinsBuild:1:Create_License:204,"{""ConnectionId"":1}",_raw_jenkins_api_builds,2,,jenkins:JenkinsJob:1:Create_License,#204,,0,SUCCESS,2022-02-18T02:45:59.000+00:00
-jenkins:JenkinsBuild:1:Create_License:205,jenkins:JenkinsBuild:1:Create_License:205,"{""ConnectionId"":1}",_raw_jenkins_api_builds,1,,jenkins:JenkinsJob:1:Create_License,#205,,0,SUCCESS,2022-03-21T05:50:30.000+00:00
+id,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark,job_id,name,commit_sha,duration_sec,status,started_date
+jenkins:JenkinsBuild:1:Create_License:186,"{""ConnectionId"":1}",_raw_jenkins_api_builds,20,,jenkins:JenkinsJob:1:Create_License,#186,,0,SUCCESS,2021-08-11T09:38:52.000+00:00
+jenkins:JenkinsBuild:1:Create_License:187,"{""ConnectionId"":1}",_raw_jenkins_api_builds,19,,jenkins:JenkinsJob:1:Create_License,#187,,0,SUCCESS,2021-08-13T07:11:52.000+00:00
+jenkins:JenkinsBuild:1:Create_License:188,"{""ConnectionId"":1}",_raw_jenkins_api_builds,18,,jenkins:JenkinsJob:1:Create_License,#188,,0,SUCCESS,2021-08-17T03:00:39.000+00:00
+jenkins:JenkinsBuild:1:Create_License:189,"{""ConnectionId"":1}",_raw_jenkins_api_builds,17,,jenkins:JenkinsJob:1:Create_License,#189,,0,SUCCESS,2021-09-03T01:23:54.000+00:00
+jenkins:JenkinsBuild:1:Create_License:190,"{""ConnectionId"":1}",_raw_jenkins_api_builds,16,,jenkins:JenkinsJob:1:Create_License,#190,,0,SUCCESS,2021-09-07T07:42:26.000+00:00
+jenkins:JenkinsBuild:1:Create_License:191,"{""ConnectionId"":1}",_raw_jenkins_api_builds,15,,jenkins:JenkinsJob:1:Create_License,#191,,0,SUCCESS,2021-09-10T01:39:26.000+00:00
+jenkins:JenkinsBuild:1:Create_License:192,"{""ConnectionId"":1}",_raw_jenkins_api_builds,14,,jenkins:JenkinsJob:1:Create_License,#192,,0,SUCCESS,2021-09-18T03:30:45.000+00:00
+jenkins:JenkinsBuild:1:Create_License:193,"{""ConnectionId"":1}",_raw_jenkins_api_builds,13,,jenkins:JenkinsJob:1:Create_License,#193,,0,SUCCESS,2021-10-13T07:08:52.000+00:00
+jenkins:JenkinsBuild:1:Create_License:194,"{""ConnectionId"":1}",_raw_jenkins_api_builds,12,,jenkins:JenkinsJob:1:Create_License,#194,,0,SUCCESS,2021-10-13T07:54:40.000+00:00
+jenkins:JenkinsBuild:1:Create_License:195,"{""ConnectionId"":1}",_raw_jenkins_api_builds,11,,jenkins:JenkinsJob:1:Create_License,#195,,0,SUCCESS,2021-10-15T09:10:09.000+00:00
+jenkins:JenkinsBuild:1:Create_License:196,"{""ConnectionId"":1}",_raw_jenkins_api_builds,10,,jenkins:JenkinsJob:1:Create_License,#196,,0,SUCCESS,2021-10-20T07:10:46.000+00:00
+jenkins:JenkinsBuild:1:Create_License:197,"{""ConnectionId"":1}",_raw_jenkins_api_builds,9,,jenkins:JenkinsJob:1:Create_License,#197,,0,SUCCESS,2021-10-25T06:37:10.000+00:00
+jenkins:JenkinsBuild:1:Create_License:198,"{""ConnectionId"":1}",_raw_jenkins_api_builds,8,,jenkins:JenkinsJob:1:Create_License,#198,,0,SUCCESS,2021-11-02T08:45:17.000+00:00
+jenkins:JenkinsBuild:1:Create_License:199,"{""ConnectionId"":1}",_raw_jenkins_api_builds,7,,jenkins:JenkinsJob:1:Create_License,#199,,0,FAILURE,2021-11-23T03:08:24.000+00:00
+jenkins:JenkinsBuild:1:Create_License:200,"{""ConnectionId"":1}",_raw_jenkins_api_builds,6,,jenkins:JenkinsJob:1:Create_License,#200,,0,SUCCESS,2021-12-02T06:14:57.000+00:00
+jenkins:JenkinsBuild:1:Create_License:201,"{""ConnectionId"":1}",_raw_jenkins_api_builds,5,,jenkins:JenkinsJob:1:Create_License,#201,,0,SUCCESS,2022-01-04T09:00:09.000+00:00
+jenkins:JenkinsBuild:1:Create_License:202,"{""ConnectionId"":1}",_raw_jenkins_api_builds,4,,jenkins:JenkinsJob:1:Create_License,#202,,0,SUCCESS,2022-01-07T03:00:12.000+00:00
+jenkins:JenkinsBuild:1:Create_License:203,"{""ConnectionId"":1}",_raw_jenkins_api_builds,3,,jenkins:JenkinsJob:1:Create_License,#203,,0,SUCCESS,2022-01-19T02:40:20.000+00:00
+jenkins:JenkinsBuild:1:Create_License:204,"{""ConnectionId"":1}",_raw_jenkins_api_builds,2,,jenkins:JenkinsJob:1:Create_License,#204,,0,SUCCESS,2022-02-18T02:45:59.000+00:00
+jenkins:JenkinsBuild:1:Create_License:205,"{""ConnectionId"":1}",_raw_jenkins_api_builds,1,,jenkins:JenkinsJob:1:Create_License,#205,,0,SUCCESS,2022-03-21T05:50:30.000+00:00
diff --git a/plugins/jenkins/e2e/snapshot_tables/jobs.csv 
b/plugins/jenkins/e2e/snapshot_tables/jobs.csv
index a2dc167c..372e8a97 100644
--- a/plugins/jenkins/e2e/snapshot_tables/jobs.csv
+++ b/plugins/jenkins/e2e/snapshot_tables/jobs.csv
@@ -1,12 +1,12 @@
-id,name,id,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
-jenkins:JenkinsJob:1:auto_test,auto_test,jenkins:JenkinsJob:1:auto_test,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,1,
-jenkins:JenkinsJob:1:auto_test_framework,auto_test_framework,jenkins:JenkinsJob:1:auto_test_framework,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,2,
-jenkins:JenkinsJob:1:build_devlake,build_devlake,jenkins:JenkinsJob:1:build_devlake,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,3,
-jenkins:JenkinsJob:1:Create_License,Create_License,jenkins:JenkinsJob:1:Create_License,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,4,
-jenkins:JenkinsJob:1:Create_License_new,Create_License_new,jenkins:JenkinsJob:1:Create_License_new,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,5,
-jenkins:JenkinsJob:1:Deploy k8s single_data,Deploy k8s 
single_data,jenkins:JenkinsJob:1:Deploy k8s 
single_data,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,6,
-jenkins:JenkinsJob:1:Deploy k8s single_new,Deploy k8s 
single_new,jenkins:JenkinsJob:1:Deploy k8s 
single_new,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,7,
-jenkins:JenkinsJob:1:Deploy k8s test,Deploy k8s 
test,jenkins:JenkinsJob:1:Deploy k8s 
test,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,8,
-jenkins:JenkinsJob:1:devlake_empty_build,devlake_empty_build,jenkins:JenkinsJob:1:devlake_empty_build,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,9,
-jenkins:JenkinsJob:1:test-platform-backend,test-platform-backend,jenkins:JenkinsJob:1:test-platform-backend,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,10,
-jenkins:JenkinsJob:1:test-platform-frontend,test-platform-frontend,jenkins:JenkinsJob:1:test-platform-frontend,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,11,
+id,name,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+jenkins:JenkinsJob:1:auto_test,auto_test,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,1,
+jenkins:JenkinsJob:1:auto_test_framework,auto_test_framework,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,2,
+jenkins:JenkinsJob:1:build_devlake,build_devlake,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,3,
+jenkins:JenkinsJob:1:Create_License,Create_License,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,4,
+jenkins:JenkinsJob:1:Create_License_new,Create_License_new,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,5,
+jenkins:JenkinsJob:1:Deploy k8s single_data,Deploy k8s 
single_data,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,6,
+jenkins:JenkinsJob:1:Deploy k8s single_new,Deploy k8s 
single_new,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,7,
+jenkins:JenkinsJob:1:Deploy k8s test,Deploy k8s 
test,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,8,
+jenkins:JenkinsJob:1:devlake_empty_build,devlake_empty_build,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,9,
+jenkins:JenkinsJob:1:test-platform-backend,test-platform-backend,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,10,
+jenkins:JenkinsJob:1:test-platform-frontend,test-platform-frontend,"{""ConnectionId"":1}",_raw_jenkins_api_jobs,11,
diff --git a/plugins/jira/e2e/board_test.go b/plugins/jira/e2e/board_test.go
index ef0af874..6126bd9d 100644
--- a/plugins/jira/e2e/board_test.go
+++ b/plugins/jira/e2e/board_test.go
@@ -47,7 +47,6 @@ func TestBoardDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.JiraBoard{},
                "./snapshot_tables/_tool_jira_boards.csv",
-               []string{"connection_id", "board_id"},
                []string{
                        "connection_id",
                        "board_id",
@@ -68,7 +67,6 @@ func TestBoardDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                ticket.Board{},
                "./snapshot_tables/boards.csv",
-               []string{"id"},
                []string{
                        "id",
                        "name",
diff --git a/plugins/jira/e2e/issue_test.go b/plugins/jira/e2e/issue_test.go
index 4ea35e9c..81f5964b 100644
--- a/plugins/jira/e2e/issue_test.go
+++ b/plugins/jira/e2e/issue_test.go
@@ -53,7 +53,6 @@ func TestIssueDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.JiraIssue{},
                "./snapshot_tables/_tool_jira_issues.csv",
-               []string{"connection_id", "issue_id"},
                []string{
                        "connection_id",
                        "issue_id",
@@ -101,7 +100,6 @@ func TestIssueDataFlow(t *testing.T) {
                models.JiraBoardIssue{},
                "./snapshot_tables/_tool_jira_board_issues.csv",
                []string{"connection_id", "board_id", "issue_id"},
-               []string{},
        )
 
        // verify issue conversion
@@ -111,8 +109,8 @@ func TestIssueDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                ticket.Issue{},
                "./snapshot_tables/issues.csv",
-               []string{"id"},
                []string{
+                       "id",
                        "url",
                        "icon_url",
                        "issue_key",
@@ -144,6 +142,5 @@ func TestIssueDataFlow(t *testing.T) {
                ticket.BoardIssue{},
                "./snapshot_tables/board_issues.csv",
                []string{"board_id", "issue_id"},
-               []string{},
        )
 }
diff --git a/plugins/jira/e2e/project_test.go b/plugins/jira/e2e/project_test.go
index 3669c0d7..436874d5 100644
--- a/plugins/jira/e2e/project_test.go
+++ b/plugins/jira/e2e/project_test.go
@@ -46,7 +46,6 @@ func TestProjectDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.JiraProject{},
                "./snapshot_tables/_tool_jira_projects.csv",
-               []string{"connection_id", "id"},
                []string{
                        "connection_id",
                        "id",
diff --git a/plugins/tapd/e2e/bug_changelog_test.go 
b/plugins/tapd/e2e/bug_changelog_test.go
index f0ad8cc2..c8db46d7 100644
--- a/plugins/tapd/e2e/bug_changelog_test.go
+++ b/plugins/tapd/e2e/bug_changelog_test.go
@@ -52,8 +52,10 @@ func TestTapdBugChangelogDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.TapdBugChangelog{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
models.TapdBugChangelog{}.TableName()),
-               []string{"connection_id", "id", "field"},
                []string{
+                       "connection_id",
+                       "id",
+                       "field",
                        "workspace_id",
                        "bug_id",
                        "author",
@@ -74,8 +76,6 @@ func TestTapdBugChangelogDataFlow(t *testing.T) {
                        "connection_id",
                        "changelog_id",
                        "field",
-               },
-               []string{
                        "value_before_parsed",
                        "value_after_parsed",
                        "iteration_id_from",
@@ -92,12 +92,12 @@ func TestTapdBugChangelogDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                ticket.Changelog{},
                fmt.Sprintf("./snapshot_tables/%s_bug.csv", 
ticket.Changelog{}.TableName()),
-               []string{"id"},
                []string{
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
                        "_raw_data_remark",
+                       "id",
                        "issue_id",
                        "author_id",
                        "author_name",
diff --git a/plugins/tapd/e2e/bug_custom_field_test.go 
b/plugins/tapd/e2e/bug_custom_field_test.go
index 65a7d40b..068a366c 100644
--- a/plugins/tapd/e2e/bug_custom_field_test.go
+++ b/plugins/tapd/e2e/bug_custom_field_test.go
@@ -49,8 +49,9 @@ func TestTapdBugCustomFieldDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.TapdBugCustomFields{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
models.TapdBugCustomFields{}.TableName()),
-               []string{"connection_id", "id"},
                []string{
+                       "connection_id",
+                       "id",
                        "workspace_id",
                        "entry_type",
                        "custom_field",
diff --git a/plugins/tapd/e2e/bugs_commit_test.go 
b/plugins/tapd/e2e/bugs_commit_test.go
index 14e90a58..483f5735 100644
--- a/plugins/tapd/e2e/bugs_commit_test.go
+++ b/plugins/tapd/e2e/bugs_commit_test.go
@@ -51,8 +51,9 @@ func TestTapdBugCommitDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.TapdBugCommit{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
models.TapdBugCommit{}.TableName()),
-               []string{"connection_id", "id"},
                []string{
+                       "connection_id",
+                       "id",
                        "user_id",
                        "hook_user_name",
                        "commit_id",
@@ -83,8 +84,6 @@ func TestTapdBugCommitDataFlow(t *testing.T) {
                []string{
                        "issue_id",
                        "commit_sha",
-               },
-               []string{
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
diff --git a/plugins/tapd/e2e/bugs_test.go b/plugins/tapd/e2e/bugs_test.go
index 291d9942..3cfe609d 100644
--- a/plugins/tapd/e2e/bugs_test.go
+++ b/plugins/tapd/e2e/bugs_test.go
@@ -52,8 +52,10 @@ func TestTapdBugDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.TapdBugStatus{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
models.TapdBugStatus{}.TableName()),
-               []string{"connection_id", "workspace_id", "english_name"},
                []string{
+                       "connection_id",
+                       "workspace_id",
+                       "english_name",
                        "chinese_name",
                        "is_last_step",
                        "_raw_data_params",
@@ -76,8 +78,9 @@ func TestTapdBugDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.TapdBug{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
models.TapdBug{}.TableName()),
-               []string{"connection_id", "id"},
                []string{
+                       "connection_id",
+                       "id",
                        "epic_key",
                        "title",
                        "description",
@@ -219,8 +222,6 @@ func TestTapdBugDataFlow(t *testing.T) {
                        "connection_id",
                        "workspace_id",
                        "bug_id",
-               },
-               []string{
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
@@ -235,8 +236,6 @@ func TestTapdBugDataFlow(t *testing.T) {
                        "workspace_id",
                        "iteration_id",
                        "bug_id",
-               },
-               []string{
                        "resolution_date",
                        "bug_created_date",
                        "_raw_data_params",
@@ -251,8 +250,6 @@ func TestTapdBugDataFlow(t *testing.T) {
                []string{
                        "label_name",
                        "bug_id",
-               },
-               []string{
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
@@ -268,8 +265,8 @@ func TestTapdBugDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                ticket.Issue{},
                fmt.Sprintf("./snapshot_tables/%s_bug.csv", 
ticket.Issue{}.TableName()),
-               []string{"id"},
                []string{
+                       "id",
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
@@ -307,8 +304,6 @@ func TestTapdBugDataFlow(t *testing.T) {
                []string{
                        "board_id",
                        "issue_id",
-               },
-               []string{
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
@@ -321,8 +316,6 @@ func TestTapdBugDataFlow(t *testing.T) {
                []string{
                        "issue_id",
                        "sprint_id",
-               },
-               []string{
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
@@ -336,8 +329,6 @@ func TestTapdBugDataFlow(t *testing.T) {
                []string{
                        "issue_id",
                        "label_name",
-               },
-               []string{
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
diff --git a/plugins/tapd/e2e/company_test.go b/plugins/tapd/e2e/company_test.go
index 1f3581fa..190d6015 100644
--- a/plugins/tapd/e2e/company_test.go
+++ b/plugins/tapd/e2e/company_test.go
@@ -49,8 +49,9 @@ func TestTapdCompanyDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.TapdWorkspace{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
models.TapdWorkspace{}.TableName()),
-               []string{"connection_id", "id"},
                []string{
+                       "connection_id",
+                       "id",
                        "name",
                        "pretty_name",
                        "category",
diff --git a/plugins/tapd/e2e/iterations_test.go 
b/plugins/tapd/e2e/iterations_test.go
index 81db376c..cbc7679a 100644
--- a/plugins/tapd/e2e/iterations_test.go
+++ b/plugins/tapd/e2e/iterations_test.go
@@ -51,8 +51,9 @@ func TestTapdIterationDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.TapdIteration{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
models.TapdIteration{}.TableName()),
-               []string{"connection_id", "id"},
                []string{
+                       "connection_id",
+                       "id",
                        "name",
                        "workspace_id",
                        "startdate",
@@ -81,8 +82,6 @@ func TestTapdIterationDataFlow(t *testing.T) {
                        "connection_id",
                        "workspace_id",
                        "iteration_id",
-               },
-               []string{
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
@@ -95,12 +94,12 @@ func TestTapdIterationDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                ticket.Sprint{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
ticket.Sprint{}.TableName()),
-               []string{"id"},
                []string{
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
                        "_raw_data_remark",
+                       "id",
                        "name",
                        "url",
                        "status",
@@ -116,8 +115,6 @@ func TestTapdIterationDataFlow(t *testing.T) {
                []string{
                        "board_id",
                        "sprint_id",
-               },
-               []string{
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
diff --git a/plugins/tapd/e2e/stories_commit_test.go 
b/plugins/tapd/e2e/stories_commit_test.go
index 4cb48a96..95178f2b 100644
--- a/plugins/tapd/e2e/stories_commit_test.go
+++ b/plugins/tapd/e2e/stories_commit_test.go
@@ -51,8 +51,9 @@ func TestTapdStoryCommitDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.TapdStoryCommit{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
models.TapdStoryCommit{}.TableName()),
-               []string{"connection_id", "id"},
                []string{
+                       "connection_id",
+                       "id",
                        "user_id",
                        "hook_user_name",
                        "commit_id",
@@ -83,8 +84,6 @@ func TestTapdStoryCommitDataFlow(t *testing.T) {
                []string{
                        "issue_id",
                        "commit_sha",
-               },
-               []string{
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
diff --git a/plugins/tapd/e2e/stories_test.go b/plugins/tapd/e2e/stories_test.go
index defe147d..17dc34a6 100644
--- a/plugins/tapd/e2e/stories_test.go
+++ b/plugins/tapd/e2e/stories_test.go
@@ -49,8 +49,10 @@ func TestTapdStoryDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.TapdStoryStatus{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
models.TapdStoryStatus{}.TableName()),
-               []string{"connection_id", "workspace_id", "english_name"},
                []string{
+                       "connection_id",
+                       "workspace_id",
+                       "english_name",
                        "chinese_name",
                        "is_last_step",
                        "_raw_data_params",
@@ -73,8 +75,9 @@ func TestTapdStoryDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.TapdStory{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
models.TapdStory{}.TableName()),
-               []string{"connection_id", "id"},
                []string{
+                       "connection_id",
+                       "id",
                        "workitem_type_id",
                        "name",
                        "description",
@@ -189,8 +192,6 @@ func TestTapdStoryDataFlow(t *testing.T) {
                        "connection_id",
                        "workspace_id",
                        "story_id",
-               },
-               []string{
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
@@ -205,8 +206,6 @@ func TestTapdStoryDataFlow(t *testing.T) {
                        "workspace_id",
                        "iteration_id",
                        "story_id",
-               },
-               []string{
                        "resolution_date",
                        "story_created_date",
                        "_raw_data_params",
@@ -221,8 +220,6 @@ func TestTapdStoryDataFlow(t *testing.T) {
                []string{
                        "label_name",
                        "story_id",
-               },
-               []string{
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
@@ -238,8 +235,8 @@ func TestTapdStoryDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                ticket.Issue{},
                fmt.Sprintf("./snapshot_tables/%s_story.csv", 
ticket.Issue{}.TableName()),
-               []string{"id"},
                []string{
+                       "id",
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
@@ -277,8 +274,6 @@ func TestTapdStoryDataFlow(t *testing.T) {
                []string{
                        "board_id",
                        "issue_id",
-               },
-               []string{
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
@@ -291,8 +286,6 @@ func TestTapdStoryDataFlow(t *testing.T) {
                []string{
                        "issue_id",
                        "sprint_id",
-               },
-               []string{
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
@@ -306,8 +299,6 @@ func TestTapdStoryDataFlow(t *testing.T) {
                []string{
                        "issue_id",
                        "label_name",
-               },
-               []string{
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
diff --git a/plugins/tapd/e2e/story_and_bug_status_test.go 
b/plugins/tapd/e2e/story_and_bug_status_test.go
index ee4b6944..2e375cbe 100644
--- a/plugins/tapd/e2e/story_and_bug_status_test.go
+++ b/plugins/tapd/e2e/story_and_bug_status_test.go
@@ -50,8 +50,10 @@ func TestTapdStoryAndBugStatusDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.TapdStoryStatus{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
models.TapdStoryStatus{}.TableName()),
-               []string{"connection_id", "workspace_id", "english_name"},
                []string{
+                       "connection_id",
+                       "workspace_id",
+                       "english_name",
                        "chinese_name",
                        "is_last_step",
                        "_raw_data_params",
@@ -72,8 +74,10 @@ func TestTapdStoryAndBugStatusDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.TapdBugStatus{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
models.TapdBugStatus{}.TableName()),
-               []string{"connection_id", "workspace_id", "english_name"},
                []string{
+                       "connection_id",
+                       "workspace_id",
+                       "english_name",
                        "chinese_name",
                        "is_last_step",
                        "_raw_data_params",
diff --git a/plugins/tapd/e2e/story_bug_test.go 
b/plugins/tapd/e2e/story_bug_test.go
index a9d4eedf..2a76ba2d 100644
--- a/plugins/tapd/e2e/story_bug_test.go
+++ b/plugins/tapd/e2e/story_bug_test.go
@@ -54,8 +54,6 @@ func TestTapdStoryBugDataFlow(t *testing.T) {
                        "workspace_id",
                        "story_id",
                        "bug_id",
-               },
-               []string{
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
diff --git a/plugins/tapd/e2e/story_category_test.go 
b/plugins/tapd/e2e/story_category_test.go
index 5b76861a..bc0dc2f3 100644
--- a/plugins/tapd/e2e/story_category_test.go
+++ b/plugins/tapd/e2e/story_category_test.go
@@ -49,8 +49,9 @@ func TestTapdStoryCategoriesDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.TapdStoryCategory{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
models.TapdStoryCategory{}.TableName()),
-               []string{"connection_id", "id"},
                []string{
+                       "connection_id",
+                       "id",
                        "name",
                        "description",
                        "parent_id",
diff --git a/plugins/tapd/e2e/story_changelog_test.go 
b/plugins/tapd/e2e/story_changelog_test.go
index 82b61f7b..2bac9414 100644
--- a/plugins/tapd/e2e/story_changelog_test.go
+++ b/plugins/tapd/e2e/story_changelog_test.go
@@ -52,8 +52,9 @@ func TestTapdStoryChangelogDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.TapdStoryChangelog{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
models.TapdStoryChangelog{}.TableName()),
-               []string{"connection_id", "id"},
                []string{
+                       "connection_id",
+                       "id",
                        "workspace_id",
                        "workitem_type_id",
                        "creator",
@@ -77,8 +78,6 @@ func TestTapdStoryChangelogDataFlow(t *testing.T) {
                        "connection_id",
                        "changelog_id",
                        "field",
-               },
-               []string{
                        "value_before_parsed",
                        "value_after_parsed",
                        "iteration_id_from",
@@ -95,8 +94,8 @@ func TestTapdStoryChangelogDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                ticket.Changelog{},
                fmt.Sprintf("./snapshot_tables/%s_story.csv", 
ticket.Changelog{}.TableName()),
-               []string{"id"},
                []string{
+                       "id",
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
diff --git a/plugins/tapd/e2e/story_custom_field_test.go 
b/plugins/tapd/e2e/story_custom_field_test.go
index d81724d4..9da54ed5 100644
--- a/plugins/tapd/e2e/story_custom_field_test.go
+++ b/plugins/tapd/e2e/story_custom_field_test.go
@@ -49,8 +49,9 @@ func TestTapdStoryCustomFieldDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.TapdStoryCustomFields{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
models.TapdStoryCustomFields{}.TableName()),
-               []string{"connection_id", "id"},
                []string{
+                       "connection_id",
+                       "id",
                        "workspace_id",
                        "entry_type",
                        "custom_field",
diff --git a/plugins/tapd/e2e/sub_workspace_test.go 
b/plugins/tapd/e2e/sub_workspace_test.go
index fd5dd2af..d510a852 100644
--- a/plugins/tapd/e2e/sub_workspace_test.go
+++ b/plugins/tapd/e2e/sub_workspace_test.go
@@ -50,8 +50,9 @@ func TestTapdWorkspaceDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.TapdSubWorkspace{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
models.TapdSubWorkspace{}.TableName()),
-               []string{"connection_id", "id"},
                []string{
+                       "connection_id",
+                       "id",
                        "name",
                        "pretty_name",
                        "category",
@@ -74,8 +75,8 @@ func TestTapdWorkspaceDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                ticket.Board{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
ticket.Board{}.TableName()),
-               []string{"id"},
                []string{
+                       "id",
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
diff --git a/plugins/tapd/e2e/task_custom_field_test.go 
b/plugins/tapd/e2e/task_custom_field_test.go
index c8d0ab2f..5ade6bfa 100644
--- a/plugins/tapd/e2e/task_custom_field_test.go
+++ b/plugins/tapd/e2e/task_custom_field_test.go
@@ -49,8 +49,9 @@ func TestTapdTaskCustomFieldDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.TapdTaskCustomFields{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
models.TapdTaskCustomFields{}.TableName()),
-               []string{"connection_id", "id"},
                []string{
+                       "connection_id",
+                       "id",
                        "workspace_id",
                        "entry_type",
                        "custom_field",
diff --git a/plugins/tapd/e2e/tasks_commit_test.go 
b/plugins/tapd/e2e/tasks_commit_test.go
index 64f022d7..0902f232 100644
--- a/plugins/tapd/e2e/tasks_commit_test.go
+++ b/plugins/tapd/e2e/tasks_commit_test.go
@@ -51,8 +51,9 @@ func TestTapdTaskCommitDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.TapdTaskCommit{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
models.TapdTaskCommit{}.TableName()),
-               []string{"connection_id", "id"},
                []string{
+                       "connection_id",
+                       "id",
                        "user_id",
                        "hook_user_name",
                        "commit_id",
@@ -83,8 +84,6 @@ func TestTapdTaskCommitDataFlow(t *testing.T) {
                []string{
                        "issue_id",
                        "commit_sha",
-               },
-               []string{
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
diff --git a/plugins/tapd/e2e/tasks_test.go b/plugins/tapd/e2e/tasks_test.go
index 443bfdcf..2c67b096 100644
--- a/plugins/tapd/e2e/tasks_test.go
+++ b/plugins/tapd/e2e/tasks_test.go
@@ -53,8 +53,9 @@ func TestTapdTaskDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.TapdTask{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
models.TapdTask{}.TableName()),
-               []string{"connection_id", "id"},
                []string{
+                       "connection_id",
+                       "id",
                        "name",
                        "description",
                        "workspace_id",
@@ -151,8 +152,6 @@ func TestTapdTaskDataFlow(t *testing.T) {
                        "connection_id",
                        "workspace_id",
                        "task_id",
-               },
-               []string{
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
@@ -167,8 +166,6 @@ func TestTapdTaskDataFlow(t *testing.T) {
                        "workspace_id",
                        "iteration_id",
                        "task_id",
-               },
-               []string{
                        "resolution_date",
                        "task_created_date",
                        "_raw_data_params",
@@ -183,8 +180,6 @@ func TestTapdTaskDataFlow(t *testing.T) {
                []string{
                        "label_name",
                        "task_id",
-               },
-               []string{
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
@@ -200,8 +195,8 @@ func TestTapdTaskDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                ticket.Issue{},
                fmt.Sprintf("./snapshot_tables/%s_task.csv", 
ticket.Issue{}.TableName()),
-               []string{"id"},
                []string{
+                       "id",
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
@@ -239,8 +234,6 @@ func TestTapdTaskDataFlow(t *testing.T) {
                []string{
                        "board_id",
                        "issue_id",
-               },
-               []string{
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
@@ -253,8 +246,6 @@ func TestTapdTaskDataFlow(t *testing.T) {
                []string{
                        "issue_id",
                        "sprint_id",
-               },
-               []string{
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
@@ -268,8 +259,6 @@ func TestTapdTaskDataFlow(t *testing.T) {
                []string{
                        "issue_id",
                        "label_name",
-               },
-               []string{
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
diff --git a/plugins/tapd/e2e/user_test.go b/plugins/tapd/e2e/user_test.go
index 9beb86c8..c6d173f5 100644
--- a/plugins/tapd/e2e/user_test.go
+++ b/plugins/tapd/e2e/user_test.go
@@ -50,8 +50,10 @@ func TestTapdUserDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.TapdUser{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
models.TapdUser{}.TableName()),
-               []string{"connection_id", "workspace_id", "user"},
                []string{
+                       "connection_id",
+                       "workspace_id",
+                       "user",
                        "name",
                        "_raw_data_params",
                        "_raw_data_table",
@@ -66,8 +68,8 @@ func TestTapdUserDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                user.User{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
user.User{}.TableName()),
-               []string{"id"},
                []string{
+                       "id",
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",
diff --git a/plugins/tapd/e2e/worklog_test.go b/plugins/tapd/e2e/worklog_test.go
index db6bec0a..adbcc725 100644
--- a/plugins/tapd/e2e/worklog_test.go
+++ b/plugins/tapd/e2e/worklog_test.go
@@ -51,8 +51,9 @@ func TestTapdWorklogDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                models.TapdWorklog{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
models.TapdWorklog{}.TableName()),
-               []string{"connection_id", "id"},
                []string{
+                       "connection_id",
+                       "id",
                        "workspace_id",
                        "entity_type",
                        "entity_id",
@@ -73,8 +74,8 @@ func TestTapdWorklogDataFlow(t *testing.T) {
        dataflowTester.VerifyTable(
                ticket.IssueWorklog{},
                fmt.Sprintf("./snapshot_tables/%s.csv", 
ticket.IssueWorklog{}.TableName()),
-               []string{"id"},
                []string{
+                       "id",
                        "_raw_data_params",
                        "_raw_data_table",
                        "_raw_data_id",

Reply via email to