diff --git a/.erda/migrations/qa/20211021-auto-test-file-record-space-id.sql b/.erda/migrations/qa/20211021-auto-test-file-record-space-id.sql new file mode 100644 index 000000000000..7b37d186c8d0 --- /dev/null +++ b/.erda/migrations/qa/20211021-auto-test-file-record-space-id.sql @@ -0,0 +1 @@ +ALTER TABLE dice_test_file_records ADD COLUMN `space_id` int(11) DEFAULT false COMMENT 'autotest space id'; \ No newline at end of file diff --git a/apistructs/autotest_scene_set.go b/apistructs/autotest_scene_set.go index cd03830f1b7f..e63417cc46c0 100644 --- a/apistructs/autotest_scene_set.go +++ b/apistructs/autotest_scene_set.go @@ -49,3 +49,8 @@ type AutoTestSceneSetImportRequest struct { IdentityInfo } + +type AutoTestSceneSetImportResponse struct { + Header + Data uint64 `json:"data"` +} diff --git a/apistructs/component_protocol.go b/apistructs/component_protocol.go index 5a84e2e302c3..ed670a7df4ea 100644 --- a/apistructs/component_protocol.go +++ b/apistructs/component_protocol.go @@ -180,6 +180,7 @@ const ( UpdateSceneOperationKey OperationKey = "UpdateScene" DeleteSceneOperationKey OperationKey = "DeleteScene" DeleteSceneSetOperationKey OperationKey = "DeleteSceneSet" + ExportSceneSetOperationKey OperationKey = "exportSceneSet" ClickAddSceneSeButtonOperationKey OperationKey = "ClickAddSceneSet" DragSceneSetOperationKey OperationKey = "DragSceneSet" CopySceneOperationKey OperationKey = "CopyScene" diff --git a/apistructs/file_records.go b/apistructs/file_records.go index 4ea6abcc69ea..dfee3baff7a8 100644 --- a/apistructs/file_records.go +++ b/apistructs/file_records.go @@ -27,6 +27,7 @@ type TestFileRecord struct { ProjectID uint64 `json:"projectID"` TestSetID uint64 `json:"testSetID"` ApiFileUUID string `json:"apiFileUUID"` + SpaceID uint64 `json:"spaceID"` Type FileActionType `json:"type"` State FileRecordState `json:"state"` CreatedAt time.Time `json:"createdAt"` @@ -38,6 +39,7 @@ type TestFileRecordRequest struct { ID uint64 `json:"id"` FileName string `json:"name"` ProjectID uint64 `json:"projectID"` + SpaceID uint64 `json:"spaceID"` Description string `json:"description"` ApiFileUUID string `json:"apiFileUUID"` Type FileActionType `json:"type"` @@ -89,6 +91,7 @@ const ( type ListTestFileRecordsRequest struct { ProjectID uint64 `json:"projectID"` + SpaceID uint64 `json:"spaceID"` Types []FileActionType `json:"types"` Locale string `json:"locale"` } diff --git a/bundle/autotest_sceneset.go b/bundle/autotest_sceneset.go index f1688b5a8b8c..af2d9fb40dde 100644 --- a/bundle/autotest_sceneset.go +++ b/bundle/autotest_sceneset.go @@ -145,3 +145,21 @@ func (b *Bundle) DragSceneSet(req apistructs.SceneSetRequest) error { } return nil } + +// ExportAutotestSceneSet export autotest scene set +func (b *Bundle) ExportAutotestSceneSet(userID string, req apistructs.AutoTestSceneSetExportRequest) error { + host, err := b.urls.DOP() + if err != nil { + return err + } + hc := b.hc + var exportID uint64 + _, err = hc.Post(host).Path("/api/autotests/scenesets/actions/export"). + Header(httputil.UserHeader, userID). + JSONBody(req).Do().JSON(&exportID) + if err != nil { + return apierrors.ErrInvoke.InternalError(err) + } + + return nil +} diff --git a/bundle/bundle.go b/bundle/bundle.go index 3c92c3c4e103..6d4bb334bac0 100644 --- a/bundle/bundle.go +++ b/bundle/bundle.go @@ -52,7 +52,7 @@ func New(options ...Option) *Bundle { } if b.i18nLoader == nil { b.i18nLoader = i18n.NewLoader() - b.i18nLoader.LoadDir("pkg/erda-configs/i18n") + b.i18nLoader.LoadDir("erda-configs/i18n") b.i18nLoader.DefaultLocale("zh-CN") } return b diff --git a/conf/dop/dop.yaml b/conf/dop/dop.yaml index 0ae69ce64fc7..1ba6ddcfb630 100644 --- a/conf/dop/dop.yaml +++ b/conf/dop/dop.yaml @@ -64,6 +64,9 @@ component-protocol.components.code-coverage.tip: component-protocol.components.code-coverage.executeHistoryButton: component-protocol.components.code-coverage.downloadButton: +component-protocol.components.scenes-import-record.filter: +component-protocol.components.scenes-import-record.table: + diff --git a/conf/dop/i18n/component-protocol.yaml b/conf/dop/i18n/component-protocol.yaml index 5f241a81fbfb..cf5e771edd7b 100644 --- a/conf/dop/i18n/component-protocol.yaml +++ b/conf/dop/i18n/component-protocol.yaml @@ -95,6 +95,23 @@ zh: test-case-num-notpassed: 未通过 test-case-rate-passed: 通过率 test-case-rate-executed: 执行率 + import: 导入 + export: 导出 + scene-set-export-confirm: 是否确认导出 + scene-set-export-success-msg: 导出任务已创建, 请在导入导出记录表中查看进度 + status-success: 成功 + status-failed: 失败, + status-pending: 排队中 + status-processing: 进行中 + download-file: 下载文件 + import-export-record: 导入导出记录 + import-export-table: 导入导出记录表 + type: 类型 + operator: 操作人 + time: 时间 + desc: 描述 + status: 状态 + result: 结果 en: issue-manage: issue manage issue: issue @@ -183,6 +200,23 @@ en: board-view: Board View custom: Custom state: State + import: Import + export: Export + scene-set-export-confirm: Are you sure to export? + scene-set-export-success-msg: The export task has been created, please check the progress in the import and export record table + status-success: Success + status-failed: Failed, + status-pending: Pending + status-processing: Processing + download-file: Download File + import-export-record: Import and export records + import-export-table: Import and export records table + type: Type + operator: Operator + time: Time, + desc: Description + status: Status + result: Result test-case-num-total: Test Case Total Number test-case-num-done: Finished test-case-num-block: Block diff --git a/conf/openapi/openapi.yaml b/conf/openapi/openapi.yaml index 58284ae69e36..9e8c4fb71253 100644 --- a/conf/openapi/openapi.yaml +++ b/conf/openapi/openapi.yaml @@ -70,6 +70,7 @@ openapi-v1-routes: - issue-manage - issue-dashboard - code-coverage + - scenes-import-record - addr: http://localhost:8080 scenarios: - demo diff --git a/modules/dop/component-protocol/components/all.go b/modules/dop/component-protocol/components/all.go index 9cf8c865b398..c3c1bed7f25f 100644 --- a/modules/dop/component-protocol/components/all.go +++ b/modules/dop/component-protocol/components/all.go @@ -19,4 +19,5 @@ import ( _ "github.com/erda-project/erda/modules/dop/component-protocol/components/issue-dashboard" _ "github.com/erda-project/erda/modules/dop/component-protocol/components/issue-manage" _ "github.com/erda-project/erda/modules/dop/component-protocol/components/test-dashboard" + _ "github.com/erda-project/erda/modules/dop/component-protocol/components/scenes-import-record" ) diff --git a/modules/dop/component-protocol/components/scenes-import-record/filter/render.go b/modules/dop/component-protocol/components/scenes-import-record/filter/render.go new file mode 100644 index 000000000000..3e39676672ac --- /dev/null +++ b/modules/dop/component-protocol/components/scenes-import-record/filter/render.go @@ -0,0 +1,103 @@ +// Copyright (c) 2021 Terminus, Inc. +// +// Licensed 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 filter + +import ( + "context" + "encoding/json" + + "github.com/sirupsen/logrus" + + "github.com/erda-project/erda-infra/base/servicehub" + "github.com/erda-project/erda-infra/providers/component-protocol/cptype" + "github.com/erda-project/erda/modules/openapi/component-protocol/components/base" +) + +type ComponentAction struct { + base.DefaultProvider + + Name string `json:"name"` + Type string `json:"type"` + State State `json:"state"` + Props map[string]interface{} `json:"props"` + Operations map[string]interface{} `json:"operations"` +} + +type State struct { + Conditions []interface{} `json:"conditions"` + Values struct { + Type []string `json:"type"` + } `json:"values"` +} + +func (i *ComponentAction) GenComponentState(c *cptype.Component) error { + if c == nil || c.State == nil { + return nil + } + var state State + cont, err := json.Marshal(c.State) + if err != nil { + logrus.Errorf("marshal component state failed, content:%v, err:%v", c.State, err) + return err + } + err = json.Unmarshal(cont, &state) + if err != nil { + logrus.Errorf("unmarshal component state failed, content:%v, err:%v", cont, err) + return err + } + i.State = state + return nil +} + +func (ca *ComponentAction) Render(ctx context.Context, c *cptype.Component, scenario cptype.Scenario, event cptype.ComponentEvent, gs *cptype.GlobalStateData) error { + if err := ca.GenComponentState(c); err != nil { + return err + } + ca.Props = map[string]interface{}{ + "delay": 1000, + } + ca.State.Conditions = []interface{}{ + map[string]interface{}{ + "emptyText": "全部", + "fixed": true, + "key": "type", + "label": "类型", + "options": []interface{}{ + map[string]interface{}{ + "label": "导入", + "value": "import", + }, + map[string]interface{}{ + "label": "导出", + "value": "export", + }, + }, + "type": "select", + }, + } + ca.Operations = map[string]interface{}{ + "filter": map[string]interface{}{ + "key": "filter", + "reload": true, + }, + } + return nil +} + +func init() { + base.InitProviderWithCreator("scenes-import-record", "filter", func() servicehub.Provider { + return &ComponentAction{} + }) +} diff --git a/modules/openapi/component-protocol/scenarios/auto-test-scenes/components/leftHeadButton/model.go b/modules/dop/component-protocol/components/scenes-import-record/scenario.go similarity index 53% rename from modules/openapi/component-protocol/scenarios/auto-test-scenes/components/leftHeadButton/model.go rename to modules/dop/component-protocol/components/scenes-import-record/scenario.go index 984b198de8f0..af4074217cf5 100644 --- a/modules/openapi/component-protocol/scenarios/auto-test-scenes/components/leftHeadButton/model.go +++ b/modules/dop/component-protocol/components/scenes-import-record/scenario.go @@ -12,25 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -package leftHeadButton +package scenes_import_record import ( - protocol "github.com/erda-project/erda/modules/openapi/component-protocol" + _ "github.com/erda-project/erda/modules/dop/component-protocol/components/scenes-import-record/filter" + _ "github.com/erda-project/erda/modules/dop/component-protocol/components/scenes-import-record/table" ) - -type ComponentleftHeadButtonModal struct { - CtxBdl protocol.ContextBundle - Props map[string]interface{} `json:"props"` - Operations map[string]interface{} `json:"operations"` - State State `json:"state"` -} - -type State struct { - ActionType string `json:"actionType"` - FormVisible bool `json:"formVisible"` -} - -type Operation struct { - Key string `json:"key"` - Reload bool `json:"reload"` -} diff --git a/modules/dop/component-protocol/components/scenes-import-record/table/render.go b/modules/dop/component-protocol/components/scenes-import-record/table/render.go new file mode 100644 index 000000000000..16d5acf02936 --- /dev/null +++ b/modules/dop/component-protocol/components/scenes-import-record/table/render.go @@ -0,0 +1,246 @@ +// Copyright (c) 2021 Terminus, Inc. +// +// Licensed 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 table + +import ( + "context" + "encoding/json" + "fmt" + "strconv" + + "github.com/sirupsen/logrus" + + "github.com/erda-project/erda-infra/base/servicehub" + "github.com/erda-project/erda-infra/providers/component-protocol/cptype" + "github.com/erda-project/erda-infra/providers/component-protocol/utils/cputil" + "github.com/erda-project/erda/apistructs" + "github.com/erda-project/erda/bundle" + "github.com/erda-project/erda/modules/core-services/conf" + "github.com/erda-project/erda/modules/dop/component-protocol/types" + "github.com/erda-project/erda/modules/openapi/component-protocol/components/base" +) + +type ComponentAction struct { + base.DefaultProvider + sdk *cptype.SDK + ctxBdl *bundle.Bundle + + Name string `json:"name"` + Type string `json:"type"` + State State `json:"state"` + Props map[string]interface{} `json:"props"` + Operations map[string]interface{} `json:"operations"` + Data Data `json:"data"` +} + +type State struct { + Values struct { + Type []string `json:"type"` + } `json:"values"` + AutoRefresh bool `json:"autoRefresh"` +} + +type Status struct { + RenderType string `json:"renderType"` + Value string `json:"value"` + Status string `json:"status"` +} + +type Result struct { + RenderType string `json:"renderType"` + URL string `json:"url"` + Value string `json:"value"` +} + +type DataItem struct { + ID string `json:"id"` + Type string `json:"type"` + Operator string `json:"operator"` + Time string `json:"time"` + Desc string `json:"desc"` + Status Status `json:"status"` + Result Result `json:"result"` +} + +type Data struct { + List []DataItem `json:"list"` +} + +type Column struct { + DataIndex string `json:"dataIndex"` + Title string `json:"title"` + Width uint64 `json:"width,omitempty"` +} + +func (ca *ComponentAction) GenComponentState(c *cptype.Component) error { + if c == nil || c.State == nil { + return nil + } + var state State + cont, err := json.Marshal(c.State) + if err != nil { + logrus.Errorf("marshal component state failed, content:%v, err:%v", c.State, err) + return err + } + err = json.Unmarshal(cont, &state) + if err != nil { + logrus.Errorf("unmarshal component state failed, content:%v, err:%v", cont, err) + return err + } + ca.State = state + return nil +} + +func (ca *ComponentAction) setData() error { + projectID, ok := ca.sdk.InParams["projectId"].(float64) + if !ok { + return fmt.Errorf("invalid project id: %v", projectID) + } + spacIDStr, ok := ca.sdk.InParams["spaceId"].(string) + if !ok { + return fmt.Errorf("invalid space id: %v", spacIDStr) + } + spaceID, err := strconv.ParseUint(spacIDStr, 10, 64) + if err != nil { + return err + } + fileTypes := []apistructs.FileActionType{} + for _, t := range ca.State.Values.Type { + if t == "export" { + fileTypes = append(fileTypes, apistructs.FileSceneSetActionTypeExport) + } + if t == "import" { + fileTypes = append(fileTypes, apistructs.FileSceneSetActionTypeImport) + } + } + if len(fileTypes) == 0 { + fileTypes = append(fileTypes, apistructs.FileSceneSetActionTypeExport, apistructs.FileSceneSetActionTypeImport) + } + rsp, err := ca.ctxBdl.ListFileRecords(ca.sdk.Identity.UserID, apistructs.ListTestFileRecordsRequest{ + ProjectID: uint64(projectID), + Types: fileTypes, + SpaceID: spaceID, + }) + if err != nil { + return err + } + + ca.Data.List = make([]DataItem, 0) + for _, fileRecord := range rsp.Data.List { + var recordType string + switch fileRecord.Type { + case apistructs.FileSceneSetActionTypeImport: + recordType = ca.sdk.I18n("import") + case apistructs.FileSceneSetActionTypeExport: + recordType = ca.sdk.I18n("export") + default: + } + var status string + var recordState string + switch fileRecord.State { + case apistructs.FileRecordStateFail: + status = ca.sdk.I18n("status-failed") + recordState = "error" + case apistructs.FileRecordStateSuccess: + status = ca.sdk.I18n("status-success") + recordState = "success" + case apistructs.FileRecordStatePending: + status = ca.sdk.I18n("status-pending") + recordState = "pending" + case apistructs.FileRecordStateProcessing: + status = ca.sdk.I18n("status-processing") + recordState = "processing" + default: + } + var operatorName string + operator, err := ca.ctxBdl.GetCurrentUser(fileRecord.OperatorID) + if err == nil { + operatorName = operator.Nick + } + ca.Data.List = append(ca.Data.List, DataItem{ + ID: strconv.FormatInt(int64(fileRecord.ID), 10), + Type: recordType, + Operator: operatorName, + Time: fileRecord.CreatedAt.Format("2006-01-02 15:04:05"), + Desc: fileRecord.Description, + Status: Status{ + RenderType: "textWithBadge", + Value: status, + Status: recordState, + }, + Result: Result{ + RenderType: "downloadUrl", + URL: fmt.Sprintf("%s/api/files/%s", conf.RootDomain(), fileRecord.ApiFileUUID), + Value: fileRecord.FileName, + }, + }) + } + return nil +} + +func (ca *ComponentAction) Render(ctx context.Context, c *cptype.Component, scenario cptype.Scenario, event cptype.ComponentEvent, gs *cptype.GlobalStateData) error { + if err := ca.GenComponentState(c); err != nil { + return err + } + + bdl := ctx.Value(types.GlobalCtxKeyBundle).(*bundle.Bundle) + ca.ctxBdl = bdl + ca.sdk = cputil.SDK(ctx) + ca.Type = "Table" + ca.Name = "recordTable" + columns := make([]Column, 0) + columns = append(columns, Column{ + DataIndex: "id", + Title: "ID", + Width: 80, + }, Column{ + DataIndex: "type", + Title: ca.sdk.I18n("type"), + Width: 80, + }, Column{ + DataIndex: "operator", + Title: ca.sdk.I18n("operator"), + Width: 150, + }, Column{ + DataIndex: "time", + Title: ca.sdk.I18n("time"), + Width: 150, + }, Column{ + DataIndex: "desc", + Title: ca.sdk.I18n("desc"), + }, Column{ + DataIndex: "status", + Title: ca.sdk.I18n("status"), + Width: 80, + }, Column{ + DataIndex: "result", + Title: ca.sdk.I18n("result"), + }) + ca.Props = map[string]interface{}{ + "columns": columns, + "rowKey": "id", + } + if err := ca.setData(); err != nil { + return err + } + ca.State.AutoRefresh = false + return nil +} + +func init() { + base.InitProviderWithCreator("scenes-import-record", "table", func() servicehub.Provider { + return &ComponentAction{} + }) +} diff --git a/modules/dop/component-protocol/scenarios/scenes-import-record.yml b/modules/dop/component-protocol/scenarios/scenes-import-record.yml new file mode 100644 index 000000000000..9031d9f9f243 --- /dev/null +++ b/modules/dop/component-protocol/scenarios/scenes-import-record.yml @@ -0,0 +1,33 @@ +version: 1.0 +scenario: scenes-import-record + +hierarchy: + root: page + structure: + page: + - filter + - table + +components: + page: + type: Container + filter: + type: ContractiveFilter + table: + name: recordTable + type: Table + +rendering: + filter: + - name: table + state: + - name: "values" + value: "{{ filter.values }}" + + __DefaultRendering__: + - name: filter + - name: table + state: + - name: "values" + value: "{{ filter.values }}" + diff --git a/modules/dop/dao/file_records.go b/modules/dop/dao/file_records.go index 575dac6c3a6d..34498a0fbeeb 100644 --- a/modules/dop/dao/file_records.go +++ b/modules/dop/dao/file_records.go @@ -33,6 +33,7 @@ type TestFileRecord struct { Description string ApiFileUUID string ProjectID uint64 + SpaceID uint64 Type apistructs.FileActionType State apistructs.FileRecordState OperatorID string @@ -90,17 +91,19 @@ type stateCounter struct { Count int } -// Get Records by projectId +// Get Records by projectId, spaceId, types func (client *DBClient) ListRecordsByProject(req apistructs.ListTestFileRecordsRequest) ([]TestFileRecord, map[string]int, error) { var res []TestFileRecord + sql := client.Table("dice_test_file_records").Where("`project_id` = ?", req.ProjectID) + if req.SpaceID > 0 { + sql = sql.Where("`space_id` = ?", req.SpaceID) + } if len(req.Types) > 0 { - if err := client.Where("`project_id` = ? AND `type` IN (?)", req.ProjectID, req.Types).Order("created_at desc").Find(&res).Error; err != nil { - return nil, nil, err - } - } else { - if err := client.Where("`project_id` = ?", req.ProjectID).Order("created_at desc").Find(&res).Error; err != nil { - return nil, nil, err - } + sql = sql.Where("`type` IN (?)", req.Types) + } + + if err := sql.Order("created_at desc").Find(&res).Error; err != nil { + return nil, nil, err } var counterList []stateCounter diff --git a/modules/dop/services/autotest_v2/export.go b/modules/dop/services/autotest_v2/export.go index 6ca9a40e35de..906a8e4a9e71 100644 --- a/modules/dop/services/autotest_v2/export.go +++ b/modules/dop/services/autotest_v2/export.go @@ -266,6 +266,7 @@ func (svc *Service) ExportSceneSet(req apistructs.AutoTestSceneSetExportRequest) Type: apistructs.FileSceneSetActionTypeExport, State: apistructs.FileRecordStatePending, ProjectID: req.ProjectID, + SpaceID: req.SpaceID, IdentityInfo: req.IdentityInfo, Extra: apistructs.TestFileExtra{ AutotestSceneSetFileExtraInfo: &apistructs.AutoTestSceneSetFileExtraInfo{ diff --git a/modules/dop/services/autotest_v2/import.go b/modules/dop/services/autotest_v2/import.go index 8af53ba2fad2..6dbe5e1f0c1b 100644 --- a/modules/dop/services/autotest_v2/import.go +++ b/modules/dop/services/autotest_v2/import.go @@ -470,7 +470,7 @@ func (svc *Service) ImportSceneSet(req apistructs.AutoTestSceneSetImportRequest, return 0, apierrors.ErrImportAutotestSceneSet.InvalidParameter("fileType") } if req.SpaceID == 0 { - return 0, apierrors.ErrExportAutoTestSceneSet.MissingParameter("projectID") + return 0, apierrors.ErrImportAutotestSceneSet.MissingParameter("spaceID") } space, err := svc.bdl.GetTestSpace(req.SpaceID) @@ -502,6 +502,7 @@ func (svc *Service) ImportSceneSet(req apistructs.AutoTestSceneSetImportRequest, ProjectID: uint64(space.ProjectID), Type: apistructs.FileSceneSetActionTypeImport, ApiFileUUID: file.UUID, + SpaceID: space.ID, State: apistructs.FileRecordStatePending, IdentityInfo: req.IdentityInfo, Extra: apistructs.TestFileExtra{ diff --git a/modules/dop/services/testcase/file_records.go b/modules/dop/services/testcase/file_records.go index 3a22199dc6cb..6bccdb0583fa 100644 --- a/modules/dop/services/testcase/file_records.go +++ b/modules/dop/services/testcase/file_records.go @@ -29,6 +29,7 @@ func (svc *Service) CreateFileRecord(req apistructs.TestFileRecordRequest) (uint FileName: req.FileName, Description: req.Description, ProjectID: req.ProjectID, + SpaceID: req.SpaceID, Type: req.Type, State: req.State, ApiFileUUID: req.ApiFileUUID, @@ -140,6 +141,7 @@ func mapping(s *dao.TestFileRecord, project, testSet string) *apistructs.TestFil } return 0 }(), + SpaceID: s.SpaceID, Description: s.Description, Type: s.Type, State: s.State, diff --git a/modules/openapi/api/apis/testplatform/autotest/scene_set_import.go b/modules/openapi/api/apis/testplatform/autotest/scene_set_import.go new file mode 100644 index 000000000000..9c17e8ebbd53 --- /dev/null +++ b/modules/openapi/api/apis/testplatform/autotest/scene_set_import.go @@ -0,0 +1,34 @@ +// Copyright (c) 2021 Terminus, Inc. +// +// Licensed 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 autotest + +import ( + "net/http" + + "github.com/erda-project/erda/apistructs" + "github.com/erda-project/erda/modules/openapi/api/apis" +) + +var SCENE_SET_IMPORT = apis.ApiSpec{ + Path: "/api/autotests/scenesets/actions/import", + BackendPath: "/api/autotests/scenesets/actions/import", + Host: "dop.marathon.l4lb.thisdcos.directory:9527", + Scheme: "http", + Method: http.MethodPost, + RequestType: apistructs.AutoTestSceneSetImportRequest{}, + ResponseType: apistructs.AutoTestSceneSetImportResponse{}, + Doc: "summary: 导入自动化测试场景集", + CheckLogin: true, +} diff --git a/modules/openapi/component-protocol/scenarios/auto-test-scenes/components/fileTree/operation.go b/modules/openapi/component-protocol/scenarios/auto-test-scenes/components/fileTree/operation.go index 1aa445a2aaea..78a71c093c47 100644 --- a/modules/openapi/component-protocol/scenarios/auto-test-scenes/components/fileTree/operation.go +++ b/modules/openapi/component-protocol/scenarios/auto-test-scenes/components/fileTree/operation.go @@ -15,11 +15,12 @@ package fileTree type SceneSetOperation struct { - Key string `json:"key"` - Text string `json:"text"` - Reload bool `json:"reload"` - Show bool `json:"show"` - Meta SceneSetOperationMeta `json:"meta"` + Key string `json:"key"` + Text string `json:"text"` + Reload bool `json:"reload"` + Show bool `json:"show"` + SuccessMsg string `json:"successMsg"` + Meta SceneSetOperationMeta `json:"meta"` } type SceneSetOperationMeta struct { diff --git a/modules/openapi/component-protocol/scenarios/auto-test-scenes/components/fileTree/render.go b/modules/openapi/component-protocol/scenarios/auto-test-scenes/components/fileTree/render.go index 12f5244afd8d..c5064d725768 100644 --- a/modules/openapi/component-protocol/scenarios/auto-test-scenes/components/fileTree/render.go +++ b/modules/openapi/component-protocol/scenarios/auto-test-scenes/components/fileTree/render.go @@ -249,6 +249,13 @@ func (i *ComponentFileTree) Render(ctx context.Context, c *apistructs.Component, if err := i.RenderSceneSets(inParams); err != nil { return err } + case apistructs.ExportSceneSetOperationKey: + if err := i.RenderExportSceneSet(event, inParams); err != nil { + return err + } + if err := i.RenderSceneSets(inParams); err != nil { + return err + } case apistructs.DragSceneSetOperationKey: if err := i.RenderDragHelper(inParams); err != nil { return err diff --git a/modules/openapi/component-protocol/scenarios/auto-test-scenes/components/fileTree/scenes.go b/modules/openapi/component-protocol/scenarios/auto-test-scenes/components/fileTree/scenes.go index 07996d4e74c3..1ad2290f1f5e 100644 --- a/modules/openapi/component-protocol/scenarios/auto-test-scenes/components/fileTree/scenes.go +++ b/modules/openapi/component-protocol/scenarios/auto-test-scenes/components/fileTree/scenes.go @@ -229,6 +229,17 @@ func initSceneSet(s *apistructs.SceneSet) SceneSet { }, } + var export = SceneSetOperation{ + Key: "exportSceneSet", + Text: "导出场景集", + Reload: true, + Show: true, + SuccessMsg: "导出完成,请在导入导出记录中下载导出结果", + Meta: SceneSetOperationMeta{ + ParentKey: id, + }, + } + set.Operations = map[string]interface{}{} set.Operations["expand"] = expand set.Operations["click"] = click @@ -236,6 +247,7 @@ func initSceneSet(s *apistructs.SceneSet) SceneSet { set.Operations["editScene"] = edit set.Operations["delete"] = delete set.Operations["refSceneSet"] = refSceneSet + set.Operations["exportSceneSet"] = export return set } @@ -350,6 +362,24 @@ func (i *ComponentFileTree) RenderDeleteSceneSet(event apistructs.ComponentEvent return nil } +func (i *ComponentFileTree) RenderExportSceneSet(event apistructs.ComponentEvent, inParams InParams) error { + var operationData SceneSetOperation + if err := getOperation(&operationData, event); err != nil { + return err + } + setId := operationData.Meta.ParentKey + req := apistructs.AutoTestSceneSetExportRequest{ + ID: uint64(setId), + FileType: apistructs.TestSceneSetFileTypeExcel, + ProjectID: inParams.ProjectId, + } + req.UserID = i.CtxBdl.Identity.UserID + if err := i.CtxBdl.Bdl.ExportAutotestSceneSet(i.CtxBdl.Identity.UserID, req); err != nil { + return err + } + return nil +} + func (i *ComponentFileTree) resetKeys() { i.State.SceneSetKey = 0 l := len(i.State.SelectedKeys) diff --git a/modules/openapi/component-protocol/scenarios/auto-test-scenes/components/leftHeadAddSceneSet/render.go b/modules/openapi/component-protocol/scenarios/auto-test-scenes/components/leftHeadAddSceneSet/render.go new file mode 100644 index 000000000000..3079813307b9 --- /dev/null +++ b/modules/openapi/component-protocol/scenarios/auto-test-scenes/components/leftHeadAddSceneSet/render.go @@ -0,0 +1,135 @@ +// Copyright (c) 2021 Terminus, Inc. +// +// Licensed 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 leftHeadAddSceneSet + +import ( + "context" + "encoding/json" + + "github.com/sirupsen/logrus" + + "github.com/erda-project/erda/apistructs" + protocol "github.com/erda-project/erda/modules/openapi/component-protocol" +) + +type LeftHeadAddSceneSet struct { + Name string `json:"name"` + Type string `json:"type"` + State State `json:"state"` + Props map[string]interface{} `json:"props"` + Operations map[string]interface{} `json:"operations"` +} + +type State struct { + ActionType string `json:"actionType"` + FormVisible bool `json:"formVisible"` +} + +func (l *LeftHeadAddSceneSet) GenComponentState(c *apistructs.Component) error { + if c == nil || c.State == nil { + return nil + } + var state State + cont, err := json.Marshal(c.State) + if err != nil { + logrus.Errorf("marshal component state failed, content:%v, err:%v", c.State, err) + return err + } + err = json.Unmarshal(cont, &state) + if err != nil { + logrus.Errorf("unmarshal component state failed, content:%v, err:%v", cont, err) + return err + } + l.State = state + return nil +} + +func (l *LeftHeadAddSceneSet) marshal(c *apistructs.Component) error { + stateValue, err := json.Marshal(l.State) + if err != nil { + return err + } + var state map[string]interface{} + err = json.Unmarshal(stateValue, &state) + if err != nil { + return err + } + + propValue, err := json.Marshal(l.Props) + if err != nil { + return err + } + var props interface{} + err = json.Unmarshal(propValue, &props) + if err != nil { + return err + } + + c.Operations = l.Operations + c.Props = props + c.State = state + c.Type = l.Type + c.Name = l.Name + return nil +} + +func (l *LeftHeadAddSceneSet) Import(c *apistructs.Component) error { + b, err := json.Marshal(c) + if err != nil { + return err + } + if err := json.Unmarshal(b, l); err != nil { + return err + } + return nil +} + +func (l *LeftHeadAddSceneSet) Render(ctx context.Context, c *apistructs.Component, scenario apistructs.ComponentProtocolScenario, event apistructs.ComponentEvent, gs *apistructs.GlobalStateData) (err error) { + if err := l.Import(c); err != nil { + logrus.Errorf("import component failed, err:%v", err) + return err + } + + defer func() { + fail := l.marshal(c) + if err == nil && fail != nil { + err = fail + } + }() + + l.Type = "Icon" + l.Name = "leftHeadAddSceneSet" + l.Props = map[string]interface{}{ + "iconType": "plus", + "size": 18, + "visible": true, + } + l.Operations = map[string]interface{}{ + "click": map[string]interface{}{ + "key": "ClickAddSceneSet", + "reload": true, + }, + } + switch event.Operation { + case apistructs.ClickAddSceneSeButtonOperationKey: + l.State.ActionType = "ClickAddSceneSetButton" + l.State.FormVisible = true + } + return nil +} + +func RenderCreator() protocol.CompRender { + return &LeftHeadAddSceneSet{} +} diff --git a/modules/openapi/component-protocol/scenarios/auto-test-scenes/components/leftHeadButton/render.go b/modules/openapi/component-protocol/scenarios/auto-test-scenes/components/leftHeadButton/render.go deleted file mode 100644 index 21d5ec24a3bf..000000000000 --- a/modules/openapi/component-protocol/scenarios/auto-test-scenes/components/leftHeadButton/render.go +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright (c) 2021 Terminus, Inc. -// -// Licensed 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 leftHeadButton - -import ( - "context" - "encoding/json" - "fmt" - - "github.com/sirupsen/logrus" - - "github.com/erda-project/erda/apistructs" - protocol "github.com/erda-project/erda/modules/openapi/component-protocol" - "github.com/erda-project/erda/modules/openapi/component-protocol/scenarios/auto-test-scenes/components/fileTree" -) - -// SetCtxBundle 设置bundle -func (i *ComponentleftHeadButtonModal) SetCtxBundle(b protocol.ContextBundle) error { - if b.Bdl == nil || b.I18nPrinter == nil { - err := fmt.Errorf("invalie context bundle") - return err - } - logrus.Infof("inParams:%+v, identity:%+v", b.InParams, b.Identity) - i.CtxBdl = b - return nil -} - -func (i *ComponentleftHeadButtonModal) marshal(c *apistructs.Component) error { - stateValue, err := json.Marshal(i.State) - if err != nil { - return err - } - var state map[string]interface{} - err = json.Unmarshal(stateValue, &state) - if err != nil { - return err - } - - // var data apistructs.ComponentData = map[string]interface{}{} - // data["treeData"] = i.Data - // c.Data = data - c.State = state - // c.Type = i.Type - return nil -} - -func (a *ComponentleftHeadButtonModal) unmarshal(c *apistructs.Component) error { - stateValue, err := json.Marshal(c.State) - if err != nil { - return err - } - var state State - err = json.Unmarshal(stateValue, &state) - if err != nil { - return err - } - if err != nil { - return err - } - a.State = state - // a.Type = c.Type - // a.Data = data - return nil -} - -func (i *ComponentleftHeadButtonModal) Render(ctx context.Context, c *apistructs.Component, scenario apistructs.ComponentProtocolScenario, event apistructs.ComponentEvent, gs *apistructs.GlobalStateData) (err error) { - if event.Operation != apistructs.InitializeOperation && event.Operation != apistructs.RenderingOperation { - err = i.unmarshal(c) - if err != nil { - return err - } - } - - defer func() { - fail := i.marshal(c) - if err == nil && fail != nil { - err = fail - } - }() - - bdl := ctx.Value(protocol.GlobalInnerKeyCtxBundle.String()).(protocol.ContextBundle) - err = i.SetCtxBundle(bdl) - if err != nil { - return err - } - - err = i.GenComponentState(c) - if err != nil { - return err - } - - if i.CtxBdl.InParams == nil { - return fmt.Errorf("params is empty") - } - - inParamsBytes, err := json.Marshal(i.CtxBdl.InParams) - if err != nil { - return fmt.Errorf("failed to marshal inParams, inParams:%+v, err:%v", i.CtxBdl.InParams, err) - } - - var inParams fileTree.InParams - if err := json.Unmarshal(inParamsBytes, &inParams); err != nil { - return err - } - - i.Initial() - // i.Props = - - switch event.Operation { - // case apistructs.InitializeOperation, apistructs.RenderingOperation: - // if err := i.Initial(bdl, inParams); err != nil { - // return err - // } - case apistructs.ClickAddSceneSeButtonOperationKey: - if err := i.RenderClickButton(); err != nil { - return err - } - } - i.RenderProtocol(c, gs) - return nil -} - -func (i *ComponentleftHeadButtonModal) RenderProtocol(c *apistructs.Component, g *apistructs.GlobalStateData) { - if c.Operations == nil { - d := make(apistructs.ComponentOps) - c.Operations = d - } - if c.State == nil { - d := make(apistructs.ComponentData) - c.State = d - } - // c.State = - c.Operations = i.Operations - // c.Props = i.Props -} - -// GenComponentState 获取state -func (i *ComponentleftHeadButtonModal) GenComponentState(c *apistructs.Component) error { - if c == nil || c.State == nil { - return nil - } - var state State - cont, err := json.Marshal(c.State) - if err != nil { - logrus.Errorf("marshal component state failed, content:%v, err:%v", c.State, err) - return err - } - err = json.Unmarshal(cont, &state) - if err != nil { - logrus.Errorf("unmarshal component state failed, content:%v, err:%v", cont, err) - return err - } - i.State = state - return nil -} - -func (i *ComponentleftHeadButtonModal) RenderClickButton() error { - i.State.ActionType = "ClickAddSceneSetButton" - i.State.FormVisible = true - return nil -} - -func (i *ComponentleftHeadButtonModal) Initial() { - var click = Operation{ - Key: "ClickAddSceneSet", - Reload: true, - } - i.Operations = map[string]interface{}{} - i.Operations["click"] = click -} - -func RenderCreator() protocol.CompRender { - return &ComponentleftHeadButtonModal{} -} diff --git a/modules/openapi/component-protocol/scenarios/auto-test-scenes/components/moreOperation/render.go b/modules/openapi/component-protocol/scenarios/auto-test-scenes/components/moreOperation/render.go new file mode 100644 index 000000000000..0265e651c330 --- /dev/null +++ b/modules/openapi/component-protocol/scenarios/auto-test-scenes/components/moreOperation/render.go @@ -0,0 +1,107 @@ +// Copyright (c) 2021 Terminus, Inc. +// +// Licensed 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 moreOperation + +import ( + "context" + "encoding/json" + + "github.com/sirupsen/logrus" + + "github.com/erda-project/erda/apistructs" + protocol "github.com/erda-project/erda/modules/openapi/component-protocol" +) + +type MoreOperation struct { + CtxBdl protocol.ContextBundle + + Name string `json:"name"` + Type string `json:"type"` + Props map[string]interface{} `json:"props"` + Operations map[string]interface{} `json:"operations"` +} + +func (m *MoreOperation) marshal(c *apistructs.Component) error { + + propValue, err := json.Marshal(m.Props) + if err != nil { + return err + } + var props interface{} + err = json.Unmarshal(propValue, &props) + if err != nil { + return err + } + + c.Operations = m.Operations + c.Props = props + c.Type = m.Type + c.Name = m.Name + return nil +} + +func (l *MoreOperation) Import(c *apistructs.Component) error { + b, err := json.Marshal(c) + if err != nil { + return err + } + if err := json.Unmarshal(b, l); err != nil { + return err + } + return nil +} + +func (m *MoreOperation) Render(ctx context.Context, c *apistructs.Component, scenario apistructs.ComponentProtocolScenario, event apistructs.ComponentEvent, gs *apistructs.GlobalStateData) (err error) { + if err := m.Import(c); err != nil { + logrus.Errorf("import component failed, err:%v", err) + return err + } + + defer func() { + fail := m.marshal(c) + if err == nil && fail != nil { + err = fail + } + }() + m.Name = "moreOperation" + m.Type = "Dropdown" + m.Props = map[string]interface{}{ + "menus": []interface{}{ + map[string]interface{}{ + "key": "import", + "label": "导入", + }, + map[string]interface{}{ + "key": "record", + "label": "导入导出记录", + }, + }, + } + m.Operations = map[string]interface{}{ + "import": map[string]interface{}{ + "key": "import", + "reload": false, + }, + "record": map[string]interface{}{ + "key": "record", + "reload": false, + }, + } + return nil +} + +func RenderCreator() protocol.CompRender { + return &MoreOperation{} +} diff --git a/modules/openapi/component-protocol/scenarios/auto-test-scenes/protocol.yml b/modules/openapi/component-protocol/scenarios/auto-test-scenes/protocol.yml index 073c36c12186..8ad68ee2b191 100644 --- a/modules/openapi/component-protocol/scenarios/auto-test-scenes/protocol.yml +++ b/modules/openapi/component-protocol/scenarios/auto-test-scenes/protocol.yml @@ -14,7 +14,11 @@ hierarchy: - fileFormModal leftHead: left: leftHeadTitle - right: leftHeadButton + right: + - leftHeadAddSceneSet + - moreOperation + moreOperation: + children: moreIcon rightPage: - folderDetail - fileDetail @@ -149,11 +153,16 @@ components: type: Alert leftHeadTitle: type: Title - leftHeadButton: - type: Button + leftHeadAddSceneSet: + type: Icon + moreOperation: + type: Dropdown + moreIcon: + type: Icon props: - text: 添加 - type: primary + iconType: moreOne + size: 18 + hoverAction: true fileTree: type: FileTree fileFormModal: @@ -532,13 +541,13 @@ rendering: - name: "pipelineId" value: "{{ executeHistoryTable.pipelineId }}" - name: resultDrawer - leftHeadButton: + leftHeadAddSceneSet: - name: "fileFormModal" state: - name: "visible" - value: "{{ leftHeadButton.formVisible }}" + value: "{{ leftHeadAddSceneSet.formVisible }}" - name: "actionType" - value: "{{ leftHeadButton.actionType }}" + value: "{{ leftHeadAddSceneSet.actionType }}" fileDetail: - name: fileConfig state: @@ -888,11 +897,12 @@ rendering: value: "{{ executeTaskBreadcrumb.visible }}" __DefaultRendering__: + - name: moreOperation - name: autoTestScenes - name: leftPage - name: leftHead - name: leftHeadTitle - - name: leftHeadButton + - name: leftHeadAddSceneSet - name: fileSearch - name: fileTree - name: rightPage