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 e434e0dcd feat(domain): create QA tables and models (#8395)
e434e0dcd is described below
commit e434e0dcd10c15c3c76c4279348e6e2116955303
Author: NaRro <[email protected]>
AuthorDate: Mon Apr 28 09:06:22 2025 +0000
feat(domain): create QA tables and models (#8395)
* feat(core): create QA tables and models
- Add QaApi, QaProject, QaTestCase, and QaTestCaseExecution models
- Create corresponding database tables for QA
- Update migration scripts to include QA table creation
* feat(customize): add CSV import APIs for QA data
- Implement API endpoints for importing QA APIs, test cases, and test case
executions from CSV files
- Add file extraction and CSV parsing logic
- Include error handling and input validation
- Placeholder for service layer integration
#8393
* feat(customize): add incremental import functionality for QA APIs and
Test Cases
- Add incremental import parameter to API endpoints for QA APIs and Test
Cases
- Implement service layer functions for importing CSV data into qa_apis and
qa_test_cases tables
- Handle both incremental and full imports
- Update API handlers to use new service layer functions
* feat(customize): add import functionality for QA test case executions
- Implement ImportQaTestCaseExecutions function in Service
- Add related CSV import handlers and data processors
- Include incremental import support for QA test case executions
- Update API documentation to reflect new import functionality
- Add unit tests for new import features
#8393
* feat(core): add QA domain layer models
- Import QA package in domaininfo.go
- Add QA-related domain tables to GetDomainTablesInfo() function
- Fix lint
* feat(customize): import creator information for QA entities
- Add creator_name field to CSV imports for QA APIs, test cases, and
executions
- Create or update Account entities based on creator_name
- Link Account entities to QA entities in the database
- Update API and test case imports to handle creator information
- Remove qa_apis handling when upload qa_test_cases
#8393
* feat(qa): add API path and method fields and update related tests
- Add 'Path' and 'Method' fields to QaApi struct
- Update QaTestCase to use QaApiId instead of TargetId
- Modify migration script to include new fields
- Update CSV files and snapshots to reflect changes
#8393
---
.../models/domainlayer/domaininfo/domaininfo.go | 6 +
backend/core/models/domainlayer/qa/qa_api.go | 39 ++++++
backend/core/models/domainlayer/qa/qa_project.go | 32 +++++
backend/core/models/domainlayer/qa/qa_test_case.go | 39 ++++++
.../domainlayer/qa/qa_test_case_execution.go | 40 +++++++
.../migrationscripts/20250421_create_qa_tables.go | 98 +++++++++++++++
backend/core/models/migrationscripts/register.go | 1 +
.../plugins/customize/api/{csv.go => csv_issue.go} | 0
backend/plugins/customize/api/csv_qa.go | 132 +++++++++++++++++++++
.../plugins/customize/e2e/import_qa_apis_test.go | 115 ++++++++++++++++++
.../customize/e2e/import_qa_test_cases_test.go | 119 +++++++++++++++++++
.../e2e/import_test_case_execution_test.go | 119 +++++++++++++++++++
.../customize/e2e/raw_tables/qa_apis_input.csv | 4 +
.../e2e/raw_tables/qa_apis_input_incremental.csv | 3 +
.../raw_tables/qa_test_case_executions_input.csv | 4 +
.../qa_test_case_executions_input_incremental.csv | 3 +
.../e2e/raw_tables/qa_test_cases_input.csv | 4 +
.../raw_tables/qa_test_cases_input_incremental.csv | 3 +
.../accounts_from_qa_apis_incremental_output.csv | 4 +
.../accounts_from_qa_apis_output.csv | 3 +
...ccounts_from_qa_test_case_executions_output.csv | 3 +
..._qa_test_case_executions_output_incremental.csv | 4 +
.../accounts_from_qa_test_cases.csv | 4 +
.../e2e/snapshot_tables/qa_apis_output.csv | 4 +
.../snapshot_tables/qa_apis_output_incremental.csv | 5 +
.../e2e/snapshot_tables/qa_projects_output.csv | 2 +
.../qa_test_case_executions_output.csv | 4 +
.../qa_test_case_executions_output_incremental.csv | 5 +
.../e2e/snapshot_tables/qa_test_cases_output.csv | 4 +
.../qa_test_cases_output_incremental.csv | 5 +
backend/plugins/customize/service/service.go | 120 +++++++++++++++++--
31 files changed, 917 insertions(+), 11 deletions(-)
diff --git a/backend/core/models/domainlayer/domaininfo/domaininfo.go
b/backend/core/models/domainlayer/domaininfo/domaininfo.go
index 3bec3b810..b2cc5431f 100644
--- a/backend/core/models/domainlayer/domaininfo/domaininfo.go
+++ b/backend/core/models/domainlayer/domaininfo/domaininfo.go
@@ -23,6 +23,7 @@ import (
"github.com/apache/incubator-devlake/core/models/domainlayer/codequality"
"github.com/apache/incubator-devlake/core/models/domainlayer/crossdomain"
"github.com/apache/incubator-devlake/core/models/domainlayer/devops"
+ "github.com/apache/incubator-devlake/core/models/domainlayer/qa"
"github.com/apache/incubator-devlake/core/models/domainlayer/ticket"
)
@@ -94,5 +95,10 @@ func GetDomainTablesInfo() []dal.Tabler {
&ticket.IssueCustomArrayField{},
&ticket.Incident{},
&ticket.IncidentAssignee{},
+ // qa
+ &qa.QaProject{},
+ &qa.QaApi{},
+ &qa.QaTestCase{},
+ &qa.QaTestCaseExecution{},
}
}
diff --git a/backend/core/models/domainlayer/qa/qa_api.go
b/backend/core/models/domainlayer/qa/qa_api.go
new file mode 100644
index 000000000..faae195a8
--- /dev/null
+++ b/backend/core/models/domainlayer/qa/qa_api.go
@@ -0,0 +1,39 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package qa
+
+import (
+ "time"
+
+ "github.com/apache/incubator-devlake/core/models/domainlayer"
+)
+
+// QaApi represents a QA API in the domain layer
+type QaApi struct {
+ domainlayer.DomainEntityExtended
+ Name string `gorm:"type:varchar(255);comment:API name"`
+ Path string `gorm:"type:varchar(255);comment:API path"`
+ Method string `gorm:"type:varchar(255);comment:API method"`
+ CreateTime time.Time `gorm:"comment:API creation time"`
+ CreatorId string `gorm:"type:varchar(255);comment:Creator ID"`
+ QaProjectId string `gorm:"type:varchar(255);index;comment:Project
ID"`
+}
+
+func (QaApi) TableName() string {
+ return "qa_apis"
+}
diff --git a/backend/core/models/domainlayer/qa/qa_project.go
b/backend/core/models/domainlayer/qa/qa_project.go
new file mode 100644
index 000000000..09f0274d4
--- /dev/null
+++ b/backend/core/models/domainlayer/qa/qa_project.go
@@ -0,0 +1,32 @@
+/*
+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 qa
+
+import (
+ "github.com/apache/incubator-devlake/core/models/domainlayer"
+)
+
+// QaProject represents a QA project in the domain layer
+type QaProject struct {
+ domainlayer.DomainEntityExtended
+ Name string `gorm:"type:varchar(255);comment:Project name"`
+}
+
+func (QaProject) TableName() string {
+ return "qa_projects"
+}
diff --git a/backend/core/models/domainlayer/qa/qa_test_case.go
b/backend/core/models/domainlayer/qa/qa_test_case.go
new file mode 100644
index 000000000..ed6dcb189
--- /dev/null
+++ b/backend/core/models/domainlayer/qa/qa_test_case.go
@@ -0,0 +1,39 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package qa
+
+import (
+ "time"
+
+ "github.com/apache/incubator-devlake/core/models/domainlayer"
+)
+
+// QaTestCase represents a QA test case in the domain layer
+type QaTestCase struct {
+ domainlayer.DomainEntityExtended
+ Name string `gorm:"type:varchar(255);comment:Test case name"`
+ CreateTime time.Time `gorm:"comment:Test case creation time"`
+ CreatorId string `gorm:"type:varchar(255);comment:Creator ID"`
+ Type string `gorm:"type:varchar(255);comment:Test case type |
functional | api"` // enum in image, using string
+ QaApiId string `gorm:"type:varchar(255);comment:Valid only when
type = api, represents qa_api_id"` // nullable in image, using string
+ QaProjectId string `gorm:"type:varchar(255);index;comment:Project
ID"`
+}
+
+func (qaTestCase *QaTestCase) TableName() string {
+ return "qa_test_cases"
+}
diff --git a/backend/core/models/domainlayer/qa/qa_test_case_execution.go
b/backend/core/models/domainlayer/qa/qa_test_case_execution.go
new file mode 100644
index 000000000..cfab89b6d
--- /dev/null
+++ b/backend/core/models/domainlayer/qa/qa_test_case_execution.go
@@ -0,0 +1,40 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package qa
+
+import (
+ "time"
+
+ "github.com/apache/incubator-devlake/core/models/domainlayer"
+)
+
+// QaTestCaseExecution represents a QA test case execution in the domain layer
+type QaTestCaseExecution struct {
+ domainlayer.DomainEntityExtended
+ QaProjectId string `gorm:"type:varchar(255);index;comment:Project
ID"`
+ QaTestCaseId string `gorm:"type:varchar(255);index;comment:Test case
ID"`
+ CreateTime time.Time `gorm:"comment:Test (plan) creation time"`
+ StartTime time.Time `gorm:"comment:Test start time"`
+ FinishTime time.Time `gorm:"comment:Test finish time"`
+ CreatorId string `gorm:"type:varchar(255);comment:Executor ID"`
+ Status string `gorm:"type:varchar(255);comment:Test execution
status | PENDING | IN_PROGRESS | SUCCESS | FAILED"` // enum, using string
+}
+
+func (QaTestCaseExecution) TableName() string {
+ return "qa_test_case_executions"
+}
diff --git a/backend/core/models/migrationscripts/20250421_create_qa_tables.go
b/backend/core/models/migrationscripts/20250421_create_qa_tables.go
new file mode 100644
index 000000000..cd7381767
--- /dev/null
+++ b/backend/core/models/migrationscripts/20250421_create_qa_tables.go
@@ -0,0 +1,98 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package migrationscripts
+
+import (
+ "time"
+
+ "github.com/apache/incubator-devlake/core/context"
+ "github.com/apache/incubator-devlake/core/errors"
+
"github.com/apache/incubator-devlake/core/models/migrationscripts/archived"
+ "github.com/apache/incubator-devlake/core/plugin"
+)
+
+var _ plugin.MigrationScript = (*createQaTables)(nil)
+
+type createQaTables struct{}
+
+func (*createQaTables) Version() uint64 {
+ return 20250421104500 // YYYYMMDDHHMMSS format
+}
+
+func (*createQaTables) Name() string {
+ return "create QA tables"
+}
+
+// QaProject represents a QA project in the domain layer
+type QaProject struct {
+ archived.DomainEntityExtended
+ Name string `gorm:"type:varchar(255);comment:Project name"`
+}
+
+// QaApi represents a QA API in the domain layer
+type QaApi struct {
+ archived.DomainEntityExtended
+ Name string `gorm:"type:varchar(255);comment:API name"`
+ Path string `gorm:"type:varchar(255);comment:API path"`
+ Method string `gorm:"type:varchar(255);comment:API method"`
+ CreateTime time.Time `gorm:"comment:API creation time"`
+ CreatorId string `gorm:"type:varchar(255);comment:Creator ID"`
+ QaProjectId string `gorm:"type:varchar(255);index;comment:Project
ID"`
+}
+
+// QaTestCase represents a QA test case in the domain layer
+type QaTestCase struct {
+ archived.DomainEntityExtended
+ Name string `gorm:"type:varchar(255);comment:Test case name"`
+ CreateTime time.Time `gorm:"comment:Test case creation time"`
+ CreatorId string `gorm:"type:varchar(255);comment:Creator ID"`
+ Type string `gorm:"type:varchar(255);comment:Test case type |
functional | api"` // enum in image, using string
+ QaApiId string `gorm:"type:varchar(255);comment:Valid only when
type = api, represents qa_api_id"` // nullable in image, using string
+ QaProjectId string `gorm:"type:varchar(255);index;comment:Project
ID"`
+}
+
+// QaTestCaseExecution represents a QA test case execution in the domain layer
+type QaTestCaseExecution struct {
+ archived.DomainEntityExtended
+ QaProjectId string `gorm:"type:varchar(255);index;comment:Project
ID"`
+ QaTestCaseId string `gorm:"type:varchar(255);index;comment:Test case
ID"`
+ CreateTime time.Time `gorm:"comment:Test (plan) creation time"`
+ StartTime time.Time `gorm:"comment:Test start time"`
+ FinishTime time.Time `gorm:"comment:Test finish time"`
+ CreatorId string `gorm:"type:varchar(255);comment:Executor ID"`
+ Status string `gorm:"type:varchar(255);comment:Test execution
status | PENDING | IN_PROGRESS | SUCCESS | FAILED"` // enum, using string
+}
+
+func (*createQaTables) Up(basicRes context.BasicRes) errors.Error {
+ db := basicRes.GetDal()
+
+ if err := db.AutoMigrate(&QaProject{}); err != nil {
+ return err
+ }
+ if err := db.AutoMigrate(&QaApi{}); err != nil {
+ return err
+ }
+ if err := db.AutoMigrate(&QaTestCase{}); err != nil {
+ return err
+ }
+ if err := db.AutoMigrate(&QaTestCaseExecution{}); err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/backend/core/models/migrationscripts/register.go
b/backend/core/models/migrationscripts/register.go
index c7dd8b759..4886f66a4 100644
--- a/backend/core/models/migrationscripts/register.go
+++ b/backend/core/models/migrationscripts/register.go
@@ -135,5 +135,6 @@ func All() []plugin.MigrationScript {
new(addIsChildToCicdPipeline),
new(addCqIssueImpacts),
new(addDueDateToIssues),
+ new(createQaTables),
}
}
diff --git a/backend/plugins/customize/api/csv.go
b/backend/plugins/customize/api/csv_issue.go
similarity index 100%
rename from backend/plugins/customize/api/csv.go
rename to backend/plugins/customize/api/csv_issue.go
diff --git a/backend/plugins/customize/api/csv_qa.go
b/backend/plugins/customize/api/csv_qa.go
new file mode 100644
index 000000000..e4770ad8d
--- /dev/null
+++ b/backend/plugins/customize/api/csv_qa.go
@@ -0,0 +1,132 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package api
+
+import (
+ "strings"
+
+ "github.com/apache/incubator-devlake/core/errors"
+ "github.com/apache/incubator-devlake/core/plugin"
+)
+
+// ImportQaApis accepts a CSV file, parses and saves it to the database
+// @Summary Upload qa_apis.csv file
+// @Description Upload qa_apis.csv file.
+// @Tags plugins/customize
+// @Accept multipart/form-data
+// @Param qaProjectId formData string true "the ID of the QA project"
+// @Param file formData file true "select file to upload"
+// @Param incremental formData bool false "incremental import"
+// @Produce json
+// @Success 200
+// @Failure 400 {object} shared.ApiBody "Bad Request"
+// @Failure 500 {object} shared.ApiBody "Internal Error"
+// @Router /plugins/customize/csvfiles/qa_apis.csv [post]
+func (h *Handlers) ImportQaApis(input *plugin.ApiResourceInput)
(*plugin.ApiResourceOutput, errors.Error) {
+ file, err := h.extractFile(input)
+ if err != nil {
+ return nil, err
+ }
+ // nolint
+ defer file.Close()
+
+ incremental := false
+ if input.Request.FormValue("incremental") == "true" {
+ incremental = true
+ }
+
+ qaProjectId := strings.TrimSpace(input.Request.FormValue("qaProjectId"))
+ if qaProjectId == "" {
+ return nil, errors.BadInput.New("empty qaProjectId")
+ }
+
+ return nil, h.svc.ImportQaApis(qaProjectId, file, incremental)
+
+}
+
+// ImportQaTestCases accepts a CSV file, parses and saves it to the database
+// @Summary Upload qa_test_cases.csv file
+// @Description Upload qa_test_cases.csv file.
+// @Tags plugins/customize
+// @Accept multipart/form-data
+// @Param qaProjectId formData string true "the ID of the QA project"
+// @Param qaProjectName formData string true "the name of the QA
project"
+// @Param file formData file true "select file to upload"
+// @Param incremental formData bool false "incremental update"
+// @Produce json
+// @Success 200
+// @Failure 400 {object} shared.ApiBody "Bad Request"
+// @Failure 500 {object} shared.ApiBody "Internal Error"
+// @Router /plugins/customize/csvfiles/qa_test_cases.csv [post]
+func (h *Handlers) ImportQaTestCases(input *plugin.ApiResourceInput)
(*plugin.ApiResourceOutput, errors.Error) {
+ file, err := h.extractFile(input)
+ if err != nil {
+ return nil, err
+ }
+ // nolint
+ defer file.Close()
+
+ incremental := false
+ if input.Request.FormValue("incremental") == "true" {
+ incremental = true
+ }
+
+ qaProjectId := strings.TrimSpace(input.Request.FormValue("qaProjectId"))
+ if qaProjectId == "" {
+ return nil, errors.BadInput.New("empty qaProjectId")
+ }
+ qaProjectName :=
strings.TrimSpace(input.Request.FormValue("qaProjectName"))
+ if qaProjectName == "" {
+ return nil, errors.BadInput.New("empty qaProjectName")
+ }
+ return nil, h.svc.ImportQaTestCases(qaProjectId, qaProjectName, file,
incremental) // records contains the CSV data
+}
+
+// ImportQaTestCaseExecutions accepts a CSV file, parses and saves it to the
database
+// @Summary Upload qa_test_case_executions.csv file
+// @Description Upload qa_test_case_executions.csv file.
+// @Tags plugins/customize
+// @Accept multipart/form-data
+// @Param qaProjectId formData string true "the ID of the QA project"
+// @Param file formData file true "select file to upload"
+// @Param incremental formData bool false "incremental update"
+// @Produce json
+// @Success 200
+// @Failure 400 {object} shared.ApiBody "Bad Request"
+// @Failure 500 {object} shared.ApiBody "Internal Error"
+// @Router /plugins/customize/csvfiles/qa_test_case_executions.csv [post]
+func (h *Handlers) ImportQaTestCaseExecutions(input *plugin.ApiResourceInput)
(*plugin.ApiResourceOutput, errors.Error) {
+ file, err := h.extractFile(input)
+ if err != nil {
+ return nil, err
+ }
+ // nolint
+ defer file.Close()
+
+ incremental := false
+ if input.Request.FormValue("incremental") == "true" {
+ incremental = true
+ }
+
+ qaProjectId := strings.TrimSpace(input.Request.FormValue("qaProjectId"))
+ if qaProjectId == "" {
+ return nil, errors.BadInput.New("empty qaProjectId")
+ }
+
+ return nil, h.svc.ImportQaTestCaseExecutions(qaProjectId, file,
incremental)
+}
diff --git a/backend/plugins/customize/e2e/import_qa_apis_test.go
b/backend/plugins/customize/e2e/import_qa_apis_test.go
new file mode 100644
index 000000000..1e0c9c5b4
--- /dev/null
+++ b/backend/plugins/customize/e2e/import_qa_apis_test.go
@@ -0,0 +1,115 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package e2e
+
+import (
+ "os"
+ "testing"
+
+
"github.com/apache/incubator-devlake/core/models/domainlayer/crossdomain"
+ "github.com/apache/incubator-devlake/core/models/domainlayer/qa"
+ "github.com/apache/incubator-devlake/helpers/e2ehelper"
+ "github.com/apache/incubator-devlake/plugins/customize/impl"
+ "github.com/apache/incubator-devlake/plugins/customize/service"
+)
+
+func TestImportQaApisDataFlow(t *testing.T) {
+ var plugin impl.Customize
+ dataflowTester := e2ehelper.NewDataFlowTester(t, "customize", plugin)
+
+ // Flush the qa_apis table
+ dataflowTester.FlushTabler(&qa.QaApi{})
+ dataflowTester.FlushTabler(&crossdomain.Account{})
+
+ // Create a new service instance
+ svc := service.NewService(dataflowTester.Dal)
+
+ // Assume a dummy CSV file exists for testing
+ // You will need to create this file at
backend/plugins/customize/e2e/raw_tables/qa_apis_input.csv
+ // with appropriate test data.
+ qaApisFile, err := os.Open("raw_tables/qa_apis_input.csv")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer qaApisFile.Close()
+
+ // Define a dummy qaProjectId
+ qaProjectId := "test-qa-project-id"
+
+ // Import data from the CSV file
+ err = svc.ImportQaApis(qaProjectId, qaApisFile, false) // Use false for
initial import
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Verify the imported data
+ // You will need to create the expected snapshot file at
backend/plugins/customize/e2e/snapshot_tables/qa_apis_output.csv
+ // and define the columns to verify based on your qa_apis_input.csv and
the qa.QaApi struct.
+ dataflowTester.VerifyTableWithRawData(
+ &qa.QaApi{},
+ "snapshot_tables/qa_apis_output.csv",
+ []string{
+ "id",
+ "name",
+ "path",
+ "method",
+ "create_time",
+ "creator_id",
+ "qa_project_id",
+ })
+
+ dataflowTester.VerifyTableWithRawData(
+ &crossdomain.Account{},
+ "snapshot_tables/accounts_from_qa_apis_output.csv",
+ []string{
+ "id",
+ "full_name",
+ "user_name",
+ })
+ // Add incremental import test if needed, similar to
import_issues_test.go
+ qaApisIncrementalFile, err :=
os.Open("raw_tables/qa_apis_input_incremental.csv")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer qaApisIncrementalFile.Close()
+ err = svc.ImportQaApis(qaProjectId, qaApisIncrementalFile, true) // Use
true for incremental import
+ if err != nil {
+ t.Fatal(err)
+ }
+ dataflowTester.VerifyTableWithRawData(
+ &qa.QaApi{},
+ "snapshot_tables/qa_apis_output_incremental.csv",
+ []string{
+ "id",
+ "name",
+ "path",
+ "method",
+ "create_time",
+ "creator_id",
+ "qa_project_id",
+ })
+ dataflowTester.VerifyTableWithRawData(
+ &crossdomain.Account{},
+ "snapshot_tables/accounts_from_qa_apis_incremental_output.csv",
+ []string{
+ "id",
+ "full_name",
+ "user_name",
+ },
+ )
+}
diff --git a/backend/plugins/customize/e2e/import_qa_test_cases_test.go
b/backend/plugins/customize/e2e/import_qa_test_cases_test.go
new file mode 100644
index 000000000..58d448298
--- /dev/null
+++ b/backend/plugins/customize/e2e/import_qa_test_cases_test.go
@@ -0,0 +1,119 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package e2e
+
+import (
+ "os"
+ "testing"
+
+
"github.com/apache/incubator-devlake/core/models/domainlayer/crossdomain"
+ "github.com/apache/incubator-devlake/core/models/domainlayer/qa"
+ "github.com/apache/incubator-devlake/helpers/e2ehelper"
+ "github.com/apache/incubator-devlake/plugins/customize/impl"
+ "github.com/apache/incubator-devlake/plugins/customize/service"
+)
+
+func TestImportQaTestCasesDataFlow(t *testing.T) {
+ var plugin impl.Customize
+ dataflowTester := e2ehelper.NewDataFlowTester(t, "customize", plugin)
+
+ // Flush the relevant tables
+ dataflowTester.FlushTabler(&qa.QaTestCase{})
+ dataflowTester.FlushTabler(&qa.QaProject{}) // qaTestCaseHandler
also creates/updates QaProject
+ dataflowTester.FlushTabler(&qa.QaApi{}) // qaTestCaseHandler
also creates/updates QaApi for API test cases
+ dataflowTester.FlushTabler(&crossdomain.Account{}) // qaTestCaseHandler
also creates/updates Account for API test cases
+
+ // Create a new service instance
+ svc := service.NewService(dataflowTester.Dal)
+
+ // Assume a dummy CSV file exists for testing
+ // You will need to create this file at
backend/plugins/customize/e2e/raw_tables/qa_test_cases_input.csv
+ // with appropriate test data.
+ qaTestCasesFile, err := os.Open("raw_tables/qa_test_cases_input.csv")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer qaTestCasesFile.Close()
+
+ // Define dummy qaProjectId and qaProjectName
+ qaProjectId := "test-qa-project-id"
+ qaProjectName := "Test QA Project"
+
+ // Import data from the CSV file
+ err = svc.ImportQaTestCases(qaProjectId, qaProjectName,
qaTestCasesFile, false) // Use false for initial import
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Verify the imported data in qa_test_cases table
+ // You will need to create the expected snapshot file at
backend/plugins/customize/e2e/snapshot_tables/qa_test_cases_output.csv
+ // and define the columns to verify based on your
qa_test_cases_input.csv and the qa.QaTestCase struct.
+ dataflowTester.VerifyTableWithRawData(
+ &qa.QaTestCase{},
+ "snapshot_tables/qa_test_cases_output.csv",
+ []string{
+ "id",
+ "name",
+ "create_time",
+ "creator_id",
+ "type",
+ "qa_project_id",
+ "qa_api_id",
+ })
+
+ // Add incremental import test
+ qaTestCasesIncrementalFile, err :=
os.Open("raw_tables/qa_test_cases_input_incremental.csv")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer qaTestCasesIncrementalFile.Close()
+ err = svc.ImportQaTestCases(qaProjectId, qaProjectName,
qaTestCasesIncrementalFile, true) // Use true for incremental import
+ if err != nil {
+ t.Fatal(err)
+ }
+ dataflowTester.VerifyTableWithRawData(
+ &qa.QaTestCase{},
+ "snapshot_tables/qa_test_cases_output_incremental.csv",
+ []string{
+ "id",
+ "name",
+ "create_time",
+ "creator_id",
+ "type",
+ "qa_project_id",
+ "qa_api_id",
+ })
+
+ // verify qa_projects and qa_apis
+ dataflowTester.VerifyTableWithRawData(
+ &qa.QaProject{},
+ "snapshot_tables/qa_projects_output.csv",
+ []string{
+ "id",
+ "name",
+ })
+ dataflowTester.VerifyTableWithRawData(
+ &crossdomain.Account{},
+ "snapshot_tables/accounts_from_qa_test_cases.csv",
+ []string{
+ "id",
+ "full_name",
+ "user_name",
+ },
+ )
+}
diff --git a/backend/plugins/customize/e2e/import_test_case_execution_test.go
b/backend/plugins/customize/e2e/import_test_case_execution_test.go
new file mode 100644
index 000000000..92d5750b8
--- /dev/null
+++ b/backend/plugins/customize/e2e/import_test_case_execution_test.go
@@ -0,0 +1,119 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package e2e
+
+import (
+ "os"
+ "testing"
+
+
"github.com/apache/incubator-devlake/core/models/domainlayer/crossdomain"
+ "github.com/apache/incubator-devlake/core/models/domainlayer/qa"
+ "github.com/apache/incubator-devlake/helpers/e2ehelper"
+ "github.com/apache/incubator-devlake/plugins/customize/impl"
+ "github.com/apache/incubator-devlake/plugins/customize/service"
+)
+
+func TestImportQaTestCaseExecutionsDataFlow(t *testing.T) {
+ var plugin impl.Customize
+ dataflowTester := e2ehelper.NewDataFlowTester(t, "customize", plugin)
+
+ // Flush the relevant table
+ dataflowTester.FlushTabler(&qa.QaTestCaseExecution{})
+ dataflowTester.FlushTabler(&crossdomain.Account{})
+
+ // Create a new service instance
+ svc := service.NewService(dataflowTester.Dal)
+
+ // Assume a dummy CSV file exists for testing
+ // You will need to create this file at
backend/plugins/customize/e2e/raw_tables/qa_test_case_executions_input.csv
+ // with appropriate test data.
+ qaTestCaseExecutionsFile, err :=
os.Open("raw_tables/qa_test_case_executions_input.csv")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer qaTestCaseExecutionsFile.Close()
+
+ // Define a dummy qaProjectId
+ qaProjectId := "test-qa-project-id"
+
+ // Import data from the CSV file
+ err = svc.ImportQaTestCaseExecutions(qaProjectId,
qaTestCaseExecutionsFile, false) // Use false for initial import
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Verify the imported data in qa_test_case_executions table
+ // You will need to create the expected snapshot file at
backend/plugins/customize/e2e/snapshot_tables/qa_test_case_executions_output.csv
+ // and define the columns to verify based on your
qa_test_case_executions_input.csv and the qa.QaTestCaseExecution struct.
+ dataflowTester.VerifyTableWithRawData(
+ &qa.QaTestCaseExecution{},
+ "snapshot_tables/qa_test_case_executions_output.csv",
+ []string{
+ "id",
+ "qa_project_id",
+ "qa_test_case_id",
+ "create_time",
+ "start_time",
+ "finish_time",
+ "creator_id",
+ "status",
+ })
+ dataflowTester.VerifyTableWithRawData(
+ &crossdomain.Account{},
+
"snapshot_tables/accounts_from_qa_test_case_executions_output.csv",
+ []string{
+ "id",
+ "full_name",
+ "user_name",
+ },
+ )
+
+ // Add incremental import test
+ qaTestCaseExecutionsIncrementalFile, err :=
os.Open("raw_tables/qa_test_case_executions_input_incremental.csv")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer qaTestCaseExecutionsIncrementalFile.Close()
+ err = svc.ImportQaTestCaseExecutions(qaProjectId,
qaTestCaseExecutionsIncrementalFile, true) // Use true for incremental import
+ if err != nil {
+ t.Fatal(err)
+ }
+ dataflowTester.VerifyTableWithRawData(
+ &qa.QaTestCaseExecution{},
+
"snapshot_tables/qa_test_case_executions_output_incremental.csv",
+ []string{
+ "id",
+ "qa_project_id",
+ "qa_test_case_id",
+ "create_time",
+ "start_time",
+ "finish_time",
+ "creator_id",
+ "status",
+ })
+
+ dataflowTester.VerifyTableWithRawData(
+ &crossdomain.Account{},
+
"snapshot_tables/accounts_from_qa_test_case_executions_output_incremental.csv",
+ []string{
+ "id",
+ "full_name",
+ "user_name",
+ },
+ )
+}
diff --git a/backend/plugins/customize/e2e/raw_tables/qa_apis_input.csv
b/backend/plugins/customize/e2e/raw_tables/qa_apis_input.csv
new file mode 100644
index 000000000..518a11197
--- /dev/null
+++ b/backend/plugins/customize/e2e/raw_tables/qa_apis_input.csv
@@ -0,0 +1,4 @@
+id,name,path,method,create_time,creator_name
+api-1,Get User Info,/users/{id},GET,2023-01-01T10:00:00.000+00:00,user-1
+api-2,Create Order,/orders,POST,2023-01-02T11:00:00.000+00:00,user-2
+api-3,Delete Item,/items/{id},DELETE,2023-01-03T12:00:00.000+00:00,user-1
\ No newline at end of file
diff --git
a/backend/plugins/customize/e2e/raw_tables/qa_apis_input_incremental.csv
b/backend/plugins/customize/e2e/raw_tables/qa_apis_input_incremental.csv
new file mode 100644
index 000000000..fa21d4b4e
--- /dev/null
+++ b/backend/plugins/customize/e2e/raw_tables/qa_apis_input_incremental.csv
@@ -0,0 +1,3 @@
+id,name,path,method,create_time,creator_name
+api-4,Update Profile,/profiles/{id},PUT,2023-01-04T13:00:00.000+00:00,user-3
+api-1,Get User Info
Updated,/users/{id},GET,2023-01-05T14:00:00.000+00:00,user-1
\ No newline at end of file
diff --git
a/backend/plugins/customize/e2e/raw_tables/qa_test_case_executions_input.csv
b/backend/plugins/customize/e2e/raw_tables/qa_test_case_executions_input.csv
new file mode 100644
index 000000000..f6e64a0d7
--- /dev/null
+++ b/backend/plugins/customize/e2e/raw_tables/qa_test_case_executions_input.csv
@@ -0,0 +1,4 @@
+id,qa_test_case_id,create_time,start_time,finish_time,creator_name,status
+exec-1,tc-1,2023-03-01T10:00:00.000+00:00,2023-03-01T10:01:00.000+00:00,2023-03-01T10:05:00.000+00:00,user-a,SUCCESS
+exec-2,tc-2,2023-03-01T11:00:00.000+00:00,2023-03-01T11:02:00.000+00:00,2023-03-01T11:06:00.000+00:00,user-b,FAILED
+exec-3,tc-1,2023-03-02T10:00:00.000+00:00,2023-03-02T10:01:00.000+00:00,2023-03-02T10:04:00.000+00:00,user-a,SUCCESS
\ No newline at end of file
diff --git
a/backend/plugins/customize/e2e/raw_tables/qa_test_case_executions_input_incremental.csv
b/backend/plugins/customize/e2e/raw_tables/qa_test_case_executions_input_incremental.csv
new file mode 100644
index 000000000..ed83207a9
--- /dev/null
+++
b/backend/plugins/customize/e2e/raw_tables/qa_test_case_executions_input_incremental.csv
@@ -0,0 +1,3 @@
+id,qa_test_case_id,create_time,start_time,finish_time,creator_name,status
+exec-4,tc-3,2023-03-03T10:00:00.000+00:00,2023-03-03T10:01:00.000+00:00,2023-03-03T10:05:00.000+00:00,user-c,SUCCESS
+exec-1,tc-1,2023-03-04T10:00:00.000+00:00,2023-03-04T10:01:00.000+00:00,2023-03-04T10:06:00.000+00:00,user-a,FAILED
\ No newline at end of file
diff --git a/backend/plugins/customize/e2e/raw_tables/qa_test_cases_input.csv
b/backend/plugins/customize/e2e/raw_tables/qa_test_cases_input.csv
new file mode 100644
index 000000000..04776c9ec
--- /dev/null
+++ b/backend/plugins/customize/e2e/raw_tables/qa_test_cases_input.csv
@@ -0,0 +1,4 @@
+id,name,create_time,creator_name,type,qa_api_id
+tc-2,Test Case 2 API,2023-02-02T11:00:00.000+00:00,user-b,api,api-1
+tc-1,Test Case 1,2023-02-01T10:00:00.000+00:00,user-a,functional,
+tc-3,Test Case 3,2023-02-03T12:00:00.000+00:00,user-a,functional,
\ No newline at end of file
diff --git
a/backend/plugins/customize/e2e/raw_tables/qa_test_cases_input_incremental.csv
b/backend/plugins/customize/e2e/raw_tables/qa_test_cases_input_incremental.csv
new file mode 100644
index 000000000..82bf8f010
--- /dev/null
+++
b/backend/plugins/customize/e2e/raw_tables/qa_test_cases_input_incremental.csv
@@ -0,0 +1,3 @@
+id,name,create_time,creator_name,type,qa_api_id
+tc-4,Test Case 4 New,2023-02-04T13:00:00.000+00:00,user-c,functional,
+tc-1,Test Case 1 Updated,2023-02-05T14:00:00.000+00:00,user-a,functional,
\ No newline at end of file
diff --git
a/backend/plugins/customize/e2e/snapshot_tables/accounts_from_qa_apis_incremental_output.csv
b/backend/plugins/customize/e2e/snapshot_tables/accounts_from_qa_apis_incremental_output.csv
new file mode 100644
index 000000000..267e7678d
--- /dev/null
+++
b/backend/plugins/customize/e2e/snapshot_tables/accounts_from_qa_apis_incremental_output.csv
@@ -0,0 +1,4 @@
+id,full_name,user_name,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+csv:CsvAccount:0:user-1,user-1,user-1,test-qa-project-id,,0,
+csv:CsvAccount:0:user-2,user-2,user-2,test-qa-project-id,,0,
+csv:CsvAccount:0:user-3,user-3,user-3,test-qa-project-id,,0,
diff --git
a/backend/plugins/customize/e2e/snapshot_tables/accounts_from_qa_apis_output.csv
b/backend/plugins/customize/e2e/snapshot_tables/accounts_from_qa_apis_output.csv
new file mode 100644
index 000000000..4b2a04c78
--- /dev/null
+++
b/backend/plugins/customize/e2e/snapshot_tables/accounts_from_qa_apis_output.csv
@@ -0,0 +1,3 @@
+id,full_name,user_name,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+csv:CsvAccount:0:user-1,user-1,user-1,test-qa-project-id,,0,
+csv:CsvAccount:0:user-2,user-2,user-2,test-qa-project-id,,0,
diff --git
a/backend/plugins/customize/e2e/snapshot_tables/accounts_from_qa_test_case_executions_output.csv
b/backend/plugins/customize/e2e/snapshot_tables/accounts_from_qa_test_case_executions_output.csv
new file mode 100644
index 000000000..d7b592173
--- /dev/null
+++
b/backend/plugins/customize/e2e/snapshot_tables/accounts_from_qa_test_case_executions_output.csv
@@ -0,0 +1,3 @@
+id,full_name,user_name,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+csv:CsvAccount:0:user-a,user-a,user-a,test-qa-project-id,,0,
+csv:CsvAccount:0:user-b,user-b,user-b,test-qa-project-id,,0,
diff --git
a/backend/plugins/customize/e2e/snapshot_tables/accounts_from_qa_test_case_executions_output_incremental.csv
b/backend/plugins/customize/e2e/snapshot_tables/accounts_from_qa_test_case_executions_output_incremental.csv
new file mode 100644
index 000000000..27804bc77
--- /dev/null
+++
b/backend/plugins/customize/e2e/snapshot_tables/accounts_from_qa_test_case_executions_output_incremental.csv
@@ -0,0 +1,4 @@
+id,full_name,user_name,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+csv:CsvAccount:0:user-a,user-a,user-a,test-qa-project-id,,0,
+csv:CsvAccount:0:user-b,user-b,user-b,test-qa-project-id,,0,
+csv:CsvAccount:0:user-c,user-c,user-c,test-qa-project-id,,0,
diff --git
a/backend/plugins/customize/e2e/snapshot_tables/accounts_from_qa_test_cases.csv
b/backend/plugins/customize/e2e/snapshot_tables/accounts_from_qa_test_cases.csv
new file mode 100644
index 000000000..27804bc77
--- /dev/null
+++
b/backend/plugins/customize/e2e/snapshot_tables/accounts_from_qa_test_cases.csv
@@ -0,0 +1,4 @@
+id,full_name,user_name,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+csv:CsvAccount:0:user-a,user-a,user-a,test-qa-project-id,,0,
+csv:CsvAccount:0:user-b,user-b,user-b,test-qa-project-id,,0,
+csv:CsvAccount:0:user-c,user-c,user-c,test-qa-project-id,,0,
diff --git a/backend/plugins/customize/e2e/snapshot_tables/qa_apis_output.csv
b/backend/plugins/customize/e2e/snapshot_tables/qa_apis_output.csv
new file mode 100644
index 000000000..b92e669ed
--- /dev/null
+++ b/backend/plugins/customize/e2e/snapshot_tables/qa_apis_output.csv
@@ -0,0 +1,4 @@
+id,name,path,method,create_time,creator_id,qa_project_id,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+api-1,Get User
Info,/users/{id},GET,2023-01-01T10:00:00.000+00:00,csv:CsvAccount:0:user-1,test-qa-project-id,test-qa-project-id,,,
+api-2,Create
Order,/orders,POST,2023-01-02T11:00:00.000+00:00,csv:CsvAccount:0:user-2,test-qa-project-id,test-qa-project-id,,,
+api-3,Delete
Item,/items/{id},DELETE,2023-01-03T12:00:00.000+00:00,csv:CsvAccount:0:user-1,test-qa-project-id,test-qa-project-id,,,
diff --git
a/backend/plugins/customize/e2e/snapshot_tables/qa_apis_output_incremental.csv
b/backend/plugins/customize/e2e/snapshot_tables/qa_apis_output_incremental.csv
new file mode 100644
index 000000000..085053c70
--- /dev/null
+++
b/backend/plugins/customize/e2e/snapshot_tables/qa_apis_output_incremental.csv
@@ -0,0 +1,5 @@
+id,name,path,method,create_time,creator_id,qa_project_id,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+api-1,Get User Info
Updated,/users/{id},GET,2023-01-05T14:00:00.000+00:00,csv:CsvAccount:0:user-1,test-qa-project-id,test-qa-project-id,,,
+api-2,Create
Order,/orders,POST,2023-01-02T11:00:00.000+00:00,csv:CsvAccount:0:user-2,test-qa-project-id,test-qa-project-id,,,
+api-3,Delete
Item,/items/{id},DELETE,2023-01-03T12:00:00.000+00:00,csv:CsvAccount:0:user-1,test-qa-project-id,test-qa-project-id,,,
+api-4,Update
Profile,/profiles/{id},PUT,2023-01-04T13:00:00.000+00:00,csv:CsvAccount:0:user-3,test-qa-project-id,test-qa-project-id,,,
diff --git
a/backend/plugins/customize/e2e/snapshot_tables/qa_projects_output.csv
b/backend/plugins/customize/e2e/snapshot_tables/qa_projects_output.csv
new file mode 100644
index 000000000..8da21a8a6
--- /dev/null
+++ b/backend/plugins/customize/e2e/snapshot_tables/qa_projects_output.csv
@@ -0,0 +1,2 @@
+id,name,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+test-qa-project-id,Test QA Project,,,0,
diff --git
a/backend/plugins/customize/e2e/snapshot_tables/qa_test_case_executions_output.csv
b/backend/plugins/customize/e2e/snapshot_tables/qa_test_case_executions_output.csv
new file mode 100644
index 000000000..1d51d4a0d
--- /dev/null
+++
b/backend/plugins/customize/e2e/snapshot_tables/qa_test_case_executions_output.csv
@@ -0,0 +1,4 @@
+id,qa_project_id,qa_test_case_id,create_time,start_time,finish_time,creator_id,status,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+exec-1,test-qa-project-id,tc-1,2023-03-01T10:00:00.000+00:00,2023-03-01T10:01:00.000+00:00,2023-03-01T10:05:00.000+00:00,csv:CsvAccount:0:user-a,SUCCESS,test-qa-project-id,,,
+exec-2,test-qa-project-id,tc-2,2023-03-01T11:00:00.000+00:00,2023-03-01T11:02:00.000+00:00,2023-03-01T11:06:00.000+00:00,csv:CsvAccount:0:user-b,FAILED,test-qa-project-id,,,
+exec-3,test-qa-project-id,tc-1,2023-03-02T10:00:00.000+00:00,2023-03-02T10:01:00.000+00:00,2023-03-02T10:04:00.000+00:00,csv:CsvAccount:0:user-a,SUCCESS,test-qa-project-id,,,
diff --git
a/backend/plugins/customize/e2e/snapshot_tables/qa_test_case_executions_output_incremental.csv
b/backend/plugins/customize/e2e/snapshot_tables/qa_test_case_executions_output_incremental.csv
new file mode 100644
index 000000000..e214d734b
--- /dev/null
+++
b/backend/plugins/customize/e2e/snapshot_tables/qa_test_case_executions_output_incremental.csv
@@ -0,0 +1,5 @@
+id,qa_project_id,qa_test_case_id,create_time,start_time,finish_time,creator_id,status,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+exec-1,test-qa-project-id,tc-1,2023-03-04T10:00:00.000+00:00,2023-03-04T10:01:00.000+00:00,2023-03-04T10:06:00.000+00:00,csv:CsvAccount:0:user-a,FAILED,test-qa-project-id,,,
+exec-2,test-qa-project-id,tc-2,2023-03-01T11:00:00.000+00:00,2023-03-01T11:02:00.000+00:00,2023-03-01T11:06:00.000+00:00,csv:CsvAccount:0:user-b,FAILED,test-qa-project-id,,,
+exec-3,test-qa-project-id,tc-1,2023-03-02T10:00:00.000+00:00,2023-03-02T10:01:00.000+00:00,2023-03-02T10:04:00.000+00:00,csv:CsvAccount:0:user-a,SUCCESS,test-qa-project-id,,,
+exec-4,test-qa-project-id,tc-3,2023-03-03T10:00:00.000+00:00,2023-03-03T10:01:00.000+00:00,2023-03-03T10:05:00.000+00:00,csv:CsvAccount:0:user-c,SUCCESS,test-qa-project-id,,,
diff --git
a/backend/plugins/customize/e2e/snapshot_tables/qa_test_cases_output.csv
b/backend/plugins/customize/e2e/snapshot_tables/qa_test_cases_output.csv
new file mode 100644
index 000000000..41dd08029
--- /dev/null
+++ b/backend/plugins/customize/e2e/snapshot_tables/qa_test_cases_output.csv
@@ -0,0 +1,4 @@
+id,name,create_time,creator_id,type,qa_project_id,qa_api_id,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+tc-1,Test Case
1,2023-02-01T10:00:00.000+00:00,csv:CsvAccount:0:user-a,functional,test-qa-project-id,,test-qa-project-id,,,
+tc-2,Test Case 2
API,2023-02-02T11:00:00.000+00:00,csv:CsvAccount:0:user-b,api,test-qa-project-id,api-1,test-qa-project-id,,,
+tc-3,Test Case
3,2023-02-03T12:00:00.000+00:00,csv:CsvAccount:0:user-a,functional,test-qa-project-id,,test-qa-project-id,,,
diff --git
a/backend/plugins/customize/e2e/snapshot_tables/qa_test_cases_output_incremental.csv
b/backend/plugins/customize/e2e/snapshot_tables/qa_test_cases_output_incremental.csv
new file mode 100644
index 000000000..a472df482
--- /dev/null
+++
b/backend/plugins/customize/e2e/snapshot_tables/qa_test_cases_output_incremental.csv
@@ -0,0 +1,5 @@
+id,name,create_time,creator_id,type,qa_project_id,qa_api_id,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+tc-1,Test Case 1
Updated,2023-02-05T14:00:00.000+00:00,csv:CsvAccount:0:user-a,functional,test-qa-project-id,,test-qa-project-id,,,
+tc-2,Test Case 2
API,2023-02-02T11:00:00.000+00:00,csv:CsvAccount:0:user-b,api,test-qa-project-id,api-1,test-qa-project-id,,,
+tc-3,Test Case
3,2023-02-03T12:00:00.000+00:00,csv:CsvAccount:0:user-a,functional,test-qa-project-id,,test-qa-project-id,,,
+tc-4,Test Case 4
New,2023-02-04T13:00:00.000+00:00,csv:CsvAccount:0:user-c,functional,test-qa-project-id,,test-qa-project-id,,,
diff --git a/backend/plugins/customize/service/service.go
b/backend/plugins/customize/service/service.go
index bd8b0aa9d..6f6b0d097 100644
--- a/backend/plugins/customize/service/service.go
+++ b/backend/plugins/customize/service/service.go
@@ -29,9 +29,10 @@ import (
"github.com/apache/incubator-devlake/core/models/common"
"github.com/apache/incubator-devlake/core/models/domainlayer"
"github.com/apache/incubator-devlake/core/models/domainlayer/crossdomain"
+ "github.com/apache/incubator-devlake/core/models/domainlayer/qa"
"github.com/apache/incubator-devlake/core/models/domainlayer/ticket"
"github.com/apache/incubator-devlake/helpers/pluginhelper"
- "github.com/apache/incubator-devlake/plugins/customize/models"
+ customizeModels
"github.com/apache/incubator-devlake/plugins/customize/models"
)
// Service wraps database operations
@@ -45,9 +46,9 @@ func NewService(dal dal.Dal) *Service {
}
// GetFields returns all the fields of the table
-func (s *Service) GetFields(table string) ([]models.CustomizedField,
errors.Error) {
+func (s *Service) GetFields(table string) ([]customizeModels.CustomizedField,
errors.Error) {
// the customized fields created before v0.16.0 were not recorded in
the table `_tool_customized_field`, we should take care of them
- columns, err := s.dal.GetColumns(&models.Table{Name: table},
func(columnMeta dal.ColumnMeta) bool {
+ columns, err := s.dal.GetColumns(&customizeModels.Table{Name: table},
func(columnMeta dal.ColumnMeta) bool {
return true
})
if err != nil {
@@ -57,16 +58,16 @@ func (s *Service) GetFields(table string)
([]models.CustomizedField, errors.Erro
if err != nil {
return nil, err
}
- fieldMap := make(map[string]models.CustomizedField)
+ fieldMap := make(map[string]customizeModels.CustomizedField)
for _, f := range ff {
fieldMap[f.ColumnName] = f
}
- var result []models.CustomizedField
+ var result []customizeModels.CustomizedField
for _, col := range columns {
// original fields
if !strings.HasPrefix(col.Name(), "x_") {
dataType, _ := col.ColumnType()
- result = append(result, models.CustomizedField{
+ result = append(result, customizeModels.CustomizedField{
TbName: table,
ColumnName: col.Name(),
DataType: dal.ColumnType(dataType),
@@ -76,7 +77,7 @@ func (s *Service) GetFields(table string)
([]models.CustomizedField, errors.Erro
if field, ok := fieldMap[col.Name()]; ok {
result = append(result, field)
} else {
- result = append(result, models.CustomizedField{
+ result = append(result,
customizeModels.CustomizedField{
ColumnName: col.Name(),
DataType: dal.Varchar,
})
@@ -110,7 +111,7 @@ func (s *Service) checkField(table, field string) (bool,
errors.Error) {
}
// CreateField creates a new column for the table cf.TbName and creates a new
record in the table `_tool_customized_fields`
-func (s *Service) CreateField(cf *models.CustomizedField) errors.Error {
+func (s *Service) CreateField(cf *customizeModels.CustomizedField)
errors.Error {
exists, err := s.checkField(cf.TbName, cf.ColumnName)
if err != nil {
return err
@@ -142,12 +143,12 @@ func (s *Service) DeleteField(table, field string)
errors.Error {
if err != nil {
return errors.Default.Wrap(err, "DropColumn error")
}
- return s.dal.Delete(&models.CustomizedField{}, dal.Where("tb_name = ?
AND column_name = ?", table, field))
+ return s.dal.Delete(&customizeModels.CustomizedField{},
dal.Where("tb_name = ? AND column_name = ?", table, field))
}
// getCustomizedFields returns all the customized fields definitions of the
table
-func (s *Service) getCustomizedFields(table string) ([]models.CustomizedField,
errors.Error) {
- var result []models.CustomizedField
+func (s *Service) getCustomizedFields(table string)
([]customizeModels.CustomizedField, errors.Error) {
+ var result []customizeModels.CustomizedField
err := s.dal.All(&result, dal.Where("tb_name = ?", table))
return result, err
}
@@ -417,6 +418,103 @@ func (s *Service) issueCommitHandler(record
map[string]interface{}) errors.Error
return s.dal.CreateWithMap(&crossdomain.IssueCommit{}, record)
}
+// ImportQaApis imports csv file to the table `qa_apis`
+func (s *Service) ImportQaApis(qaProjectId string, file io.ReadCloser,
incremental bool) errors.Error {
+ if !incremental {
+ // delete old data associated with this qaProjectId
+ err := s.dal.Delete(&qa.QaApi{}, dal.Where("qa_project_id = ?",
qaProjectId))
+ if err != nil {
+ return errors.Default.Wrap(err, fmt.Sprintf("failed to
delete old qa_apis for qaProjectId %s", qaProjectId))
+ }
+ }
+ return s.importCSV(file, qaProjectId, s.qaApiHandler(qaProjectId))
+}
+
+// qaApiHandler saves a record into the `qa_apis` table
+func (s *Service) qaApiHandler(qaProjectId string) func(record
map[string]interface{}) errors.Error {
+ return func(record map[string]interface{}) errors.Error {
+ creatorName, err := getStringField(record, "creator_name",
false)
+ if err != nil {
+ return err
+ }
+ if creatorName != "" {
+ creatorId, _ := s.createOrUpdateAccount(creatorName,
qaProjectId)
+ if creatorId != "" {
+ record["creator_id"] = creatorId
+ }
+ }
+ delete(record, "creator_name")
+ record["qa_project_id"] = qaProjectId
+ return s.dal.CreateWithMap(&qa.QaApi{}, record)
+ }
+}
+
+// ImportQaTestCases imports csv file to the table `qa_test_cases`
+func (s *Service) ImportQaTestCases(qaProjectId, qaProjectName string, file
io.ReadCloser, incremental bool) errors.Error {
+ if !incremental {
+ // delete old data associated with this qaProjectId
+ err := s.dal.Delete(&qa.QaTestCase{}, dal.Where("qa_project_id
= ?", qaProjectId))
+ if err != nil {
+ return errors.Default.Wrap(err, fmt.Sprintf("failed to
delete old qa_test_cases for qaProjectId %s", qaProjectId))
+ }
+ // using ImportQaApis to delete data in qa_apis
+ // never delete data in qa_projects
+ }
+ // create or update qa_projects
+ err := s.dal.CreateOrUpdate(&qa.QaProject{
+ DomainEntityExtended: domainlayer.DomainEntityExtended{
+ Id: qaProjectId,
+ },
+ Name: qaProjectName,
+ })
+ if err != nil {
+ return err
+ }
+ return s.importCSV(file, qaProjectId, s.qaTestCaseHandler(qaProjectId))
+}
+
+// qaTestCaseHandler saves a record into the `qa_test_cases` table
+func (s *Service) qaTestCaseHandler(qaProjectId string) func(record
map[string]interface{}) errors.Error {
+ return func(record map[string]interface{}) errors.Error {
+ creatorName, _ := getStringField(record, "creator_name", false)
+ if creatorName != "" {
+ creatorId, _ := s.createOrUpdateAccount(creatorName,
qaProjectId)
+ record["creator_id"] = creatorId
+ }
+ // remove fields
+ delete(record, "creator_name")
+ record["qa_project_id"] = qaProjectId
+ return s.dal.CreateWithMap(&qa.QaTestCase{}, record)
+ }
+}
+
+// ImportQaTestCaseExecutions imports csv file to the table
`qa_test_case_executions`
+func (s *Service) ImportQaTestCaseExecutions(qaProjectId string, file
io.ReadCloser, incremental bool) errors.Error {
+ if !incremental {
+ // delete old data associated with this qaProjectId
+ err := s.dal.Delete(&qa.QaTestCaseExecution{},
dal.Where("qa_project_id = ?", qaProjectId))
+ if err != nil {
+ return errors.Default.Wrap(err, fmt.Sprintf("failed to
delete old qa_test_case_executions for qaProjectId %s", qaProjectId))
+ }
+ }
+ return s.importCSV(file, qaProjectId,
s.qaTestCaseExecutionHandler(qaProjectId))
+}
+
+// qaTestCaseExecutionHandler saves a record into the
`qa_test_case_executions` table
+func (s *Service) qaTestCaseExecutionHandler(qaProjectId string) func(record
map[string]interface{}) errors.Error {
+ // Assuming qa.QaTestCaseExecution model exists and CreateWithMap is
suitable
+ return func(record map[string]interface{}) errors.Error {
+ creatorName, _ := getStringField(record, "creator_name", false)
+ if creatorName != "" {
+ creatorId, _ := s.createOrUpdateAccount(creatorName,
qaProjectId)
+ record["creator_id"] = creatorId
+ }
+ delete(record, "creator_name")
+ record["qa_project_id"] = qaProjectId
+ return s.dal.CreateWithMap(&qa.QaTestCaseExecution{}, record)
+ }
+}
+
// issueRepoCommitHandlerFactory returns a handler that will populate the
`issue_commits` and `issue_repo_commits` table
// ths issueCommitsFields is used to filter the fields that should be inserted
into the `issue_commits` table
func (s *Service) issueRepoCommitHandler(record map[string]interface{})
errors.Error {