From 09ce919b2b5c740fe250f3c18dafcfae93f49bdf Mon Sep 17 00:00:00 2001 From: barnettZQG Date: Mon, 23 May 2022 12:17:28 +0800 Subject: [PATCH] Feat: Refactoring the API Server module for better layering (#3945) * Feat: Refactoring the API Server module for better layering Signed-off-by: barnettZQG * Fix: code style Signed-off-by: barnettZQG * Fix: code style Signed-off-by: barnettZQG * Fix: fix the lint errors Signed-off-by: barnettZQG --- .github/workflows/apiserver-test.yaml | 6 +- cmd/apiserver/main.go | 58 +-- docs/apidoc/swagger.json | 169 +++++-- go.mod | 2 + go.sum | 10 + pkg/addon/cache.go | 2 +- pkg/addon/type.go | 2 +- pkg/apiserver/README.md | 48 ++ pkg/apiserver/apiserver.jpg | Bin 0 -> 248296 bytes pkg/apiserver/config/config.go | 49 ++ .../{ => domain}/model/application.go | 0 pkg/apiserver/{ => domain}/model/cluster.go | 0 pkg/apiserver/{ => domain}/model/env.go | 0 .../{ => domain}/model/envbinding.go | 0 pkg/apiserver/{ => domain}/model/model.go | 2 +- pkg/apiserver/{ => domain}/model/project.go | 0 .../{ => domain}/model/system_info.go | 0 pkg/apiserver/{ => domain}/model/target.go | 0 pkg/apiserver/{ => domain}/model/user.go | 0 pkg/apiserver/{ => domain}/model/whole.go | 0 pkg/apiserver/{ => domain}/model/workflow.go | 0 .../env_model.go => domain/repository/env.go} | 19 +- pkg/apiserver/domain/repository/envbinding.go | 112 +++++ pkg/apiserver/domain/repository/suite_test.go | 117 +++++ .../repository/target.go} | 22 +- .../repository/workflow.go} | 55 +- .../repository/workflow_test.go} | 139 +++--- .../{rest/usecase => domain/service}/addon.go | 49 +- .../usecase => domain/service}/addon_test.go | 6 +- .../usecase => domain/service}/application.go | 470 +++++++----------- .../service}/application_test.go | 317 ++++++------ .../service}/authentication.go | 82 ++- .../service}/authentication_test.go | 40 +- .../usecase => domain/service}/cluster.go | 156 +++--- .../service}/cluster_test.go | 42 +- .../usecase => domain/service}/config.go | 59 +-- .../usecase => domain/service}/config_test.go | 28 +- .../usecase => domain/service}/definition.go | 61 ++- .../service}/definition_test.go | 34 +- .../{rest/usecase => domain/service}/env.go | 86 ++-- .../usecase => domain/service}/env_test.go | 36 +- pkg/apiserver/domain/service/envbinding.go | 301 +++++++++++ .../service}/envbinding_test.go | 71 +-- .../{rest/usecase => domain/service}/helm.go | 46 +- .../usecase => domain/service}/helm_test.go | 23 +- .../service}/oam_application.go | 41 +- .../service}/oam_application_test.go | 20 +- .../usecase => domain/service}/project.go | 146 +++--- .../service}/project_test.go | 86 ++-- .../{rest/usecase => domain/service}/rbac.go | 100 ++-- .../usecase => domain/service}/rbac_test.go | 34 +- pkg/apiserver/domain/service/service.go | 70 +++ .../usecase => domain/service}/suite_test.go | 14 +- .../usecase => domain/service}/system_info.go | 56 +-- .../usecase => domain/service}/target.go | 84 ++-- .../usecase => domain/service}/target_test.go | 34 +- .../testdata/addon-uischema-test.yaml | 0 .../service}/testdata/api-schema.json | 0 .../testdata/apply-application-hide.yaml | 0 .../service}/testdata/apply-object.yaml | 0 .../testdata/applyapplication-sd.yaml | 0 .../service}/testdata/deploy.yaml | 0 .../service}/testdata/dex-config-def.yaml | 0 .../service}/testdata/example-app-error.yaml | 0 .../service}/testdata/example-app.yaml | 0 .../service}/testdata/helm/index.yaml | 0 .../service}/testdata/helm/mysql-8.8.23.tgz | Bin .../service}/testdata/myingress-td.yaml | 0 .../service}/testdata/scaler.yaml | 0 .../testdata/terraform-alibaba-rds.yaml | 0 .../service}/testdata/ui-custom-schema.yaml | 0 .../service}/testdata/ui-default-schema.yaml | 0 .../service}/testdata/ui-schema.yaml | 0 .../service}/testdata/webserver-cd.yaml | 0 .../service}/testdata/webservice.yaml | 0 .../testdata/workflowstep-apply-object.yaml | 0 .../{rest/usecase => domain/service}/user.go | 113 ++--- .../usecase => domain/service}/user_test.go | 46 +- .../usecase => domain/service}/velaql.go | 24 +- .../usecase => domain/service}/webhook.go | 83 ++-- .../service}/webhook_test.go | 118 ++--- .../usecase => domain/service}/workflow.go | 213 +++----- .../service}/workflow_test.go | 176 +++---- .../{ => event}/collect/suit_test.go | 10 +- .../collect/system_info_collect.go | 39 +- .../collect/system_info_collect_test.go | 6 +- pkg/apiserver/event/event.go | 50 ++ pkg/apiserver/event/event_test.go | 30 ++ pkg/apiserver/{ => event}/sync/cache.go | 4 +- pkg/apiserver/{ => event}/sync/cache_test.go | 4 +- pkg/apiserver/{ => event}/sync/convert.go | 4 +- .../{ => event}/sync/convert/convert.go | 2 +- pkg/apiserver/{ => event}/sync/cr2ux.go | 22 +- pkg/apiserver/{ => event}/sync/cr2ux_test.go | 4 +- pkg/apiserver/{ => event}/sync/store.go | 16 +- pkg/apiserver/{ => event}/sync/suit_test.go | 10 +- .../{ => event}/sync/testdata/test-app1.yaml | 0 .../{ => event}/sync/testdata/test-app2.yaml | 0 .../{ => event}/sync/testdata/test-app3.yaml | 0 pkg/apiserver/{ => event}/sync/worker.go | 66 ++- pkg/apiserver/{ => event}/sync/worker_test.go | 12 +- pkg/apiserver/event/sync/workflow_record.go | 49 ++ .../clients/kubeclient.go | 0 .../datastore/datastore.go | 0 .../datastore/datastore_suite_test.go | 0 .../datastore/datastore_test.go | 2 +- .../datastore/kubeapi/kubeapi.go | 26 +- .../datastore/kubeapi/kubeapi_suite_test.go | 86 ++++ .../datastore/kubeapi/kubeapi_test.go | 68 +-- .../datastore/kubeapi/migrate.go | 4 +- .../datastore/kubeapi/migrate_test.go | 8 +- .../datastore/mongodb/iterator.go | 0 .../datastore/mongodb/mongodb.go | 4 +- .../datastore/mongodb/mongodb_suite_test.go | 2 +- .../datastore/mongodb/mongodb_test.go | 4 +- .../webservice => interfaces/api}/addon.go | 89 ++-- .../api}/addon_registry.go | 55 +- .../api/api_suite_test.go} | 14 +- .../api}/application.go | 328 ++++++------ .../interfaces/api/assembler/v1/do2dto.go | 209 ++++++++ .../interfaces/api/assembler/v1/dto2do.go | 67 +++ .../api}/authentication.go | 49 +- .../webservice => interfaces/api}/cluster.go | 124 ++--- .../webservice => interfaces/api}/config.go | 61 ++- .../api}/definition.go | 51 +- .../api/dto}/v1/oam_application.go | 0 .../apis => interfaces/api/dto}/v1/types.go | 4 +- .../webservice => interfaces/api}/env.go | 56 +-- .../webservice => interfaces/api}/helm.go | 41 +- pkg/apiserver/interfaces/api/interfaces.go | 94 ++++ .../api/interfaces_test.go} | 12 +- .../api}/oam_application.go | 43 +- .../api}/payload_types.go | 19 +- .../webservice => interfaces/api}/project.go | 148 +++--- .../webservice => interfaces/api}/rbac.go | 58 +-- .../api}/system_info.go | 34 +- .../webservice => interfaces/api}/target.go | 78 ++- .../webservice => interfaces/api}/user.go | 75 ++- .../webservice => interfaces/api}/validate.go | 6 +- .../api}/validate_test.go | 4 +- .../webservice => interfaces/api}/velaql.go | 31 +- .../webservice => interfaces/api}/webhook.go | 29 +- .../webservice => interfaces/api}/workflow.go | 72 +-- pkg/apiserver/rest/usecase/converter.go | 78 --- pkg/apiserver/rest/usecase/envbinding.go | 425 ---------------- .../rest/usecase/envbinding_model.go | 53 -- pkg/apiserver/rest/webservice/webservice.go | 132 ----- .../{rest/rest_server.go => server.go} | 200 ++++---- pkg/apiserver/server_test.go | 17 + .../{rest => }/utils/bcode/001_application.go | 0 .../{rest => }/utils/bcode/002_workflow.go | 0 .../{rest => }/utils/bcode/003_project.go | 0 .../{rest => }/utils/bcode/004_cluster.go | 0 .../{rest => }/utils/bcode/005_addon.go | 0 .../{rest => }/utils/bcode/006_velaql.go | 0 .../{rest => }/utils/bcode/007_definition.go | 0 .../{rest => }/utils/bcode/008_target.go | 0 .../{rest => }/utils/bcode/009_envbinding.go | 0 .../{rest => }/utils/bcode/011_env.go | 0 .../utils/bcode/012_authentication.go | 0 .../{rest => }/utils/bcode/013_repository.go | 0 .../{rest => }/utils/bcode/014_user.go | 0 .../{rest => }/utils/bcode/015_rbac.go | 0 pkg/apiserver/{rest => }/utils/bcode/bcode.go | 4 +- .../utils/bcode/bcode_suite_test.go | 0 .../{rest => }/utils/bcode/bcode_test.go | 0 pkg/apiserver/{rest => }/utils/cache.go | 0 pkg/apiserver/{rest => }/utils/cache_test.go | 0 pkg/apiserver/utils/container/container.go | 61 +++ pkg/apiserver/{rest => }/utils/dex.go | 0 pkg/apiserver/{rest => }/utils/dex_test.go | 0 pkg/apiserver/{rest => }/utils/http.go | 0 pkg/apiserver/{rest => }/utils/http_test.go | 0 pkg/apiserver/{ => utils}/log/logger.go | 0 pkg/apiserver/{rest => }/utils/params.go | 0 pkg/apiserver/{rest => }/utils/params_test.go | 0 pkg/apiserver/{rest => }/utils/ui_schema.go | 0 .../{rest => }/utils/ui_schema_test.go | 0 .../{rest => }/utils/utils_suite_test.go | 0 pkg/apiserver/{rest => }/utils/version.go | 0 .../{rest => }/utils/version_test.go | 0 pkg/utils/config/application.go | 2 +- pkg/utils/helm/helm_helper.go | 3 +- pkg/velaql/suite_test.go | 2 +- references/cli/uischema.go | 2 +- test/e2e-apiserver-test/addon_test.go | 2 +- test/e2e-apiserver-test/application_test.go | 4 +- test/e2e-apiserver-test/cluster_test.go | 2 +- test/e2e-apiserver-test/definition_test.go | 4 +- test/e2e-apiserver-test/env_test.go | 2 +- .../oam_application_test.go | 2 +- test/e2e-apiserver-test/project_test.go | 2 +- test/e2e-apiserver-test/suite_test.go | 17 +- test/e2e-apiserver-test/system_info_test.go | 2 +- test/e2e-apiserver-test/user_test.go | 2 +- test/e2e-apiserver-test/velaql_test.go | 2 +- 196 files changed, 4046 insertions(+), 3534 deletions(-) create mode 100644 pkg/apiserver/README.md create mode 100644 pkg/apiserver/apiserver.jpg create mode 100644 pkg/apiserver/config/config.go rename pkg/apiserver/{ => domain}/model/application.go (100%) rename pkg/apiserver/{ => domain}/model/cluster.go (100%) rename pkg/apiserver/{ => domain}/model/env.go (100%) rename pkg/apiserver/{ => domain}/model/envbinding.go (100%) rename pkg/apiserver/{ => domain}/model/model.go (98%) rename pkg/apiserver/{ => domain}/model/project.go (100%) rename pkg/apiserver/{ => domain}/model/system_info.go (100%) rename pkg/apiserver/{ => domain}/model/target.go (100%) rename pkg/apiserver/{ => domain}/model/user.go (100%) rename pkg/apiserver/{ => domain}/model/whole.go (100%) rename pkg/apiserver/{ => domain}/model/workflow.go (100%) rename pkg/apiserver/{rest/usecase/env_model.go => domain/repository/env.go} (80%) create mode 100644 pkg/apiserver/domain/repository/envbinding.go create mode 100644 pkg/apiserver/domain/repository/suite_test.go rename pkg/apiserver/{rest/usecase/target_model.go => domain/repository/target.go} (81%) rename pkg/apiserver/{rest/usecase/workflow_model.go => domain/repository/workflow.go} (91%) rename pkg/apiserver/{rest/usecase/workflow_model_test.go => domain/repository/workflow_test.go} (72%) rename pkg/apiserver/{rest/usecase => domain/service}/addon.go (87%) rename pkg/apiserver/{rest/usecase => domain/service}/addon_test.go (95%) rename pkg/apiserver/{rest/usecase => domain/service}/application.go (77%) rename pkg/apiserver/{rest/usecase => domain/service}/application_test.go (69%) rename pkg/apiserver/{rest/usecase => domain/service}/authentication.go (85%) rename pkg/apiserver/{rest/usecase => domain/service}/authentication_test.go (85%) rename pkg/apiserver/{rest/usecase => domain/service}/cluster.go (83%) rename pkg/apiserver/{rest/usecase => domain/service}/cluster_test.go (85%) rename pkg/apiserver/{rest/usecase => domain/service}/config.go (88%) rename pkg/apiserver/{rest/usecase => domain/service}/config_test.go (96%) rename pkg/apiserver/{rest/usecase => domain/service}/definition.go (89%) rename pkg/apiserver/{rest/usecase => domain/service}/definition_test.go (92%) rename pkg/apiserver/{rest/usecase => domain/service}/env.go (75%) rename pkg/apiserver/{rest/usecase => domain/service}/env_test.go (76%) create mode 100644 pkg/apiserver/domain/service/envbinding.go rename pkg/apiserver/{rest/usecase => domain/service}/envbinding_test.go (63%) rename pkg/apiserver/{rest/usecase => domain/service}/helm.go (75%) rename pkg/apiserver/{rest/usecase => domain/service}/helm_test.go (95%) rename pkg/apiserver/{rest/usecase => domain/service}/oam_application.go (67%) rename pkg/apiserver/{rest/usecase => domain/service}/oam_application_test.go (88%) rename pkg/apiserver/{rest/usecase => domain/service}/project.go (80%) rename pkg/apiserver/{rest/usecase => domain/service}/project_test.go (86%) rename pkg/apiserver/{rest/usecase => domain/service}/rbac.go (88%) rename pkg/apiserver/{rest/usecase => domain/service}/rbac_test.go (91%) create mode 100644 pkg/apiserver/domain/service/service.go rename pkg/apiserver/{rest/usecase => domain/service}/suite_test.go (91%) rename pkg/apiserver/{rest/usecase => domain/service}/system_info.go (73%) rename pkg/apiserver/{rest/usecase => domain/service}/target.go (69%) rename pkg/apiserver/{rest/usecase => domain/service}/target_test.go (67%) rename pkg/apiserver/{rest/usecase => domain/service}/testdata/addon-uischema-test.yaml (100%) rename pkg/apiserver/{rest/usecase => domain/service}/testdata/api-schema.json (100%) rename pkg/apiserver/{rest/usecase => domain/service}/testdata/apply-application-hide.yaml (100%) rename pkg/apiserver/{rest/usecase => domain/service}/testdata/apply-object.yaml (100%) rename pkg/apiserver/{rest/usecase => domain/service}/testdata/applyapplication-sd.yaml (100%) rename pkg/apiserver/{rest/usecase => domain/service}/testdata/deploy.yaml (100%) rename pkg/apiserver/{rest/usecase => domain/service}/testdata/dex-config-def.yaml (100%) rename pkg/apiserver/{rest/usecase => domain/service}/testdata/example-app-error.yaml (100%) rename pkg/apiserver/{rest/usecase => domain/service}/testdata/example-app.yaml (100%) rename pkg/apiserver/{rest/usecase => domain/service}/testdata/helm/index.yaml (100%) rename pkg/apiserver/{rest/usecase => domain/service}/testdata/helm/mysql-8.8.23.tgz (100%) rename pkg/apiserver/{rest/usecase => domain/service}/testdata/myingress-td.yaml (100%) rename pkg/apiserver/{rest/usecase => domain/service}/testdata/scaler.yaml (100%) rename pkg/apiserver/{rest/usecase => domain/service}/testdata/terraform-alibaba-rds.yaml (100%) rename pkg/apiserver/{rest/usecase => domain/service}/testdata/ui-custom-schema.yaml (100%) rename pkg/apiserver/{rest/usecase => domain/service}/testdata/ui-default-schema.yaml (100%) rename pkg/apiserver/{rest/usecase => domain/service}/testdata/ui-schema.yaml (100%) rename pkg/apiserver/{rest/usecase => domain/service}/testdata/webserver-cd.yaml (100%) rename pkg/apiserver/{rest/usecase => domain/service}/testdata/webservice.yaml (100%) rename pkg/apiserver/{rest/usecase => domain/service}/testdata/workflowstep-apply-object.yaml (100%) rename pkg/apiserver/{rest/usecase => domain/service}/user.go (76%) rename pkg/apiserver/{rest/usecase => domain/service}/user_test.go (81%) rename pkg/apiserver/{rest/usecase => domain/service}/velaql.go (80%) rename pkg/apiserver/{rest/usecase => domain/service}/webhook.go (86%) rename pkg/apiserver/{rest/usecase => domain/service}/webhook_test.go (73%) rename pkg/apiserver/{rest/usecase => domain/service}/workflow.go (76%) rename pkg/apiserver/{rest/usecase => domain/service}/workflow_test.go (76%) rename pkg/apiserver/{ => event}/collect/suit_test.go (87%) rename pkg/apiserver/{ => event}/collect/system_info_collect.go (88%) rename pkg/apiserver/{ => event}/collect/system_info_collect_test.go (98%) create mode 100644 pkg/apiserver/event/event.go create mode 100644 pkg/apiserver/event/event_test.go rename pkg/apiserver/{ => event}/sync/cache.go (96%) rename pkg/apiserver/{ => event}/sync/cache_test.go (96%) rename pkg/apiserver/{ => event}/sync/convert.go (96%) rename pkg/apiserver/{ => event}/sync/convert/convert.go (98%) rename pkg/apiserver/{ => event}/sync/cr2ux.go (91%) rename pkg/apiserver/{ => event}/sync/cr2ux_test.go (98%) rename pkg/apiserver/{ => event}/sync/store.go (94%) rename pkg/apiserver/{ => event}/sync/suit_test.go (87%) rename pkg/apiserver/{ => event}/sync/testdata/test-app1.yaml (100%) rename pkg/apiserver/{ => event}/sync/testdata/test-app2.yaml (100%) rename pkg/apiserver/{ => event}/sync/testdata/test-app3.yaml (100%) rename pkg/apiserver/{ => event}/sync/worker.go (57%) rename pkg/apiserver/{ => event}/sync/worker_test.go (94%) create mode 100644 pkg/apiserver/event/sync/workflow_record.go rename pkg/apiserver/{ => infrastructure}/clients/kubeclient.go (100%) rename pkg/apiserver/{ => infrastructure}/datastore/datastore.go (100%) rename pkg/apiserver/{ => infrastructure}/datastore/datastore_suite_test.go (100%) rename pkg/apiserver/{ => infrastructure}/datastore/datastore_test.go (96%) rename pkg/apiserver/{ => infrastructure}/datastore/kubeapi/kubeapi.go (94%) create mode 100644 pkg/apiserver/infrastructure/datastore/kubeapi/kubeapi_suite_test.go rename pkg/apiserver/{ => infrastructure}/datastore/kubeapi/kubeapi_test.go (82%) rename pkg/apiserver/{ => infrastructure}/datastore/kubeapi/migrate.go (95%) rename pkg/apiserver/{ => infrastructure}/datastore/kubeapi/migrate_test.go (86%) rename pkg/apiserver/{ => infrastructure}/datastore/mongodb/iterator.go (100%) rename pkg/apiserver/{ => infrastructure}/datastore/mongodb/mongodb.go (98%) rename pkg/apiserver/{ => infrastructure}/datastore/mongodb/mongodb_suite_test.go (92%) rename pkg/apiserver/{ => infrastructure}/datastore/mongodb/mongodb_test.go (98%) rename pkg/apiserver/{rest/webservice => interfaces/api}/addon.go (73%) rename pkg/apiserver/{rest/webservice => interfaces/api}/addon_registry.go (70%) rename pkg/apiserver/{rest/webservice/webservice_suite_test.go => interfaces/api/api_suite_test.go} (86%) rename pkg/apiserver/{rest/webservice => interfaces/api}/application.go (79%) create mode 100644 pkg/apiserver/interfaces/api/assembler/v1/do2dto.go create mode 100644 pkg/apiserver/interfaces/api/assembler/v1/dto2do.go rename pkg/apiserver/{rest/webservice => interfaces/api}/authentication.go (70%) rename pkg/apiserver/{rest/webservice => interfaces/api}/cluster.go (77%) rename pkg/apiserver/{rest/webservice => interfaces/api}/config.go (70%) rename pkg/apiserver/{rest/webservice => interfaces/api}/definition.go (75%) rename pkg/apiserver/{rest/apis => interfaces/api/dto}/v1/oam_application.go (100%) rename pkg/apiserver/{rest/apis => interfaces/api/dto}/v1/types.go (99%) rename pkg/apiserver/{rest/webservice => interfaces/api}/env.go (71%) rename pkg/apiserver/{rest/webservice => interfaces/api}/helm.go (76%) create mode 100644 pkg/apiserver/interfaces/api/interfaces.go rename pkg/apiserver/{datastore/kubeapi/kubeapi_suite_test.go => interfaces/api/interfaces_test.go} (73%) rename pkg/apiserver/{rest/webservice => interfaces/api}/oam_application.go (72%) rename pkg/apiserver/{rest/webservice => interfaces/api}/payload_types.go (70%) rename pkg/apiserver/{rest/webservice => interfaces/api}/project.go (76%) rename pkg/apiserver/{rest/webservice => interfaces/api}/rbac.go (69%) rename pkg/apiserver/{rest/webservice => interfaces/api}/system_info.go (66%) rename pkg/apiserver/{rest/webservice => interfaces/api}/target.go (69%) rename pkg/apiserver/{rest/webservice => interfaces/api}/user.go (71%) rename pkg/apiserver/{rest/webservice => interfaces/api}/validate.go (95%) rename pkg/apiserver/{rest/webservice => interfaces/api}/validate_test.go (97%) rename pkg/apiserver/{rest/webservice => interfaces/api}/velaql.go (66%) rename pkg/apiserver/{rest/webservice => interfaces/api}/webhook.go (64%) rename pkg/apiserver/{rest/webservice => interfaces/api}/workflow.go (67%) delete mode 100644 pkg/apiserver/rest/usecase/converter.go delete mode 100644 pkg/apiserver/rest/usecase/envbinding.go delete mode 100644 pkg/apiserver/rest/usecase/envbinding_model.go delete mode 100644 pkg/apiserver/rest/webservice/webservice.go rename pkg/apiserver/{rest/rest_server.go => server.go} (53%) create mode 100644 pkg/apiserver/server_test.go rename pkg/apiserver/{rest => }/utils/bcode/001_application.go (100%) rename pkg/apiserver/{rest => }/utils/bcode/002_workflow.go (100%) rename pkg/apiserver/{rest => }/utils/bcode/003_project.go (100%) rename pkg/apiserver/{rest => }/utils/bcode/004_cluster.go (100%) rename pkg/apiserver/{rest => }/utils/bcode/005_addon.go (100%) rename pkg/apiserver/{rest => }/utils/bcode/006_velaql.go (100%) rename pkg/apiserver/{rest => }/utils/bcode/007_definition.go (100%) rename pkg/apiserver/{rest => }/utils/bcode/008_target.go (100%) rename pkg/apiserver/{rest => }/utils/bcode/009_envbinding.go (100%) rename pkg/apiserver/{rest => }/utils/bcode/011_env.go (100%) rename pkg/apiserver/{rest => }/utils/bcode/012_authentication.go (100%) rename pkg/apiserver/{rest => }/utils/bcode/013_repository.go (100%) rename pkg/apiserver/{rest => }/utils/bcode/014_user.go (100%) rename pkg/apiserver/{rest => }/utils/bcode/015_rbac.go (100%) rename pkg/apiserver/{rest => }/utils/bcode/bcode.go (96%) rename pkg/apiserver/{rest => }/utils/bcode/bcode_suite_test.go (100%) rename pkg/apiserver/{rest => }/utils/bcode/bcode_test.go (100%) rename pkg/apiserver/{rest => }/utils/cache.go (100%) rename pkg/apiserver/{rest => }/utils/cache_test.go (100%) create mode 100644 pkg/apiserver/utils/container/container.go rename pkg/apiserver/{rest => }/utils/dex.go (100%) rename pkg/apiserver/{rest => }/utils/dex_test.go (100%) rename pkg/apiserver/{rest => }/utils/http.go (100%) rename pkg/apiserver/{rest => }/utils/http_test.go (100%) rename pkg/apiserver/{ => utils}/log/logger.go (100%) rename pkg/apiserver/{rest => }/utils/params.go (100%) rename pkg/apiserver/{rest => }/utils/params_test.go (100%) rename pkg/apiserver/{rest => }/utils/ui_schema.go (100%) rename pkg/apiserver/{rest => }/utils/ui_schema_test.go (100%) rename pkg/apiserver/{rest => }/utils/utils_suite_test.go (100%) rename pkg/apiserver/{rest => }/utils/version.go (100%) rename pkg/apiserver/{rest => }/utils/version_test.go (100%) diff --git a/.github/workflows/apiserver-test.yaml b/.github/workflows/apiserver-test.yaml index f13ee9618..e12a17730 100644 --- a/.github/workflows/apiserver-test.yaml +++ b/.github/workflows/apiserver-test.yaml @@ -99,6 +99,9 @@ jobs: kind create cluster --image kindest/node:${{ matrix.k8s-version }} kubectl version kubectl cluster-info + + - name: Run api server unit test + run: make unit-test-apiserver - name: Load Image to kind cluster run: make kind-load @@ -114,9 +117,6 @@ jobs: kubectl wait --for=condition=Ready pod -l app=source-controller -n flux-system --timeout=600s kubectl wait --for=condition=Ready pod -l app=helm-controller -n flux-system --timeout=600s - - name: Run api server unit test - run: make unit-test-apiserver - - name: Run api server e2e test run: | export ALIYUN_ACCESS_KEY_ID=${{ secrets.ALIYUN_ACCESS_KEY_ID }} diff --git a/cmd/apiserver/main.go b/cmd/apiserver/main.go index e7f1872d5..ba479b75d 100644 --- a/cmd/apiserver/main.go +++ b/cmd/apiserver/main.go @@ -30,23 +30,24 @@ import ( "github.com/go-openapi/spec" "github.com/google/uuid" - "github.com/oam-dev/kubevela/pkg/apiserver/log" - "github.com/oam-dev/kubevela/pkg/apiserver/rest" + "github.com/oam-dev/kubevela/pkg/apiserver" + "github.com/oam-dev/kubevela/pkg/apiserver/config" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/log" "github.com/oam-dev/kubevela/version" ) func main() { s := &Server{} - flag.StringVar(&s.restCfg.BindAddr, "bind-addr", "0.0.0.0:8000", "The bind address used to serve the http APIs.") - flag.StringVar(&s.restCfg.MetricPath, "metrics-path", "/metrics", "The path to expose the metrics.") - flag.StringVar(&s.restCfg.Datastore.Type, "datastore-type", "kubeapi", "Metadata storage driver type, support kubeapi and mongodb") - flag.StringVar(&s.restCfg.Datastore.Database, "datastore-database", "kubevela", "Metadata storage database name, takes effect when the storage driver is mongodb.") - flag.StringVar(&s.restCfg.Datastore.URL, "datastore-url", "", "Metadata storage database url,takes effect when the storage driver is mongodb.") - flag.StringVar(&s.restCfg.LeaderConfig.ID, "id", uuid.New().String(), "the holder identity name") - flag.StringVar(&s.restCfg.LeaderConfig.LockName, "lock-name", "apiserver-lock", "the lease lock resource name") - flag.DurationVar(&s.restCfg.LeaderConfig.Duration, "duration", time.Second*5, "the lease lock resource name") - flag.DurationVar(&s.restCfg.AddonCacheTime, "addon-cache-duration", time.Minute*10, "how long between two addon cache operation") - flag.BoolVar(&s.restCfg.DisableStatisticCronJob, "disable-statistic-cronJob", false, "close the system statistic info calculating cronJob") + flag.StringVar(&s.serverConfig.BindAddr, "bind-addr", "0.0.0.0:8000", "The bind address used to serve the http APIs.") + flag.StringVar(&s.serverConfig.MetricPath, "metrics-path", "/metrics", "The path to expose the metrics.") + flag.StringVar(&s.serverConfig.Datastore.Type, "datastore-type", "kubeapi", "Metadata storage driver type, support kubeapi and mongodb") + flag.StringVar(&s.serverConfig.Datastore.Database, "datastore-database", "kubevela", "Metadata storage database name, takes effect when the storage driver is mongodb.") + flag.StringVar(&s.serverConfig.Datastore.URL, "datastore-url", "", "Metadata storage database url,takes effect when the storage driver is mongodb.") + flag.StringVar(&s.serverConfig.LeaderConfig.ID, "id", uuid.New().String(), "the holder identity name") + flag.StringVar(&s.serverConfig.LeaderConfig.LockName, "lock-name", "apiserver-lock", "the lease lock resource name") + flag.DurationVar(&s.serverConfig.LeaderConfig.Duration, "duration", time.Second*5, "the lease lock resource name") + flag.DurationVar(&s.serverConfig.AddonCacheTime, "addon-cache-duration", time.Minute*10, "how long between two addon cache operation") + flag.BoolVar(&s.serverConfig.DisableStatisticCronJob, "disable-statistic-cronJob", false, "close the system statistic info calculating cronJob") flag.Parse() if len(os.Args) > 2 && os.Args[1] == "build-swagger" { @@ -77,14 +78,13 @@ func main() { return } - srvc := make(chan struct{}) + errChan := make(chan error) ctx, cancel := context.WithCancel(context.Background()) - + defer cancel() go func() { - if err := s.run(ctx); err != nil { - log.Logger.Errorf("failed to run apiserver: %v", err) + if err := s.run(ctx, errChan); err != nil { + errChan <- fmt.Errorf("failed to run apiserver: %w", err) } - close(srvc) }() var term = make(chan os.Signal, 1) signal.Notify(term, os.Interrupt, syscall.SIGTERM) @@ -92,34 +92,36 @@ func main() { select { case <-term: log.Logger.Infof("Received SIGTERM, exiting gracefully...") - cancel() - case <-srvc: - cancel() - os.Exit(1) + case err := <-errChan: + log.Logger.Errorf("Received an error: %s, exiting gracefully...", err.Error()) } log.Logger.Infof("See you next time!") } // Server apiserver type Server struct { - restCfg rest.Config + serverConfig config.Config } -func (s *Server) run(ctx context.Context) error { +func (s *Server) run(ctx context.Context, errChan chan error) error { log.Logger.Infof("KubeVela information: version: %v, gitRevision: %v", version.VelaVersion, version.GitRevision) - server, err := rest.New(s.restCfg) + server, err := apiserver.New(s.serverConfig) if err != nil { return fmt.Errorf("create apiserver failed : %w ", err) } - return server.Run(ctx) + return server.Run(ctx, errChan) } func (s *Server) buildSwagger() (*spec.Swagger, error) { - server, err := rest.New(s.restCfg) + server, err := apiserver.New(s.serverConfig) if err != nil { - return nil, fmt.Errorf("create apiserver failed : %w ", err) + return nil, err } - return restfulspec.BuildSwagger(server.RegisterServices(context.Background(), false)), nil + config, err := server.BuildRestfulConfig() + if err != nil { + return nil, err + } + return restfulspec.BuildSwagger(*config), nil } diff --git a/docs/apidoc/swagger.json b/docs/apidoc/swagger.json index 2ca605151..a628c1761 100644 --- a/docs/apidoc/swagger.json +++ b/docs/apidoc/swagger.json @@ -4644,6 +4644,55 @@ } } }, + "/api/v1/projects/{projectName}/validate_image": { + "get": { + "consumes": [ + "application/xml", + "application/json" + ], + "produces": [ + "application/json", + "application/xml" + ], + "tags": [ + "project" + ], + "summary": "validate an image in a project", + "operationId": "validateImage", + "parameters": [ + { + "type": "string", + "description": "image name", + "name": "image", + "in": "query" + }, + { + "type": "string", + "description": "identifier of the project", + "name": "projectName", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/*v1.ImageResponse" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/bcode.Bcode" + } + } + } + } + }, "/api/v1/query": { "get": { "consumes": [ @@ -5683,6 +5732,7 @@ "*v1.ApplicationTriggerBase": {}, "*v1.Config": {}, "*v1.EmptyResponse": {}, + "*v1.ImageResponse": {}, "addon.Dependency": { "properties": { "name": { @@ -5752,8 +5802,14 @@ }, "addon.HelmSource": { "properties": { + "password": { + "type": "string" + }, "url": { "type": "string" + }, + "username": { + "type": "string" } } }, @@ -6722,6 +6778,9 @@ "updateTime": { "type": "string", "format": "date-time" + }, + "workloadType": { + "$ref": "#/definitions/common.WorkloadTypeDescriptor" } } }, @@ -6838,8 +6897,8 @@ }, "model.Cluster": { "required": [ - "updateTime", "createTime", + "updateTime", "name", "alias", "description", @@ -7547,12 +7606,12 @@ }, "v1.ApplicationDeployResponse": { "required": [ - "version", - "status", + "note", + "envName", "triggerType", "createTime", - "note", - "envName" + "version", + "status" ], "properties": { "codeInfo": { @@ -8057,6 +8116,9 @@ "updateTime": { "type": "string", "format": "date-time" + }, + "workloadType": { + "$ref": "#/definitions/common.WorkloadTypeDescriptor" } } }, @@ -8714,11 +8776,11 @@ }, "v1.DetailAddonResponse": { "required": [ - "icon", - "description", - "invisible", "name", "version", + "description", + "icon", + "invisible", "schema", "uiSchema", "definitions", @@ -8798,25 +8860,21 @@ }, "v1.DetailApplicationResponse": { "required": [ - "name", - "description", - "alias", "project", + "description", "createTime", - "updateTime", "icon", + "alias", + "updateTime", + "name", "policies", "envBindings", - "applicationType", "resourceInfo" ], "properties": { "alias": { "type": "string" }, - "applicationType": { - "type": "string" - }, "createTime": { "type": "string", "format": "date-time" @@ -8865,20 +8923,20 @@ }, "v1.DetailClusterResponse": { "required": [ + "kubeConfig", "name", + "description", "apiServerURL", - "labels", "status", + "reason", + "icon", "provider", "kubeConfigSecret", - "kubeConfig", - "updateTime", - "createTime", - "alias", - "description", - "icon", - "reason", + "labels", "dashboardURL", + "createTime", + "updateTime", + "alias", "resourceInfo" ], "properties": { @@ -8936,14 +8994,14 @@ }, "v1.DetailComponentResponse": { "required": [ - "creator", - "name", - "main", - "updateTime", - "appPrimaryKey", - "createTime", "alias", + "updateTime", + "name", "type", + "main", + "createTime", + "appPrimaryKey", + "creator", "definition" ], "properties": { @@ -9023,17 +9081,20 @@ "updateTime": { "type": "string", "format": "date-time" + }, + "workloadType": { + "$ref": "#/definitions/common.WorkloadTypeDescriptor" } } }, "v1.DetailDefinitionResponse": { "required": [ - "alias", - "icon", + "name", "status", "labels", - "name", + "alias", "description", + "icon", "schema", "uiSchema" ], @@ -9091,6 +9152,7 @@ "properties", "createTime", "updateTime", + "envName", "name", "type", "description" @@ -9106,6 +9168,9 @@ "description": { "type": "string" }, + "envName": { + "type": "string" + }, "name": { "type": "string" }, @@ -9123,17 +9188,17 @@ }, "v1.DetailRevisionResponse": { "required": [ - "reason", - "deployUser", - "triggerType", - "createTime", + "updateTime", "note", + "triggerType", + "reason", + "envName", + "createTime", + "deployUser", "appPrimaryKey", "version", "status", - "workflowName", - "envName", - "updateTime" + "workflowName" ], "properties": { "appPrimaryKey": { @@ -9187,9 +9252,9 @@ }, "v1.DetailTargetResponse": { "required": [ + "name", "createTime", "project", - "name", "updateTime" ], "properties": { @@ -9230,11 +9295,11 @@ }, "v1.DetailUserResponse": { "required": [ - "email", - "disabled", "createTime", "lastLoginTime", "name", + "email", + "disabled", "projects", "roles" ], @@ -9332,14 +9397,14 @@ }, "v1.DetailWorkflowResponse": { "required": [ - "name", + "createTime", "alias", "description", "enable", - "envName", + "updateTime", + "name", "default", - "createTime", - "updateTime" + "envName" ], "properties": { "alias": { @@ -9918,11 +9983,11 @@ }, "v1.LoginUserInfoResponse": { "required": [ + "lastLoginTime", + "name", "email", "disabled", "createTime", - "lastLoginTime", - "name", "projects", "platformPermissions", "projectPermissions" @@ -10068,7 +10133,8 @@ "creator", "properties", "createTime", - "updateTime" + "updateTime", + "envName" ], "properties": { "createTime": { @@ -10081,6 +10147,9 @@ "description": { "type": "string" }, + "envName": { + "type": "string" + }, "name": { "type": "string" }, diff --git a/go.mod b/go.mod index a77f9fcf9..e2a64cb62 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/alibabacloud-go/darabonba-openapi v0.1.4 github.com/alibabacloud-go/tea v1.1.15 github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b + github.com/barnettZQG/inject v0.0.1 github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 github.com/briandowns/spinner v1.11.1 github.com/containerd/containerd v1.5.10 @@ -165,6 +166,7 @@ require ( github.com/emirpasic/gods v1.12.0 // indirect github.com/evanphx/json-patch/v5 v5.1.0 // indirect github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect + github.com/facebookgo/structtag v0.0.0-20150214074306-217e25fb9691 // indirect github.com/felixge/httpsnoop v1.0.1 // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect github.com/fvbommel/sortorder v1.0.1 // indirect diff --git a/go.sum b/go.sum index f55477033..4c0cfd122 100644 --- a/go.sum +++ b/go.sum @@ -303,6 +303,8 @@ github.com/aws/aws-sdk-go v1.36.30 h1:hAwyfe7eZa7sM+S5mIJZFiNFwJMia9Whz6CYblioLo github.com/aws/aws-sdk-go v1.36.30/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc= +github.com/barnettZQG/inject v0.0.1 h1:VjJHLxvl47FynKZEC3rpMZJYsNZmvP0Eaxucl9LqiwI= +github.com/barnettZQG/inject v0.0.1/go.mod h1:o83X2SlEKFliJx6UHai8bzFj+5isdBfOZsl6nQ2nT3M= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= @@ -643,6 +645,14 @@ github.com/evanphx/json-patch/v5 v5.1.0 h1:B0aXl1o/1cP8NbviYiBMkcHBtUjIJ1/Ccg6b+ github.com/evanphx/json-patch/v5 v5.1.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= +github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c h1:8ISkoahWXwZR41ois5lSJBSVw4D0OV19Ht/JSTzvSv0= +github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= +github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A= +github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg= +github.com/facebookgo/structtag v0.0.0-20150214074306-217e25fb9691 h1:KnnwHN59Jxec0htA2pe/i0/WI9vxXLQifdhBrP3lqcQ= +github.com/facebookgo/structtag v0.0.0-20150214074306-217e25fb9691/go.mod h1:sKLL1iua/0etWfo/nPCmyz+v2XDMXy+Ho53W7RAuZNY= +github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 h1:7HZCaLC5+BZpmbhCOZJ293Lz68O7PYrF2EzeiFMwCLk= +github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb/go.mod h1:bH6Xx7IW64qjjJq8M2u4dxNaBiDfKK+z/3eGDpXEQhc= github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= diff --git a/pkg/addon/cache.go b/pkg/addon/cache.go index d3c464df9..de68e5fe0 100644 --- a/pkg/addon/cache.go +++ b/pkg/addon/cache.go @@ -23,7 +23,7 @@ import ( "sync" "time" - "github.com/oam-dev/kubevela/pkg/apiserver/log" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/log" "github.com/oam-dev/kubevela/pkg/utils" "github.com/oam-dev/kubevela/pkg/utils/common" ) diff --git a/pkg/addon/type.go b/pkg/addon/type.go index d921ed8e3..ec50255c7 100644 --- a/pkg/addon/type.go +++ b/pkg/addon/type.go @@ -20,7 +20,7 @@ import ( "github.com/getkin/kin-openapi/openapi3" "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils" + "github.com/oam-dev/kubevela/pkg/apiserver/utils" ) // UIData contains all information represent an addon for UI diff --git a/pkg/apiserver/README.md b/pkg/apiserver/README.md new file mode 100644 index 000000000..90f1ef1e1 --- /dev/null +++ b/pkg/apiserver/README.md @@ -0,0 +1,48 @@ +# APIServer Module Architecture + +![apiserver architecture](./apiserver.jpg) + +## Interfaces + +Typically, it includes user interfaces, web services, and so on. + +* API + +Define the Restful API and perform basic validation on user input parameters. + +* DTO + +The carrier of data transmission. Here does not have any business logic inside, and the internal domain models can be isolated from the outside world through DTO. + +* Assembler + +Realize the mutual conversion and data exchange between DTO and DO. + +## Event + +Asynchronous task module. All workers under this module only run on the master node. Domain layer can be called. + +## Domain + +Code related to core business logic. + +* Service + +A domain service is a piece of business logic composed of multiple entities. You can put all domain services in the aggregate in one domain service class, or you can design each domain service as a class. + +* Repository + +Usually includes repository interface and repository implementation method. In order to facilitate the splitting and combination of aggregates, the principle is set: one aggregate corresponds to one repository. For example, save user information to a database. + +* Model + +Database model entity. + +## Infrastructure + +Provide general technical basic services for other layers, such as: + +* Database +* Cache +* Kube Client +* MQ diff --git a/pkg/apiserver/apiserver.jpg b/pkg/apiserver/apiserver.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e506e57367518cefa6ae11c17bbcaacf26679ccc GIT binary patch literal 248296 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 H(&>m`|HjfTdgm7M562|JCSF1&FhAkHIi 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 literal 0 HcmV?d00001 diff --git a/pkg/apiserver/config/config.go b/pkg/apiserver/config/config.go new file mode 100644 index 000000000..617d60a2b --- /dev/null +++ b/pkg/apiserver/config/config.go @@ -0,0 +1,49 @@ +/* +Copyright 2022 The KubeVela Authors. + +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 config + +import ( + "time" + + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" +) + +// Config config for server +type Config struct { + // api server bind address + BindAddr string + // monitor metric path + MetricPath string + + // Datastore config + Datastore datastore.Config + + // LeaderConfig for leader election + LeaderConfig leaderConfig + + // AddonCacheTime is how long between two cache operations + AddonCacheTime time.Duration + + // DisableStatisticCronJob close the calculate system info cronJob + DisableStatisticCronJob bool +} + +type leaderConfig struct { + ID string + LockName string + Duration time.Duration +} diff --git a/pkg/apiserver/model/application.go b/pkg/apiserver/domain/model/application.go similarity index 100% rename from pkg/apiserver/model/application.go rename to pkg/apiserver/domain/model/application.go diff --git a/pkg/apiserver/model/cluster.go b/pkg/apiserver/domain/model/cluster.go similarity index 100% rename from pkg/apiserver/model/cluster.go rename to pkg/apiserver/domain/model/cluster.go diff --git a/pkg/apiserver/model/env.go b/pkg/apiserver/domain/model/env.go similarity index 100% rename from pkg/apiserver/model/env.go rename to pkg/apiserver/domain/model/env.go diff --git a/pkg/apiserver/model/envbinding.go b/pkg/apiserver/domain/model/envbinding.go similarity index 100% rename from pkg/apiserver/model/envbinding.go rename to pkg/apiserver/domain/model/envbinding.go diff --git a/pkg/apiserver/model/model.go b/pkg/apiserver/domain/model/model.go similarity index 98% rename from pkg/apiserver/model/model.go rename to pkg/apiserver/domain/model/model.go index 57fe45c50..503c1bf11 100644 --- a/pkg/apiserver/model/model.go +++ b/pkg/apiserver/domain/model/model.go @@ -25,7 +25,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/yaml" - "github.com/oam-dev/kubevela/pkg/apiserver/log" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/log" ) var tableNamePrefix = "vela_" diff --git a/pkg/apiserver/model/project.go b/pkg/apiserver/domain/model/project.go similarity index 100% rename from pkg/apiserver/model/project.go rename to pkg/apiserver/domain/model/project.go diff --git a/pkg/apiserver/model/system_info.go b/pkg/apiserver/domain/model/system_info.go similarity index 100% rename from pkg/apiserver/model/system_info.go rename to pkg/apiserver/domain/model/system_info.go diff --git a/pkg/apiserver/model/target.go b/pkg/apiserver/domain/model/target.go similarity index 100% rename from pkg/apiserver/model/target.go rename to pkg/apiserver/domain/model/target.go diff --git a/pkg/apiserver/model/user.go b/pkg/apiserver/domain/model/user.go similarity index 100% rename from pkg/apiserver/model/user.go rename to pkg/apiserver/domain/model/user.go diff --git a/pkg/apiserver/model/whole.go b/pkg/apiserver/domain/model/whole.go similarity index 100% rename from pkg/apiserver/model/whole.go rename to pkg/apiserver/domain/model/whole.go diff --git a/pkg/apiserver/model/workflow.go b/pkg/apiserver/domain/model/workflow.go similarity index 100% rename from pkg/apiserver/model/workflow.go rename to pkg/apiserver/domain/model/workflow.go diff --git a/pkg/apiserver/rest/usecase/env_model.go b/pkg/apiserver/domain/repository/env.go similarity index 80% rename from pkg/apiserver/rest/usecase/env_model.go rename to pkg/apiserver/domain/repository/env.go index 7abd4454d..7b30dbca7 100644 --- a/pkg/apiserver/rest/usecase/env_model.go +++ b/pkg/apiserver/domain/repository/env.go @@ -14,7 +14,7 @@ limitations under the License. */ -package usecase +package repository import ( "context" @@ -22,16 +22,17 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/log" - "github.com/oam-dev/kubevela/pkg/apiserver/model" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/log" "github.com/oam-dev/kubevela/pkg/oam" util "github.com/oam-dev/kubevela/pkg/utils" velaerr "github.com/oam-dev/kubevela/pkg/utils/errors" ) -func createEnv(ctx context.Context, kubeClient client.Client, ds datastore.DataStore, env *model.Env) error { +// CreateEnv create the environment +func CreateEnv(ctx context.Context, kubeClient client.Client, ds datastore.DataStore, env *model.Env) error { tenv := &model.Env{} tenv.Name = env.Name @@ -67,7 +68,8 @@ func createEnv(ctx context.Context, kubeClient client.Client, ds datastore.DataS return nil } -func getEnv(ctx context.Context, ds datastore.DataStore, envName string) (*model.Env, error) { +// GetEnv get the environment +func GetEnv(ctx context.Context, ds datastore.DataStore, envName string) (*model.Env, error) { env := &model.Env{} env.Name = envName if err := ds.Get(ctx, env); err != nil { @@ -79,7 +81,8 @@ func getEnv(ctx context.Context, ds datastore.DataStore, envName string) (*model return env, nil } -func listEnvs(ctx context.Context, ds datastore.DataStore, listOption *datastore.ListOptions) ([]*model.Env, error) { +// ListEnvs list the environments +func ListEnvs(ctx context.Context, ds datastore.DataStore, listOption *datastore.ListOptions) ([]*model.Env, error) { var env = model.Env{} entities, err := ds.List(ctx, &env, listOption) if err != nil { diff --git a/pkg/apiserver/domain/repository/envbinding.go b/pkg/apiserver/domain/repository/envbinding.go new file mode 100644 index 000000000..67acdbb45 --- /dev/null +++ b/pkg/apiserver/domain/repository/envbinding.go @@ -0,0 +1,112 @@ +/* + Copyright 2021 The KubeVela Authors. + + 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 repository + +import ( + "context" + + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" + assembler "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/assembler/v1" + apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/log" +) + +const ( + // EnvBindingPolicyDefaultName default policy name + EnvBindingPolicyDefaultName string = "env-bindings" +) + +// EnvListOption the option for listing the env +type EnvListOption struct { + AppPrimaryKey string + EnvName string + ProjectName string +} + +// ListFullEnvBinding list the envbinding and convert to DTO +func ListFullEnvBinding(ctx context.Context, ds datastore.DataStore, option EnvListOption) ([]*apisv1.EnvBindingBase, error) { + envBindings, err := ListEnvBindings(ctx, ds, option) + if err != nil { + return nil, bcode.ErrEnvBindingsNotExist + } + targets, err := ListTarget(ctx, ds, option.ProjectName, nil) + if err != nil { + return nil, err + } + var listOption *datastore.ListOptions + if option.ProjectName != "" { + listOption = &datastore.ListOptions{ + FilterOptions: datastore.FilterOptions{ + In: []datastore.InQueryOption{ + { + Key: "project", + Values: []string{option.ProjectName}, + }, + }, + }, + } + } + envs, err := ListEnvs(ctx, ds, listOption) + if err != nil { + return nil, err + } + var list []*apisv1.EnvBindingBase + for _, eb := range envBindings { + env, err := pickEnv(envs, eb.Name) + if err != nil { + log.Logger.Errorf("envbinding invalid %s", err.Error()) + continue + } + list = append(list, assembler.ConvertEnvBindingModelToBase(eb, env, targets)) + } + return list, nil +} + +// ListEnvBindings list the envbinding +func ListEnvBindings(ctx context.Context, ds datastore.DataStore, listOption EnvListOption) ([]*model.EnvBinding, error) { + var envBinding = model.EnvBinding{} + if listOption.AppPrimaryKey != "" { + envBinding.AppPrimaryKey = listOption.AppPrimaryKey + } + if listOption.EnvName != "" { + envBinding.Name = listOption.EnvName + } + envBindings, err := ds.List(ctx, &envBinding, &datastore.ListOptions{}) + if err != nil { + return nil, err + } + var ret []*model.EnvBinding + for _, et := range envBindings { + eb, ok := et.(*model.EnvBinding) + if !ok { + continue + } + ret = append(ret, eb) + } + return ret, nil +} + +func pickEnv(envs []*model.Env, name string) (*model.Env, error) { + for _, e := range envs { + if e.Name == name { + return e, nil + } + } + return nil, bcode.ErrEnvNotExisted +} diff --git a/pkg/apiserver/domain/repository/suite_test.go b/pkg/apiserver/domain/repository/suite_test.go new file mode 100644 index 000000000..67a19ab47 --- /dev/null +++ b/pkg/apiserver/domain/repository/suite_test.go @@ -0,0 +1,117 @@ +/* +Copyright 2021 The KubeVela Authors. + +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 repository + +import ( + "context" + "fmt" + "math/rand" + "testing" + "time" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + k8stypes "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/rest" + "k8s.io/utils/pointer" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" + + "github.com/oam-dev/kubevela/apis/types" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/clients" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore/kubeapi" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore/mongodb" + "github.com/oam-dev/kubevela/pkg/utils/common" +) + +var cfg *rest.Config +var k8sClient client.Client +var testEnv *envtest.Environment + +func TestService(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Service Suite") +} + +var _ = BeforeSuite(func(done Done) { + rand.Seed(time.Now().UnixNano()) + By("bootstrapping test environment") + + testEnv = &envtest.Environment{ + ControlPlaneStartTimeout: time.Minute * 3, + ControlPlaneStopTimeout: time.Minute, + UseExistingCluster: pointer.BoolPtr(false), + CRDDirectoryPaths: []string{"../../../../charts/vela-core/crds"}, + } + + By("start kube test env") + var err error + cfg, err = testEnv.Start() + Expect(err).ShouldNot(HaveOccurred()) + Expect(cfg).ToNot(BeNil()) + + By("new kube client") + cfg.Timeout = time.Minute * 2 + k8sClient, err = client.New(cfg, client.Options{Scheme: common.Scheme}) + Expect(err).Should(BeNil()) + Expect(k8sClient).ToNot(BeNil()) + By("new kube client success") + clients.SetKubeClient(k8sClient) + Expect(err).Should(BeNil()) + + var namespace corev1.Namespace + err = k8sClient.Get(context.TODO(), k8stypes.NamespacedName{Name: types.DefaultKubeVelaNS}, &namespace) + if apierrors.IsNotFound(err) { + err := k8sClient.Create(context.TODO(), &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: types.DefaultKubeVelaNS, + }, + }) + Expect(err).Should(BeNil()) + } else { + Expect(err).Should(BeNil()) + } + close(done) +}, 240) + +var _ = AfterSuite(func() { + By("tearing down the test environment") + err := testEnv.Stop() + Expect(err).ToNot(HaveOccurred()) +}) + +func NewDatastore(cfg datastore.Config) (ds datastore.DataStore, err error) { + switch cfg.Type { + case "mongodb": + ds, err = mongodb.New(context.Background(), cfg) + if err != nil { + return nil, fmt.Errorf("create mongodb datastore instance failure %w", err) + } + case "kubeapi": + ds, err = kubeapi.New(context.Background(), cfg) + if err != nil { + return nil, fmt.Errorf("create mongodb datastore instance failure %w", err) + } + default: + return nil, fmt.Errorf("not support datastore type %s", cfg.Type) + } + return ds, nil +} diff --git a/pkg/apiserver/rest/usecase/target_model.go b/pkg/apiserver/domain/repository/target.go similarity index 81% rename from pkg/apiserver/rest/usecase/target_model.go rename to pkg/apiserver/domain/repository/target.go index 2a5042656..2f4a793a5 100644 --- a/pkg/apiserver/rest/usecase/target_model.go +++ b/pkg/apiserver/domain/repository/target.go @@ -14,7 +14,7 @@ limitations under the License. */ -package usecase +package repository import ( "context" @@ -22,17 +22,18 @@ import ( apierror "k8s.io/apimachinery/pkg/api/errors" "sigs.k8s.io/controller-runtime/pkg/client" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/log" - "github.com/oam-dev/kubevela/pkg/apiserver/model" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/log" "github.com/oam-dev/kubevela/pkg/multicluster" "github.com/oam-dev/kubevela/pkg/oam" "github.com/oam-dev/kubevela/pkg/utils" velaerr "github.com/oam-dev/kubevela/pkg/utils/errors" ) -func createTargetNamespace(ctx context.Context, k8sClient client.Client, clusterName, namespace, targetName string) error { +// CreateTargetNamespace create the namespace of the target +func CreateTargetNamespace(ctx context.Context, k8sClient client.Client, clusterName, namespace, targetName string) error { if clusterName == "" || namespace == "" { return bcode.ErrTargetInvalidWithEmptyClusterOrNamespace } @@ -51,7 +52,8 @@ func createTargetNamespace(ctx context.Context, k8sClient client.Client, cluster return nil } -func deleteTargetNamespace(ctx context.Context, k8sClient client.Client, clusterName, namespace, targetName string) error { +// DeleteTargetNamespace delete the namespace of the target +func DeleteTargetNamespace(ctx context.Context, k8sClient client.Client, clusterName, namespace, targetName string) error { err := utils.UpdateNamespace(multicluster.ContextWithClusterName(ctx, clusterName), k8sClient, namespace, // check no conflict label first to make sure the namespace belong to the target, then override it utils.MergeNoConflictLabels(map[string]string{ @@ -67,7 +69,8 @@ func deleteTargetNamespace(ctx context.Context, k8sClient client.Client, cluster return err } -func createTarget(ctx context.Context, ds datastore.DataStore, tg *model.Target) error { +// CreateTarget create a target +func CreateTarget(ctx context.Context, ds datastore.DataStore, tg *model.Target) error { // check Target name. exit, err := ds.IsExist(ctx, tg) if err != nil { @@ -85,7 +88,8 @@ func createTarget(ctx context.Context, ds datastore.DataStore, tg *model.Target) return nil } -func listTarget(ctx context.Context, ds datastore.DataStore, project string, dsOption *datastore.ListOptions) ([]*model.Target, error) { +// ListTarget list the targets +func ListTarget(ctx context.Context, ds datastore.DataStore, project string, dsOption *datastore.ListOptions) ([]*model.Target, error) { if dsOption == nil { dsOption = &datastore.ListOptions{} } diff --git a/pkg/apiserver/rest/usecase/workflow_model.go b/pkg/apiserver/domain/repository/workflow.go similarity index 91% rename from pkg/apiserver/rest/usecase/workflow_model.go rename to pkg/apiserver/domain/repository/workflow.go index a1a29c259..58aef23d3 100644 --- a/pkg/apiserver/rest/usecase/workflow_model.go +++ b/pkg/apiserver/domain/repository/workflow.go @@ -14,7 +14,7 @@ limitations under the License. */ -package usecase +package repository import ( "context" @@ -30,11 +30,11 @@ import ( "github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha1" "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" "github.com/oam-dev/kubevela/apis/types" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/log" - "github.com/oam-dev/kubevela/pkg/apiserver/model" - apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" + apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/log" "github.com/oam-dev/kubevela/pkg/oam/util" utils2 "github.com/oam-dev/kubevela/pkg/utils" "github.com/oam-dev/kubevela/pkg/workflow/step" @@ -55,6 +55,17 @@ const ( modifyState ) +const ( + // Deploy2Env deploy app to target cluster, suitable for common applications + Deploy2Env string = "deploy2env" + // DeployCloudResource deploy app to local and copy secret to target cluster, suitable for cloud application. + DeployCloudResource string = "deploy-cloud-resource" + // TerraformWorkloadType cloud application + TerraformWorkloadType string = "configurations.terraform.core.oam.dev" + // TerraformWorkloadKind terraform workload kind + TerraformWorkloadKind string = "Configuration" +) + type workflowStep struct { name string stepType string @@ -325,7 +336,7 @@ func compareWorkflowSteps(old, new steps) steps { func UpdateEnvWorkflow(ctx context.Context, kubeClient client.Client, ds datastore.DataStore, app *model.Application, env *model.Env) error { // The existing step configuration should be maintained and the delivery target steps should be automatically updated. envSteps, policies := GenEnvWorkflowStepsAndPolicies(ctx, kubeClient, ds, env, app) - workflow, err := getWorkflowForApp(ctx, ds, app, convertWorkflowName(env.Name)) + workflow, err := GetWorkflowForApp(ctx, ds, app, ConvertWorkflowName(env.Name)) if err != nil { // no workflow exist mean no need to update if errors.Is(err, bcode.ErrWorkflowNotExist) { @@ -349,7 +360,7 @@ func UpdateEnvWorkflow(ctx context.Context, kubeClient client.Client, ds datasto workflowSteps := compareWorkflowSteps(createWorkflowSteps(workflow.Steps, existPolicies), createWorkflowSteps(envSteps, policies)) // update the workflow - if err := updateWorkflowSteps(ctx, ds, workflow, workflowSteps.getSteps(envSteps, workflow.Steps)); err != nil { + if err := UpdateWorkflowSteps(ctx, ds, workflow, workflowSteps.getSteps(envSteps, workflow.Steps)); err != nil { return fmt.Errorf("fail to update the workflow steps %w", err) } @@ -378,7 +389,7 @@ func UpdateEnvWorkflow(ctx context.Context, kubeClient client.Client, ds datasto // UpdateAppEnvWorkflow will update the all env workflows internally of the specified app func UpdateAppEnvWorkflow(ctx context.Context, kubeClient client.Client, ds datastore.DataStore, app *model.Application) error { - envbindings, err := listEnvBindings(ctx, ds, envListOption{appPrimaryKey: app.PrimaryKey(), projectName: app.Project}) + envbindings, err := ListEnvBindings(ctx, ds, EnvListOption{AppPrimaryKey: app.PrimaryKey(), ProjectName: app.Project}) if err != nil { return err } @@ -389,7 +400,7 @@ func UpdateAppEnvWorkflow(ctx context.Context, kubeClient client.Client, ds data if len(envNames) == 0 { return nil } - envs, err := listEnvs(ctx, ds, &datastore.ListOptions{ + envs, err := ListEnvs(ctx, ds, &datastore.ListOptions{ FilterOptions: datastore.FilterOptions{ In: []datastore.InQueryOption{ { @@ -407,7 +418,7 @@ func UpdateAppEnvWorkflow(ctx context.Context, kubeClient client.Client, ds data log.Logger.Errorf("fail to update the env workflow %s", envs[i].PrimaryKey()) } } - log.Logger.Infof("The env workflows of app %s updated successfully", app.PrimaryKey()) + log.Logger.Infof("The env workflows of app %s updated successfully", utils2.Sanitize(app.PrimaryKey())) return nil } @@ -606,7 +617,14 @@ func GenEnvWorkflowStepsAndPolicies(ctx context.Context, kubeClient client.Clien return steps, policies } -func getWorkflowForApp(ctx context.Context, ds datastore.DataStore, app *model.Application, workflowName string) (*model.Workflow, error) { +// UpdateWorkflowSteps will update workflow with new steps +func UpdateWorkflowSteps(ctx context.Context, ds datastore.DataStore, workflow *model.Workflow, steps []model.WorkflowStep) error { + workflow.Steps = steps + return ds.Put(ctx, workflow) +} + +// GetWorkflowForApp get the specified workflow of the application +func GetWorkflowForApp(ctx context.Context, ds datastore.DataStore, app *model.Application, workflowName string) (*model.Workflow, error) { var workflow = model.Workflow{ Name: workflowName, AppPrimaryKey: app.PrimaryKey(), @@ -619,3 +637,16 @@ func getWorkflowForApp(ctx context.Context, ds datastore.DataStore, app *model.A } return &workflow, nil } + +// ConvertWorkflowName generate the workflow name +func ConvertWorkflowName(envName string) string { + return fmt.Sprintf("workflow-%s", envName) +} + +func genPolicyName(envName string) string { + return fmt.Sprintf("%s-%s", EnvBindingPolicyDefaultName, envName) +} + +func genPolicyEnvName(targetName string) string { + return targetName +} diff --git a/pkg/apiserver/rest/usecase/workflow_model_test.go b/pkg/apiserver/domain/repository/workflow_test.go similarity index 72% rename from pkg/apiserver/rest/usecase/workflow_model_test.go rename to pkg/apiserver/domain/repository/workflow_test.go index f923ce810..4d8ce24a1 100644 --- a/pkg/apiserver/rest/usecase/workflow_model_test.go +++ b/pkg/apiserver/domain/repository/workflow_test.go @@ -14,24 +14,27 @@ See the License for the specific language governing permissions and limitations under the License. */ -package usecase +package repository import ( "context" + "fmt" "testing" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "github.com/stretchr/testify/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" "github.com/oam-dev/kubevela/apis/core.oam.dev/common" "github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha1" "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" "github.com/oam-dev/kubevela/apis/types" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/model" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/log" + utils2 "github.com/oam-dev/kubevela/pkg/utils" ) func TestCompareWorkflowSteps(t *testing.T) { @@ -171,26 +174,14 @@ func TestCompareWorkflowSteps(t *testing.T) { } var _ = Describe("Test workflow model", func() { - var ds datastore.DataStore - var ( - envUsecase *envUsecaseImpl - envBindingUsecase *envBindingUsecaseImpl - workflowUsecase *workflowUsecaseImpl - definitionUsecase DefinitionUsecase - ) + var store datastore.DataStore BeforeEach(func() { var err error - ds, err = NewDatastore(datastore.Config{Type: "kubeapi", Database: "workflow-test-kubevela"}) - Expect(ds).ToNot(BeNil()) + store, err = NewDatastore(datastore.Config{Type: "kubeapi", Database: "workflow-test-kubevela"}) Expect(err).Should(BeNil()) - rbacUsecase := &rbacUsecaseImpl{ds: ds} - projectUsecase := &projectUsecaseImpl{ds: ds, k8sClient: k8sClient, rbacUsecase: rbacUsecase} - envUsecase = &envUsecaseImpl{ds: ds, kubeClient: k8sClient, projectUsecase: projectUsecase} - workflowUsecase = &workflowUsecaseImpl{ds: ds, kubeClient: k8sClient, envUsecase: envUsecase} - definitionUsecase = &definitionUsecaseImpl{kubeClient: k8sClient, caches: utils.NewMemoryCacheStore(context.TODO())} - envBindingUsecase = &envBindingUsecaseImpl{ds: ds, workflowUsecase: workflowUsecase, definitionUsecase: definitionUsecase, kubeClient: k8sClient, envUsecase: envUsecase} - + Expect(store).ToNot(BeNil()) }) + It("update the workflow after added a cloud component", func() { definition := &v1beta1.ComponentDefinition{ ObjectMeta: metav1.ObjectMeta{ @@ -230,13 +221,13 @@ var _ = Describe("Test workflow model", func() { Project: "default", Cluster: &model.ClusterTarget{ClusterName: "local", Namespace: "target2"}, } - err = ds.BatchAdd(context.TODO(), []datastore.Entity{app, webComponent, target1, target2, env}) + err = store.BatchAdd(context.TODO(), []datastore.Entity{app, webComponent, target1, target2, env}) Expect(err).Should(BeNil()) - err = envBindingUsecase.createEnvWorkflow(context.TODO(), app, env, true) + err = CreateEnvWorkflow(context.TODO(), store, k8sClient, app, env, true) Expect(err).Should(BeNil()) - workflow, err := workflowUsecase.GetWorkflow(context.TODO(), app, convertWorkflowName(env.Name)) + workflow, err := GetWorkflowForApp(context.TODO(), store, app, ConvertWorkflowName(env.Name)) Expect(err).Should(BeNil()) Expect(len(workflow.Steps)).Should(Equal(2)) @@ -246,18 +237,18 @@ var _ = Describe("Test workflow model", func() { Type: "aliyun-rds", } - err = ds.BatchAdd(context.TODO(), []datastore.Entity{cloudComponent}) + err = store.BatchAdd(context.TODO(), []datastore.Entity{cloudComponent}) Expect(err).Should(BeNil()) - err = UpdateEnvWorkflow(context.Background(), k8sClient, ds, app, env) + err = UpdateEnvWorkflow(context.Background(), k8sClient, store, app, env) Expect(err).Should(BeNil()) - workflow, err = workflowUsecase.GetWorkflow(context.TODO(), app, convertWorkflowName(env.Name)) + workflow, err = GetWorkflowForApp(context.TODO(), store, app, ConvertWorkflowName(env.Name)) Expect(err).Should(BeNil()) Expect(len(workflow.Steps)).Should(Equal(4)) Expect(workflow.Steps[0].Type).Should(Equal(DeployCloudResource)) - entities, err := ds.List(context.TODO(), &model.ApplicationPolicy{AppPrimaryKey: app.PrimaryKey()}, &datastore.ListOptions{ + entities, err := store.List(context.TODO(), &model.ApplicationPolicy{AppPrimaryKey: app.PrimaryKey()}, &datastore.ListOptions{ FilterOptions: datastore.FilterOptions{ In: []datastore.InQueryOption{{ Key: "type", @@ -270,11 +261,11 @@ var _ = Describe("Test workflow model", func() { Expect(entities[0].(*model.ApplicationPolicy).Name).Should(Equal("env-bindings-dev")) By("test the case that delete the cloud component") - err = ds.Delete(context.TODO(), cloudComponent) + err = store.Delete(context.TODO(), cloudComponent) Expect(err).Should(BeNil()) - err = UpdateEnvWorkflow(context.Background(), k8sClient, ds, app, env) + err = UpdateEnvWorkflow(context.Background(), k8sClient, store, app, env) Expect(err).Should(BeNil()) - workflow, err = workflowUsecase.GetWorkflow(context.TODO(), app, convertWorkflowName(env.Name)) + workflow, err = GetWorkflowForApp(context.TODO(), store, app, ConvertWorkflowName(env.Name)) Expect(err).Should(BeNil()) Expect(len(workflow.Steps)).Should(Equal(2)) }) @@ -297,13 +288,13 @@ var _ = Describe("Test workflow model", func() { Targets: []string{"target1", "target2"}, } - err := ds.BatchAdd(context.TODO(), []datastore.Entity{app, cloudComponent}) + err := store.BatchAdd(context.TODO(), []datastore.Entity{app, cloudComponent}) Expect(err).Should(BeNil()) - err = envBindingUsecase.createEnvWorkflow(context.TODO(), app, env, true) + err = CreateEnvWorkflow(context.TODO(), store, k8sClient, app, env, true) Expect(err).Should(BeNil()) - workflow, err := workflowUsecase.GetWorkflow(context.TODO(), app, convertWorkflowName(env.Name)) + workflow, err := GetWorkflowForApp(context.TODO(), store, app, ConvertWorkflowName(env.Name)) Expect(err).Should(BeNil()) Expect(len(workflow.Steps)).Should(Equal(2)) @@ -321,21 +312,21 @@ var _ = Describe("Test workflow model", func() { env.Targets = []string{"target1", "target2", "target3"} - err = ds.Put(context.TODO(), env) + err = store.Put(context.TODO(), env) Expect(err).Should(BeNil()) - err = ds.BatchAdd(context.TODO(), []datastore.Entity{webComponent, target3}) + err = store.BatchAdd(context.TODO(), []datastore.Entity{webComponent, target3}) Expect(err).Should(BeNil()) - err = UpdateEnvWorkflow(context.Background(), k8sClient, ds, app, env) + err = UpdateEnvWorkflow(context.Background(), k8sClient, store, app, env) Expect(err).Should(BeNil()) - workflow, err = workflowUsecase.GetWorkflow(context.TODO(), app, convertWorkflowName(env.Name)) + workflow, err = GetWorkflowForApp(context.TODO(), store, app, ConvertWorkflowName(env.Name)) Expect(err).Should(BeNil()) Expect(len(workflow.Steps)).Should(Equal(6)) Expect(workflow.Steps[0].Type).Should(Equal(DeployCloudResource)) - entities, err := ds.List(context.TODO(), &model.ApplicationPolicy{AppPrimaryKey: app.PrimaryKey()}, &datastore.ListOptions{ + entities, err := store.List(context.TODO(), &model.ApplicationPolicy{AppPrimaryKey: app.PrimaryKey()}, &datastore.ListOptions{ FilterOptions: datastore.FilterOptions{ In: []datastore.InQueryOption{{ Key: "type", @@ -366,13 +357,13 @@ var _ = Describe("Test workflow model", func() { Targets: []string{"target1", "target2"}, } - err := ds.BatchAdd(context.TODO(), []datastore.Entity{app, webComponent}) + err := store.BatchAdd(context.TODO(), []datastore.Entity{app, webComponent}) Expect(err).Should(BeNil()) - err = envBindingUsecase.createEnvWorkflow(context.TODO(), app, env, true) + err = CreateEnvWorkflow(context.TODO(), store, k8sClient, app, env, true) Expect(err).Should(BeNil()) - workflow, err := workflowUsecase.GetWorkflow(context.TODO(), app, convertWorkflowName(env.Name)) + workflow, err := GetWorkflowForApp(context.TODO(), store, app, ConvertWorkflowName(env.Name)) Expect(err).Should(BeNil()) workflow.Steps = []model.WorkflowStep{ @@ -385,18 +376,18 @@ var _ = Describe("Test workflow model", func() { }, } - err = ds.Put(context.TODO(), workflow) + err = store.Put(context.TODO(), workflow) Expect(err).Should(BeNil()) env.Targets = []string{"target1", "target2", "target3"} - err = ds.Put(context.TODO(), env) + err = store.Put(context.TODO(), env) Expect(err).Should(BeNil()) - err = UpdateEnvWorkflow(context.Background(), k8sClient, ds, app, env) + err = UpdateEnvWorkflow(context.Background(), k8sClient, store, app, env) Expect(err).Should(BeNil()) - workflow, err = workflowUsecase.GetWorkflow(context.TODO(), app, convertWorkflowName(env.Name)) + workflow, err = GetWorkflowForApp(context.TODO(), store, app, ConvertWorkflowName(env.Name)) Expect(err).Should(BeNil()) Expect(len(workflow.Steps)).Should(Equal(5)) Expect(workflow.Steps[1].Type).Should(Equal("suspend")) @@ -420,31 +411,31 @@ var _ = Describe("Test workflow model", func() { Targets: []string{"target1", "target2"}, } - err := ds.BatchAdd(context.TODO(), []datastore.Entity{app, webComponent}) + err := store.BatchAdd(context.TODO(), []datastore.Entity{app, webComponent}) Expect(err).Should(BeNil()) - err = envBindingUsecase.createEnvWorkflow(context.TODO(), app, env, true) + err = CreateEnvWorkflow(context.TODO(), store, k8sClient, app, env, true) Expect(err).Should(BeNil()) - workflow, err := workflowUsecase.GetWorkflow(context.TODO(), app, convertWorkflowName(env.Name)) + workflow, err := GetWorkflowForApp(context.TODO(), store, app, ConvertWorkflowName(env.Name)) Expect(err).Should(BeNil()) step := workflow.Steps[0] (*step.Properties)["policies"] = []string{"target1", "target2"} workflow.Steps = []model.WorkflowStep{step} - err = ds.Put(context.TODO(), workflow) + err = store.Put(context.TODO(), workflow) Expect(err).Should(BeNil()) env.Targets = []string{"target1", "target2", "target3"} - err = ds.Put(context.TODO(), env) + err = store.Put(context.TODO(), env) Expect(err).Should(BeNil()) - err = UpdateEnvWorkflow(context.Background(), k8sClient, ds, app, env) + err = UpdateEnvWorkflow(context.Background(), k8sClient, store, app, env) Expect(err).Should(BeNil()) - workflow, err = workflowUsecase.GetWorkflow(context.TODO(), app, convertWorkflowName(env.Name)) + workflow, err = GetWorkflowForApp(context.TODO(), store, app, ConvertWorkflowName(env.Name)) Expect(err).Should(BeNil()) Expect(len(workflow.Steps)).Should(Equal(2)) Expect(workflow.Steps[0].Properties).ShouldNot(BeNil()) @@ -454,13 +445,13 @@ var _ = Describe("Test workflow model", func() { env.Targets = []string{"target2", "target3"} - err = ds.Put(context.TODO(), env) + err = store.Put(context.TODO(), env) Expect(err).Should(BeNil()) - err = UpdateEnvWorkflow(context.Background(), k8sClient, ds, app, env) + err = UpdateEnvWorkflow(context.Background(), k8sClient, store, app, env) Expect(err).Should(BeNil()) - workflow, err = workflowUsecase.GetWorkflow(context.TODO(), app, convertWorkflowName(env.Name)) + workflow, err = GetWorkflowForApp(context.TODO(), store, app, ConvertWorkflowName(env.Name)) Expect(err).Should(BeNil()) Expect(len(workflow.Steps)).Should(Equal(2)) // the concurrent step should be kept. @@ -469,7 +460,7 @@ var _ = Describe("Test workflow model", func() { Expect((*workflow.Steps[0].Properties)["policies"].([]interface{})[0]).Should(Equal("target2")) Expect((*workflow.Steps[1].Properties)["policies"].([]interface{})[0].(string)).Should(Equal("target3")) - entities, err := ds.List(context.TODO(), &model.ApplicationPolicy{AppPrimaryKey: app.PrimaryKey()}, &datastore.ListOptions{ + entities, err := store.List(context.TODO(), &model.ApplicationPolicy{AppPrimaryKey: app.PrimaryKey()}, &datastore.ListOptions{ FilterOptions: datastore.FilterOptions{ In: []datastore.InQueryOption{{ Key: "type", @@ -505,30 +496,30 @@ var _ = Describe("Test workflow model", func() { Targets: []string{"target1", "target2"}, } - err := ds.BatchAdd(context.TODO(), []datastore.Entity{app, cloudComponent, webComponent}) + err := store.BatchAdd(context.TODO(), []datastore.Entity{app, cloudComponent, webComponent}) Expect(err).Should(BeNil()) - err = envBindingUsecase.createEnvWorkflow(context.TODO(), app, env, true) + err = CreateEnvWorkflow(context.TODO(), store, k8sClient, app, env, true) Expect(err).Should(BeNil()) - workflow, err := workflowUsecase.GetWorkflow(context.TODO(), app, convertWorkflowName(env.Name)) + workflow, err := GetWorkflowForApp(context.TODO(), store, app, ConvertWorkflowName(env.Name)) Expect(err).Should(BeNil()) Expect(len(workflow.Steps)).Should(Equal(4)) env.Targets = []string{"target1"} - err = ds.Put(context.TODO(), env) + err = store.Put(context.TODO(), env) Expect(err).Should(BeNil()) - err = UpdateEnvWorkflow(context.Background(), k8sClient, ds, app, env) + err = UpdateEnvWorkflow(context.Background(), k8sClient, store, app, env) Expect(err).Should(BeNil()) - workflow, err = workflowUsecase.GetWorkflow(context.TODO(), app, convertWorkflowName(env.Name)) + workflow, err = GetWorkflowForApp(context.TODO(), store, app, ConvertWorkflowName(env.Name)) Expect(err).Should(BeNil()) Expect(len(workflow.Steps)).Should(Equal(2)) Expect(workflow.Steps[0].Type).Should(Equal(DeployCloudResource)) - entities, err := ds.List(context.TODO(), &model.ApplicationPolicy{AppPrimaryKey: app.PrimaryKey()}, &datastore.ListOptions{ + entities, err := store.List(context.TODO(), &model.ApplicationPolicy{AppPrimaryKey: app.PrimaryKey()}, &datastore.ListOptions{ FilterOptions: datastore.FilterOptions{ In: []datastore.InQueryOption{{ Key: "type", @@ -540,3 +531,25 @@ var _ = Describe("Test workflow model", func() { Expect(len(entities)).Should(Equal(2)) }) }) + +func CreateEnvWorkflow(ctx context.Context, store datastore.DataStore, kubeClient client.Client, app *model.Application, env *model.Env, isDefault bool) error { + steps, policies := GenEnvWorkflowStepsAndPolicies(ctx, kubeClient, store, env, app) + workflow := &model.Workflow{ + Steps: steps, + Name: ConvertWorkflowName(env.Name), + Alias: fmt.Sprintf("%s Workflow", env.Alias), + Description: "Created automatically by envbinding.", + Default: &isDefault, + EnvName: env.Name, + AppPrimaryKey: app.PrimaryKey(), + } + log.Logger.Infof("create workflow %s for app %s", utils2.Sanitize(workflow.Name), utils2.Sanitize(app.PrimaryKey())) + if err := store.Add(ctx, workflow); err != nil { + return err + } + err := store.BatchAdd(ctx, policies) + if err != nil { + return fmt.Errorf("fail to create policies %w", err) + } + return nil +} diff --git a/pkg/apiserver/rest/usecase/addon.go b/pkg/apiserver/domain/service/addon.go similarity index 87% rename from pkg/apiserver/rest/usecase/addon.go rename to pkg/apiserver/domain/service/addon.go index c84064f5f..a6c669a44 100644 --- a/pkg/apiserver/rest/usecase/addon.go +++ b/pkg/apiserver/domain/service/addon.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package usecase +package service import ( "context" @@ -39,19 +39,19 @@ import ( "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" "github.com/oam-dev/kubevela/apis/types" pkgaddon "github.com/oam-dev/kubevela/pkg/addon" - "github.com/oam-dev/kubevela/pkg/apiserver/clients" - "github.com/oam-dev/kubevela/pkg/apiserver/log" - apis "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/clients" + apis "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/log" "github.com/oam-dev/kubevela/pkg/multicluster" "github.com/oam-dev/kubevela/pkg/oam" "github.com/oam-dev/kubevela/pkg/utils/apply" velaerr "github.com/oam-dev/kubevela/pkg/utils/errors" ) -// AddonHandler handle CRUD and installation of addons -type AddonHandler interface { +// AddonService handle CRUD and installation of addons +type AddonService interface { GetAddonRegistry(ctx context.Context, name string) (*apis.AddonRegistry, error) CreateAddonRegistry(ctx context.Context, req apis.CreateAddonRegistryRequest) (*apis.AddonRegistry, error) DeleteAddonRegistry(ctx context.Context, name string) error @@ -97,8 +97,8 @@ func AddonImpl2AddonRes(impl *pkgaddon.UIData) (*apis.DetailAddonResponse, error }, nil } -// NewAddonUsecase returns an addon usecase -func NewAddonUsecase(cacheTime time.Duration) AddonHandler { +// NewAddonService returns an addon service +func NewAddonService(cacheTime time.Duration) AddonService { config, err := clients.GetKubeConfig() if err != nil { panic(err) @@ -117,7 +117,7 @@ func NewAddonUsecase(cacheTime time.Duration) AddonHandler { // TODO(@wonderflow): it's better to add a close channel here, but it should be fine as it's only invoke once in APIServer. go cache.DiscoverAndRefreshLoop(cacheTime) - return &defaultAddonHandler{ + return &addonServiceImpl{ addonRegistryCache: cache, addonRegistryDS: ds, kubeClient: kubecli, @@ -128,7 +128,7 @@ func NewAddonUsecase(cacheTime time.Duration) AddonHandler { } } -type defaultAddonHandler struct { +type addonServiceImpl struct { addonRegistryCache *pkgaddon.Cache addonRegistryDS pkgaddon.RegistryDataStore kubeClient client.Client @@ -140,7 +140,7 @@ type defaultAddonHandler struct { } // GetAddon will get addon information -func (u *defaultAddonHandler) GetAddon(ctx context.Context, name string, registry string, version string) (*apis.DetailAddonResponse, error) { +func (u *addonServiceImpl) GetAddon(ctx context.Context, name string, registry string, version string) (*apis.DetailAddonResponse, error) { var addon *pkgaddon.UIData var err error if registry == "" { @@ -181,7 +181,7 @@ func (u *defaultAddonHandler) GetAddon(ctx context.Context, name string, registr return a, nil } -func (u *defaultAddonHandler) StatusAddon(ctx context.Context, name string) (*apis.AddonStatusResponse, error) { +func (u *addonServiceImpl) StatusAddon(ctx context.Context, name string) (*apis.AddonStatusResponse, error) { status, err := pkgaddon.GetAddonStatus(ctx, u.kubeClient, name) if err != nil { return nil, bcode.ErrGetAddonApplication @@ -236,7 +236,7 @@ func (u *defaultAddonHandler) StatusAddon(ctx context.Context, name string) (*ap return &res, nil } -func (u *defaultAddonHandler) ListAddons(ctx context.Context, registry, query string) ([]*apis.DetailAddonResponse, error) { +func (u *addonServiceImpl) ListAddons(ctx context.Context, registry, query string) ([]*apis.DetailAddonResponse, error) { var addons []*pkgaddon.UIData rs, err := u.addonRegistryDS.ListRegistries(ctx) if err != nil { @@ -296,11 +296,11 @@ func (u *defaultAddonHandler) ListAddons(ctx context.Context, registry, query st return addonResources, nil } -func (u *defaultAddonHandler) DeleteAddonRegistry(ctx context.Context, name string) error { +func (u *addonServiceImpl) DeleteAddonRegistry(ctx context.Context, name string) error { return u.addonRegistryDS.DeleteRegistry(ctx, name) } -func (u *defaultAddonHandler) CreateAddonRegistry(ctx context.Context, req apis.CreateAddonRegistryRequest) (*apis.AddonRegistry, error) { +func (u *addonServiceImpl) CreateAddonRegistry(ctx context.Context, req apis.CreateAddonRegistryRequest) (*apis.AddonRegistry, error) { r := addonRegistryModelFromCreateAddonRegistryRequest(req) err := u.addonRegistryDS.AddRegistry(ctx, r) @@ -322,7 +322,7 @@ func convertAddonRegistry(r pkgaddon.Registry) *apis.AddonRegistry { } } -func (u *defaultAddonHandler) GetAddonRegistry(ctx context.Context, name string) (*apis.AddonRegistry, error) { +func (u *addonServiceImpl) GetAddonRegistry(ctx context.Context, name string) (*apis.AddonRegistry, error) { r, err := u.addonRegistryDS.GetRegistry(ctx, name) if err != nil { return nil, err @@ -330,7 +330,7 @@ func (u *defaultAddonHandler) GetAddonRegistry(ctx context.Context, name string) return convertAddonRegistry(r), nil } -func (u defaultAddonHandler) UpdateAddonRegistry(ctx context.Context, name string, req apis.UpdateAddonRegistryRequest) (*apis.AddonRegistry, error) { +func (u addonServiceImpl) UpdateAddonRegistry(ctx context.Context, name string, req apis.UpdateAddonRegistryRequest) (*apis.AddonRegistry, error) { r, err := u.addonRegistryDS.GetRegistry(ctx, name) if err != nil { return nil, bcode.ErrAddonRegistryNotExist @@ -356,7 +356,7 @@ func (u defaultAddonHandler) UpdateAddonRegistry(ctx context.Context, name strin return convertAddonRegistry(r), nil } -func (u *defaultAddonHandler) ListAddonRegistries(ctx context.Context) ([]*apis.AddonRegistry, error) { +func (u *addonServiceImpl) ListAddonRegistries(ctx context.Context) ([]*apis.AddonRegistry, error) { var list []*apis.AddonRegistry registries, err := u.addonRegistryDS.ListRegistries(ctx) @@ -377,7 +377,7 @@ func (u *defaultAddonHandler) ListAddonRegistries(ctx context.Context) ([]*apis. return list, nil } -func (u *defaultAddonHandler) EnableAddon(ctx context.Context, name string, args apis.EnableAddonRequest) error { +func (u *addonServiceImpl) EnableAddon(ctx context.Context, name string, args apis.EnableAddonRequest) error { var err error registries, err := u.addonRegistryDS.ListRegistries(ctx) if err != nil { @@ -402,7 +402,6 @@ func (u *defaultAddonHandler) EnableAddon(ctx context.Context, name string, args // wrap this error with special bcode if errors.As(err, &pkgaddon.VersionUnMatchError{}) { - log.Logger.Error(err) return bcode.ErrAddonSystemVersionMismatch } // except `addon not found`, other errors should return directly @@ -411,7 +410,7 @@ func (u *defaultAddonHandler) EnableAddon(ctx context.Context, name string, args return bcode.ErrAddonNotExist } -func (u *defaultAddonHandler) DisableAddon(ctx context.Context, name string, force bool) error { +func (u *addonServiceImpl) DisableAddon(ctx context.Context, name string, force bool) error { err := pkgaddon.DisableAddon(ctx, u.kubeClient, name, u.config, force) if err != nil { log.Logger.Errorf("delete application fail: %s", err.Error()) @@ -420,7 +419,7 @@ func (u *defaultAddonHandler) DisableAddon(ctx context.Context, name string, for return nil } -func (u *defaultAddonHandler) ListEnabledAddon(ctx context.Context) ([]*apis.AddonBaseStatus, error) { +func (u *addonServiceImpl) ListEnabledAddon(ctx context.Context) ([]*apis.AddonBaseStatus, error) { apps := &v1beta1.ApplicationList{} if err := u.kubeClient.List(ctx, apps, client.InNamespace(types.DefaultKubeVelaNS), client.HasLabels{oam.LabelAddonName}); err != nil { return nil, err @@ -440,7 +439,7 @@ func (u *defaultAddonHandler) ListEnabledAddon(ctx context.Context) ([]*apis.Add return response, nil } -func (u *defaultAddonHandler) UpdateAddon(ctx context.Context, name string, args apis.EnableAddonRequest) error { +func (u *addonServiceImpl) UpdateAddon(ctx context.Context, name string, args apis.EnableAddonRequest) error { var app v1beta1.Application // check addon application whether exist diff --git a/pkg/apiserver/rest/usecase/addon_test.go b/pkg/apiserver/domain/service/addon_test.go similarity index 95% rename from pkg/apiserver/rest/usecase/addon_test.go rename to pkg/apiserver/domain/service/addon_test.go index 39acc8d44..810ff088d 100644 --- a/pkg/apiserver/rest/usecase/addon_test.go +++ b/pkg/apiserver/domain/service/addon_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package usecase +package service import ( "context" @@ -30,10 +30,10 @@ import ( "sigs.k8s.io/yaml" "github.com/oam-dev/kubevela/apis/types" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils" + "github.com/oam-dev/kubevela/pkg/apiserver/utils" ) -var _ = Describe("addon usecase test", func() { +var _ = Describe("addon service test", func() { var ctx context.Context BeforeEach(func() { diff --git a/pkg/apiserver/rest/usecase/application.go b/pkg/apiserver/domain/service/application.go similarity index 77% rename from pkg/apiserver/rest/usecase/application.go rename to pkg/apiserver/domain/service/application.go index 176ac3635..1b8f343a7 100644 --- a/pkg/apiserver/rest/usecase/application.go +++ b/pkg/apiserver/domain/service/application.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package usecase +package service import ( "bytes" @@ -41,14 +41,15 @@ import ( "github.com/oam-dev/kubevela/apis/core.oam.dev/common" "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" velatypes "github.com/oam-dev/kubevela/apis/types" - "github.com/oam-dev/kubevela/pkg/apiserver/clients" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/log" - "github.com/oam-dev/kubevela/pkg/apiserver/model" - apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" - syncconvert "github.com/oam-dev/kubevela/pkg/apiserver/sync/convert" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/repository" + syncconvert "github.com/oam-dev/kubevela/pkg/apiserver/event/sync/convert" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" + assembler "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/assembler/v1" + apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/log" "github.com/oam-dev/kubevela/pkg/appfile/dryrun" "github.com/oam-dev/kubevela/pkg/oam" "github.com/oam-dev/kubevela/pkg/oam/discoverymapper" @@ -61,15 +62,11 @@ import ( type PolicyType string const ( - - // EnvBindingPolicyDefaultName default policy name - EnvBindingPolicyDefaultName string = "env-bindings" - defaultTokenLen int = 16 ) -// ApplicationUsecase application usecase -type ApplicationUsecase interface { +// ApplicationService application service +type ApplicationService interface { ListApplications(ctx context.Context, listOptions apisv1.ListApplicationOptions) ([]*apisv1.ApplicationBase, error) GetApplication(ctx context.Context, appName string) (*model.Application, error) GetApplicationStatus(ctx context.Context, app *model.Application, envName string) (*common.AppStatus, error) @@ -105,51 +102,23 @@ type ApplicationUsecase interface { DeleteApplicationTrigger(ctx context.Context, app *model.Application, triggerName string) error } -type applicationUsecaseImpl struct { - ds datastore.DataStore - kubeClient client.Client - kubeConfig *rest.Config - apply apply.Applicator - workflowUsecase WorkflowUsecase - envUsecase EnvUsecase - envBindingUsecase EnvBindingUsecase - targetUsecase TargetUsecase - definitionUsecase DefinitionUsecase - projectUsecase ProjectUsecase - userUsecase UserUsecase +type applicationServiceImpl struct { + Store datastore.DataStore `inject:"datastore"` + KubeClient client.Client `inject:"kubeClient"` + KubeConfig *rest.Config `inject:"kubeConfig"` + Apply apply.Applicator `inject:"apply"` + WorkflowService WorkflowService `inject:""` + EnvService EnvService `inject:""` + EnvBindingService EnvBindingService `inject:""` + TargetService TargetService `inject:""` + DefinitionService DefinitionService `inject:""` + ProjectService ProjectService `inject:""` + UserService UserService `inject:""` } -// NewApplicationUsecase new application usecase -func NewApplicationUsecase(ds datastore.DataStore, - workflowUsecase WorkflowUsecase, - envBindingUsecase EnvBindingUsecase, - envUsecase EnvUsecase, - targetUsecase TargetUsecase, - definitionUsecase DefinitionUsecase, - projectUsecase ProjectUsecase, - userUsecase UserUsecase, -) ApplicationUsecase { - kubecli, err := clients.GetKubeClient() - if err != nil { - log.Logger.Fatalf("get kube client failure %s", err.Error()) - } - config, err := clients.GetKubeConfig() - if err != nil { - log.Logger.Fatalf("get kube rest config failure %s", err.Error()) - } - return &applicationUsecaseImpl{ - ds: ds, - workflowUsecase: workflowUsecase, - envBindingUsecase: envBindingUsecase, - targetUsecase: targetUsecase, - kubeClient: kubecli, - kubeConfig: config, - apply: apply.NewAPIApplicator(kubecli), - definitionUsecase: definitionUsecase, - projectUsecase: projectUsecase, - envUsecase: envUsecase, - userUsecase: userUsecase, - } +// NewApplicationService new application service +func NewApplicationService() ApplicationService { + return &applicationServiceImpl{} } func listApp(ctx context.Context, ds datastore.DataStore, listOptions apisv1.ListApplicationOptions) ([]*model.Application, error) { @@ -157,7 +126,7 @@ func listApp(ctx context.Context, ds datastore.DataStore, listOptions apisv1.Lis var err error var envBinding []*apisv1.EnvBindingBase if listOptions.Env != "" || listOptions.TargetName != "" { - envBinding, err = listFullEnvBinding(ctx, ds, envListOption{}) + envBinding, err = repository.ListFullEnvBinding(ctx, ds, repository.EnvListOption{}) if err != nil { log.Logger.Errorf("list envbinding for list application in env %s err %v", utils2.Sanitize(listOptions.Env), err) return nil, err @@ -211,12 +180,12 @@ func listApp(ctx context.Context, ds datastore.DataStore, listOptions apisv1.Lis } // ListApplications list applications -func (c *applicationUsecaseImpl) ListApplications(ctx context.Context, listOptions apisv1.ListApplicationOptions) ([]*apisv1.ApplicationBase, error) { +func (c *applicationServiceImpl) ListApplications(ctx context.Context, listOptions apisv1.ListApplicationOptions) ([]*apisv1.ApplicationBase, error) { userName, ok := ctx.Value(&apisv1.CtxKeyUser).(string) if !ok { return nil, bcode.ErrUnauthorized } - projects, err := c.projectUsecase.ListUserProjects(ctx, userName) + projects, err := c.ProjectService.ListUserProjects(ctx, userName) if err != nil { return nil, err } @@ -235,13 +204,13 @@ func (c *applicationUsecaseImpl) ListApplications(ctx context.Context, listOptio if len(listOptions.Projects) == 0 { listOptions.Projects = availableProjectNames } - apps, err := listApp(ctx, c.ds, listOptions) + apps, err := listApp(ctx, c.Store, listOptions) if err != nil { return nil, err } var list []*apisv1.ApplicationBase for _, app := range apps { - appBase := c.convertAppModelToBase(app, projects) + appBase := assembler.ConvertAppModelToBase(app, projects) list = append(list, appBase) } sort.Slice(list, func(i, j int) bool { @@ -251,11 +220,11 @@ func (c *applicationUsecaseImpl) ListApplications(ctx context.Context, listOptio } // GetApplication get application model -func (c *applicationUsecaseImpl) GetApplication(ctx context.Context, appName string) (*model.Application, error) { +func (c *applicationServiceImpl) GetApplication(ctx context.Context, appName string) (*model.Application, error) { var app = model.Application{ Name: appName, } - if err := c.ds.Get(ctx, &app); err != nil { + if err := c.Store.Get(ctx, &app); err != nil { if errors.Is(err, datastore.ErrRecordNotExist) { return nil, bcode.ErrApplicationNotExist } @@ -265,25 +234,25 @@ func (c *applicationUsecaseImpl) GetApplication(ctx context.Context, appName str } // DetailApplication detail application info -func (c *applicationUsecaseImpl) DetailApplication(ctx context.Context, app *model.Application) (*apisv1.DetailApplicationResponse, error) { +func (c *applicationServiceImpl) DetailApplication(ctx context.Context, app *model.Application) (*apisv1.DetailApplicationResponse, error) { var project *apisv1.ProjectBase if app.Project != "" { var err error - project, err = c.projectUsecase.DetailProject(ctx, app.Project) + project, err = c.ProjectService.DetailProject(ctx, app.Project) if err != nil { return nil, bcode.ErrProjectIsNotExist } } - base := c.convertAppModelToBase(app, []*apisv1.ProjectBase{project}) + base := assembler.ConvertAppModelToBase(app, []*apisv1.ProjectBase{project}) policies, err := c.queryApplicationPolicies(ctx, app) if err != nil { return nil, err } - componentNum, err := c.ds.Count(ctx, &model.ApplicationComponent{AppPrimaryKey: app.PrimaryKey()}, &datastore.FilterOptions{}) + componentNum, err := c.Store.Count(ctx, &model.ApplicationComponent{AppPrimaryKey: app.PrimaryKey()}, &datastore.FilterOptions{}) if err != nil { return nil, err } - envBindings, err := c.envBindingUsecase.GetEnvBindings(ctx, app) + envBindings, err := c.EnvBindingService.GetEnvBindings(ctx, app) if err != nil { return nil, err } @@ -308,13 +277,13 @@ func (c *applicationUsecaseImpl) DetailApplication(ctx context.Context, app *mod } // GetApplicationStatus get application status from controller cluster -func (c *applicationUsecaseImpl) GetApplicationStatus(ctx context.Context, appmodel *model.Application, envName string) (*common.AppStatus, error) { +func (c *applicationServiceImpl) GetApplicationStatus(ctx context.Context, appmodel *model.Application, envName string) (*common.AppStatus, error) { var app v1beta1.Application - env, err := c.envUsecase.GetEnv(ctx, envName) + env, err := c.EnvService.GetEnv(ctx, envName) if err != nil { return nil, err } - err = c.kubeClient.Get(ctx, types.NamespacedName{Namespace: env.Namespace, Name: appmodel.GetAppNameForSynced()}, &app) + err = c.KubeClient.Get(ctx, types.NamespacedName{Namespace: env.Namespace, Name: appmodel.GetAppNameForSynced()}, &app) if err != nil { if apierrors.IsNotFound(err) { return nil, nil @@ -328,11 +297,11 @@ func (c *applicationUsecaseImpl) GetApplicationStatus(ctx context.Context, appmo } // GetApplicationCR get application CR in cluster -func (c *applicationUsecaseImpl) GetApplicationCR(ctx context.Context, appModel *model.Application) (*v1beta1.ApplicationList, error) { +func (c *applicationServiceImpl) GetApplicationCR(ctx context.Context, appModel *model.Application) (*v1beta1.ApplicationList, error) { var apps v1beta1.ApplicationList if appModel.IsSynced() { var app v1beta1.Application - err := c.kubeClient.Get(ctx, types.NamespacedName{Namespace: appModel.GetAppNamespaceForSynced(), Name: appModel.GetAppNameForSynced()}, &app) + err := c.KubeClient.Get(ctx, types.NamespacedName{Namespace: appModel.GetAppNamespaceForSynced(), Name: appModel.GetAppNameForSynced()}, &app) if err != nil && !apierrors.IsNotFound(err) { return nil, err } @@ -347,7 +316,7 @@ func (c *applicationUsecaseImpl) GetApplicationCR(ctx context.Context, appModel return nil, err } selector = selector.Add(*re) - err = c.kubeClient.List(ctx, &apps, &client.ListOptions{ + err = c.KubeClient.List(ctx, &apps, &client.ListOptions{ LabelSelector: selector, }) if err != nil { @@ -360,13 +329,13 @@ func (c *applicationUsecaseImpl) GetApplicationCR(ctx context.Context, appModel } // PublishApplicationTemplate publish app template -func (c *applicationUsecaseImpl) PublishApplicationTemplate(ctx context.Context, app *model.Application) (*apisv1.ApplicationTemplateBase, error) { +func (c *applicationServiceImpl) PublishApplicationTemplate(ctx context.Context, app *model.Application) (*apisv1.ApplicationTemplateBase, error) { // TODO: return nil, nil } // CreateApplication create application -func (c *applicationUsecaseImpl) CreateApplication(ctx context.Context, req apisv1.CreateApplicationRequest) (*apisv1.ApplicationBase, error) { +func (c *applicationServiceImpl) CreateApplication(ctx context.Context, req apisv1.CreateApplicationRequest) (*apisv1.ApplicationBase, error) { application := model.Application{ Name: req.Name, Alias: req.Alias, @@ -375,7 +344,7 @@ func (c *applicationUsecaseImpl) CreateApplication(ctx context.Context, req apis Labels: req.Labels, } // check app name. - exist, err := c.ds.IsExist(ctx, &application) + exist, err := c.Store.IsExist(ctx, &application) if err != nil { log.Logger.Errorf("check application name is exist failure %s", err.Error()) return nil, bcode.ErrApplicationExist @@ -384,7 +353,7 @@ func (c *applicationUsecaseImpl) CreateApplication(ctx context.Context, req apis return nil, bcode.ErrApplicationExist } // check project - project, err := c.projectUsecase.DetailProject(ctx, req.Project) + project, err := c.ProjectService.DetailProject(ctx, req.Project) if err != nil { return nil, bcode.ErrProjectIsNotExist } @@ -408,25 +377,25 @@ func (c *applicationUsecaseImpl) CreateApplication(ctx context.Context, req apis Name: fmt.Sprintf("%s-%s", application.Name, "default"), PayloadType: model.PayloadTypeCustom, Type: apisv1.TriggerTypeWebhook, - WorkflowName: convertWorkflowName(req.EnvBinding[0].Name), + WorkflowName: repository.ConvertWorkflowName(req.EnvBinding[0].Name), }); err != nil { return nil, err } } // add application to db. - if err := c.ds.Add(ctx, &application); err != nil { + if err := c.Store.Add(ctx, &application); err != nil { if errors.Is(err, datastore.ErrRecordExist) { return nil, bcode.ErrApplicationExist } return nil, err } // render app base info. - base := c.convertAppModelToBase(&application, []*apisv1.ProjectBase{project}) + base := assembler.ConvertAppModelToBase(&application, []*apisv1.ProjectBase{project}) return base, nil } // CreateApplicationTrigger create application trigger -func (c *applicationUsecaseImpl) CreateApplicationTrigger(ctx context.Context, app *model.Application, req apisv1.CreateApplicationTriggerRequest) (*apisv1.ApplicationTriggerBase, error) { +func (c *applicationServiceImpl) CreateApplicationTrigger(ctx context.Context, app *model.Application, req apisv1.CreateApplicationTriggerRequest) (*apisv1.ApplicationTriggerBase, error) { trigger := &model.ApplicationTrigger{ AppPrimaryKey: app.Name, WorkflowName: req.WorkflowName, @@ -438,7 +407,7 @@ func (c *applicationUsecaseImpl) CreateApplicationTrigger(ctx context.Context, a ComponentName: req.ComponentName, Token: genWebhookToken(), } - if err := c.ds.Add(ctx, trigger); err != nil { + if err := c.Store.Add(ctx, trigger); err != nil { log.Logger.Errorf("failed to create application trigger, %s", err.Error()) return nil, err } @@ -458,12 +427,12 @@ func (c *applicationUsecaseImpl) CreateApplicationTrigger(ctx context.Context, a } // DeleteApplicationTrigger delete application trigger -func (c *applicationUsecaseImpl) DeleteApplicationTrigger(ctx context.Context, app *model.Application, token string) error { +func (c *applicationServiceImpl) DeleteApplicationTrigger(ctx context.Context, app *model.Application, token string) error { trigger := model.ApplicationTrigger{ AppPrimaryKey: app.PrimaryKey(), Token: token, } - if err := c.ds.Delete(ctx, &trigger); err != nil { + if err := c.Store.Delete(ctx, &trigger); err != nil { if errors.Is(err, datastore.ErrRecordNotExist) { return bcode.ErrApplicationTriggerNotExist } @@ -474,11 +443,11 @@ func (c *applicationUsecaseImpl) DeleteApplicationTrigger(ctx context.Context, a } // ListApplicationTrigger list application triggers -func (c *applicationUsecaseImpl) ListApplicationTriggers(ctx context.Context, app *model.Application) ([]*apisv1.ApplicationTriggerBase, error) { +func (c *applicationServiceImpl) ListApplicationTriggers(ctx context.Context, app *model.Application) ([]*apisv1.ApplicationTriggerBase, error) { trigger := &model.ApplicationTrigger{ AppPrimaryKey: app.Name, } - triggers, err := c.ds.List(ctx, trigger, &datastore.ListOptions{ + triggers, err := c.Store.List(ctx, trigger, &datastore.ListOptions{ SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderDescending}}}, ) if err != nil { @@ -507,19 +476,19 @@ func (c *applicationUsecaseImpl) ListApplicationTriggers(ctx context.Context, ap return resp, nil } -func (c *applicationUsecaseImpl) saveApplicationEnvBinding(ctx context.Context, app model.Application, envBindings []*apisv1.EnvBinding) error { - err := c.envBindingUsecase.BatchCreateEnvBinding(ctx, &app, envBindings) +func (c *applicationServiceImpl) saveApplicationEnvBinding(ctx context.Context, app model.Application, envBindings []*apisv1.EnvBinding) error { + err := c.EnvBindingService.BatchCreateEnvBinding(ctx, &app, envBindings) if err != nil { return err } return nil } -func (c *applicationUsecaseImpl) UpdateApplication(ctx context.Context, app *model.Application, req apisv1.UpdateApplicationRequest) (*apisv1.ApplicationBase, error) { +func (c *applicationServiceImpl) UpdateApplication(ctx context.Context, app *model.Application, req apisv1.UpdateApplicationRequest) (*apisv1.ApplicationBase, error) { var project *apisv1.ProjectBase if app.Project != "" { var err error - project, err = c.projectUsecase.DetailProject(ctx, app.Project) + project, err = c.ProjectService.DetailProject(ctx, app.Project) if err != nil { return nil, bcode.ErrProjectIsNotExist } @@ -528,25 +497,25 @@ func (c *applicationUsecaseImpl) UpdateApplication(ctx context.Context, app *mod app.Description = req.Description app.Labels = req.Labels app.Icon = req.Icon - if err := c.ds.Put(ctx, app); err != nil { + if err := c.Store.Put(ctx, app); err != nil { return nil, err } - return c.convertAppModelToBase(app, []*apisv1.ProjectBase{project}), nil + return assembler.ConvertAppModelToBase(app, []*apisv1.ProjectBase{project}), nil } // ListRecords list application record -func (c *applicationUsecaseImpl) ListRecords(ctx context.Context, appName string) (*apisv1.ListWorkflowRecordsResponse, error) { +func (c *applicationServiceImpl) ListRecords(ctx context.Context, appName string) (*apisv1.ListWorkflowRecordsResponse, error) { var record = model.WorkflowRecord{ AppPrimaryKey: appName, Finished: "false", } - records, err := c.ds.List(ctx, &record, &datastore.ListOptions{}) + records, err := c.Store.List(ctx, &record, &datastore.ListOptions{}) if err != nil { return nil, err } if len(records) == 0 { record.Finished = "true" - records, err = c.ds.List(ctx, &record, &datastore.ListOptions{ + records, err = c.Store.List(ctx, &record, &datastore.ListOptions{ Page: 1, PageSize: 1, SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderDescending}}, @@ -562,7 +531,7 @@ func (c *applicationUsecaseImpl) ListRecords(ctx context.Context, appName string for _, raw := range records { record, ok := raw.(*model.WorkflowRecord) if ok { - resp.Records = append(resp.Records, *convertFromRecordModel(record)) + resp.Records = append(resp.Records, *assembler.ConvertFromRecordModel(record)) } } resp.Total = int64(len(records)) @@ -570,11 +539,11 @@ func (c *applicationUsecaseImpl) ListRecords(ctx context.Context, appName string return resp, nil } -func (c *applicationUsecaseImpl) ListComponents(ctx context.Context, app *model.Application, op apisv1.ListApplicationComponentOptions) ([]*apisv1.ComponentBase, error) { +func (c *applicationServiceImpl) ListComponents(ctx context.Context, app *model.Application, op apisv1.ListApplicationComponentOptions) ([]*apisv1.ComponentBase, error) { var component = model.ApplicationComponent{ AppPrimaryKey: app.PrimaryKey(), } - components, err := c.ds.List(ctx, &component, &datastore.ListOptions{SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderDescending}}}) + components, err := c.Store.List(ctx, &component, &datastore.ListOptions{SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderDescending}}}) if err != nil { return nil, err } @@ -584,9 +553,9 @@ func (c *applicationUsecaseImpl) ListComponents(ctx context.Context, app *model. for _, component := range components { pm := component.(*model.ApplicationComponent) if !pm.Main { - list = append(list, convertComponentModelToBase(pm)) + list = append(list, assembler.ConvertComponentModelToBase(pm)) } else { - main = convertComponentModelToBase(pm) + main = assembler.ConvertComponentModelToBase(pm) } } // the main component must be first @@ -598,18 +567,18 @@ func (c *applicationUsecaseImpl) ListComponents(ctx context.Context, app *model. // DetailComponent detail app component // TODO: Add status data about the component. -func (c *applicationUsecaseImpl) DetailComponent(ctx context.Context, app *model.Application, compName string) (*apisv1.DetailComponentResponse, error) { +func (c *applicationServiceImpl) DetailComponent(ctx context.Context, app *model.Application, compName string) (*apisv1.DetailComponentResponse, error) { var component = model.ApplicationComponent{ AppPrimaryKey: app.PrimaryKey(), Name: compName, } - err := c.ds.Get(ctx, &component) + err := c.Store.Get(ctx, &component) if err != nil { return nil, err } var cd v1beta1.ComponentDefinition - if err := c.kubeClient.Get(ctx, types.NamespacedName{Name: component.Type, Namespace: velatypes.DefaultKubeVelaNS}, &cd); err != nil { - log.Logger.Warnf("component definition %s get failure. %s", component.Type, err.Error()) + if err := c.KubeClient.Get(ctx, types.NamespacedName{Name: component.Type, Namespace: velatypes.DefaultKubeVelaNS}, &cd); err != nil { + log.Logger.Warnf("component definition %s get failure. %s", utils2.Sanitize(component.Type), err.Error()) } return &apisv1.DetailComponentResponse{ @@ -619,23 +588,23 @@ func (c *applicationUsecaseImpl) DetailComponent(ctx context.Context, app *model } // ListPolicies list application policies -func (c *applicationUsecaseImpl) ListPolicies(ctx context.Context, app *model.Application) ([]*apisv1.PolicyBase, error) { +func (c *applicationServiceImpl) ListPolicies(ctx context.Context, app *model.Application) ([]*apisv1.PolicyBase, error) { policies, err := c.queryApplicationPolicies(ctx, app) if err != nil { return nil, err } var list []*apisv1.PolicyBase for _, policy := range policies { - list = append(list, convertPolicyModelToBase(policy)) + list = append(list, assembler.ConvertPolicyModelToBase(policy)) } return list, nil } -func (c *applicationUsecaseImpl) queryApplicationPolicies(ctx context.Context, app *model.Application) (list []*model.ApplicationPolicy, err error) { +func (c *applicationServiceImpl) queryApplicationPolicies(ctx context.Context, app *model.Application) (list []*model.ApplicationPolicy, err error) { var policy = model.ApplicationPolicy{ AppPrimaryKey: app.PrimaryKey(), } - policies, err := c.ds.List(ctx, &policy, &datastore.ListOptions{}) + policies, err := c.Store.List(ctx, &policy, &datastore.ListOptions{}) if err != nil { return nil, err } @@ -648,24 +617,24 @@ func (c *applicationUsecaseImpl) queryApplicationPolicies(ctx context.Context, a // DetailPolicy detail app policy // TODO: Add status data about the policy. -func (c *applicationUsecaseImpl) DetailPolicy(ctx context.Context, app *model.Application, policyName string) (*apisv1.DetailPolicyResponse, error) { +func (c *applicationServiceImpl) DetailPolicy(ctx context.Context, app *model.Application, policyName string) (*apisv1.DetailPolicyResponse, error) { var policy = model.ApplicationPolicy{ AppPrimaryKey: app.PrimaryKey(), Name: policyName, } - err := c.ds.Get(ctx, &policy) + err := c.Store.Get(ctx, &policy) if err != nil { return nil, err } return &apisv1.DetailPolicyResponse{ - PolicyBase: *convertPolicyModelToBase(&policy), + PolicyBase: *assembler.ConvertPolicyModelToBase(&policy), }, nil } // Deploy deploys app to cluster // means to render oam application config and apply to cluster. // An event record is generated for each deploy. -func (c *applicationUsecaseImpl) Deploy(ctx context.Context, app *model.Application, req apisv1.ApplicationDeployRequest) (*apisv1.ApplicationDeployResponse, error) { +func (c *applicationServiceImpl) Deploy(ctx context.Context, app *model.Application, req apisv1.ApplicationDeployRequest) (*apisv1.ApplicationDeployResponse, error) { var userName string if user := ctx.Value(&apisv1.CtxKeyUser); user != nil { if u, ok := user.(string); ok { @@ -682,7 +651,7 @@ func (c *applicationUsecaseImpl) Deploy(ctx context.Context, app *model.Applicat } configByte, _ := yaml.Marshal(oamApp) - workflow, err := c.workflowUsecase.GetWorkflow(ctx, app, oamApp.Annotations[oam.AnnotationWorkflowName]) + workflow, err := c.WorkflowService.GetWorkflow(ctx, app, oamApp.Annotations[oam.AnnotationWorkflowName]) if err != nil { return nil, err } @@ -698,7 +667,7 @@ func (c *applicationUsecaseImpl) Deploy(ctx context.Context, app *model.Applicat AppPrimaryKey: app.PrimaryKey(), EnvName: workflow.EnvName, } - list, err := c.ds.List(ctx, &lastVersion, &datastore.ListOptions{ + list, err := c.Store.List(ctx, &lastVersion, &datastore.ListOptions{ PageSize: 1, Page: 1, SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderDescending}}}) if err != nil && !errors.Is(err, datastore.ErrRecordNotExist) { log.Logger.Errorf("query app latest revision failure %s", err.Error()) @@ -712,7 +681,7 @@ func (c *applicationUsecaseImpl) Deploy(ctx context.Context, app *model.Applicat AppPrimaryKey: revision.AppPrimaryKey, Version: revision.RollbackVersion, } - if err := c.ds.Get(ctx, rollbackRevision); err == nil { + if err := c.Store.Get(ctx, rollbackRevision); err == nil { status = rollbackRevision.Status } } else { @@ -738,24 +707,24 @@ func (c *applicationUsecaseImpl) Deploy(ctx context.Context, app *model.Applicat CodeInfo: req.CodeInfo, ImageInfo: req.ImageInfo, } - if err := c.ds.Add(ctx, appRevision); err != nil { + if err := c.Store.Add(ctx, appRevision); err != nil { return nil, err } // step3: check and create namespace var namespace corev1.Namespace - if err := c.kubeClient.Get(ctx, types.NamespacedName{Name: oamApp.Namespace}, &namespace); apierrors.IsNotFound(err) { + if err := c.KubeClient.Get(ctx, types.NamespacedName{Name: oamApp.Namespace}, &namespace); apierrors.IsNotFound(err) { namespace.Name = oamApp.Namespace - if err := c.kubeClient.Create(ctx, &namespace); err != nil { + if err := c.KubeClient.Create(ctx, &namespace); err != nil { log.Logger.Errorf("auto create namespace failure %s", err.Error()) return nil, bcode.ErrCreateNamespace } } // step4: apply to controller cluster - err = c.apply.Apply(ctx, oamApp) + err = c.Apply.Apply(ctx, oamApp) if err != nil { appRevision.Status = model.RevisionStatusFail appRevision.Reason = err.Error() - if err := c.ds.Put(ctx, appRevision); err != nil { + if err := c.Store.Put(ctx, appRevision); err != nil { log.Logger.Warnf("update deploy event failure %s", err.Error()) } @@ -764,13 +733,13 @@ func (c *applicationUsecaseImpl) Deploy(ctx context.Context, app *model.Applicat } // step5: create workflow record - if err := c.workflowUsecase.CreateWorkflowRecord(ctx, app, oamApp, workflow); err != nil { + if err := c.WorkflowService.CreateWorkflowRecord(ctx, app, oamApp, workflow); err != nil { log.Logger.Warnf("create workflow record failure %s", err.Error()) } // step6: update app revision status appRevision.Status = model.RevisionStatusRunning - if err := c.ds.Put(ctx, appRevision); err != nil { + if err := c.Store.Put(ctx, appRevision); err != nil { log.Logger.Warnf("update app revision failure %s", err.Error()) } @@ -780,11 +749,11 @@ func (c *applicationUsecaseImpl) Deploy(ctx context.Context, app *model.Applicat } // sync configs to clusters -func (c *applicationUsecaseImpl) syncConfigs4Application(ctx context.Context, app *v1beta1.Application, projectName, envName string) error { +func (c *applicationServiceImpl) syncConfigs4Application(ctx context.Context, app *v1beta1.Application, projectName, envName string) error { var areTerraformComponents = true for _, m := range app.Spec.Components { d := &v1beta1.ComponentDefinition{} - if err := c.kubeClient.Get(ctx, client.ObjectKey{Namespace: velatypes.DefaultKubeVelaNS, Name: m.Type}, d); err != nil { + if err := c.KubeClient.Get(ctx, client.ObjectKey{Namespace: velatypes.DefaultKubeVelaNS, Name: m.Type}, d); err != nil { klog.ErrorS(err, "failed to get config type", "ComponentDefinition", m.Type) } // check the type of the componentDefinition is Terraform @@ -796,13 +765,13 @@ func (c *applicationUsecaseImpl) syncConfigs4Application(ctx context.Context, ap if areTerraformComponents { return nil } - env, err := c.envUsecase.GetEnv(ctx, envName) + env, err := c.EnvService.GetEnv(ctx, envName) if err != nil { return err } var clusterTargets []*model.ClusterTarget for _, t := range env.Targets { - target, err := c.targetUsecase.GetTarget(ctx, t) + target, err := c.TargetService.GetTarget(ctx, t) if err != nil { return err } @@ -811,24 +780,24 @@ func (c *applicationUsecaseImpl) syncConfigs4Application(ctx context.Context, ap } } - if err := SyncConfigs(ctx, c.kubeClient, projectName, clusterTargets); err != nil { + if err := SyncConfigs(ctx, c.KubeClient, projectName, clusterTargets); err != nil { return fmt.Errorf("sync config failure %w", err) } return nil } -func (c *applicationUsecaseImpl) renderOAMApplication(ctx context.Context, appModel *model.Application, reqWorkflowName, version string) (*v1beta1.Application, error) { +func (c *applicationServiceImpl) renderOAMApplication(ctx context.Context, appModel *model.Application, reqWorkflowName, version string) (*v1beta1.Application, error) { // Priority 1 uses the requested workflow as release . // Priority 2 uses the default workflow as release . var workflow *model.Workflow var err error if reqWorkflowName != "" { - workflow, err = c.workflowUsecase.GetWorkflow(ctx, appModel, reqWorkflowName) + workflow, err = c.WorkflowService.GetWorkflow(ctx, appModel, reqWorkflowName) if err != nil { return nil, err } } else { - workflow, err = c.workflowUsecase.GetApplicationDefaultWorkflow(ctx, appModel) + workflow, err = c.WorkflowService.GetApplicationDefaultWorkflow(ctx, appModel) if err != nil && !errors.Is(err, bcode.ErrWorkflowNoDefault) { return nil, err } @@ -836,7 +805,7 @@ func (c *applicationUsecaseImpl) renderOAMApplication(ctx context.Context, appMo if workflow == nil || workflow.EnvName == "" { return nil, bcode.ErrWorkflowNotExist } - env, err := c.envUsecase.GetEnv(ctx, workflow.EnvName) + env, err := c.EnvService.GetEnv(ctx, workflow.EnvName) if err != nil { return nil, err } @@ -865,7 +834,7 @@ func (c *applicationUsecaseImpl) renderOAMApplication(ctx context.Context, appMo }, } originalApp := &v1beta1.Application{} - if err := c.kubeClient.Get(ctx, types.NamespacedName{ + if err := c.KubeClient.Get(ctx, types.NamespacedName{ Name: appModel.Name, Namespace: env.Namespace, }, originalApp); err == nil { @@ -875,7 +844,7 @@ func (c *applicationUsecaseImpl) renderOAMApplication(ctx context.Context, appMo var component = model.ApplicationComponent{ AppPrimaryKey: appModel.PrimaryKey(), } - components, err := c.ds.List(ctx, &component, &datastore.ListOptions{}) + components, err := c.Store.List(ctx, &component, &datastore.ListOptions{}) if err != nil { return nil, err } @@ -887,7 +856,7 @@ func (c *applicationUsecaseImpl) renderOAMApplication(ctx context.Context, appMo var policy = model.ApplicationPolicy{ AppPrimaryKey: appModel.PrimaryKey(), } - policies, err := c.ds.List(ctx, &policy, &datastore.ListOptions{ + policies, err := c.Store.List(ctx, &policy, &datastore.ListOptions{ FilterOptions: datastore.FilterOptions{ IsNotExist: []datastore.IsNotExistQueryOption{{ Key: "envName", @@ -899,7 +868,7 @@ func (c *applicationUsecaseImpl) renderOAMApplication(ctx context.Context, appMo return nil, err } policy.EnvName = env.Name - envPolicies, err := c.ds.List(ctx, &policy, &datastore.ListOptions{}) + envPolicies, err := c.Store.List(ctx, &policy, &datastore.ListOptions{}) if err != nil { return nil, err } @@ -967,52 +936,16 @@ func (c *applicationUsecaseImpl) renderOAMApplication(ctx context.Context, appMo return app, nil } -func (c *applicationUsecaseImpl) convertAppModelToBase(app *model.Application, projects []*apisv1.ProjectBase) *apisv1.ApplicationBase { - appBase := &apisv1.ApplicationBase{ - Name: app.Name, - Alias: app.Alias, - CreateTime: app.CreateTime, - UpdateTime: app.UpdateTime, - Description: app.Description, - Icon: app.Icon, - Labels: app.Labels, - Project: &apisv1.ProjectBase{Name: app.Project}, - } - if app.IsSynced() { - appBase.ReadOnly = true - } - for _, project := range projects { - if project.Name == app.Project { - appBase.Project = project - } - } - return appBase -} - -func (c *applicationUsecaseImpl) convertRevisionModelToBase(ctx context.Context, revision *model.ApplicationRevision) apisv1.ApplicationRevisionBase { - base := apisv1.ApplicationRevisionBase{ - Version: revision.Version, - Status: revision.Status, - Reason: revision.Reason, - Note: revision.Note, - TriggerType: revision.TriggerType, - CreateTime: revision.CreateTime, - EnvName: revision.EnvName, - CodeInfo: revision.CodeInfo, - ImageInfo: revision.ImageInfo, - } +func (c *applicationServiceImpl) convertRevisionModelToBase(ctx context.Context, revision *model.ApplicationRevision) apisv1.ApplicationRevisionBase { + var deployUser *model.User if revision.DeployUser != "" { - base.DeployUser = &apisv1.NameAlias{Name: revision.DeployUser} - deployUser, _ := c.userUsecase.GetUser(ctx, revision.DeployUser) - if deployUser != nil { - base.DeployUser.Alias = deployUser.Alias - } + deployUser, _ = c.UserService.GetUser(ctx, revision.DeployUser) } - return base + return assembler.ConvertRevisionModelToBase(revision, deployUser) } // DeleteApplication delete application -func (c *applicationUsecaseImpl) DeleteApplication(ctx context.Context, app *model.Application) error { +func (c *applicationServiceImpl) DeleteApplication(ctx context.Context, app *model.Application) error { crs, err := c.GetApplicationCR(ctx, app) if err != nil { return err @@ -1034,7 +967,7 @@ func (c *applicationUsecaseImpl) DeleteApplication(ctx context.Context, app *mod var revision = model.ApplicationRevision{ AppPrimaryKey: app.PrimaryKey(), } - revisions, err := c.ds.List(ctx, &revision, &datastore.ListOptions{}) + revisions, err := c.Store.List(ctx, &revision, &datastore.ListOptions{}) if err != nil { return err } @@ -1045,19 +978,19 @@ func (c *applicationUsecaseImpl) DeleteApplication(ctx context.Context, app *mod } // delete workflow - if err := c.workflowUsecase.DeleteWorkflowByApp(ctx, app); err != nil && !errors.Is(err, bcode.ErrWorkflowNotExist) { + if err := c.WorkflowService.DeleteWorkflowByApp(ctx, app); err != nil && !errors.Is(err, bcode.ErrWorkflowNotExist) { log.Logger.Errorf("delete workflow %s failure %s", app.Name, err.Error()) } for _, component := range components { - err := c.ds.Delete(ctx, &model.ApplicationComponent{AppPrimaryKey: app.PrimaryKey(), Name: component.Name}) + err := c.Store.Delete(ctx, &model.ApplicationComponent{AppPrimaryKey: app.PrimaryKey(), Name: component.Name}) if err != nil && !errors.Is(err, datastore.ErrRecordNotExist) { log.Logger.Errorf("delete component %s in app %s failure %s", component.Name, app.Name, err.Error()) } } for _, policy := range policies { - err := c.ds.Delete(ctx, &model.ApplicationPolicy{AppPrimaryKey: app.PrimaryKey(), Name: policy.Name}) + err := c.Store.Delete(ctx, &model.ApplicationPolicy{AppPrimaryKey: app.PrimaryKey(), Name: policy.Name}) if err != nil && errors.Is(err, datastore.ErrRecordNotExist) { log.Logger.Errorf("delete policy %s in app %s failure %s", policy.Name, app.Name, err.Error()) } @@ -1065,30 +998,30 @@ func (c *applicationUsecaseImpl) DeleteApplication(ctx context.Context, app *mod for _, entity := range revisions { revision := entity.(*model.ApplicationRevision) - if err := c.ds.Delete(ctx, &model.ApplicationRevision{AppPrimaryKey: app.PrimaryKey(), Version: revision.Version}); err != nil { + if err := c.Store.Delete(ctx, &model.ApplicationRevision{AppPrimaryKey: app.PrimaryKey(), Version: revision.Version}); err != nil { log.Logger.Errorf("delete revision %s in app %s failure %s", revision.Version, app.Name, err.Error()) } } for _, trigger := range triggers { - if err := c.ds.Delete(ctx, &model.ApplicationTrigger{AppPrimaryKey: app.PrimaryKey(), Name: trigger.Name, Token: trigger.Token}); err != nil { + if err := c.Store.Delete(ctx, &model.ApplicationTrigger{AppPrimaryKey: app.PrimaryKey(), Name: trigger.Name, Token: trigger.Token}); err != nil { log.Logger.Errorf("delete trigger %s in app %s failure %s", trigger.Name, app.Name, err.Error()) } } - if err := c.envBindingUsecase.BatchDeleteEnvBinding(ctx, app); err != nil { + if err := c.EnvBindingService.BatchDeleteEnvBinding(ctx, app); err != nil { log.Logger.Errorf("delete envbindings in app %s failure %s", app.Name, err.Error()) } - return c.ds.Delete(ctx, app) + return c.Store.Delete(ctx, app) } -func (c *applicationUsecaseImpl) GetApplicationComponent(ctx context.Context, app *model.Application, componentName string) (*model.ApplicationComponent, error) { +func (c *applicationServiceImpl) GetApplicationComponent(ctx context.Context, app *model.Application, componentName string) (*model.ApplicationComponent, error) { var component = model.ApplicationComponent{ AppPrimaryKey: app.PrimaryKey(), Name: componentName, } - err := c.ds.Get(ctx, &component) + err := c.Store.Get(ctx, &component) if err != nil { if errors.Is(err, datastore.ErrRecordNotExist) { return nil, bcode.ErrApplicationComponentNotExist @@ -1098,7 +1031,7 @@ func (c *applicationUsecaseImpl) GetApplicationComponent(ctx context.Context, ap return &component, nil } -func (c *applicationUsecaseImpl) UpdateComponent(ctx context.Context, app *model.Application, component *model.ApplicationComponent, req apisv1.UpdateApplicationComponentRequest) (*apisv1.ComponentBase, error) { +func (c *applicationServiceImpl) UpdateComponent(ctx context.Context, app *model.Application, component *model.ApplicationComponent, req apisv1.UpdateApplicationComponentRequest) (*apisv1.ComponentBase, error) { if req.Alias != nil { component.Alias = *req.Alias } @@ -1121,16 +1054,16 @@ func (c *applicationUsecaseImpl) UpdateComponent(ctx context.Context, app *model } component.Properties = properties } - if err := c.ds.Put(ctx, component); err != nil { + if err := c.Store.Put(ctx, component); err != nil { return nil, err } - return convertComponentModelToBase(component), nil + return assembler.ConvertComponentModelToBase(component), nil } -func (c *applicationUsecaseImpl) createComponent(ctx context.Context, app *model.Application, com apisv1.CreateComponentRequest, main bool) (*apisv1.ComponentBase, error) { +func (c *applicationServiceImpl) createComponent(ctx context.Context, app *model.Application, com apisv1.CreateComponentRequest, main bool) (*apisv1.ComponentBase, error) { var cd v1beta1.ComponentDefinition - if err := c.kubeClient.Get(ctx, types.NamespacedName{Name: com.ComponentType, Namespace: velatypes.DefaultKubeVelaNS}, &cd); err != nil { - log.Logger.Warnf("component definition %s get failure. %s", com.ComponentType, err.Error()) + if err := c.KubeClient.Get(ctx, types.NamespacedName{Name: com.ComponentType, Namespace: velatypes.DefaultKubeVelaNS}, &cd); err != nil { + log.Logger.Warnf("component definition %s get failure. %s", utils2.Sanitize(com.ComponentType), err.Error()) return nil, bcode.ErrComponentTypeNotSupport } userName, _ := ctx.Value(&apisv1.CtxKeyUser).(string) @@ -1181,7 +1114,7 @@ func (c *applicationUsecaseImpl) createComponent(ctx context.Context, app *model c.initCreateDefaultTrait(&componentModel) } - if err := c.ds.Add(ctx, &componentModel); err != nil { + if err := c.Store.Add(ctx, &componentModel); err != nil { if errors.Is(err, datastore.ErrRecordExist) { return nil, bcode.ErrApplicationComponentExist } @@ -1189,18 +1122,18 @@ func (c *applicationUsecaseImpl) createComponent(ctx context.Context, app *model return nil, err } // update the env workflow, the automatically generated workflow is determined by the component type. - if err := UpdateAppEnvWorkflow(ctx, c.kubeClient, c.ds, app); err != nil { + if err := repository.UpdateAppEnvWorkflow(ctx, c.KubeClient, c.Store, app); err != nil { return nil, bcode.ErrEnvBindingUpdateWorkflow } - return convertComponentModelToBase(&componentModel), nil + return assembler.ConvertComponentModelToBase(&componentModel), nil } -func (c *applicationUsecaseImpl) CreateComponent(ctx context.Context, app *model.Application, com apisv1.CreateComponentRequest) (*apisv1.ComponentBase, error) { +func (c *applicationServiceImpl) CreateComponent(ctx context.Context, app *model.Application, com apisv1.CreateComponentRequest) (*apisv1.ComponentBase, error) { return c.createComponent(ctx, app, com, false) } -func (c *applicationUsecaseImpl) initCreateDefaultTrait(component *model.ApplicationComponent) { +func (c *applicationServiceImpl) initCreateDefaultTrait(component *model.ApplicationComponent) { replicationTrait := model.ApplicationTrait{ Alias: "Set Replicas", Type: "scaler", @@ -1218,59 +1151,24 @@ func (c *applicationUsecaseImpl) initCreateDefaultTrait(component *model.Applica component.Traits = initTraits } -func convertComponentModelToBase(componentModel *model.ApplicationComponent) *apisv1.ComponentBase { - if componentModel == nil { - return nil - } - return &apisv1.ComponentBase{ - Name: componentModel.Name, - Alias: componentModel.Alias, - Description: componentModel.Description, - Labels: componentModel.Labels, - ComponentType: componentModel.Type, - Icon: componentModel.Icon, - DependsOn: componentModel.DependsOn, - Inputs: componentModel.Inputs, - Outputs: componentModel.Outputs, - Creator: componentModel.Creator, - Main: componentModel.Main, - CreateTime: componentModel.CreateTime, - UpdateTime: componentModel.UpdateTime, - Traits: func() (traits []*apisv1.ApplicationTrait) { - for _, trait := range componentModel.Traits { - traits = append(traits, &apisv1.ApplicationTrait{ - Type: trait.Type, - Properties: trait.Properties, - Alias: trait.Alias, - Description: trait.Description, - CreateTime: trait.CreateTime, - UpdateTime: trait.UpdateTime, - }) - } - return - }(), - WorkloadType: componentModel.WorkloadType, - } -} - -func (c *applicationUsecaseImpl) DeleteComponent(ctx context.Context, app *model.Application, component *model.ApplicationComponent) error { +func (c *applicationServiceImpl) DeleteComponent(ctx context.Context, app *model.Application, component *model.ApplicationComponent) error { if component.Main { return bcode.ErrApplicationComponentNotAllowDelete } - if err := c.ds.Delete(ctx, component); err != nil { + if err := c.Store.Delete(ctx, component); err != nil { if errors.Is(err, datastore.ErrRecordNotExist) { return bcode.ErrApplicationComponentNotExist } log.Logger.Warnf("delete app component %s failure %s", app.PrimaryKey(), err.Error()) return err } - if err := UpdateAppEnvWorkflow(ctx, c.kubeClient, c.ds, app); err != nil { + if err := repository.UpdateAppEnvWorkflow(ctx, c.KubeClient, c.Store, app); err != nil { return bcode.ErrEnvBindingUpdateWorkflow } return nil } -func (c *applicationUsecaseImpl) CreatePolicy(ctx context.Context, app *model.Application, createpolicy apisv1.CreatePolicyRequest) (*apisv1.PolicyBase, error) { +func (c *applicationServiceImpl) CreatePolicy(ctx context.Context, app *model.Application, createpolicy apisv1.CreatePolicyRequest) (*apisv1.PolicyBase, error) { userName, _ := ctx.Value(&apisv1.CtxKeyUser).(string) policyModel := model.ApplicationPolicy{ AppPrimaryKey: app.PrimaryKey(), @@ -1284,22 +1182,22 @@ func (c *applicationUsecaseImpl) CreatePolicy(ctx context.Context, app *model.Ap return nil, bcode.ErrInvalidProperties } policyModel.Properties = properties - if err := c.ds.Add(ctx, &policyModel); err != nil { + if err := c.Store.Add(ctx, &policyModel); err != nil { if errors.Is(err, datastore.ErrRecordExist) { return nil, bcode.ErrApplicationPolicyExist } log.Logger.Warnf("add policy for app %s failure %s", app.PrimaryKey(), err.Error()) return nil, err } - return convertPolicyModelToBase(&policyModel), nil + return assembler.ConvertPolicyModelToBase(&policyModel), nil } -func (c *applicationUsecaseImpl) DeletePolicy(ctx context.Context, app *model.Application, policyName string) error { +func (c *applicationServiceImpl) DeletePolicy(ctx context.Context, app *model.Application, policyName string) error { var policy = model.ApplicationPolicy{ AppPrimaryKey: app.PrimaryKey(), Name: policyName, } - if err := c.ds.Delete(ctx, &policy); err != nil { + if err := c.Store.Delete(ctx, &policy); err != nil { if errors.Is(err, datastore.ErrRecordNotExist) { return bcode.ErrApplicationPolicyNotExist } @@ -1309,12 +1207,12 @@ func (c *applicationUsecaseImpl) DeletePolicy(ctx context.Context, app *model.Ap return nil } -func (c *applicationUsecaseImpl) UpdatePolicy(ctx context.Context, app *model.Application, policyName string, policyUpdate apisv1.UpdatePolicyRequest) (*apisv1.DetailPolicyResponse, error) { +func (c *applicationServiceImpl) UpdatePolicy(ctx context.Context, app *model.Application, policyName string, policyUpdate apisv1.UpdatePolicyRequest) (*apisv1.DetailPolicyResponse, error) { var policy = model.ApplicationPolicy{ AppPrimaryKey: app.PrimaryKey(), Name: policyName, } - err := c.ds.Get(ctx, &policy) + err := c.Store.Get(ctx, &policy) if err != nil { if errors.Is(err, datastore.ErrRecordNotExist) { return nil, bcode.ErrApplicationPolicyNotExist @@ -1330,20 +1228,20 @@ func (c *applicationUsecaseImpl) UpdatePolicy(ctx context.Context, app *model.Ap policy.Properties = properties policy.Description = policyUpdate.Description - if err := c.ds.Put(ctx, &policy); err != nil { + if err := c.Store.Put(ctx, &policy); err != nil { return nil, err } return &apisv1.DetailPolicyResponse{ - PolicyBase: *convertPolicyModelToBase(&policy), + PolicyBase: *assembler.ConvertPolicyModelToBase(&policy), }, nil } -func (c *applicationUsecaseImpl) CreateApplicationTrait(ctx context.Context, app *model.Application, component *model.ApplicationComponent, req apisv1.CreateApplicationTraitRequest) (*apisv1.ApplicationTrait, error) { +func (c *applicationServiceImpl) CreateApplicationTrait(ctx context.Context, app *model.Application, component *model.ApplicationComponent, req apisv1.CreateApplicationTraitRequest) (*apisv1.ApplicationTrait, error) { var comp = model.ApplicationComponent{ AppPrimaryKey: app.PrimaryKey(), Name: component.Name, } - if err := c.ds.Get(ctx, &comp); err != nil { + if err := c.Store.Get(ctx, &comp); err != nil { return nil, err } for _, trait := range comp.Traits { @@ -1358,24 +1256,24 @@ func (c *applicationUsecaseImpl) CreateApplicationTrait(ctx context.Context, app } trait := model.ApplicationTrait{CreateTime: time.Now(), Type: req.Type, Properties: properties, Alias: req.Alias, Description: req.Description} comp.Traits = append(comp.Traits, trait) - if err := c.ds.Put(ctx, &comp); err != nil { + if err := c.Store.Put(ctx, &comp); err != nil { return nil, err } return &apisv1.ApplicationTrait{Type: trait.Type, Properties: properties, Alias: req.Alias, Description: req.Description, CreateTime: trait.CreateTime, UpdateTime: trait.UpdateTime}, nil } -func (c *applicationUsecaseImpl) DeleteApplicationTrait(ctx context.Context, app *model.Application, component *model.ApplicationComponent, traitType string) error { +func (c *applicationServiceImpl) DeleteApplicationTrait(ctx context.Context, app *model.Application, component *model.ApplicationComponent, traitType string) error { var comp = model.ApplicationComponent{ AppPrimaryKey: app.PrimaryKey(), Name: component.Name, } - if err := c.ds.Get(ctx, &comp); err != nil { + if err := c.Store.Get(ctx, &comp); err != nil { return err } for i, trait := range comp.Traits { if trait.Type == traitType { comp.Traits = append(comp.Traits[:i], comp.Traits[i+1:]...) - if err := c.ds.Put(ctx, &comp); err != nil { + if err := c.Store.Put(ctx, &comp); err != nil { return err } return nil @@ -1384,12 +1282,12 @@ func (c *applicationUsecaseImpl) DeleteApplicationTrait(ctx context.Context, app return bcode.ErrTraitNotExist } -func (c *applicationUsecaseImpl) UpdateApplicationTrait(ctx context.Context, app *model.Application, component *model.ApplicationComponent, traitType string, req apisv1.UpdateApplicationTraitRequest) (*apisv1.ApplicationTrait, error) { +func (c *applicationServiceImpl) UpdateApplicationTrait(ctx context.Context, app *model.Application, component *model.ApplicationComponent, traitType string, req apisv1.UpdateApplicationTraitRequest) (*apisv1.ApplicationTrait, error) { var comp = model.ApplicationComponent{ AppPrimaryKey: app.PrimaryKey(), Name: component.Name, } - if err := c.ds.Get(ctx, &comp); err != nil { + if err := c.Store.Get(ctx, &comp); err != nil { return nil, err } for i, trait := range comp.Traits { @@ -1401,7 +1299,7 @@ func (c *applicationUsecaseImpl) UpdateApplicationTrait(ctx context.Context, app } updatedTrait := model.ApplicationTrait{CreateTime: trait.CreateTime, UpdateTime: time.Now(), Properties: properties, Type: traitType, Alias: req.Alias, Description: req.Description} comp.Traits[i] = updatedTrait - if err := c.ds.Put(ctx, &comp); err != nil { + if err := c.Store.Put(ctx, &comp); err != nil { return nil, err } return &apisv1.ApplicationTrait{Type: trait.Type, Properties: properties, @@ -1411,7 +1309,7 @@ func (c *applicationUsecaseImpl) UpdateApplicationTrait(ctx context.Context, app return nil, bcode.ErrTraitNotExist } -func (c *applicationUsecaseImpl) ListRevisions(ctx context.Context, appName, envName, status string, page, pageSize int) (*apisv1.ListRevisionsResponse, error) { +func (c *applicationServiceImpl) ListRevisions(ctx context.Context, appName, envName, status string, page, pageSize int) (*apisv1.ListRevisionsResponse, error) { var revision = model.ApplicationRevision{ AppPrimaryKey: appName, } @@ -1422,7 +1320,7 @@ func (c *applicationUsecaseImpl) ListRevisions(ctx context.Context, appName, env revision.Status = status } - revisions, err := c.ds.List(ctx, &revision, &datastore.ListOptions{ + revisions, err := c.Store.List(ctx, &revision, &datastore.ListOptions{ Page: page, PageSize: pageSize, SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderDescending}}, @@ -1440,7 +1338,7 @@ func (c *applicationUsecaseImpl) ListRevisions(ctx context.Context, appName, env resp.Revisions = append(resp.Revisions, c.convertRevisionModelToBase(ctx, r)) } } - count, err := c.ds.Count(ctx, &revision, nil) + count, err := c.Store.Count(ctx, &revision, nil) if err != nil { return nil, err } @@ -1449,12 +1347,12 @@ func (c *applicationUsecaseImpl) ListRevisions(ctx context.Context, appName, env return resp, nil } -func (c *applicationUsecaseImpl) DetailRevision(ctx context.Context, appName, revisionVersion string) (*apisv1.DetailRevisionResponse, error) { +func (c *applicationServiceImpl) DetailRevision(ctx context.Context, appName, revisionVersion string) (*apisv1.DetailRevisionResponse, error) { var revision = model.ApplicationRevision{ AppPrimaryKey: appName, Version: revisionVersion, } - if err := c.ds.Get(ctx, &revision); err != nil { + if err := c.Store.Get(ctx, &revision); err != nil { return nil, err } @@ -1466,7 +1364,7 @@ func (c *applicationUsecaseImpl) DetailRevision(ctx context.Context, appName, re } if revision.DeployUser != "" { - deployUser, _ := c.userUsecase.GetUser(ctx, revision.DeployUser) + deployUser, _ := c.UserService.GetUser(ctx, revision.DeployUser) if deployUser != nil { resp.DeployUser.Alias = deployUser.Alias } @@ -1475,9 +1373,9 @@ func (c *applicationUsecaseImpl) DetailRevision(ctx context.Context, appName, re return resp, nil } -func (c *applicationUsecaseImpl) Statistics(ctx context.Context, app *model.Application) (*apisv1.ApplicationStatisticsResponse, error) { +func (c *applicationServiceImpl) Statistics(ctx context.Context, app *model.Application) (*apisv1.ApplicationStatisticsResponse, error) { var targetMap = make(map[string]int) - envbinding, err := c.envBindingUsecase.GetEnvBindings(ctx, app) + envbinding, err := c.EnvBindingService.GetEnvBindings(ctx, app) if err != nil { log.Logger.Errorf("query app envbinding failure %s", err.Error()) } @@ -1486,7 +1384,7 @@ func (c *applicationUsecaseImpl) Statistics(ctx context.Context, app *model.Appl targetMap[target]++ } } - count, err := c.ds.Count(ctx, &model.ApplicationRevision{AppPrimaryKey: app.PrimaryKey()}, &datastore.FilterOptions{}) + count, err := c.Store.Count(ctx, &model.ApplicationRevision{AppPrimaryKey: app.PrimaryKey()}, &datastore.FilterOptions{}) if err != nil { return nil, err } @@ -1494,15 +1392,15 @@ func (c *applicationUsecaseImpl) Statistics(ctx context.Context, app *model.Appl EnvCount: int64(len(envbinding)), TargetCount: int64(len(targetMap)), RevisionCount: count, - WorkflowCount: c.workflowUsecase.CountWorkflow(ctx, app), + WorkflowCount: c.WorkflowService.CountWorkflow(ctx, app), }, nil } // CompareAppWithLatestRevision compare application with last revision -func (c *applicationUsecaseImpl) CompareAppWithLatestRevision(ctx context.Context, appModel *model.Application, compareReq apisv1.AppCompareReq) (*apisv1.AppCompareResponse, error) { +func (c *applicationServiceImpl) CompareAppWithLatestRevision(ctx context.Context, appModel *model.Application, compareReq apisv1.AppCompareReq) (*apisv1.AppCompareResponse, error) { var reqWorkflowName string if compareReq.Env != "" { - reqWorkflowName = convertWorkflowName(compareReq.Env) + reqWorkflowName = repository.ConvertWorkflowName(compareReq.Env) } newApp, err := c.renderOAMApplication(ctx, appModel, reqWorkflowName, "") if err != nil { @@ -1529,8 +1427,8 @@ func (c *applicationUsecaseImpl) CompareAppWithLatestRevision(ctx context.Contex args := common2.Args{ Schema: common2.Scheme, } - _ = args.SetConfig(c.kubeConfig) - args.SetClient(c.kubeClient) + _ = args.SetConfig(c.KubeConfig) + args.SetClient(c.KubeClient) diffResult, buff, err := compare(ctx, args, newApp, oldApp) if err != nil { log.Logger.Errorf("fail to compare the app %s", err.Error()) @@ -1540,7 +1438,7 @@ func (c *applicationUsecaseImpl) CompareAppWithLatestRevision(ctx context.Contex } // ResetAppToLatestRevision reset app's component to last revision -func (c *applicationUsecaseImpl) ResetAppToLatestRevision(ctx context.Context, appName string) (*apisv1.AppResetResponse, error) { +func (c *applicationServiceImpl) ResetAppToLatestRevision(ctx context.Context, appName string) (*apisv1.AppResetResponse, error) { targetApp, err := c.getAppFromLatestRevision(ctx, appName, "", "") if err != nil { return nil, err @@ -1549,13 +1447,13 @@ func (c *applicationUsecaseImpl) ResetAppToLatestRevision(ctx context.Context, a } // DryRunAppOrRevision dry-run application or revision -func (c *applicationUsecaseImpl) DryRunAppOrRevision(ctx context.Context, appModel *model.Application, dryRunReq apisv1.AppDryRunReq) (*apisv1.AppDryRunResponse, error) { +func (c *applicationServiceImpl) DryRunAppOrRevision(ctx context.Context, appModel *model.Application, dryRunReq apisv1.AppDryRunReq) (*apisv1.AppDryRunResponse, error) { var app *v1beta1.Application var err error if dryRunReq.DryRunType == "APP" { var reqWorkflowName string if dryRunReq.Env != "" { - reqWorkflowName = convertWorkflowName(dryRunReq.Env) + reqWorkflowName = repository.ConvertWorkflowName(dryRunReq.Env) } app, err = c.renderOAMApplication(ctx, appModel, reqWorkflowName, "") if err != nil { @@ -1570,8 +1468,8 @@ func (c *applicationUsecaseImpl) DryRunAppOrRevision(ctx context.Context, appMod args := common2.Args{ Schema: common2.Scheme, } - _ = args.SetConfig(c.kubeConfig) - args.SetClient(c.kubeClient) + _ = args.SetConfig(c.KubeConfig) + args.SetClient(c.KubeClient) dryRunResult, err := dryRunApplication(ctx, args, app) if err != nil { return nil, err @@ -1579,14 +1477,6 @@ func (c *applicationUsecaseImpl) DryRunAppOrRevision(ctx context.Context, appMod return &apisv1.AppDryRunResponse{YAML: dryRunResult.String()}, nil } -func genPolicyName(envName string) string { - return fmt.Sprintf("%s-%s", EnvBindingPolicyDefaultName, envName) -} - -func genPolicyEnvName(targetName string) string { - return targetName -} - func genWebhookToken() string { rand.Seed(time.Now().UnixNano()) runes := []rune("abcdefghijklmnopqrstuvwxyz0123456789") @@ -1598,7 +1488,7 @@ func genWebhookToken() string { return string(b) } -func (c *applicationUsecaseImpl) getAppFromLatestRevision(ctx context.Context, appName string, envName string, version string) (*v1beta1.Application, error) { +func (c *applicationServiceImpl) getAppFromLatestRevision(ctx context.Context, appName string, envName string, version string) (*v1beta1.Application, error) { ar := &model.ApplicationRevision{AppPrimaryKey: appName} if envName != "" { @@ -1607,7 +1497,7 @@ func (c *applicationUsecaseImpl) getAppFromLatestRevision(ctx context.Context, a if version != "" { ar.Version = version } - revisions, err := c.ds.List(ctx, ar, &datastore.ListOptions{ + revisions, err := c.Store.List(ctx, ar, &datastore.ListOptions{ Page: 1, PageSize: 1, SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderDescending}}, @@ -1627,10 +1517,10 @@ func (c *applicationUsecaseImpl) getAppFromLatestRevision(ctx context.Context, a return oldApp, nil } -func (c *applicationUsecaseImpl) resetApp(ctx context.Context, targetApp *v1beta1.Application) (*apisv1.AppResetResponse, error) { +func (c *applicationServiceImpl) resetApp(ctx context.Context, targetApp *v1beta1.Application) (*apisv1.AppResetResponse, error) { appPrimaryKey := targetApp.Name - originComps, err := c.ds.List(ctx, &model.ApplicationComponent{AppPrimaryKey: appPrimaryKey}, &datastore.ListOptions{}) + originComps, err := c.Store.List(ctx, &model.ApplicationComponent{AppPrimaryKey: appPrimaryKey}, &datastore.ListOptions{}) if err != nil { return nil, bcode.ErrApplicationComponentNotExist } @@ -1655,7 +1545,7 @@ func (c *applicationUsecaseImpl) resetApp(ctx context.Context, targetApp *v1beta AppPrimaryKey: appPrimaryKey, Name: compName, } - if err := c.ds.Delete(ctx, &component); err != nil { + if err := c.Store.Delete(ctx, &component); err != nil { if errors.Is(err, datastore.ErrRecordNotExist) { continue } @@ -1675,9 +1565,9 @@ func (c *applicationUsecaseImpl) resetApp(ctx context.Context, targetApp *v1beta return &apisv1.AppResetResponse{}, bcode.ErrInvalidProperties } compModel.Properties = properties - if err := c.ds.Add(ctx, &compModel); err != nil { + if err := c.Store.Add(ctx, &compModel); err != nil { if errors.Is(err, datastore.ErrRecordExist) { - err := c.ds.Put(ctx, &compModel) + err := c.Store.Put(ctx, &compModel) if err != nil { log.Logger.Warnf("update comp %s for app %s failure %s", comp.Name, utils2.Sanitize(appPrimaryKey), err.Error()) } diff --git a/pkg/apiserver/rest/usecase/application_test.go b/pkg/apiserver/domain/service/application_test.go similarity index 69% rename from pkg/apiserver/rest/usecase/application_test.go rename to pkg/apiserver/domain/service/application_test.go index 9de6a3692..2ba8dc373 100644 --- a/pkg/apiserver/rest/usecase/application_test.go +++ b/pkg/apiserver/domain/service/application_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package usecase +package service import ( "context" @@ -36,27 +36,28 @@ import ( "github.com/oam-dev/kubevela/apis/core.oam.dev/common" "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" "github.com/oam-dev/kubevela/apis/types" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/model" - v1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/repository" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" + v1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" "github.com/oam-dev/kubevela/pkg/oam" "github.com/oam-dev/kubevela/pkg/oam/util" "github.com/oam-dev/kubevela/pkg/utils/apply" ) -var _ = Describe("Test application usecase function", func() { +var _ = Describe("Test application service function", func() { var ( - rbacUsecase *rbacUsecaseImpl - appUsecase *applicationUsecaseImpl - workflowUsecase *workflowUsecaseImpl - envUsecase *envUsecaseImpl - envBindingUsecase *envBindingUsecaseImpl - targetUsecase *targetUsecaseImpl - definitionUsecase *definitionUsecaseImpl - projectUsecase *projectUsecaseImpl - userUsecase *userUsecaseImpl + rbacService *rbacServiceImpl + appService *applicationServiceImpl + workflowService *workflowServiceImpl + envService *envServiceImpl + envBindingService *envBindingServiceImpl + targetService *targetServiceImpl + definitionService *definitionServiceImpl + projectService *projectServiceImpl + userService *userServiceImpl testProject = "app-project" testApp = "test-app" defaultTarget = "default" @@ -72,26 +73,26 @@ var _ = Describe("Test application usecase function", func() { ds, err := NewDatastore(datastore.Config{Type: "kubeapi", Database: "app-test-kubevela"}) Expect(ds).ToNot(BeNil()) Expect(err).Should(BeNil()) - rbacUsecase = &rbacUsecaseImpl{ds: ds} - userUsecase = &userUsecaseImpl{ds: ds, k8sClient: k8sClient} - projectUsecase = &projectUsecaseImpl{ds: ds, k8sClient: k8sClient, rbacUsecase: rbacUsecase} - envUsecase = &envUsecaseImpl{ds: ds, kubeClient: k8sClient, projectUsecase: projectUsecase} - workflowUsecase = &workflowUsecaseImpl{ds: ds, envUsecase: envUsecase} - definitionUsecase = &definitionUsecaseImpl{kubeClient: k8sClient, caches: utils.NewMemoryCacheStore(context.Background())} - envBindingUsecase = &envBindingUsecaseImpl{ds: ds, envUsecase: envUsecase, workflowUsecase: workflowUsecase, kubeClient: k8sClient, definitionUsecase: definitionUsecase} - targetUsecase = &targetUsecaseImpl{ds: ds, k8sClient: k8sClient} - appUsecase = &applicationUsecaseImpl{ - ds: ds, - workflowUsecase: workflowUsecase, - apply: apply.NewAPIApplicator(k8sClient), - kubeClient: k8sClient, - kubeConfig: cfg, - envBindingUsecase: envBindingUsecase, - envUsecase: envUsecase, - definitionUsecase: definitionUsecase, - targetUsecase: targetUsecase, - projectUsecase: projectUsecase, - userUsecase: userUsecase, + rbacService = &rbacServiceImpl{Store: ds} + userService = &userServiceImpl{Store: ds, K8sClient: k8sClient} + projectService = &projectServiceImpl{Store: ds, K8sClient: k8sClient, RbacService: rbacService} + envService = &envServiceImpl{Store: ds, KubeClient: k8sClient, ProjectService: projectService} + workflowService = &workflowServiceImpl{Store: ds, EnvService: envService} + definitionService = &definitionServiceImpl{KubeClient: k8sClient, caches: utils.NewMemoryCacheStore(context.Background())} + envBindingService = &envBindingServiceImpl{Store: ds, EnvService: envService, WorkflowService: workflowService, KubeClient: k8sClient, DefinitionService: definitionService} + targetService = &targetServiceImpl{Store: ds, K8sClient: k8sClient} + appService = &applicationServiceImpl{ + Store: ds, + WorkflowService: workflowService, + Apply: apply.NewAPIApplicator(k8sClient), + KubeClient: k8sClient, + KubeConfig: cfg, + EnvBindingService: envBindingService, + EnvService: envService, + DefinitionService: definitionService, + TargetService: targetService, + ProjectService: projectService, + UserService: userService, } }) @@ -103,25 +104,25 @@ var _ = Describe("Test application usecase function", func() { err := k8sClient.Create(context.TODO(), &ns) Expect(err).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{})) - err = userUsecase.Init(context.TODO()) + err = userService.Init(context.TODO()) Expect(err).Should(BeNil()) By("prepare test project") - _, err = projectUsecase.CreateProject(context.TODO(), v1.CreateProjectRequest{Name: testProject, Owner: model.DefaultAdminUserName}) + _, err = projectService.CreateProject(context.TODO(), v1.CreateProjectRequest{Name: testProject, Owner: model.DefaultAdminUserName}) Expect(err).Should(BeNil()) - _, err = targetUsecase.CreateTarget(context.TODO(), v1.CreateTargetRequest{ + _, err = targetService.CreateTarget(context.TODO(), v1.CreateTargetRequest{ Name: defaultTarget, Project: testProject, Cluster: &v1.ClusterTarget{ClusterName: "local", Namespace: namespace1}}) Expect(err).Should(BeNil()) - _, err = targetUsecase.CreateTarget(context.TODO(), v1.CreateTargetRequest{ + _, err = targetService.CreateTarget(context.TODO(), v1.CreateTargetRequest{ Name: defaultTarget2, Project: testProject, Cluster: &v1.ClusterTarget{ClusterName: "local", Namespace: namespace2}}) Expect(err).Should(BeNil()) - _, err = envUsecase.CreateEnv(context.TODO(), v1.CreateEnvRequest{Name: "app-dev", Namespace: envnsdev, Targets: []string{defaultTarget}, Project: testProject}) + _, err = envService.CreateEnv(context.TODO(), v1.CreateEnvRequest{Name: "app-dev", Namespace: envnsdev, Targets: []string{defaultTarget}, Project: testProject}) Expect(err).Should(BeNil()) - _, err = envUsecase.CreateEnv(context.TODO(), v1.CreateEnvRequest{Name: "app-test", Namespace: envnstest, Targets: []string{defaultTarget2}, Project: testProject}) + _, err = envService.CreateEnv(context.TODO(), v1.CreateEnvRequest{Name: "app-test", Namespace: envnstest, Targets: []string{defaultTarget2}, Project: testProject}) Expect(err).Should(BeNil()) req := v1.CreateApplicationRequest{ Name: testApp, @@ -139,11 +140,11 @@ var _ = Describe("Test application usecase function", func() { }, } By("test create application") - base, err := appUsecase.CreateApplication(context.TODO(), req) + base, err := appService.CreateApplication(context.TODO(), req) Expect(err).Should(BeNil()) Expect(cmp.Diff(base.Description, req.Description)).Should(BeEmpty()) - triggers, err := appUsecase.ListApplicationTriggers(context.TODO(), &model.Application{Name: testApp}) + triggers, err := appService.ListApplicationTriggers(context.TODO(), &model.Application{Name: testApp}) Expect(err).Should(BeNil()) Expect(len(triggers)).Should(Equal(1)) @@ -169,21 +170,21 @@ var _ = Describe("Test application usecase function", func() { Properties: "{\"password\":\"test\"}", }, } - _, err = appUsecase.CreateApplication(context.TODO(), req2) + _, err = appService.CreateApplication(context.TODO(), req2) Expect(err).Should(BeNil()) - err = appUsecase.DeleteApplication(context.TODO(), &model.Application{Project: testProject, Name: "test-cloud-application"}) + err = appService.DeleteApplication(context.TODO(), &model.Application{Project: testProject, Name: "test-cloud-application"}) Expect(err).Should(BeNil()) err = k8sClient.Delete(context.TODO(), &cd) Expect(err).Should(BeNil()) }) It("Test ListApplications function", func() { - _, err := appUsecase.ListApplications(context.WithValue(context.TODO(), &v1.CtxKeyUser, model.DefaultAdminUserName), v1.ListApplicationOptions{}) + _, err := appService.ListApplications(context.WithValue(context.TODO(), &v1.CtxKeyUser, model.DefaultAdminUserName), v1.ListApplicationOptions{}) Expect(err).Should(BeNil()) }) It("Test ListApplications and filter by targetName function", func() { - list, err := appUsecase.ListApplications(context.WithValue(context.TODO(), &v1.CtxKeyUser, model.DefaultAdminUserName), v1.ListApplicationOptions{ + list, err := appService.ListApplications(context.WithValue(context.TODO(), &v1.CtxKeyUser, model.DefaultAdminUserName), v1.ListApplicationOptions{ Projects: []string{testProject}, TargetName: defaultTarget}) Expect(err).Should(BeNil()) @@ -191,24 +192,24 @@ var _ = Describe("Test application usecase function", func() { }) It("Test DetailApplication function", func() { - appModel, err := appUsecase.GetApplication(context.TODO(), testApp) + appModel, err := appService.GetApplication(context.TODO(), testApp) Expect(err).Should(BeNil()) Expect(cmp.Diff(appModel.Project, testProject)).Should(BeEmpty()) - detail, err := appUsecase.DetailApplication(context.TODO(), appModel) + detail, err := appService.DetailApplication(context.TODO(), appModel) Expect(err).Should(BeNil()) Expect(cmp.Diff(detail.ResourceInfo.ComponentNum, int64(1))).Should(BeEmpty()) Expect(cmp.Diff(len(detail.Policies), 2)).Should(BeEmpty()) }) It("Test CreateTrigger function", func() { - appModel, err := appUsecase.GetApplication(context.TODO(), testApp) + appModel, err := appService.GetApplication(context.TODO(), testApp) Expect(err).Should(BeNil()) - _, err = appUsecase.CreateApplicationTrigger(context.TODO(), appModel, v1.CreateApplicationTriggerRequest{ + _, err = appService.CreateApplicationTrigger(context.TODO(), appModel, v1.CreateApplicationTriggerRequest{ Name: "trigger-name", }) Expect(err).Should(BeNil()) - base, err := appUsecase.CreateApplicationTrigger(context.TODO(), appModel, v1.CreateApplicationTriggerRequest{ + base, err := appService.CreateApplicationTrigger(context.TODO(), appModel, v1.CreateApplicationTriggerRequest{ Name: "trigger-name-2", ComponentName: "trigger-component", }) @@ -217,17 +218,17 @@ var _ = Describe("Test application usecase function", func() { }) It("Test ListTriggers function", func() { - appModel, err := appUsecase.GetApplication(context.TODO(), testApp) + appModel, err := appService.GetApplication(context.TODO(), testApp) Expect(err).Should(BeNil()) - triggers, err := appUsecase.ListApplicationTriggers(context.TODO(), appModel) + triggers, err := appService.ListApplicationTriggers(context.TODO(), appModel) Expect(err).Should(BeNil()) Expect(len(triggers)).Should(Equal(3)) }) It("Test DeleteTrigger function", func() { - appModel, err := appUsecase.GetApplication(context.TODO(), testApp) + appModel, err := appService.GetApplication(context.TODO(), testApp) Expect(err).Should(BeNil()) - triggers, err := appUsecase.ListApplicationTriggers(context.TODO(), appModel) + triggers, err := appService.ListApplicationTriggers(context.TODO(), appModel) Expect(err).Should(BeNil()) Expect(len(triggers)).Should(Equal(3)) var trigger *v1.ApplicationTriggerBase @@ -238,8 +239,8 @@ var _ = Describe("Test application usecase function", func() { } } Expect(trigger).ShouldNot(BeNil()) - Expect(appUsecase.DeleteApplicationTrigger(context.TODO(), appModel, trigger.Token)).Should(BeNil()) - triggers, err = appUsecase.ListApplicationTriggers(context.TODO(), appModel) + Expect(appService.DeleteApplicationTrigger(context.TODO(), appModel, trigger.Token)).Should(BeNil()) + triggers, err = appService.ListApplicationTriggers(context.TODO(), appModel) Expect(err).Should(BeNil()) Expect(len(triggers)).Should(Equal(2)) trigger = nil @@ -253,21 +254,21 @@ var _ = Describe("Test application usecase function", func() { }) It("Test ListComponents function", func() { - appModel, err := appUsecase.GetApplication(context.TODO(), testApp) + appModel, err := appService.GetApplication(context.TODO(), testApp) Expect(err).Should(BeNil()) Expect(cmp.Diff(appModel.Project, testProject)).Should(BeEmpty()) - components, err := appUsecase.ListComponents(context.TODO(), appModel, v1.ListApplicationComponentOptions{}) + components, err := appService.ListComponents(context.TODO(), appModel, v1.ListApplicationComponentOptions{}) Expect(err).Should(BeNil()) Expect(cmp.Diff(len(components), 1)).Should(BeEmpty()) Expect(cmp.Diff(components[0].ComponentType, "webservice")).Should(BeEmpty()) }) It("Test CreateComponent function", func() { - appModel, err := appUsecase.GetApplication(context.TODO(), testApp) + appModel, err := appService.GetApplication(context.TODO(), testApp) Expect(err).Should(BeNil()) - base, err := appUsecase.CreateComponent(context.TODO(), appModel, v1.CreateComponentRequest{ + base, err := appService.CreateComponent(context.TODO(), appModel, v1.CreateComponentRequest{ Name: "test2", Description: "this is a test2 component", Labels: map[string]string{}, @@ -291,16 +292,16 @@ var _ = Describe("Test application usecase function", func() { Expect(err).Should(BeNil()) Expect(cmp.Diff(base.ComponentType, "webservice")).Should(BeEmpty()) - detailResponse, err := appUsecase.DetailComponent(context.TODO(), appModel, "test2") + detailResponse, err := appService.DetailComponent(context.TODO(), appModel, "test2") Expect(err).Should(BeNil()) Expect(cmp.Diff(len(detailResponse.Traits), 2)).Should(BeEmpty()) }) It("Test DetailComponent function", func() { - appModel, err := appUsecase.GetApplication(context.TODO(), testApp) + appModel, err := appService.GetApplication(context.TODO(), testApp) Expect(err).Should(BeNil()) Expect(cmp.Diff(appModel.Project, testProject)).Should(BeEmpty()) - detailResponse, err := appUsecase.DetailComponent(context.TODO(), appModel, "test2") + detailResponse, err := appService.DetailComponent(context.TODO(), appModel, "test2") Expect(err).Should(BeNil()) Expect(cmp.Diff(detailResponse.DependsOn[0], "component-name")).Should(BeEmpty()) Expect(detailResponse.Properties).ShouldNot(BeNil()) @@ -308,10 +309,10 @@ var _ = Describe("Test application usecase function", func() { }) It("Test AddPolicy function", func() { - appModel, err := appUsecase.GetApplication(context.TODO(), testApp) + appModel, err := appService.GetApplication(context.TODO(), testApp) Expect(err).Should(BeNil()) Expect(cmp.Diff(appModel.Project, testProject)).Should(BeEmpty()) - _, err = appUsecase.CreatePolicy(context.TODO(), appModel, v1.CreatePolicyRequest{ + _, err = appService.CreatePolicy(context.TODO(), appModel, v1.CreatePolicyRequest{ Name: overridePolicyName, Description: "this is a test2 policy", Type: "override", @@ -319,7 +320,7 @@ var _ = Describe("Test application usecase function", func() { }) Expect(err).Should(BeNil()) - _, err = appUsecase.CreatePolicy(context.TODO(), appModel, v1.CreatePolicyRequest{ + _, err = appService.CreatePolicy(context.TODO(), appModel, v1.CreatePolicyRequest{ Name: overridePolicyName, Description: "this is a test2 policy", Type: "override", @@ -329,11 +330,11 @@ var _ = Describe("Test application usecase function", func() { }) It("Test ListPolicies function", func() { - appModel, err := appUsecase.GetApplication(context.TODO(), testApp) + appModel, err := appService.GetApplication(context.TODO(), testApp) Expect(err).Should(BeNil()) Expect(cmp.Diff(appModel.Project, testProject)).Should(BeEmpty()) - policies, err := appUsecase.ListPolicies(context.TODO(), appModel) + policies, err := appService.ListPolicies(context.TODO(), appModel) Expect(err).Should(BeNil()) var count int for _, p := range policies { @@ -345,20 +346,20 @@ var _ = Describe("Test application usecase function", func() { }) It("Test DetailPolicy function", func() { - appModel, err := appUsecase.GetApplication(context.TODO(), testApp) + appModel, err := appService.GetApplication(context.TODO(), testApp) Expect(err).Should(BeNil()) Expect(cmp.Diff(appModel.Project, testProject)).Should(BeEmpty()) - detail, err := appUsecase.DetailPolicy(context.TODO(), appModel, overridePolicyName) + detail, err := appService.DetailPolicy(context.TODO(), appModel, overridePolicyName) Expect(err).Should(BeNil()) Expect(detail.Properties).ShouldNot(BeNil()) Expect((*detail.Properties)["components"]).ShouldNot(BeEmpty()) }) It("Test UpdatePolicy function", func() { - appModel, err := appUsecase.GetApplication(context.TODO(), testApp) + appModel, err := appService.GetApplication(context.TODO(), testApp) Expect(err).Should(BeNil()) Expect(cmp.Diff(appModel.Project, testProject)).Should(BeEmpty()) - base, err := appUsecase.UpdatePolicy(context.TODO(), appModel, overridePolicyName, v1.UpdatePolicyRequest{ + base, err := appService.UpdatePolicy(context.TODO(), appModel, overridePolicyName, v1.UpdatePolicyRequest{ Type: "override", Properties: `{"components":{}}`, }) @@ -367,20 +368,20 @@ var _ = Describe("Test application usecase function", func() { Expect((*base.Properties)["components"]).Should(BeEmpty()) }) It("Test DeletePolicy function", func() { - appModel, err := appUsecase.GetApplication(context.TODO(), testApp) + appModel, err := appService.GetApplication(context.TODO(), testApp) Expect(err).Should(BeNil()) Expect(cmp.Diff(appModel.Project, testProject)).Should(BeEmpty()) - err = appUsecase.DeletePolicy(context.TODO(), appModel, overridePolicyName) + err = appService.DeletePolicy(context.TODO(), appModel, overridePolicyName) Expect(err).Should(BeNil()) }) It("Test DeleteComponent function", func() { - appModel, err := appUsecase.GetApplication(context.TODO(), testApp) + appModel, err := appService.GetApplication(context.TODO(), testApp) Expect(err).Should(BeNil()) Expect(cmp.Diff(appModel.Project, testProject)).Should(BeEmpty()) - component, err := appUsecase.GetApplicationComponent(context.TODO(), appModel, "test2") + component, err := appService.GetApplicationComponent(context.TODO(), appModel, "test2") Expect(err).Should(BeNil()) - err = appUsecase.DeleteComponent(context.TODO(), appModel, component) + err = appService.DeleteComponent(context.TODO(), appModel, component) Expect(err).Should(BeNil()) }) @@ -396,36 +397,36 @@ var _ = Describe("Test application usecase function", func() { if i == 0 { appModel.Status = model.RevisionStatusTerminated } - err := workflowUsecase.createTestApplicationRevision(context.TODO(), appModel) + err := workflowService.createTestApplicationRevision(context.TODO(), appModel) Expect(err).Should(BeNil()) } - revisions, err := appUsecase.ListRevisions(context.TODO(), "test-app-sadasd", "", "", 0, 10) + revisions, err := appService.ListRevisions(context.TODO(), "test-app-sadasd", "", "", 0, 10) Expect(err).Should(BeNil()) Expect(revisions.Total).Should(Equal(int64(3))) - revisions, err = appUsecase.ListRevisions(context.TODO(), "test-app-sadasd", "env-0", "", 0, 10) + revisions, err = appService.ListRevisions(context.TODO(), "test-app-sadasd", "env-0", "", 0, 10) Expect(err).Should(BeNil()) Expect(revisions.Total).Should(Equal(int64(1))) Expect(revisions.Revisions[0].DeployUser.Name).Should(Equal(model.DefaultAdminUserName)) Expect(revisions.Revisions[0].DeployUser.Alias).Should(Equal(model.DefaultAdminUserAlias)) - revisions, err = appUsecase.ListRevisions(context.TODO(), "test-app-sadasd", "", "terminated", 0, 10) + revisions, err = appService.ListRevisions(context.TODO(), "test-app-sadasd", "", "terminated", 0, 10) Expect(err).Should(BeNil()) Expect(revisions.Total).Should(Equal(int64(1))) - revisions, err = appUsecase.ListRevisions(context.TODO(), "test-app", "env-1", "terminated", 0, 10) + revisions, err = appService.ListRevisions(context.TODO(), "test-app", "env-1", "terminated", 0, 10) Expect(err).Should(BeNil()) Expect(revisions.Total).Should(Equal(int64(0))) }) It("Test DetailRevision function", func() { - err := workflowUsecase.createTestApplicationRevision(context.TODO(), &model.ApplicationRevision{ + err := workflowService.createTestApplicationRevision(context.TODO(), &model.ApplicationRevision{ AppPrimaryKey: "test-app", Version: "123", DeployUser: model.DefaultAdminUserName, }) Expect(err).Should(BeNil()) - revision, err := appUsecase.DetailRevision(context.TODO(), "test-app", "123") + revision, err := appService.DetailRevision(context.TODO(), "test-app", "123") Expect(err).Should(BeNil()) Expect(revision.Version).Should(Equal("123")) Expect(revision.DeployUser.Name).Should(Equal(model.DefaultAdminUserName)) @@ -433,15 +434,15 @@ var _ = Describe("Test application usecase function", func() { }) It("Test ApplicationEnvRecycle function", func() { - appModel, err := appUsecase.GetApplication(context.TODO(), testApp) + appModel, err := appService.GetApplication(context.TODO(), testApp) Expect(err).Should(BeNil()) - revision, err := appUsecase.Deploy( + revision, err := appService.Deploy( context.WithValue(context.TODO(), &v1.CtxKeyUser, model.DefaultAdminUserName), - appModel, v1.ApplicationDeployRequest{WorkflowName: convertWorkflowName("app-dev")}) + appModel, v1.ApplicationDeployRequest{WorkflowName: repository.ConvertWorkflowName("app-dev")}) Expect(err).Should(BeNil()) Expect(revision.DeployUser.Name).Should(Equal(model.DefaultAdminUserName)) Expect(revision.DeployUser.Alias).Should(Equal(model.DefaultAdminUserAlias)) - err = envBindingUsecase.ApplicationEnvRecycle(context.TODO(), &model.Application{ + err = envBindingService.ApplicationEnvRecycle(context.TODO(), &model.Application{ Name: testApp, }, &model.EnvBinding{Name: "app-dev"}) Expect(err).Should(BeNil()) @@ -451,7 +452,7 @@ var _ = Describe("Test application usecase function", func() { By("no running records in application") ctx := context.TODO() for i := 0; i < 2; i++ { - appUsecase.ds.Add(ctx, &model.WorkflowRecord{ + appService.Store.Add(ctx, &model.WorkflowRecord{ AppPrimaryKey: "app-records", Name: fmt.Sprintf("list-%d", i), Finished: "true", @@ -459,13 +460,13 @@ var _ = Describe("Test application usecase function", func() { }) } - resp, err := appUsecase.ListRecords(context.TODO(), "app-records") + resp, err := appService.ListRecords(context.TODO(), "app-records") Expect(err).Should(BeNil()) Expect(resp.Total).Should(Equal(int64(1))) By("3 running records in application") for i := 0; i < 3; i++ { - appUsecase.ds.Add(ctx, &model.WorkflowRecord{ + appService.Store.Add(ctx, &model.WorkflowRecord{ AppPrimaryKey: "app-records", Name: fmt.Sprintf("list-running-%d", i), Finished: "false", @@ -473,145 +474,145 @@ var _ = Describe("Test application usecase function", func() { }) } - resp, err = appUsecase.ListRecords(context.TODO(), "app-records") + resp, err = appService.ListRecords(context.TODO(), "app-records") Expect(err).Should(BeNil()) Expect(resp.Total).Should(Equal(int64(3))) }) It("Test CompareAppWithLatestRevision function", func() { - appModel, err := appUsecase.GetApplication(context.TODO(), testApp) + appModel, err := appService.GetApplication(context.TODO(), testApp) Expect(err).Should(BeNil()) - _, err = appUsecase.Deploy(context.TODO(), appModel, v1.ApplicationDeployRequest{WorkflowName: convertWorkflowName("app-dev")}) + _, err = appService.Deploy(context.TODO(), appModel, v1.ApplicationDeployRequest{WorkflowName: repository.ConvertWorkflowName("app-dev")}) Expect(err).Should(BeNil()) - component, err := appUsecase.GetApplicationComponent(context.TODO(), appModel, "component-name") + component, err := appService.GetApplicationComponent(context.TODO(), appModel, "component-name") Expect(err).Should(BeNil()) By("compare when app not change, should return false") - compareResponse, err := appUsecase.CompareAppWithLatestRevision(context.TODO(), appModel, v1.AppCompareReq{}) + compareResponse, err := appService.CompareAppWithLatestRevision(context.TODO(), appModel, v1.AppCompareReq{}) Expect(err).Should(BeNil()) Expect(cmp.Diff(compareResponse.IsDiff, false)).Should(BeEmpty()) By("compare when app not change and env not empty, should return false") - compareResponse, err = appUsecase.CompareAppWithLatestRevision(context.TODO(), appModel, v1.AppCompareReq{Env: "app-dev"}) + compareResponse, err = appService.CompareAppWithLatestRevision(context.TODO(), appModel, v1.AppCompareReq{Env: "app-dev"}) Expect(err).Should(BeNil()) Expect(cmp.Diff(compareResponse.IsDiff, false)).Should(BeEmpty()) By("compare when app add env, not change, should return false") - _, err = envUsecase.CreateEnv(context.TODO(), v1.CreateEnvRequest{Name: "app-prod", Namespace: "envnsprod", Targets: []string{defaultTarget}, Project: "app-prod"}) + _, err = envService.CreateEnv(context.TODO(), v1.CreateEnvRequest{Name: "app-prod", Namespace: "envnsprod", Targets: []string{defaultTarget}, Project: "app-prod"}) Expect(err).Should(BeNil()) - _, err = envBindingUsecase.CreateEnvBinding(context.TODO(), appModel, v1.CreateApplicationEnvbindingRequest{EnvBinding: v1.EnvBinding{Name: "app-prod"}}) + _, err = envBindingService.CreateEnvBinding(context.TODO(), appModel, v1.CreateApplicationEnvbindingRequest{EnvBinding: v1.EnvBinding{Name: "app-prod"}}) Expect(err).Should(BeNil()) - compareResponse, err = appUsecase.CompareAppWithLatestRevision(context.TODO(), appModel, v1.AppCompareReq{}) + compareResponse, err = appService.CompareAppWithLatestRevision(context.TODO(), appModel, v1.AppCompareReq{}) Expect(err).Should(BeNil()) Expect(cmp.Diff(compareResponse.IsDiff, false)).Should(BeEmpty()) By("compare when app's env add target, should return true") - _, err = targetUsecase.CreateTarget(context.TODO(), v1.CreateTargetRequest{Name: "dev-target1", Project: appModel.Project, Cluster: &v1.ClusterTarget{ClusterName: "local", Namespace: "dev-target1"}}) + _, err = targetService.CreateTarget(context.TODO(), v1.CreateTargetRequest{Name: "dev-target1", Project: appModel.Project, Cluster: &v1.ClusterTarget{ClusterName: "local", Namespace: "dev-target1"}}) Expect(err).Should(BeNil()) - _, err = envUsecase.UpdateEnv(context.TODO(), "app-dev", + _, err = envService.UpdateEnv(context.TODO(), "app-dev", v1.UpdateEnvRequest{ Description: "this is a env description update", Targets: []string{defaultTarget, "dev-target1"}, }) Expect(err).Should(BeNil()) - compareResponse, err = appUsecase.CompareAppWithLatestRevision(context.TODO(), appModel, v1.AppCompareReq{}) + compareResponse, err = appService.CompareAppWithLatestRevision(context.TODO(), appModel, v1.AppCompareReq{}) Expect(err).Should(BeNil()) Expect(cmp.Diff(compareResponse.IsDiff, true)).Should(BeEmpty()) By("compare when update app's trait, should return true") // reset app config - _, err = appUsecase.ResetAppToLatestRevision(context.TODO(), testApp) + _, err = appService.ResetAppToLatestRevision(context.TODO(), testApp) Expect(err).Should(BeNil()) - _, err = appUsecase.UpdateApplicationTrait(context.TODO(), appModel, &model.ApplicationComponent{Name: "component-name"}, "scaler", v1.UpdateApplicationTraitRequest{ + _, err = appService.UpdateApplicationTrait(context.TODO(), appModel, &model.ApplicationComponent{Name: "component-name"}, "scaler", v1.UpdateApplicationTraitRequest{ Properties: `{"replicas":2}`, Alias: "alias", Description: "description", }) Expect(err).Should(BeNil()) - compareResponse, err = appUsecase.CompareAppWithLatestRevision(context.TODO(), appModel, v1.AppCompareReq{}) + compareResponse, err = appService.CompareAppWithLatestRevision(context.TODO(), appModel, v1.AppCompareReq{}) Expect(err).Should(BeNil()) Expect(cmp.Diff(compareResponse.IsDiff, true)).Should(BeEmpty()) By("compare when update component's target after app deployed ,should return ture") // reset app config - _, err = appUsecase.ResetAppToLatestRevision(context.TODO(), testApp) + _, err = appService.ResetAppToLatestRevision(context.TODO(), testApp) Expect(err).Should(BeNil()) newProperties := "{\"exposeType\":\"NodePort\",\"image\":\"nginx\",\"imagePullPolicy\":\"Always\"}" - _, err = appUsecase.UpdateComponent(context.TODO(), + _, err = appService.UpdateComponent(context.TODO(), appModel, component, v1.UpdateApplicationComponentRequest{ Properties: &newProperties, }) Expect(err).Should(BeNil()) - compareResponse, err = appUsecase.CompareAppWithLatestRevision(context.TODO(), appModel, v1.AppCompareReq{}) + compareResponse, err = appService.CompareAppWithLatestRevision(context.TODO(), appModel, v1.AppCompareReq{}) Expect(err).Should(BeNil()) Expect(cmp.Diff(compareResponse.IsDiff, true)).Should(BeEmpty()) - err = envBindingUsecase.ApplicationEnvRecycle(context.TODO(), &model.Application{Name: testApp}, &model.EnvBinding{Name: "app-dev"}) + err = envBindingService.ApplicationEnvRecycle(context.TODO(), &model.Application{Name: testApp}, &model.EnvBinding{Name: "app-dev"}) Expect(err).Should(BeNil()) }) It("Test ResetAppToLatestRevision function", func() { - appModel, err := appUsecase.GetApplication(context.TODO(), testApp) + appModel, err := appService.GetApplication(context.TODO(), testApp) Expect(err).Should(BeNil()) - resetResponse, err := appUsecase.ResetAppToLatestRevision(context.TODO(), testApp) + resetResponse, err := appService.ResetAppToLatestRevision(context.TODO(), testApp) Expect(err).Should(BeNil()) Expect(cmp.Diff(resetResponse.IsReset, true)).Should(BeEmpty()) - component, err := appUsecase.GetApplicationComponent(context.TODO(), appModel, "component-name") + component, err := appService.GetApplicationComponent(context.TODO(), appModel, "component-name") Expect(err).Should(BeNil()) expectProperties := "{\"image\":\"nginx\"}" Expect(cmp.Diff(component.Properties.JSON(), expectProperties)).Should(BeEmpty()) }) It("Test DryRun with app function", func() { - appModel, err := appUsecase.GetApplication(context.TODO(), testApp) + appModel, err := appService.GetApplication(context.TODO(), testApp) Expect(err).Should(BeNil()) - resetResponse, err := appUsecase.DryRunAppOrRevision(context.TODO(), appModel, v1.AppDryRunReq{DryRunType: "APP"}) + resetResponse, err := appService.DryRunAppOrRevision(context.TODO(), appModel, v1.AppDryRunReq{DryRunType: "APP"}) Expect(err).Should(BeNil()) Expect(strings.Contains(resetResponse.YAML, "# Application(test-app)")).Should(BeTrue()) Expect(strings.Contains(resetResponse.YAML, "# Application(test-app) -- Component(component-name)")).Should(BeTrue()) }) It("Test DryRun with env revision function", func() { - appModel, err := appUsecase.GetApplication(context.TODO(), testApp) + appModel, err := appService.GetApplication(context.TODO(), testApp) Expect(err).Should(BeNil()) - resetResponse, err := appUsecase.DryRunAppOrRevision(context.TODO(), appModel, v1.AppDryRunReq{DryRunType: "Revision", Env: "app-dev"}) + resetResponse, err := appService.DryRunAppOrRevision(context.TODO(), appModel, v1.AppDryRunReq{DryRunType: "Revision", Env: "app-dev"}) Expect(err).Should(BeNil()) Expect(strings.Contains(resetResponse.YAML, "# Application(test-app)")).Should(BeTrue()) Expect(strings.Contains(resetResponse.YAML, "# Application(test-app) -- Component(component-name)")).Should(BeTrue()) }) It("Test DryRun with last revision function", func() { - appModel, err := appUsecase.GetApplication(context.TODO(), testApp) + appModel, err := appService.GetApplication(context.TODO(), testApp) Expect(err).Should(BeNil()) - resetResponse, err := appUsecase.DryRunAppOrRevision(context.TODO(), appModel, v1.AppDryRunReq{DryRunType: "Revision"}) + resetResponse, err := appService.DryRunAppOrRevision(context.TODO(), appModel, v1.AppDryRunReq{DryRunType: "Revision"}) Expect(err).Should(BeNil()) Expect(strings.Contains(resetResponse.YAML, "# Application(test-app)")).Should(BeTrue()) Expect(strings.Contains(resetResponse.YAML, "# Application(test-app) -- Component(component-name)")).Should(BeTrue()) }) It("Test DeleteApplication function", func() { - appModel, err := appUsecase.GetApplication(context.TODO(), testApp) + appModel, err := appService.GetApplication(context.TODO(), testApp) Expect(err).Should(BeNil()) time.Sleep(time.Second * 3) - err = appUsecase.DeleteApplication(context.TODO(), appModel) + err = appService.DeleteApplication(context.TODO(), appModel) Expect(err).Should(BeNil()) - components, err := appUsecase.ListComponents(context.TODO(), appModel, v1.ListApplicationComponentOptions{}) + components, err := appService.ListComponents(context.TODO(), appModel, v1.ListApplicationComponentOptions{}) Expect(err).Should(BeNil()) Expect(cmp.Diff(len(components), 0)).Should(BeEmpty()) - policies, err := appUsecase.ListPolicies(context.TODO(), appModel) + policies, err := appService.ListPolicies(context.TODO(), appModel) Expect(err).Should(BeNil()) Expect(cmp.Diff(len(policies), 0)).Should(BeEmpty()) }) }) -var _ = Describe("Test application component usecase function", func() { +var _ = Describe("Test application component service function", func() { var ( - appUsecase *applicationUsecaseImpl - projectUsecase *projectUsecaseImpl - envUsecase *envUsecaseImpl + appService *applicationServiceImpl + projectService *projectServiceImpl + envService *envServiceImpl testApp string testProject string ) @@ -620,20 +621,20 @@ var _ = Describe("Test application component usecase function", func() { ds, err := NewDatastore(datastore.Config{Type: "kubeapi", Database: "app-test-kubevela"}) Expect(ds).ToNot(BeNil()) Expect(err).Should(BeNil()) - rbacUsecase := &rbacUsecaseImpl{ds: ds} - projectUsecase = &projectUsecaseImpl{ds: ds, k8sClient: k8sClient, rbacUsecase: rbacUsecase} - envUsecase = &envUsecaseImpl{ds: ds, kubeClient: k8sClient, projectUsecase: projectUsecase} - workflowUsecase := &workflowUsecaseImpl{ds: ds, envUsecase: envUsecase} - envBindingUsecase := &envBindingUsecaseImpl{ds: ds, envUsecase: envUsecase, workflowUsecase: workflowUsecase, kubeClient: k8sClient} + rbacService := &rbacServiceImpl{Store: ds} + projectService = &projectServiceImpl{Store: ds, K8sClient: k8sClient, RbacService: rbacService} + envService = &envServiceImpl{Store: ds, KubeClient: k8sClient, ProjectService: projectService} + workflowService := &workflowServiceImpl{Store: ds, EnvService: envService} + envBindingService := &envBindingServiceImpl{Store: ds, EnvService: envService, WorkflowService: workflowService, KubeClient: k8sClient} - appUsecase = &applicationUsecaseImpl{ - ds: ds, - apply: apply.NewAPIApplicator(k8sClient), - kubeClient: k8sClient, - projectUsecase: projectUsecase, - workflowUsecase: workflowUsecase, - envBindingUsecase: envBindingUsecase, - envUsecase: envUsecase, + appService = &applicationServiceImpl{ + Store: ds, + Apply: apply.NewAPIApplicator(k8sClient), + KubeClient: k8sClient, + ProjectService: projectService, + WorkflowService: workflowService, + EnvBindingService: envBindingService, + EnvService: envService, } testApp = "test-trait-app" testProject = "test-trait-project" @@ -641,17 +642,17 @@ var _ = Describe("Test application component usecase function", func() { }) It("Test add application trait", func() { - _, err := projectUsecase.CreateProject(context.TODO(), v1.CreateProjectRequest{Name: testProject}) + _, err := projectService.CreateProject(context.TODO(), v1.CreateProjectRequest{Name: testProject}) Expect(err).Should(BeNil()) - _, err = appUsecase.CreateApplication(context.TODO(), v1.CreateApplicationRequest{Name: testApp, Project: testProject}) + _, err = appService.CreateApplication(context.TODO(), v1.CreateApplicationRequest{Name: testApp, Project: testProject}) Expect(err).Should(BeNil()) - appModel, err := appUsecase.GetApplication(context.TODO(), testApp) + appModel, err := appService.GetApplication(context.TODO(), testApp) Expect(err).Should(BeNil()) - _, err = appUsecase.CreateComponent(context.TODO(), appModel, v1.CreateComponentRequest{Name: "test2", ComponentType: "webservice"}) + _, err = appService.CreateComponent(context.TODO(), appModel, v1.CreateComponentRequest{Name: "test2", ComponentType: "webservice"}) Expect(err).Should(BeNil()) alias := "alias" description := "description" - res, err := appUsecase.CreateApplicationTrait(context.TODO(), appModel, &model.ApplicationComponent{Name: "test2"}, v1.CreateApplicationTraitRequest{ + res, err := appService.CreateApplicationTrait(context.TODO(), appModel, &model.ApplicationComponent{Name: "test2"}, v1.CreateApplicationTraitRequest{ Type: "Ingress", Properties: `{"domain":"www.test.com"}`, Alias: alias, @@ -659,7 +660,7 @@ var _ = Describe("Test application component usecase function", func() { }) Expect(err).Should(BeNil()) Expect(cmp.Diff(res.Type, "Ingress")).Should(BeEmpty()) - comp, err := appUsecase.DetailComponent(context.TODO(), appModel, "test2") + comp, err := appService.DetailComponent(context.TODO(), appModel, "test2") Expect(err).Should(BeNil()) Expect(comp).ShouldNot(BeNil()) // A scaler trait is automatically generated for the webservice component. @@ -669,7 +670,7 @@ var _ = Describe("Test application component usecase function", func() { Expect(comp.Traits[1].Description).Should(BeEquivalentTo(description)) Expect(err).Should(BeNil()) - _, err = appUsecase.CreateApplicationTrait(context.TODO(), appModel, &model.ApplicationComponent{Name: "test2"}, v1.CreateApplicationTraitRequest{ + _, err = appService.CreateApplicationTrait(context.TODO(), appModel, &model.ApplicationComponent{Name: "test2"}, v1.CreateApplicationTraitRequest{ Type: "Ingress", Properties: `{"domain":"www.dup.com"}`, }) @@ -677,18 +678,18 @@ var _ = Describe("Test application component usecase function", func() { }) It("Test update application trait", func() { - appModel, err := appUsecase.GetApplication(context.TODO(), testApp) + appModel, err := appService.GetApplication(context.TODO(), testApp) Expect(err).Should(BeNil()) alias := "newAlias" description := "newDescription" - res, err := appUsecase.UpdateApplicationTrait(context.TODO(), appModel, &model.ApplicationComponent{Name: "test2"}, "Ingress", v1.UpdateApplicationTraitRequest{ + res, err := appService.UpdateApplicationTrait(context.TODO(), appModel, &model.ApplicationComponent{Name: "test2"}, "Ingress", v1.UpdateApplicationTraitRequest{ Properties: `{"domain":"www.test1.com"}`, Alias: alias, Description: description, }) Expect(err).Should(BeNil()) Expect(cmp.Diff(res.Type, "Ingress")).Should(BeEmpty()) - comp, err := appUsecase.DetailComponent(context.TODO(), appModel, "test2") + comp, err := appService.DetailComponent(context.TODO(), appModel, "test2") Expect(err).Should(BeNil()) Expect(comp).ShouldNot(BeNil()) Expect(len(comp.Traits)).Should(BeEquivalentTo(2)) @@ -698,20 +699,20 @@ var _ = Describe("Test application component usecase function", func() { }) It("Test update a not exist", func() { - appModel, err := appUsecase.GetApplication(context.TODO(), testApp) + appModel, err := appService.GetApplication(context.TODO(), testApp) Expect(err).Should(BeNil()) - _, err = appUsecase.UpdateApplicationTrait(context.TODO(), appModel, &model.ApplicationComponent{Name: "test2"}, "Ingress-1-20", v1.UpdateApplicationTraitRequest{ + _, err = appService.UpdateApplicationTrait(context.TODO(), appModel, &model.ApplicationComponent{Name: "test2"}, "Ingress-1-20", v1.UpdateApplicationTraitRequest{ Properties: `{"domain":"www.test1.com"}`, }) Expect(err).ShouldNot(BeNil()) }) It("Test delete an exist trait", func() { - appModel, err := appUsecase.GetApplication(context.TODO(), testApp) + appModel, err := appService.GetApplication(context.TODO(), testApp) Expect(err).Should(BeNil()) - err = appUsecase.DeleteApplicationTrait(context.TODO(), appModel, &model.ApplicationComponent{Name: "test2"}, "Ingress") + err = appService.DeleteApplicationTrait(context.TODO(), appModel, &model.ApplicationComponent{Name: "test2"}, "Ingress") Expect(err).Should(BeNil()) - app, err := appUsecase.DetailComponent(context.TODO(), appModel, "test2") + app, err := appService.DetailComponent(context.TODO(), appModel, "test2") Expect(err).Should(BeNil()) Expect(app).ShouldNot(BeNil()) Expect(len(app.Traits)).Should(BeEquivalentTo(1)) diff --git a/pkg/apiserver/rest/usecase/authentication.go b/pkg/apiserver/domain/service/authentication.go similarity index 85% rename from pkg/apiserver/rest/usecase/authentication.go rename to pkg/apiserver/domain/service/authentication.go index 67a468402..434485dc4 100644 --- a/pkg/apiserver/rest/usecase/authentication.go +++ b/pkg/apiserver/domain/service/authentication.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package usecase +package service import ( "context" @@ -37,12 +37,11 @@ import ( "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" velatypes "github.com/oam-dev/kubevela/apis/types" - "github.com/oam-dev/kubevela/pkg/apiserver/clients" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/log" - "github.com/oam-dev/kubevela/pkg/apiserver/model" - apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" + apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/log" ) const ( @@ -60,33 +59,24 @@ const ( var signedKey = "" -// AuthenticationUsecase is the usecase of authentication -type AuthenticationUsecase interface { +// AuthenticationService is the service of authentication +type AuthenticationService interface { Login(ctx context.Context, loginReq apisv1.LoginRequest) (*apisv1.LoginResponse, error) RefreshToken(ctx context.Context, refreshToken string) (*apisv1.RefreshTokenResponse, error) GetDexConfig(ctx context.Context) (*apisv1.DexConfigResponse, error) GetLoginType(ctx context.Context) (*apisv1.GetLoginTypeResponse, error) } -type authenticationUsecaseImpl struct { - sysUsecase SystemInfoUsecase - userUsecase UserUsecase - ds datastore.DataStore - kubeClient client.Client +type authenticationServiceImpl struct { + SysService SystemInfoService `inject:""` + UserService UserService `inject:""` + Store datastore.DataStore `inject:"datastore"` + KubeClient client.Client `inject:"kubeClient"` } -// NewAuthenticationUsecase new authentication usecase -func NewAuthenticationUsecase(ds datastore.DataStore, sysUsecase SystemInfoUsecase, userUsecase UserUsecase) AuthenticationUsecase { - kubecli, err := clients.GetKubeClient() - if err != nil { - log.Logger.Fatalf("failed to get kube client: %s", err.Error()) - } - return &authenticationUsecaseImpl{ - sysUsecase: sysUsecase, - userUsecase: userUsecase, - ds: ds, - kubeClient: kubecli, - } +// NewAuthenticationService new authentication service +func NewAuthenticationService() AuthenticationService { + return &authenticationServiceImpl{} } type authHandler interface { @@ -95,17 +85,17 @@ type authHandler interface { type dexHandlerImpl struct { idToken *oidc.IDToken - ds datastore.DataStore + Store datastore.DataStore } type localHandlerImpl struct { ds datastore.DataStore - userUsecase UserUsecase + userService UserService username string password string } -func (a *authenticationUsecaseImpl) newDexHandler(ctx context.Context, req apisv1.LoginRequest) (*dexHandlerImpl, error) { +func (a *authenticationServiceImpl) newDexHandler(ctx context.Context, req apisv1.LoginRequest) (*dexHandlerImpl, error) { if req.Code == "" { return nil, bcode.ErrInvalidLoginRequest } @@ -135,26 +125,26 @@ func (a *authenticationUsecaseImpl) newDexHandler(ctx context.Context, req apisv } return &dexHandlerImpl{ idToken: idToken, - ds: a.ds, + Store: a.Store, }, nil } -func (a *authenticationUsecaseImpl) newLocalHandler(req apisv1.LoginRequest) (*localHandlerImpl, error) { +func (a *authenticationServiceImpl) newLocalHandler(req apisv1.LoginRequest) (*localHandlerImpl, error) { if req.Username == "" || req.Password == "" { return nil, bcode.ErrInvalidLoginRequest } return &localHandlerImpl{ - ds: a.ds, - userUsecase: a.userUsecase, + ds: a.Store, + userService: a.UserService, username: req.Username, password: req.Password, }, nil } -func (a *authenticationUsecaseImpl) Login(ctx context.Context, loginReq apisv1.LoginRequest) (*apisv1.LoginResponse, error) { +func (a *authenticationServiceImpl) Login(ctx context.Context, loginReq apisv1.LoginRequest) (*apisv1.LoginResponse, error) { var handler authHandler var err error - sysInfo, err := a.sysUsecase.Get(ctx) + sysInfo, err := a.SysService.Get(ctx) if err != nil { return nil, err } @@ -196,7 +186,7 @@ func (a *authenticationUsecaseImpl) Login(ctx context.Context, loginReq apisv1.L }, nil } -func (a *authenticationUsecaseImpl) generateJWTToken(username, grantType string, expireDuration time.Duration) (string, error) { +func (a *authenticationServiceImpl) generateJWTToken(username, grantType string, expireDuration time.Duration) (string, error) { expire := time.Now().Add(expireDuration) claims := model.CustomClaims{ StandardClaims: jwt.StandardClaims{ @@ -211,7 +201,7 @@ func (a *authenticationUsecaseImpl) generateJWTToken(username, grantType string, return token.SignedString([]byte(signedKey)) } -func (a *authenticationUsecaseImpl) RefreshToken(ctx context.Context, refreshToken string) (*apisv1.RefreshTokenResponse, error) { +func (a *authenticationServiceImpl) RefreshToken(ctx context.Context, refreshToken string) (*apisv1.RefreshTokenResponse, error) { claim, err := ParseToken(refreshToken) if err != nil { if errors.Is(err, bcode.ErrTokenExpired) { @@ -260,8 +250,8 @@ func ParseToken(tokenString string) (*model.CustomClaims, error) { return nil, bcode.ErrTokenInvalid } -func (a *authenticationUsecaseImpl) GetDexConfig(ctx context.Context) (*apisv1.DexConfigResponse, error) { - config, err := getDexConfig(ctx, a.kubeClient) +func (a *authenticationServiceImpl) GetDexConfig(ctx context.Context) (*apisv1.DexConfigResponse, error) { + config, err := getDexConfig(ctx, a.KubeClient) if err != nil { return nil, err } @@ -426,8 +416,8 @@ func getDexConfig(ctx context.Context, kubeClient client.Client) (*model.DexConf return config, nil } -func (a *authenticationUsecaseImpl) GetLoginType(ctx context.Context) (*apisv1.GetLoginTypeResponse, error) { - sysInfo, err := a.sysUsecase.Get(ctx) +func (a *authenticationServiceImpl) GetLoginType(ctx context.Context) (*apisv1.GetLoginTypeResponse, error) { + sysInfo, err := a.SysService.Get(ctx) if err != nil { return nil, err } @@ -451,18 +441,18 @@ func (d *dexHandlerImpl) login(ctx context.Context) (*apisv1.UserBase, error) { user := &model.User{Email: claims.Email} userBase := &apisv1.UserBase{Email: claims.Email, Name: claims.Name} - users, err := d.ds.List(ctx, user, &datastore.ListOptions{}) + users, err := d.Store.List(ctx, user, &datastore.ListOptions{}) if err != nil { return nil, err } if len(users) > 0 { u := users[0].(*model.User) u.LastLoginTime = time.Now() - if err := d.ds.Put(ctx, u); err != nil { + if err := d.Store.Put(ctx, u); err != nil { return nil, err } userBase.Name = u.Name - } else if err := d.ds.Add(ctx, &model.User{ + } else if err := d.Store.Add(ctx, &model.User{ Email: claims.Email, Name: claims.Name, LastLoginTime: time.Now(), @@ -474,7 +464,7 @@ func (d *dexHandlerImpl) login(ctx context.Context) (*apisv1.UserBase, error) { } func (l *localHandlerImpl) login(ctx context.Context) (*apisv1.UserBase, error) { - user, err := l.userUsecase.GetUser(ctx, l.username) + user, err := l.userService.GetUser(ctx, l.username) if err != nil { if errors.Is(err, datastore.ErrRecordNotExist) { return nil, bcode.ErrUsernameNotExist @@ -484,7 +474,7 @@ func (l *localHandlerImpl) login(ctx context.Context) (*apisv1.UserBase, error) if err := compareHashWithPassword(user.Password, l.password); err != nil { return nil, err } - if err := l.userUsecase.UpdateUserLoginTime(ctx, user); err != nil { + if err := l.userService.UpdateUserLoginTime(ctx, user); err != nil { return nil, err } return &apisv1.UserBase{ diff --git a/pkg/apiserver/rest/usecase/authentication_test.go b/pkg/apiserver/domain/service/authentication_test.go similarity index 85% rename from pkg/apiserver/rest/usecase/authentication_test.go rename to pkg/apiserver/domain/service/authentication_test.go index 69d7e45d8..45e7f92f2 100644 --- a/pkg/apiserver/rest/usecase/authentication_test.go +++ b/pkg/apiserver/domain/service/authentication_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package usecase +package service import ( "context" @@ -36,18 +36,18 @@ import ( "github.com/oam-dev/kubevela/apis/core.oam.dev/common" "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/model" - apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" + apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils" "github.com/oam-dev/kubevela/pkg/oam/util" ) -var _ = Describe("Test authentication usecase functions", func() { +var _ = Describe("Test authentication service functions", func() { var ( - authUsecase *authenticationUsecaseImpl - userUsecase *userUsecaseImpl - sysUsecase *systemInfoUsecaseImpl + authService *authenticationServiceImpl + userService *userServiceImpl + sysService *systemInfoServiceImpl ds datastore.DataStore ) @@ -56,9 +56,9 @@ var _ = Describe("Test authentication usecase functions", func() { ds, err = NewDatastore(datastore.Config{Type: "kubeapi", Database: "auth-test-" + strconv.FormatInt(time.Now().UnixNano(), 10)}) Expect(ds).ToNot(BeNil()) Expect(err).Should(BeNil()) - authUsecase = &authenticationUsecaseImpl{kubeClient: k8sClient, ds: ds} - sysUsecase = &systemInfoUsecaseImpl{ds: ds, kubeClient: k8sClient} - userUsecase = &userUsecaseImpl{ds: ds, sysUsecase: sysUsecase} + authService = &authenticationServiceImpl{KubeClient: k8sClient, Store: ds} + sysService = &systemInfoServiceImpl{Store: ds, KubeClient: k8sClient} + userService = &userServiceImpl{Store: ds, SysService: sysService} }) It("Test Dex login", func() { testIDToken := &oidc.IDToken{} @@ -68,7 +68,7 @@ var _ = Describe("Test authentication usecase functions", func() { defer patch.Reset() dexHandler := dexHandlerImpl{ idToken: testIDToken, - ds: ds, + Store: ds, } resp, err := dexHandler.login(context.Background()) Expect(err).Should(BeNil()) @@ -98,14 +98,14 @@ var _ = Describe("Test authentication usecase functions", func() { }) It("Test local login", func() { - _, err := userUsecase.CreateUser(context.Background(), apisv1.CreateUserRequest{ + _, err := userService.CreateUser(context.Background(), apisv1.CreateUserRequest{ Name: "test-login", Email: "test@example.com", Password: "password1", }) Expect(err).Should(BeNil()) localHandler := localHandlerImpl{ - userUsecase: userUsecase, + userService: userService, ds: ds, username: "test-login", password: "password1", @@ -167,9 +167,9 @@ var _ = Describe("Test authentication usecase functions", func() { }) Expect(err).Should(BeNil()) By("try to update dex config without config secret") - connectors, err := utils.GetDexConnectors(context.Background(), authUsecase.kubeClient) + connectors, err := utils.GetDexConnectors(context.Background(), authService.KubeClient) Expect(err).Should(BeNil()) - err = generateDexConfig(context.Background(), authUsecase.kubeClient, &model.UpdateDexConfig{ + err = generateDexConfig(context.Background(), authService.KubeClient, &model.UpdateDexConfig{ Connectors: connectors, }) Expect(err).Should(BeNil()) @@ -181,19 +181,19 @@ var _ = Describe("Test authentication usecase functions", func() { Expect(err).Should(BeNil()) Expect(len(config.Connectors)).Should(Equal(1)) By("try to update dex config with config secret") - err = generateDexConfig(context.Background(), authUsecase.kubeClient, &model.UpdateDexConfig{}) + err = generateDexConfig(context.Background(), authService.KubeClient, &model.UpdateDexConfig{}) Expect(err).Should(BeNil()) }) It("Test get dex config", func() { err := ds.Add(context.Background(), &model.User{Name: "admin", Email: "test@test.com"}) Expect(err).Should(BeNil()) - _, err = sysUsecase.UpdateSystemInfo(context.Background(), apisv1.SystemInfoRequest{ + _, err = sysService.UpdateSystemInfo(context.Background(), apisv1.SystemInfoRequest{ LoginType: model.LoginTypeDex, VelaAddress: "http://velaux.com", }) Expect(err).Should(BeNil()) - config, err := authUsecase.GetDexConfig(context.Background()) + config, err := authService.GetDexConfig(context.Background()) Expect(err).Should(BeNil()) Expect(config.Issuer).Should(Equal("http://velaux.com/dex")) Expect(config.ClientID).Should(Equal("velaux")) diff --git a/pkg/apiserver/rest/usecase/cluster.go b/pkg/apiserver/domain/service/cluster.go similarity index 83% rename from pkg/apiserver/rest/usecase/cluster.go rename to pkg/apiserver/domain/service/cluster.go index 11e35596d..c209ce09f 100644 --- a/pkg/apiserver/rest/usecase/cluster.go +++ b/pkg/apiserver/domain/service/cluster.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package usecase +package service import ( "context" @@ -35,21 +35,21 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" velatypes "github.com/oam-dev/kubevela/apis/types" - "github.com/oam-dev/kubevela/pkg/apiserver/clients" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/log" - "github.com/oam-dev/kubevela/pkg/apiserver/model" - apis "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - utils2 "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/clients" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" + apis "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + utils2 "github.com/oam-dev/kubevela/pkg/apiserver/utils" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/log" "github.com/oam-dev/kubevela/pkg/cloudprovider" "github.com/oam-dev/kubevela/pkg/multicluster" "github.com/oam-dev/kubevela/pkg/utils" "github.com/oam-dev/kubevela/pkg/utils/util" ) -// ClusterUsecase cluster manage -type ClusterUsecase interface { +// ClusterService cluster manage +type ClusterService interface { ListKubeClusters(context.Context, string, int, int) (*apis.ListClusterResponse, error) CreateKubeCluster(context.Context, apis.CreateClusterRequest) (*apis.ClusterBase, error) GetKubeCluster(context.Context, string) (*apis.DetailClusterResponse, error) @@ -64,27 +64,25 @@ type ClusterUsecase interface { GetCloudClusterCreationStatus(context.Context, string, string) (*apis.CreateCloudClusterResponse, error) ListCloudClusterCreation(context.Context, string) (*apis.ListCloudClusterCreationResponse, error) DeleteCloudClusterCreation(context.Context, string, string) (*apis.CreateCloudClusterResponse, error) + Init(ctx context.Context) error } -type clusterUsecaseImpl struct { - ds datastore.DataStore +type clusterServiceImpl struct { + Store datastore.DataStore `inject:"datastore"` + K8sClient client.Client `inject:"kubeClient"` caches *utils2.MemoryCacheStore - k8sClient client.Client } -// NewClusterUsecase new cluster usecase -func NewClusterUsecase(ds datastore.DataStore) ClusterUsecase { - k8sClient, err := clients.GetKubeClient() - if err != nil { - log.Logger.Fatalf("get k8sClient failure: %s", err.Error()) - } - c := &clusterUsecaseImpl{ds: ds, k8sClient: k8sClient, caches: utils2.NewMemoryCacheStore(context.Background())} - if err = c.preAddLocalCluster(context.Background()); err != nil { - log.Logger.Fatalf("preAdd local cluster failure: %s", err.Error()) - } +// NewClusterService new cluster service +func NewClusterService() ClusterService { + c := &clusterServiceImpl{caches: utils2.NewMemoryCacheStore(context.Background())} return c } +func (c *clusterServiceImpl) Init(ctx context.Context) error { + return c.preAddLocalCluster(ctx) +} + func _getClusterFromDataStore(ctx context.Context, ds datastore.DataStore, clusterName string) (*model.Cluster, error) { cluster := &model.Cluster{Name: clusterName} if err := ds.Get(ctx, cluster); err != nil { @@ -93,35 +91,35 @@ func _getClusterFromDataStore(ctx context.Context, ds datastore.DataStore, clust return cluster, nil } -func (c *clusterUsecaseImpl) getClusterFromDataStore(ctx context.Context, clusterName string) (*model.Cluster, error) { - return _getClusterFromDataStore(ctx, c.ds, clusterName) +func (c *clusterServiceImpl) getClusterFromDataStore(ctx context.Context, clusterName string) (*model.Cluster, error) { + return _getClusterFromDataStore(ctx, c.Store, clusterName) } -func (c *clusterUsecaseImpl) rollbackAddedClusterInDataStore(ctx context.Context, cluster *model.Cluster) { - if e := c.ds.Delete(ctx, cluster); e != nil { +func (c *clusterServiceImpl) rollbackAddedClusterInDataStore(ctx context.Context, cluster *model.Cluster) { + if e := c.Store.Delete(ctx, cluster); e != nil { log.Logger.Errorf("failed to rollback added cluster %s in data store: %s", utils.Sanitize(cluster.Name), e.Error()) } } -func (c *clusterUsecaseImpl) rollbackDeletedClusterInDataStore(ctx context.Context, cluster *model.Cluster) { - if e := c.ds.Add(ctx, cluster); e != nil { +func (c *clusterServiceImpl) rollbackDeletedClusterInDataStore(ctx context.Context, cluster *model.Cluster) { + if e := c.Store.Add(ctx, cluster); e != nil { log.Logger.Errorf("failed to rollback deleted cluster %s in data store: %s", utils.Sanitize(cluster.Name), e.Error()) } } -func (c *clusterUsecaseImpl) rollbackJoinedKubeCluster(ctx context.Context, cluster *model.Cluster) { - if e := multicluster.DetachCluster(ctx, c.k8sClient, cluster.Name); e != nil { +func (c *clusterServiceImpl) rollbackJoinedKubeCluster(ctx context.Context, cluster *model.Cluster) { + if e := multicluster.DetachCluster(ctx, c.K8sClient, cluster.Name); e != nil { log.Logger.Errorf("failed to rollback joined cluster %s in kubevela: %s", utils.Sanitize(cluster.Name), e.Error()) } } -func (c *clusterUsecaseImpl) rollbackDetachedKubeCluster(ctx context.Context, cluster *model.Cluster) { - if _, e := joinClusterByKubeConfigString(ctx, c.k8sClient, cluster.Name, cluster.KubeConfig); e != nil { +func (c *clusterServiceImpl) rollbackDetachedKubeCluster(ctx context.Context, cluster *model.Cluster) { + if _, e := joinClusterByKubeConfigString(ctx, c.K8sClient, cluster.Name, cluster.KubeConfig); e != nil { log.Logger.Errorf("failed to rollback detached cluster %s in kubevela: %s", utils.Sanitize(cluster.Name), e.Error()) } } -func (c *clusterUsecaseImpl) preAddLocalCluster(ctx context.Context) error { +func (c *clusterServiceImpl) preAddLocalCluster(ctx context.Context) error { cfg, err := clients.GetKubeConfig() if err != nil { return err @@ -132,10 +130,10 @@ func (c *clusterUsecaseImpl) preAddLocalCluster(ctx context.Context) error { Status: model.ClusterStatusHealthy, APIServerURL: cfg.Host + cfg.APIPath, } - if err = c.ds.Get(ctx, localCluster); err != nil { + if err = c.Store.Get(ctx, localCluster); err != nil { // no local cluster in datastore if errors.Is(err, datastore.ErrRecordNotExist) { - if err = c.ds.Add(ctx, localCluster); err != nil { + if err = c.Store.Add(ctx, localCluster); err != nil { // local cluster already added in datastore if errors.Is(err, datastore.ErrRecordExist) { return nil @@ -148,19 +146,19 @@ func (c *clusterUsecaseImpl) preAddLocalCluster(ctx context.Context) error { } if localCluster.CreateTime.Before(model.LocalClusterCreatedTime) { localCluster.CreateTime = model.LocalClusterCreatedTime - if err = c.ds.Put(ctx, localCluster); err != nil { + if err = c.Store.Put(ctx, localCluster); err != nil { return err } } return nil } -func (c *clusterUsecaseImpl) ListKubeClusters(ctx context.Context, query string, page int, pageSize int) (*apis.ListClusterResponse, error) { - clusters, err := prismclusterv1alpha1.NewClusterClient(c.k8sClient).List(ctx) +func (c *clusterServiceImpl) ListKubeClusters(ctx context.Context, query string, page int, pageSize int) (*apis.ListClusterResponse, error) { + clusters, err := prismclusterv1alpha1.NewClusterClient(c.K8sClient).List(ctx) if err != nil { return nil, errors.Wrapf(err, "failed to get clusters with ClusterClient") } - clustersInfo, err := c.ds.List(ctx, &model.Cluster{}, nil) + clustersInfo, err := c.Store.List(ctx, &model.Cluster{}, nil) if err != nil { return nil, errors.Wrapf(err, "failed to list cluster in data store") } @@ -230,7 +228,7 @@ func createClusterModelFromRequest(req apis.CreateClusterRequest, oldCluster *mo return newCluster } -func (c *clusterUsecaseImpl) createKubeCluster(ctx context.Context, req apis.CreateClusterRequest, providerCluster *cloudprovider.CloudCluster) (*apis.ClusterBase, error) { +func (c *clusterServiceImpl) createKubeCluster(ctx context.Context, req apis.CreateClusterRequest, providerCluster *cloudprovider.CloudCluster) (*apis.ClusterBase, error) { var err error cluster := createClusterModelFromRequest(req, nil) if cluster.Name == multicluster.ClusterLocalName { @@ -252,18 +250,18 @@ func (c *clusterUsecaseImpl) createKubeCluster(ctx context.Context, req apis.Cre } cluster.DashboardURL = providerCluster.DashBoardURL } - if err = c.ds.Get(ctx, cluster); err == nil { + if err = c.Store.Get(ctx, cluster); err == nil { return nil, bcode.ErrClusterAlreadyExistInDataStore } else if !errors.Is(err, datastore.ErrRecordNotExist) { return nil, err } if req.KubeConfig != "" { - cluster.APIServerURL, err = joinClusterByKubeConfigString(ctx, c.k8sClient, req.Name, req.KubeConfig) + cluster.APIServerURL, err = joinClusterByKubeConfigString(ctx, c.K8sClient, req.Name, req.KubeConfig) if err != nil { return nil, err } c.setClusterStatusAndResourceInfo(ctx, cluster) - if err = c.ds.Add(ctx, cluster); err != nil { + if err = c.Store.Add(ctx, cluster); err != nil { c.rollbackJoinedKubeCluster(ctx, cluster) if errors.Is(err, datastore.ErrRecordExist) { return nil, bcode.ErrClusterAlreadyExistInDataStore @@ -278,17 +276,17 @@ func (c *clusterUsecaseImpl) createKubeCluster(ctx context.Context, req apis.Cre return nil, bcode.ErrKubeConfigAndSecretIsNotSet } -func (c *clusterUsecaseImpl) CreateKubeCluster(ctx context.Context, req apis.CreateClusterRequest) (*apis.ClusterBase, error) { +func (c *clusterServiceImpl) CreateKubeCluster(ctx context.Context, req apis.CreateClusterRequest) (*apis.ClusterBase, error) { return c.createKubeCluster(ctx, req, nil) } -func (c *clusterUsecaseImpl) GetKubeCluster(ctx context.Context, clusterName string) (*apis.DetailClusterResponse, error) { +func (c *clusterServiceImpl) GetKubeCluster(ctx context.Context, clusterName string) (*apis.DetailClusterResponse, error) { cluster, err := c.getClusterFromDataStore(ctx, clusterName) if err != nil { if !errors.Is(err, datastore.ErrRecordNotExist) { return nil, errors.Wrapf(err, "failed to find cluster %s in data store", clusterName) } - prismCluster, err := prismclusterv1alpha1.NewClusterClient(c.k8sClient).Get(ctx, clusterName) + prismCluster, err := prismclusterv1alpha1.NewClusterClient(c.K8sClient).Get(ctx, clusterName) if err != nil { if kerrors.IsNotFound(err) { return nil, bcode.ErrClusterNotFoundInDataStore @@ -296,12 +294,12 @@ func (c *clusterUsecaseImpl) GetKubeCluster(ctx context.Context, clusterName str return nil, errors.Wrapf(err, "failed to find cluster %s in control plane", clusterName) } cluster = newClusterModelFromPrismCluster(prismCluster) - if err = c.ds.Add(ctx, cluster); err != nil { + if err = c.Store.Add(ctx, cluster); err != nil { return nil, errors.Wrapf(err, "failed to add cluster %s from existing prism cluster", clusterName) } } resourceInfo := c.setClusterStatusAndResourceInfo(ctx, cluster) - if err = c.ds.Put(ctx, cluster); err != nil { + if err = c.Store.Put(ctx, cluster); err != nil { return nil, errors.Wrapf(err, "failed to update cluster %s status info", clusterName) } return &apis.DetailClusterResponse{ @@ -310,7 +308,7 @@ func (c *clusterUsecaseImpl) GetKubeCluster(ctx context.Context, clusterName str }, nil } -func (c *clusterUsecaseImpl) ModifyKubeCluster(ctx context.Context, req apis.CreateClusterRequest, clusterName string) (*apis.ClusterBase, error) { +func (c *clusterServiceImpl) ModifyKubeCluster(ctx context.Context, req apis.CreateClusterRequest, clusterName string) (*apis.ClusterBase, error) { oldCluster, err := c.getClusterFromDataStore(ctx, clusterName) if err != nil { if errors.Is(err, datastore.ErrRecordNotExist) { @@ -329,7 +327,7 @@ func (c *clusterUsecaseImpl) ModifyKubeCluster(ctx context.Context, req apis.Cre return nil, bcode.ErrKubeConfigSecretNotSupport } newClusterTempName := newCluster.Name + "_tmp_" + utils.RandomString(8) - newCluster.APIServerURL, err = joinClusterByKubeConfigString(ctx, c.k8sClient, newCluster.Name, newCluster.KubeConfig) + newCluster.APIServerURL, err = joinClusterByKubeConfigString(ctx, c.K8sClient, newCluster.Name, newCluster.KubeConfig) if err != nil { return nil, errors.Wrapf(err, "failed to join new cluster %s", newCluster.Name) } @@ -339,11 +337,11 @@ func (c *clusterUsecaseImpl) ModifyKubeCluster(ctx context.Context, req apis.Cre rollBackCluster.Name = newClusterTempName c.rollbackJoinedKubeCluster(ctx, rollBackCluster) } - if err = multicluster.DetachCluster(ctx, c.k8sClient, oldCluster.Name); err != nil { + if err = multicluster.DetachCluster(ctx, c.K8sClient, oldCluster.Name); err != nil { rollbackTempCluster() return nil, errors.Wrapf(err, "failed to detach old cluster %s", oldCluster.Name) } - if err = c.ds.Delete(ctx, oldCluster); err != nil { + if err = c.Store.Delete(ctx, oldCluster); err != nil { rollbackTempCluster() c.rollbackDetachedKubeCluster(ctx, oldCluster) if errors.Is(err, datastore.ErrRecordNotExist) { @@ -351,7 +349,7 @@ func (c *clusterUsecaseImpl) ModifyKubeCluster(ctx context.Context, req apis.Cre } return nil, errors.Wrapf(err, "failed to delete old cluster %s from datastore", oldCluster.Name) } - if err = c.ds.Add(ctx, newCluster); err != nil { + if err = c.Store.Add(ctx, newCluster); err != nil { rollbackTempCluster() c.rollbackDetachedKubeCluster(ctx, oldCluster) c.rollbackDeletedClusterInDataStore(ctx, oldCluster) @@ -360,7 +358,7 @@ func (c *clusterUsecaseImpl) ModifyKubeCluster(ctx context.Context, req apis.Cre } return nil, errors.Wrapf(err, "failed to add new cluster %s to datastore", newCluster.Name) } - if err = multicluster.RenameCluster(ctx, c.k8sClient, newClusterTempName, newCluster.Name); err != nil { + if err = multicluster.RenameCluster(ctx, c.K8sClient, newClusterTempName, newCluster.Name); err != nil { rollbackTempCluster() c.rollbackDetachedKubeCluster(ctx, oldCluster) c.rollbackDeletedClusterInDataStore(ctx, oldCluster) @@ -370,14 +368,14 @@ func (c *clusterUsecaseImpl) ModifyKubeCluster(ctx context.Context, req apis.Cre } else { newCluster.Status = oldCluster.Status newCluster.Reason = oldCluster.Reason - if err = c.ds.Put(ctx, newCluster); err != nil { + if err = c.Store.Put(ctx, newCluster); err != nil { return nil, errors.Wrapf(err, "failed to update cluster %s", newCluster.Name) } } return newClusterBaseFromCluster(newCluster), nil } -func (c *clusterUsecaseImpl) DeleteKubeCluster(ctx context.Context, clusterName string) (*apis.ClusterBase, error) { +func (c *clusterServiceImpl) DeleteKubeCluster(ctx context.Context, clusterName string) (*apis.ClusterBase, error) { if clusterName == multicluster.ClusterLocalName { return nil, bcode.ErrLocalClusterImmutable } @@ -388,20 +386,20 @@ func (c *clusterUsecaseImpl) DeleteKubeCluster(ctx context.Context, clusterName } return nil, errors.Wrapf(err, "failed to found cluster %s in data store", clusterName) } - if err = c.ds.Delete(ctx, cluster); err != nil { + if err = c.Store.Delete(ctx, cluster); err != nil { if errors.Is(err, datastore.ErrRecordNotExist) { return nil, bcode.ErrClusterNotFoundInDataStore } return nil, errors.Wrapf(err, "failed to delete cluster %s in data store", clusterName) } - if err = multicluster.DetachCluster(ctx, c.k8sClient, clusterName); err != nil { + if err = multicluster.DetachCluster(ctx, c.K8sClient, clusterName); err != nil { c.rollbackDeletedClusterInDataStore(ctx, cluster) return nil, errors.Wrapf(err, "failed to delete cluster %s in kubernetes", clusterName) } return newClusterBaseFromCluster(cluster), nil } -func (c *clusterUsecaseImpl) CreateClusterNamespace(ctx context.Context, clusterName string, req apis.CreateClusterNamespaceRequest) (*apis.CreateClusterNamespaceResponse, error) { +func (c *clusterServiceImpl) CreateClusterNamespace(ctx context.Context, clusterName string, req apis.CreateClusterNamespaceRequest) (*apis.CreateClusterNamespaceResponse, error) { _, err := c.getClusterFromDataStore(ctx, clusterName) if err != nil { if errors.Is(err, datastore.ErrRecordNotExist) { @@ -411,7 +409,7 @@ func (c *clusterUsecaseImpl) CreateClusterNamespace(ctx context.Context, cluster } ns := &v12.Namespace{} ns.Name = req.Namespace - if err = c.k8sClient.Create(multicluster.ContextWithClusterName(ctx, clusterName), ns); err != nil { + if err = c.K8sClient.Create(multicluster.ContextWithClusterName(ctx, clusterName), ns); err != nil { if kerrors.IsAlreadyExists(err) { return &apis.CreateClusterNamespaceResponse{Exists: true}, nil } @@ -423,7 +421,7 @@ func (c *clusterUsecaseImpl) CreateClusterNamespace(ctx context.Context, cluster return &apis.CreateClusterNamespaceResponse{Exists: false}, nil } -func (c *clusterUsecaseImpl) setClusterStatusAndResourceInfo(ctx context.Context, cluster *model.Cluster) apis.ClusterResourceInfo { +func (c *clusterServiceImpl) setClusterStatusAndResourceInfo(ctx context.Context, cluster *model.Cluster) apis.ClusterResourceInfo { resourceInfo, err := c.getClusterResourceInfoFromK8s(ctx, cluster.Name) if err != nil { cluster.Status = model.ClusterStatusUnhealthy @@ -435,16 +433,16 @@ func (c *clusterUsecaseImpl) setClusterStatusAndResourceInfo(ctx context.Context return resourceInfo } -func (c *clusterUsecaseImpl) getClusterResourceInfoCacheKey(clusterName string) string { +func (c *clusterServiceImpl) getClusterResourceInfoCacheKey(clusterName string) string { return "cluster-resource-info::" + clusterName } -func (c *clusterUsecaseImpl) getClusterResourceInfoFromK8s(ctx context.Context, clusterName string) (apis.ClusterResourceInfo, error) { +func (c *clusterServiceImpl) getClusterResourceInfoFromK8s(ctx context.Context, clusterName string) (apis.ClusterResourceInfo, error) { cacheKey := c.getClusterResourceInfoCacheKey(clusterName) if cache := c.caches.Get(cacheKey); cache != nil { return cache.(apis.ClusterResourceInfo), nil } - clusterInfo, err := multicluster.GetClusterInfo(ctx, c.k8sClient, clusterName) + clusterInfo, err := multicluster.GetClusterInfo(ctx, c.K8sClient, clusterName) if err != nil { return apis.ClusterResourceInfo{}, err } @@ -475,8 +473,8 @@ func (c *clusterUsecaseImpl) getClusterResourceInfoFromK8s(ctx context.Context, return clusterResourceInfo, nil } -func (c *clusterUsecaseImpl) ListCloudClusters(ctx context.Context, provider string, req apis.AccessKeyRequest, pageNumber int, pageSize int) (*apis.ListCloudClusterResponse, error) { - p, err := cloudprovider.GetClusterProvider(provider, req.AccessKeyID, req.AccessKeySecret, c.k8sClient) +func (c *clusterServiceImpl) ListCloudClusters(ctx context.Context, provider string, req apis.AccessKeyRequest, pageNumber int, pageSize int) (*apis.ListCloudClusterResponse, error) { + p, err := cloudprovider.GetClusterProvider(provider, req.AccessKeyID, req.AccessKeySecret, c.K8sClient) if err != nil { log.Logger.Errorf("failed to get cluster provider: %s", err.Error()) return nil, bcode.ErrInvalidCloudClusterProvider @@ -499,8 +497,8 @@ func (c *clusterUsecaseImpl) ListCloudClusters(ctx context.Context, provider str return resp, nil } -func (c *clusterUsecaseImpl) ConnectCloudCluster(ctx context.Context, provider string, req apis.ConnectCloudClusterRequest) (*apis.ClusterBase, error) { - p, err := cloudprovider.GetClusterProvider(provider, req.AccessKeyID, req.AccessKeySecret, c.k8sClient) +func (c *clusterServiceImpl) ConnectCloudCluster(ctx context.Context, provider string, req apis.ConnectCloudClusterRequest) (*apis.ClusterBase, error) { + p, err := cloudprovider.GetClusterProvider(provider, req.AccessKeyID, req.AccessKeySecret, c.K8sClient) if err != nil { log.Logger.Errorf("failed to get cluster provider: %s", err.Error()) return nil, bcode.ErrInvalidCloudClusterProvider @@ -529,8 +527,8 @@ func (c *clusterUsecaseImpl) ConnectCloudCluster(ctx context.Context, provider s return c.createKubeCluster(ctx, createReq, cluster) } -func (c *clusterUsecaseImpl) CreateCloudCluster(ctx context.Context, provider string, req apis.CreateCloudClusterRequest) (*apis.CreateCloudClusterResponse, error) { - p, err := cloudprovider.GetClusterProvider(provider, req.AccessKeyID, req.AccessKeySecret, c.k8sClient) +func (c *clusterServiceImpl) CreateCloudCluster(ctx context.Context, provider string, req apis.CreateCloudClusterRequest) (*apis.CreateCloudClusterResponse, error) { + p, err := cloudprovider.GetClusterProvider(provider, req.AccessKeyID, req.AccessKeySecret, c.K8sClient) if err != nil { log.Logger.Errorf("failed to get cluster provider: %s", err.Error()) return nil, bcode.ErrInvalidCloudClusterProvider @@ -546,7 +544,7 @@ func (c *clusterUsecaseImpl) CreateCloudCluster(ctx context.Context, provider st return c.GetCloudClusterCreationStatus(ctx, provider, req.Name) } -func (c *clusterUsecaseImpl) convertTerraformConfigurationStateIntoCloudClusterCreationStatus(cfg v1beta1.Configuration) (status string, clusterID string, err error) { +func (c *clusterServiceImpl) convertTerraformConfigurationStateIntoCloudClusterCreationStatus(cfg v1beta1.Configuration) (status string, clusterID string, err error) { status = string(cfg.Status.Apply.State) if status == "" { return "Initializing", "", nil @@ -565,7 +563,7 @@ func (c *clusterUsecaseImpl) convertTerraformConfigurationStateIntoCloudClusterC return status, "", nil } -func (c *clusterUsecaseImpl) getCloudClusterCreationStatus(ctx context.Context, provider string, cloudClusterName string) (*apis.CreateCloudClusterResponse, *v1beta1.Configuration, error) { +func (c *clusterServiceImpl) getCloudClusterCreationStatus(ctx context.Context, provider string, cloudClusterName string) (*apis.CreateCloudClusterResponse, *v1beta1.Configuration, error) { terraformConfigurationName := cloudprovider.GetCloudClusterFullName(provider, cloudClusterName) cfg := &v1beta1.Configuration{ ObjectMeta: v1.ObjectMeta{ @@ -573,7 +571,7 @@ func (c *clusterUsecaseImpl) getCloudClusterCreationStatus(ctx context.Context, Namespace: util.GetRuntimeNamespace(), }, } - if err := c.k8sClient.Get(ctx, client.ObjectKeyFromObject(cfg), cfg); err != nil { + if err := c.K8sClient.Get(ctx, client.ObjectKeyFromObject(cfg), cfg); err != nil { if kerrors.IsNotFound(err) { return nil, nil, bcode.ErrTerraformConfigurationNotFound } @@ -586,14 +584,14 @@ func (c *clusterUsecaseImpl) getCloudClusterCreationStatus(ctx context.Context, return &apis.CreateCloudClusterResponse{Name: cloudClusterName, Status: status, ClusterID: clusterID}, cfg, nil } -func (c *clusterUsecaseImpl) GetCloudClusterCreationStatus(ctx context.Context, provider string, cloudClusterName string) (*apis.CreateCloudClusterResponse, error) { +func (c *clusterServiceImpl) GetCloudClusterCreationStatus(ctx context.Context, provider string, cloudClusterName string) (*apis.CreateCloudClusterResponse, error) { resp, _, err := c.getCloudClusterCreationStatus(ctx, provider, cloudClusterName) return resp, err } -func (c *clusterUsecaseImpl) ListCloudClusterCreation(ctx context.Context, provider string) (*apis.ListCloudClusterCreationResponse, error) { +func (c *clusterServiceImpl) ListCloudClusterCreation(ctx context.Context, provider string) (*apis.ListCloudClusterCreationResponse, error) { cfgs := v1beta1.ConfigurationList{} - if err := c.k8sClient.List(ctx, &cfgs, client.HasLabels{cloudprovider.CloudClusterCreatorLabelKey}, client.InNamespace(util.GetRuntimeNamespace())); err != nil { + if err := c.K8sClient.List(ctx, &cfgs, client.HasLabels{cloudprovider.CloudClusterCreatorLabelKey}, client.InNamespace(util.GetRuntimeNamespace())); err != nil { return nil, err } var creations []apis.CreateCloudClusterResponse @@ -608,12 +606,12 @@ func (c *clusterUsecaseImpl) ListCloudClusterCreation(ctx context.Context, provi return &apis.ListCloudClusterCreationResponse{Creations: creations}, nil } -func (c *clusterUsecaseImpl) DeleteCloudClusterCreation(ctx context.Context, provider string, cloudClusterName string) (*apis.CreateCloudClusterResponse, error) { +func (c *clusterServiceImpl) DeleteCloudClusterCreation(ctx context.Context, provider string, cloudClusterName string) (*apis.CreateCloudClusterResponse, error) { resp, cfg, err := c.getCloudClusterCreationStatus(ctx, provider, cloudClusterName) if err != nil { return resp, err } - if err = c.k8sClient.Delete(ctx, cfg); err != nil { + if err = c.K8sClient.Delete(ctx, cfg); err != nil { if kerrors.IsNotFound(err) { return resp, nil } diff --git a/pkg/apiserver/rest/usecase/cluster_test.go b/pkg/apiserver/domain/service/cluster_test.go similarity index 85% rename from pkg/apiserver/rest/usecase/cluster_test.go rename to pkg/apiserver/domain/service/cluster_test.go index a5252635a..ec04783f0 100644 --- a/pkg/apiserver/rest/usecase/cluster_test.go +++ b/pkg/apiserver/domain/service/cluster_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package usecase +package service import ( "context" @@ -29,15 +29,15 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/model" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" + "github.com/oam-dev/kubevela/pkg/apiserver/utils" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" "github.com/oam-dev/kubevela/pkg/oam/util" pkgutil "github.com/oam-dev/kubevela/pkg/utils" ) -var _ = Describe("Test cluster usecase function", func() { +var _ = Describe("Test cluster service function", func() { var ( ds datastore.DataStore cache *utils.MemoryCacheStore @@ -74,53 +74,53 @@ var _ = Describe("Test cluster usecase function", func() { }) It("Test get kube cluster", func() { - usecase := clusterUsecaseImpl{ - ds: ds, + service := clusterServiceImpl{ + Store: ds, caches: cache, - k8sClient: k8sClient, + K8sClient: k8sClient, } Expect(ds.Add(ctx, &model.Cluster{Name: "first", Alias: "first-alias", Icon: "first-icon"})).Should(Succeed()) - resp, err := usecase.GetKubeCluster(ctx, "first") + resp, err := service.GetKubeCluster(ctx, "first") Expect(err).Should(Succeed()) Expect(resp.Alias).Should(Equal("first-alias")) Expect(resp.Icon).Should(Equal("first-icon")) - _, err = usecase.GetKubeCluster(ctx, "prism-cluster") + _, err = service.GetKubeCluster(ctx, "prism-cluster") Expect(err).Should(Equal(bcode.ErrClusterNotFoundInDataStore)) Expect(createClusterSecret("prism-cluster", "prism-alias")).Should(Succeed()) - resp, err = usecase.GetKubeCluster(ctx, "prism-cluster") + resp, err = service.GetKubeCluster(ctx, "prism-cluster") Expect(err).Should(Succeed()) Expect(resp.Alias).Should(Equal("prism-alias")) - _, err = usecase.GetKubeCluster(ctx, "non-exist-cluster") + _, err = service.GetKubeCluster(ctx, "non-exist-cluster") Expect(err).Should(Equal(bcode.ErrClusterNotFoundInDataStore)) }) It("Test list kube clusters", func() { - usecase := clusterUsecaseImpl{ - ds: ds, + service := clusterServiceImpl{ + Store: ds, caches: cache, - k8sClient: k8sClient, + K8sClient: k8sClient, } Expect(createClusterSecret("prism-cluster1", "prism-alias1")).Should(Succeed()) Expect(ds.Add(ctx, &model.Cluster{Name: "prism-cluster1", Alias: "prism-alias1", Icon: "prism-icon1"})).Should(Succeed()) Expect(ds.Add(ctx, &model.Cluster{Name: "local"})).Should(Succeed()) - resp, err := usecase.ListKubeClusters(ctx, "", 1, 5) + resp, err := service.ListKubeClusters(ctx, "", 1, 5) Expect(err).Should(Succeed()) Expect(len(resp.Clusters)).Should(Equal(2)) Expect(resp.Clusters[0].Name).Should(Equal("local")) Expect(resp.Clusters[1].Name).Should(Equal("prism-cluster1")) Expect(createClusterSecret("prism-cluster2", "prism-alias2")).Should(Succeed()) Expect(createClusterSecret("cluster3", "prism-alias3")).Should(Succeed()) - resp, err = usecase.ListKubeClusters(ctx, "", 1, 5) + resp, err = service.ListKubeClusters(ctx, "", 1, 5) Expect(err).Should(Succeed()) Expect(len(resp.Clusters)).Should(Equal(4)) Expect(resp.Clusters[3].Icon).Should(Equal("prism-icon1")) - resp, err = usecase.ListKubeClusters(ctx, "prism-cluster", 1, 5) + resp, err = service.ListKubeClusters(ctx, "prism-cluster", 1, 5) Expect(err).Should(Succeed()) Expect(len(resp.Clusters)).Should(Equal(2)) - resp, err = usecase.ListKubeClusters(ctx, "", 2, 3) + resp, err = service.ListKubeClusters(ctx, "", 2, 3) Expect(err).Should(Succeed()) Expect(len(resp.Clusters)).Should(Equal(1)) - resp, err = usecase.ListKubeClusters(ctx, "", 3, 3) + resp, err = service.ListKubeClusters(ctx, "", 3, 3) Expect(err).Should(Succeed()) Expect(len(resp.Clusters)).Should(Equal(0)) }) diff --git a/pkg/apiserver/rest/usecase/config.go b/pkg/apiserver/domain/service/config.go similarity index 88% rename from pkg/apiserver/rest/usecase/config.go rename to pkg/apiserver/domain/service/config.go index 41c63cb97..94378cc78 100644 --- a/pkg/apiserver/rest/usecase/config.go +++ b/pkg/apiserver/domain/service/config.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package usecase +package service import ( "context" @@ -35,9 +35,8 @@ import ( "github.com/oam-dev/kubevela/apis/core.oam.dev/common" "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" "github.com/oam-dev/kubevela/apis/types" - "github.com/oam-dev/kubevela/pkg/apiserver/clients" - "github.com/oam-dev/kubevela/pkg/apiserver/model" - apis "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + apis "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" "github.com/oam-dev/kubevela/pkg/definition" "github.com/oam-dev/kubevela/pkg/utils/config" ) @@ -52,8 +51,8 @@ const ( configSyncProjectPrefix = "config-sync" ) -// ConfigHandler handle CRUD of configs -type ConfigHandler interface { +// ConfigService handle CRUD of configs +type ConfigService interface { ListConfigTypes(ctx context.Context, query string) ([]*apis.ConfigType, error) GetConfigType(ctx context.Context, configType string) (*apis.ConfigType, error) CreateConfig(ctx context.Context, req apis.CreateConfigRequest) error @@ -62,27 +61,19 @@ type ConfigHandler interface { DeleteConfig(ctx context.Context, configType, name string) error } -// NewConfigUseCase returns a config use case -func NewConfigUseCase(authenticationUseCase AuthenticationUsecase) ConfigHandler { - k8sClient, err := clients.GetKubeClient() - if err != nil { - panic(err) - } - return &configUseCaseImpl{ - authenticationUseCase: authenticationUseCase, - kubeClient: k8sClient, - } +// NewConfigService returns a config use case +func NewConfigService() ConfigService { + return &configServiceImpl{} } -type configUseCaseImpl struct { - kubeClient client.Client - authenticationUseCase AuthenticationUsecase +type configServiceImpl struct { + KubeClient client.Client `inject:"kubeClient"` } // ListConfigTypes returns all config types -func (u *configUseCaseImpl) ListConfigTypes(ctx context.Context, query string) ([]*apis.ConfigType, error) { +func (u *configServiceImpl) ListConfigTypes(ctx context.Context, query string) ([]*apis.ConfigType, error) { defs := &v1beta1.ComponentDefinitionList{} - if err := u.kubeClient.List(ctx, defs, client.InNamespace(types.DefaultKubeVelaNS), + if err := u.KubeClient.List(ctx, defs, client.InNamespace(types.DefaultKubeVelaNS), client.MatchingLabels{definition.UserPrefix + "catalog.config.oam.dev": types.VelaCoreConfig}); err != nil { return nil, err } @@ -121,9 +112,9 @@ func (u *configUseCaseImpl) ListConfigTypes(ctx context.Context, query string) ( } // GetConfigType returns a config type -func (u *configUseCaseImpl) GetConfigType(ctx context.Context, configType string) (*apis.ConfigType, error) { +func (u *configServiceImpl) GetConfigType(ctx context.Context, configType string) (*apis.ConfigType, error) { d := &v1beta1.ComponentDefinition{} - if err := u.kubeClient.Get(ctx, client.ObjectKey{Namespace: types.DefaultKubeVelaNS, Name: configType}, d); err != nil { + if err := u.KubeClient.Get(ctx, client.ObjectKey{Namespace: types.DefaultKubeVelaNS, Name: configType}, d); err != nil { return nil, errors.Wrap(err, "failed to get config type") } @@ -135,7 +126,7 @@ func (u *configUseCaseImpl) GetConfigType(ctx context.Context, configType string return t, nil } -func (u *configUseCaseImpl) CreateConfig(ctx context.Context, req apis.CreateConfigRequest) error { +func (u *configServiceImpl) CreateConfig(ctx context.Context, req apis.CreateConfigRequest) error { p := req.Properties // If the component is Terraform type, set the provider name same as the application name and the component name if strings.HasPrefix(req.ComponentType, types.TerraformComponentPrefix) { @@ -155,20 +146,20 @@ func (u *configUseCaseImpl) CreateConfig(ctx context.Context, req apis.CreateCon Description: req.Description, Project: req.Project, } - return config.CreateApplication(ctx, u.kubeClient, req.Name, req.ComponentType, p, ui) + return config.CreateApplication(ctx, u.KubeClient, req.Name, req.ComponentType, p, ui) } -func (u *configUseCaseImpl) GetConfigs(ctx context.Context, configType string) ([]*apis.Config, error) { +func (u *configServiceImpl) GetConfigs(ctx context.Context, configType string) ([]*apis.Config, error) { switch configType { case types.TerraformProvider: - providers, err := config.ListTerraformProviders(ctx, u.kubeClient) + providers, err := config.ListTerraformProviders(ctx, u.KubeClient) if err != nil { return nil, err } configs := make([]*apis.Config, len(providers)) for i, p := range providers { var a v1beta1.Application - if err := u.kubeClient.Get(ctx, client.ObjectKey{Namespace: types.DefaultKubeVelaNS, Name: p.Name}, &a); err != nil { + if err := u.KubeClient.Get(ctx, client.ObjectKey{Namespace: types.DefaultKubeVelaNS, Name: p.Name}, &a); err != nil { if kerrors.IsNotFound(err) { t := p.CreationTimestamp.Time configs[i] = &apis.Config{ @@ -198,9 +189,9 @@ func (u *configUseCaseImpl) GetConfigs(ctx context.Context, configType string) ( } } -func (u *configUseCaseImpl) getConfigsByConfigType(ctx context.Context, configType string) ([]*apis.Config, error) { +func (u *configServiceImpl) getConfigsByConfigType(ctx context.Context, configType string) ([]*apis.Config, error) { var apps = &v1beta1.ApplicationList{} - if err := u.kubeClient.List(ctx, apps, client.InNamespace(types.DefaultKubeVelaNS), + if err := u.KubeClient.List(ctx, apps, client.InNamespace(types.DefaultKubeVelaNS), client.MatchingLabels{ model.LabelSourceOfTruth: model.FromInner, types.LabelConfigCatalog: types.VelaCoreConfig, @@ -216,9 +207,9 @@ func (u *configUseCaseImpl) getConfigsByConfigType(ctx context.Context, configTy return configs, nil } -func (u *configUseCaseImpl) GetConfig(ctx context.Context, configType, name string) (*apis.Config, error) { +func (u *configServiceImpl) GetConfig(ctx context.Context, configType, name string) (*apis.Config, error) { var a = &v1beta1.Application{} - if err := u.kubeClient.Get(ctx, client.ObjectKey{Namespace: types.DefaultKubeVelaNS, Name: name}, a); err != nil { + if err := u.KubeClient.Get(ctx, client.ObjectKey{Namespace: types.DefaultKubeVelaNS, Name: name}, a); err != nil { return nil, err } @@ -232,12 +223,12 @@ func (u *configUseCaseImpl) GetConfig(ctx context.Context, configType, name stri return config, nil } -func (u *configUseCaseImpl) DeleteConfig(ctx context.Context, configType, name string) error { +func (u *configServiceImpl) DeleteConfig(ctx context.Context, configType, name string) error { var isTerraformProvider bool if strings.HasPrefix(configType, types.TerraformComponentPrefix) { isTerraformProvider = true } - return config.DeleteApplication(ctx, u.kubeClient, name, isTerraformProvider) + return config.DeleteApplication(ctx, u.KubeClient, name, isTerraformProvider) } // ApplicationDeployTarget is the struct of application deploy target diff --git a/pkg/apiserver/rest/usecase/config_test.go b/pkg/apiserver/domain/service/config_test.go similarity index 96% rename from pkg/apiserver/rest/usecase/config_test.go rename to pkg/apiserver/domain/service/config_test.go index f087d8d67..fb633469e 100644 --- a/pkg/apiserver/rest/usecase/config_test.go +++ b/pkg/apiserver/domain/service/config_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package usecase +package service import ( "context" @@ -38,8 +38,8 @@ import ( "github.com/oam-dev/kubevela/apis/core.oam.dev/common" "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" "github.com/oam-dev/kubevela/apis/types" - "github.com/oam-dev/kubevela/pkg/apiserver/model" - apis "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + apis "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" "github.com/oam-dev/kubevela/pkg/definition" "github.com/oam-dev/kubevela/pkg/multicluster" ) @@ -85,10 +85,12 @@ func TestListConfigTypes(t *testing.T) { }) defer patches.Reset() - h := NewConfigUseCase(nil) + h := &configServiceImpl{ + KubeClient: k8sClient, + } type args struct { - h ConfigHandler + h ConfigService } type want struct { @@ -165,10 +167,10 @@ func TestGetConfigType(t *testing.T) { }) defer patches.Reset() - h := NewConfigUseCase(nil) + h := &configServiceImpl{KubeClient: k8sClient} type args struct { - h ConfigHandler + h ConfigService name string } @@ -227,10 +229,10 @@ func TestCreateConfig(t *testing.T) { k8sClient := fake.NewClientBuilder().WithScheme(s).Build() - h := &configUseCaseImpl{kubeClient: k8sClient} + h := &configServiceImpl{KubeClient: k8sClient} type args struct { - h ConfigHandler + h ConfigService req apis.CreateConfigRequest } @@ -337,11 +339,11 @@ func TestGetConfigs(t *testing.T) { k8sClient := fake.NewClientBuilder().WithScheme(s).WithObjects(provider1, provider2, provider3, app1).Build() - h := &configUseCaseImpl{kubeClient: k8sClient} + h := &configServiceImpl{KubeClient: k8sClient} type args struct { configType string - h ConfigHandler + h ConfigService } type want struct { @@ -698,12 +700,12 @@ func TestDeleteConfig(t *testing.T) { k8sClient := fake.NewClientBuilder().WithScheme(s).WithObjects(provider1, provider2, provider3, app1, app2, normalApp).Build() - h := &configUseCaseImpl{kubeClient: k8sClient} + h := &configServiceImpl{KubeClient: k8sClient} type args struct { configType string name string - h ConfigHandler + h ConfigService } type want struct { diff --git a/pkg/apiserver/rest/usecase/definition.go b/pkg/apiserver/domain/service/definition.go similarity index 89% rename from pkg/apiserver/rest/usecase/definition.go rename to pkg/apiserver/domain/service/definition.go index 57719bb8b..f98ca8584 100644 --- a/pkg/apiserver/rest/usecase/definition.go +++ b/pkg/apiserver/domain/service/definition.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package usecase +package service import ( "context" @@ -35,15 +35,14 @@ import ( "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" "github.com/oam-dev/kubevela/apis/types" - "github.com/oam-dev/kubevela/pkg/apiserver/clients" - "github.com/oam-dev/kubevela/pkg/apiserver/log" - apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/log" ) -// DefinitionUsecase definition usecase, Implement the management of ComponentDefinition、TraitDefinition and WorkflowStepDefinition. -type DefinitionUsecase interface { +// DefinitionService definition service, Implement the management of ComponentDefinition、TraitDefinition and WorkflowStepDefinition. +type DefinitionService interface { // ListDefinitions list definition base info ListDefinitions(ctx context.Context, ops DefinitionQueryOption) ([]*apisv1.DefinitionBase, error) // DetailDefinition get definition detail @@ -54,8 +53,8 @@ type DefinitionUsecase interface { UpdateDefinitionStatus(ctx context.Context, name string, status apisv1.UpdateDefinitionStatusRequest) (*apisv1.DetailDefinitionResponse, error) } -type definitionUsecaseImpl struct { - kubeClient client.Client +type definitionServiceImpl struct { + KubeClient client.Client `inject:"kubeClient"` caches *utils.MemoryCacheStore } @@ -79,16 +78,12 @@ const ( kindPolicyDefinition = "PolicyDefinition" ) -// NewDefinitionUsecase new definition usecase -func NewDefinitionUsecase() DefinitionUsecase { - kubecli, err := clients.GetKubeClient() - if err != nil { - log.Logger.Fatalf("get kubeclient failure %s", err.Error()) - } - return &definitionUsecaseImpl{kubeClient: kubecli, caches: utils.NewMemoryCacheStore(context.Background())} +// NewDefinitionService new definition service +func NewDefinitionService() DefinitionService { + return &definitionServiceImpl{caches: utils.NewMemoryCacheStore(context.Background())} } -func (d *definitionUsecaseImpl) ListDefinitions(ctx context.Context, ops DefinitionQueryOption) ([]*apisv1.DefinitionBase, error) { +func (d *definitionServiceImpl) ListDefinitions(ctx context.Context, ops DefinitionQueryOption) ([]*apisv1.DefinitionBase, error) { defs := &unstructured.UnstructuredList{} version, kind, err := getKindAndVersion(ops.Type) if err != nil { @@ -99,7 +94,7 @@ func (d *definitionUsecaseImpl) ListDefinitions(ctx context.Context, ops Definit return d.listDefinitions(ctx, defs, kind, ops) } -func (d *definitionUsecaseImpl) listDefinitions(ctx context.Context, list *unstructured.UnstructuredList, kind string, ops DefinitionQueryOption) ([]*apisv1.DefinitionBase, error) { +func (d *definitionServiceImpl) listDefinitions(ctx context.Context, list *unstructured.UnstructuredList, kind string, ops DefinitionQueryOption) ([]*apisv1.DefinitionBase, error) { if mc := d.caches.Get(ops.String()); mc != nil { return mc.([]*apisv1.DefinitionBase), nil } @@ -121,7 +116,7 @@ func (d *definitionUsecaseImpl) listDefinitions(ctx context.Context, list *unstr if err != nil { return nil, err } - if err := d.kubeClient.List(ctx, list, &client.ListOptions{ + if err := d.KubeClient.List(ctx, list, &client.ListOptions{ LabelSelector: selector, }); err != nil { return nil, err @@ -223,7 +218,7 @@ func convertDefinitionBase(def unstructured.Unstructured, kind string) (*apisv1. } // DetailDefinition get definition detail -func (d *definitionUsecaseImpl) DetailDefinition(ctx context.Context, name, defType string) (*apisv1.DetailDefinitionResponse, error) { +func (d *definitionServiceImpl) DetailDefinition(ctx context.Context, name, defType string) (*apisv1.DetailDefinitionResponse, error) { def := &unstructured.Unstructured{} version, kind, err := getKindAndVersion(defType) if err != nil { @@ -231,7 +226,7 @@ func (d *definitionUsecaseImpl) DetailDefinition(ctx context.Context, name, defT } def.SetAPIVersion(version) def.SetKind(kind) - if err := d.kubeClient.Get(ctx, k8stypes.NamespacedName{Namespace: types.DefaultKubeVelaNS, Name: name}, def); err != nil { + if err := d.KubeClient.Get(ctx, k8stypes.NamespacedName{Namespace: types.DefaultKubeVelaNS, Name: name}, def); err != nil { if apierrors.IsNotFound(err) { return nil, bcode.ErrDefinitionNotFound } @@ -242,7 +237,7 @@ func (d *definitionUsecaseImpl) DetailDefinition(ctx context.Context, name, defT return nil, err } var cm v1.ConfigMap - if err := d.kubeClient.Get(ctx, k8stypes.NamespacedName{ + if err := d.KubeClient.Get(ctx, k8stypes.NamespacedName{ Namespace: types.DefaultKubeVelaNS, Name: fmt.Sprintf("%s-schema-%s", defType, name), }, &cm); err != nil { @@ -271,9 +266,9 @@ func (d *definitionUsecaseImpl) DetailDefinition(ctx context.Context, name, defT }, nil } -func (d *definitionUsecaseImpl) renderCustomUISchema(ctx context.Context, name, defType string, defaultSchema []*utils.UIParameter) []*utils.UIParameter { +func (d *definitionServiceImpl) renderCustomUISchema(ctx context.Context, name, defType string, defaultSchema []*utils.UIParameter) []*utils.UIParameter { var cm v1.ConfigMap - if err := d.kubeClient.Get(ctx, k8stypes.NamespacedName{ + if err := d.KubeClient.Get(ctx, k8stypes.NamespacedName{ Namespace: types.DefaultKubeVelaNS, Name: fmt.Sprintf("%s-uischema-%s", defType, name), }, &cm); err != nil { @@ -295,19 +290,19 @@ func (d *definitionUsecaseImpl) renderCustomUISchema(ctx context.Context, name, } // AddDefinitionUISchema add definition custom ui schema config -func (d *definitionUsecaseImpl) AddDefinitionUISchema(ctx context.Context, name, defType string, schema []*utils.UIParameter) ([]*utils.UIParameter, error) { +func (d *definitionServiceImpl) AddDefinitionUISchema(ctx context.Context, name, defType string, schema []*utils.UIParameter) ([]*utils.UIParameter, error) { dataBate, err := json.Marshal(schema) if err != nil { log.Logger.Errorf("json marshal failure %s", err.Error()) return nil, bcode.ErrInvalidDefinitionUISchema } var cm v1.ConfigMap - if err := d.kubeClient.Get(ctx, k8stypes.NamespacedName{ + if err := d.KubeClient.Get(ctx, k8stypes.NamespacedName{ Namespace: types.DefaultKubeVelaNS, Name: fmt.Sprintf("%s-uischema-%s", defType, name), }, &cm); err != nil { if apierrors.IsNotFound(err) { - err = d.kubeClient.Create(ctx, &v1.ConfigMap{ + err = d.KubeClient.Create(ctx, &v1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Namespace: types.DefaultKubeVelaNS, Name: fmt.Sprintf("%s-uischema-%s", defType, name), @@ -322,7 +317,7 @@ func (d *definitionUsecaseImpl) AddDefinitionUISchema(ctx context.Context, name, } } else { cm.Data[types.UISchema] = string(dataBate) - err := d.kubeClient.Update(ctx, &cm) + err := d.KubeClient.Update(ctx, &cm) if err != nil { return nil, err } @@ -335,7 +330,7 @@ func (d *definitionUsecaseImpl) AddDefinitionUISchema(ctx context.Context, name, } // UpdateDefinitionStatus update the status of the definition -func (d *definitionUsecaseImpl) UpdateDefinitionStatus(ctx context.Context, name string, update apisv1.UpdateDefinitionStatusRequest) (*apisv1.DetailDefinitionResponse, error) { +func (d *definitionServiceImpl) UpdateDefinitionStatus(ctx context.Context, name string, update apisv1.UpdateDefinitionStatusRequest) (*apisv1.DetailDefinitionResponse, error) { def := &unstructured.Unstructured{} version, kind, err := getKindAndVersion(update.DefinitionType) if err != nil { @@ -343,7 +338,7 @@ func (d *definitionUsecaseImpl) UpdateDefinitionStatus(ctx context.Context, name } def.SetAPIVersion(version) def.SetKind(kind) - if err := d.kubeClient.Get(ctx, k8stypes.NamespacedName{Namespace: types.DefaultKubeVelaNS, Name: name}, def); err != nil { + if err := d.KubeClient.Get(ctx, k8stypes.NamespacedName{Namespace: types.DefaultKubeVelaNS, Name: name}, def); err != nil { if apierrors.IsNotFound(err) { return nil, bcode.ErrDefinitionNotFound } @@ -354,7 +349,7 @@ func (d *definitionUsecaseImpl) UpdateDefinitionStatus(ctx context.Context, name labels := def.GetLabels() delete(labels, types.LabelDefinitionHidden) def.SetLabels(labels) - if err := d.kubeClient.Update(ctx, def); err != nil { + if err := d.KubeClient.Update(ctx, def); err != nil { return nil, err } } @@ -362,7 +357,7 @@ func (d *definitionUsecaseImpl) UpdateDefinitionStatus(ctx context.Context, name labels := def.GetLabels() labels[types.LabelDefinitionHidden] = "true" def.SetLabels(labels) - if err := d.kubeClient.Update(ctx, def); err != nil { + if err := d.KubeClient.Update(ctx, def); err != nil { return nil, err } } diff --git a/pkg/apiserver/rest/usecase/definition_test.go b/pkg/apiserver/domain/service/definition_test.go similarity index 92% rename from pkg/apiserver/rest/usecase/definition_test.go rename to pkg/apiserver/domain/service/definition_test.go index e1564059a..a7f738c8a 100644 --- a/pkg/apiserver/rest/usecase/definition_test.go +++ b/pkg/apiserver/domain/service/definition_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package usecase +package service import ( "context" @@ -33,18 +33,18 @@ import ( "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" "github.com/oam-dev/kubevela/apis/types" - v1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils" + v1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils" "github.com/oam-dev/kubevela/pkg/oam/util" ) -var _ = Describe("Test namespace usecase functions", func() { +var _ = Describe("Test namespace service functions", func() { var ( - definitionUsecase *definitionUsecaseImpl + definitionService *definitionServiceImpl ) BeforeEach(func() { - definitionUsecase = &definitionUsecaseImpl{kubeClient: k8sClient, caches: utils.NewMemoryCacheStore(context.TODO())} + definitionService = &definitionServiceImpl{KubeClient: k8sClient, caches: utils.NewMemoryCacheStore(context.TODO())} err := k8sClient.Create(context.Background(), &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Name: "vela-system", @@ -62,7 +62,7 @@ var _ = Describe("Test namespace usecase functions", func() { err = k8sClient.Create(context.Background(), &cd) Expect(err).Should(Succeed()) - definitions, err := definitionUsecase.ListDefinitions(context.TODO(), DefinitionQueryOption{Type: "component"}) + definitions, err := definitionService.ListDefinitions(context.TODO(), DefinitionQueryOption{Type: "component"}) Expect(err).Should(BeNil()) var selectDefinition *v1.DefinitionBase for i, definition := range definitions { @@ -83,7 +83,7 @@ var _ = Describe("Test namespace usecase functions", func() { Expect(err).Should(Succeed()) err = k8sClient.Create(context.Background(), &td) Expect(err).Should(Succeed()) - traits, err := definitionUsecase.ListDefinitions(context.TODO(), DefinitionQueryOption{Type: "trait"}) + traits, err := definitionService.ListDefinitions(context.TODO(), DefinitionQueryOption{Type: "trait"}) Expect(err).Should(BeNil()) // there is already a scaler trait definition in the test env Expect(cmp.Diff(len(traits), 2)).Should(BeEmpty()) @@ -101,7 +101,7 @@ var _ = Describe("Test namespace usecase functions", func() { err = k8sClient.Create(context.Background(), &sd) Expect(err).Should(Succeed()) - wfstep, err := definitionUsecase.ListDefinitions(context.TODO(), DefinitionQueryOption{Type: "workflowstep"}) + wfstep, err := definitionService.ListDefinitions(context.TODO(), DefinitionQueryOption{Type: "workflowstep"}) Expect(err).Should(BeNil()) // there is already a deploy workflow step definition in the test env Expect(cmp.Diff(len(wfstep), 2)).Should(BeEmpty()) @@ -118,7 +118,7 @@ var _ = Describe("Test namespace usecase functions", func() { err = k8sClient.Create(context.Background(), &sd2) Expect(err).Should(Succeed()) - allstep, err := definitionUsecase.ListDefinitions(context.TODO(), DefinitionQueryOption{Type: "workflowstep", QueryAll: true}) + allstep, err := definitionService.ListDefinitions(context.TODO(), DefinitionQueryOption{Type: "workflowstep", QueryAll: true}) Expect(err).Should(BeNil()) Expect(cmp.Diff(len(allstep), 3)).Should(BeEmpty()) @@ -138,7 +138,7 @@ var _ = Describe("Test namespace usecase functions", func() { } err = k8sClient.Create(context.Background(), &policy) Expect(err).Should(Succeed()) - policies, err := definitionUsecase.ListDefinitions(context.TODO(), DefinitionQueryOption{Type: "policy"}) + policies, err := definitionService.ListDefinitions(context.TODO(), DefinitionQueryOption{Type: "policy"}) Expect(err).Should(BeNil()) Expect(cmp.Diff(len(policies), 1)).Should(BeEmpty()) Expect(cmp.Diff(policies[0].Name, "health")).Should(BeEmpty()) @@ -167,7 +167,7 @@ var _ = Describe("Test namespace usecase functions", func() { } err = k8sClient.Create(context.Background(), cm) Expect(err).Should(Succeed()) - definitionDetail, err := definitionUsecase.DetailDefinition(context.TODO(), "apply-object", "workflowstep") + definitionDetail, err := definitionService.DetailDefinition(context.TODO(), "apply-object", "workflowstep") Expect(err).Should(Succeed()) schemaFromCM := &openapi3.Schema{} @@ -213,7 +213,10 @@ var _ = Describe("Test namespace usecase functions", func() { It("Test sortDefaultUISchema", testSortDefaultUISchema) It("Test update ui schema", func() { - du := NewDefinitionUsecase() + du := &definitionServiceImpl{ + KubeClient: k8sClient, + caches: utils.NewMemoryCacheStore(context.Background()), + } cdata, err := ioutil.ReadFile("./testdata/workflowstep-apply-object.yaml") Expect(err).Should(Succeed()) var schema utils.UISchema @@ -230,7 +233,10 @@ var _ = Describe("Test namespace usecase functions", func() { }) It("Test update status of the definition", func() { - du := NewDefinitionUsecase() + du := &definitionServiceImpl{ + KubeClient: k8sClient, + caches: utils.NewMemoryCacheStore(context.Background()), + } detail, err := du.UpdateDefinitionStatus(context.TODO(), "apply-object", v1.UpdateDefinitionStatusRequest{ DefinitionType: "workflowstep", HiddenInUI: true, diff --git a/pkg/apiserver/rest/usecase/env.go b/pkg/apiserver/domain/service/env.go similarity index 75% rename from pkg/apiserver/rest/usecase/env.go rename to pkg/apiserver/domain/service/env.go index debbb03dc..87cd43209 100644 --- a/pkg/apiserver/rest/usecase/env.go +++ b/pkg/apiserver/domain/service/env.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package usecase +package service import ( "context" @@ -25,18 +25,18 @@ import ( apierror "k8s.io/apimachinery/pkg/api/errors" "sigs.k8s.io/controller-runtime/pkg/client" - "github.com/oam-dev/kubevela/pkg/apiserver/clients" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/log" - "github.com/oam-dev/kubevela/pkg/apiserver/model" - apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/repository" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" + apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/log" "github.com/oam-dev/kubevela/pkg/oam" util "github.com/oam-dev/kubevela/pkg/utils" ) -// EnvUsecase defines the API of Env. -type EnvUsecase interface { +// EnvService defines the API of Env. +type EnvService interface { GetEnv(ctx context.Context, envName string) (*model.Env, error) ListEnvs(ctx context.Context, page, pageSize int, listOption apisv1.ListEnvOptions) (*apisv1.ListEnvResponse, error) ListEnvCount(ctx context.Context, listOption apisv1.ListEnvOptions) (int64, error) @@ -45,41 +45,37 @@ type EnvUsecase interface { UpdateEnv(ctx context.Context, envName string, req apisv1.UpdateEnvRequest) (*apisv1.Env, error) } -type envUsecaseImpl struct { - ds datastore.DataStore - projectUsecase ProjectUsecase - kubeClient client.Client +type envServiceImpl struct { + Store datastore.DataStore `inject:"datastore"` + ProjectService ProjectService `inject:""` + KubeClient client.Client `inject:"kubeClient"` } -// NewEnvUsecase new env usecase -func NewEnvUsecase(ds datastore.DataStore, projectUsecase ProjectUsecase) EnvUsecase { - kubecli, err := clients.GetKubeClient() - if err != nil { - log.Logger.Fatalf("get kubeclient failure %s", err.Error()) - } - return &envUsecaseImpl{kubeClient: kubecli, ds: ds, projectUsecase: projectUsecase} +// NewEnvService new env service +func NewEnvService() EnvService { + return &envServiceImpl{} } // GetEnv get env -func (p *envUsecaseImpl) GetEnv(ctx context.Context, envName string) (*model.Env, error) { - return getEnv(ctx, p.ds, envName) +func (p *envServiceImpl) GetEnv(ctx context.Context, envName string) (*model.Env, error) { + return repository.GetEnv(ctx, p.Store, envName) } // DeleteEnv delete an env by name // the function assume applications contain in env already empty. // it won't delete the namespace created by the Env, but it will update the label -func (p *envUsecaseImpl) DeleteEnv(ctx context.Context, envName string) error { +func (p *envServiceImpl) DeleteEnv(ctx context.Context, envName string) error { env := &model.Env{} env.Name = envName - if err := p.ds.Get(ctx, env); err != nil { + if err := p.Store.Get(ctx, env); err != nil { if errors.Is(err, datastore.ErrRecordNotExist) { return nil } return err } // reset the labels - err := util.UpdateNamespace(ctx, p.kubeClient, env.Namespace, util.MergeOverrideLabels(map[string]string{ + err := util.UpdateNamespace(ctx, p.KubeClient, env.Namespace, util.MergeOverrideLabels(map[string]string{ oam.LabelNamespaceOfEnvName: "", oam.LabelControlPlaneNamespaceUsage: "", })) @@ -87,7 +83,7 @@ func (p *envUsecaseImpl) DeleteEnv(ctx context.Context, envName string) error { return err } - if err = p.ds.Delete(ctx, env); err != nil { + if err = p.Store.Delete(ctx, env); err != nil { if errors.Is(err, datastore.ErrRecordNotExist) { return nil } @@ -97,12 +93,12 @@ func (p *envUsecaseImpl) DeleteEnv(ctx context.Context, envName string) error { } // ListEnvs list envs -func (p *envUsecaseImpl) ListEnvs(ctx context.Context, page, pageSize int, listOption apisv1.ListEnvOptions) (*apisv1.ListEnvResponse, error) { +func (p *envServiceImpl) ListEnvs(ctx context.Context, page, pageSize int, listOption apisv1.ListEnvOptions) (*apisv1.ListEnvResponse, error) { userName, ok := ctx.Value(&apisv1.CtxKeyUser).(string) if !ok { return nil, bcode.ErrUnauthorized } - projects, err := p.projectUsecase.ListUserProjects(ctx, userName) + projects, err := p.ProjectService.ListUserProjects(ctx, userName) if err != nil { return nil, err } @@ -132,7 +128,7 @@ func (p *envUsecaseImpl) ListEnvs(ctx context.Context, page, pageSize int, listO }, }, } - entities, err := listEnvs(ctx, p.ds, &datastore.ListOptions{ + entities, err := repository.ListEnvs(ctx, p.Store, &datastore.ListOptions{ Page: page, PageSize: pageSize, SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderDescending}}, @@ -142,7 +138,7 @@ func (p *envUsecaseImpl) ListEnvs(ctx context.Context, page, pageSize int, listO return nil, err } - targets, err := listTarget(ctx, p.ds, listOption.Project, nil) + targets, err := repository.ListTarget(ctx, p.Store, listOption.Project, nil) if err != nil { return nil, err } @@ -156,15 +152,15 @@ func (p *envUsecaseImpl) ListEnvs(ctx context.Context, page, pageSize int, listO envs[i].Project.Alias = projectNameAlias[envs[i].Project.Name] } - total, err := p.ds.Count(ctx, &model.Env{Project: listOption.Project}, &filter) + total, err := p.Store.Count(ctx, &model.Env{Project: listOption.Project}, &filter) if err != nil { return nil, err } return &apisv1.ListEnvResponse{Envs: envs, Total: total}, nil } -func (p *envUsecaseImpl) ListEnvCount(ctx context.Context, listOption apisv1.ListEnvOptions) (int64, error) { - return p.ds.Count(ctx, &model.Env{Project: listOption.Project}, nil) +func (p *envServiceImpl) ListEnvCount(ctx context.Context, listOption apisv1.ListEnvOptions) (int64, error) { + return p.Store.Count(ctx, &model.Env{Project: listOption.Project}, nil) } func checkEqual(old, new []string) bool { @@ -179,15 +175,15 @@ func checkEqual(old, new []string) bool { return reflect.DeepEqual(old, new) } -func (p *envUsecaseImpl) updateAppWithNewEnv(ctx context.Context, envName string, env *model.Env) error { +func (p *envServiceImpl) updateAppWithNewEnv(ctx context.Context, envName string, env *model.Env) error { // List all apps inside the env - apps, err := listApp(ctx, p.ds, apisv1.ListApplicationOptions{Env: envName}) + apps, err := listApp(ctx, p.Store, apisv1.ListApplicationOptions{Env: envName}) if err != nil { return err } for _, app := range apps { - err = UpdateEnvWorkflow(ctx, p.kubeClient, p.ds, app, env) + err = repository.UpdateEnvWorkflow(ctx, p.KubeClient, p.Store, app, env) if err != nil { return err } @@ -197,10 +193,10 @@ func (p *envUsecaseImpl) updateAppWithNewEnv(ctx context.Context, envName string } // UpdateEnv update an env for request -func (p *envUsecaseImpl) UpdateEnv(ctx context.Context, name string, req apisv1.UpdateEnvRequest) (*apisv1.Env, error) { +func (p *envServiceImpl) UpdateEnv(ctx context.Context, name string, req apisv1.UpdateEnvRequest) (*apisv1.Env, error) { env := &model.Env{} env.Name = name - err := p.ds.Get(ctx, env) + err := p.Store.Get(ctx, env) if err != nil { log.Logger.Errorf("check if env name exists failure %s", err.Error()) return nil, bcode.ErrEnvNotExisted @@ -223,7 +219,7 @@ func (p *envUsecaseImpl) UpdateEnv(ctx context.Context, name string, req apisv1. env.Targets = req.Targets } - targets, err := listTarget(ctx, p.ds, "", nil) + targets, err := repository.ListTarget(ctx, p.Store, "", nil) if err != nil { return nil, err } @@ -238,7 +234,7 @@ func (p *envUsecaseImpl) UpdateEnv(ctx context.Context, name string, req apisv1. } // create namespace at first - if err := p.ds.Put(ctx, env); err != nil { + if err := p.Store.Put(ctx, env); err != nil { return nil, err } @@ -254,7 +250,7 @@ func (p *envUsecaseImpl) UpdateEnv(ctx context.Context, name string, req apisv1. } // CreateEnv create an env for request -func (p *envUsecaseImpl) CreateEnv(ctx context.Context, req apisv1.CreateEnvRequest) (*apisv1.Env, error) { +func (p *envServiceImpl) CreateEnv(ctx context.Context, req apisv1.CreateEnvRequest) (*apisv1.Env, error) { newEnv := &model.Env{ Name: req.Name, Alias: req.Alias, @@ -269,7 +265,7 @@ func (p *envUsecaseImpl) CreateEnv(ctx context.Context, req apisv1.CreateEnvRequ return nil, bcode.ErrEnvTargetConflict } - targets, err := listTarget(ctx, p.ds, "", nil) + targets, err := repository.ListTarget(ctx, p.Store, "", nil) if err != nil { return nil, err } @@ -285,7 +281,7 @@ func (p *envUsecaseImpl) CreateEnv(ctx context.Context, req apisv1.CreateEnvRequ } } - err = createEnv(ctx, p.kubeClient, p.ds, newEnv) + err = repository.CreateEnv(ctx, p.KubeClient, p.Store, newEnv) if err != nil { return nil, err } @@ -295,11 +291,11 @@ func (p *envUsecaseImpl) CreateEnv(ctx context.Context, req apisv1.CreateEnvRequ } // checkEnvTarget In one project, a delivery target can only belong to one env. -func (p *envUsecaseImpl) checkEnvTarget(ctx context.Context, project string, envName string, targets []string) (bool, error) { +func (p *envServiceImpl) checkEnvTarget(ctx context.Context, project string, envName string, targets []string) (bool, error) { if len(targets) == 0 { return true, nil } - entitys, err := p.ds.List(ctx, &model.Env{Project: project}, &datastore.ListOptions{}) + entitys, err := p.Store.List(ctx, &model.Env{Project: project}, &datastore.ListOptions{}) if err != nil { return false, err } diff --git a/pkg/apiserver/rest/usecase/env_test.go b/pkg/apiserver/domain/service/env_test.go similarity index 76% rename from pkg/apiserver/rest/usecase/env_test.go rename to pkg/apiserver/domain/service/env_test.go index 41aee9497..fd8d24ca5 100644 --- a/pkg/apiserver/rest/usecase/env_test.go +++ b/pkg/apiserver/domain/service/env_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package usecase +package service import ( "context" @@ -26,16 +26,16 @@ import ( corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/model" - apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" + apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" "github.com/oam-dev/kubevela/pkg/oam" ) -var _ = Describe("Test env usecase functions", func() { +var _ = Describe("Test env service functions", func() { var ( - envUsecase *envUsecaseImpl + envService *envServiceImpl ds datastore.DataStore ) BeforeEach(func() { @@ -43,9 +43,9 @@ var _ = Describe("Test env usecase functions", func() { ds, err = NewDatastore(datastore.Config{Type: "kubeapi", Database: "env-test-kubevela"}) Expect(ds).ToNot(BeNil()) Expect(err).Should(BeNil()) - rbacUsecase := &rbacUsecaseImpl{ds: ds} - projectUsecase := &projectUsecaseImpl{ds: ds, k8sClient: k8sClient, rbacUsecase: rbacUsecase} - envUsecase = &envUsecaseImpl{kubeClient: k8sClient, ds: ds, projectUsecase: projectUsecase} + rbacService := &rbacServiceImpl{Store: ds} + projectService := &projectServiceImpl{Store: ds, K8sClient: k8sClient, RbacService: rbacService} + envService = &envServiceImpl{KubeClient: k8sClient, Store: ds, ProjectService: projectService} }) It("Test Create/Get/Delete Env function", func() { // create target @@ -56,7 +56,7 @@ var _ = Describe("Test env usecase functions", func() { Name: "test-env", Description: "this is a env description", } - base, err := envUsecase.CreateEnv(context.TODO(), req) + base, err := envService.CreateEnv(context.TODO(), req) Expect(err).Should(BeNil()) Expect(cmp.Diff(base.Description, req.Description)).Should(BeEmpty()) Expect(cmp.Diff(base.Namespace, req.Name)).Should(BeEmpty()) @@ -67,7 +67,7 @@ var _ = Describe("Test env usecase functions", func() { Description: "this is a env description", Namespace: base.Namespace, } - _, err = envUsecase.CreateEnv(context.TODO(), req2) + _, err = envService.CreateEnv(context.TODO(), req2) equal := cmp.Equal(err, bcode.ErrEnvNamespaceAlreadyBound, cmpopts.EquateErrors()) Expect(equal).Should(BeTrue()) @@ -78,7 +78,7 @@ var _ = Describe("Test env usecase functions", func() { Project: "env-project", Targets: []string{"env-test"}, } - base, err = envUsecase.CreateEnv(context.TODO(), req3) + base, err = envService.CreateEnv(context.TODO(), req3) Expect(err).Should(BeNil()) Expect(cmp.Diff(base.Namespace, "default")).Should(BeEmpty()) var namespace corev1.Namespace @@ -94,7 +94,7 @@ var _ = Describe("Test env usecase functions", func() { Project: "env-project", Targets: []string{"env-test"}, } - _, err = envUsecase.CreateEnv(context.TODO(), req4) + _, err = envService.CreateEnv(context.TODO(), req4) Expect(cmp.Equal(err, bcode.ErrEnvTargetConflict, cmpopts.EquateErrors())).Should(BeTrue()) // test update env @@ -102,18 +102,18 @@ var _ = Describe("Test env usecase functions", func() { Description: "this is a env description update", Targets: []string{"env-test"}, } - env, err := envUsecase.UpdateEnv(context.TODO(), "test-env-2", req5) + env, err := envService.UpdateEnv(context.TODO(), "test-env-2", req5) Expect(err).Should(BeNil()) Expect(cmp.Diff(env.Description, req5.Description)).Should(BeEmpty()) // clean up the env - err = envUsecase.DeleteEnv(context.TODO(), "test-env") + err = envService.DeleteEnv(context.TODO(), "test-env") Expect(err).Should(BeNil()) - err = envUsecase.DeleteEnv(context.TODO(), "test-env-2") + err = envService.DeleteEnv(context.TODO(), "test-env-2") Expect(err).Should(BeNil()) By("Test ListEnvs function") - _, err = envUsecase.ListEnvs(context.WithValue(context.TODO(), &apisv1.CtxKeyUser, "admin"), 1, 1, apisv1.ListEnvOptions{}) + _, err = envService.ListEnvs(context.WithValue(context.TODO(), &apisv1.CtxKeyUser, "admin"), 1, 1, apisv1.ListEnvOptions{}) Expect(err).Should(BeNil()) }) diff --git a/pkg/apiserver/domain/service/envbinding.go b/pkg/apiserver/domain/service/envbinding.go new file mode 100644 index 000000000..c5486c39f --- /dev/null +++ b/pkg/apiserver/domain/service/envbinding.go @@ -0,0 +1,301 @@ +/* +Copyright 2021 The KubeVela Authors. + +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 service + +import ( + "context" + "errors" + "fmt" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/repository" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" + assembler "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/assembler/v1" + apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/log" + utils2 "github.com/oam-dev/kubevela/pkg/utils" +) + +// EnvBindingService envbinding service +type EnvBindingService interface { + GetEnvBindings(ctx context.Context, app *model.Application) ([]*apisv1.EnvBindingBase, error) + GetEnvBinding(ctx context.Context, app *model.Application, envName string) (*model.EnvBinding, error) + CreateEnvBinding(ctx context.Context, app *model.Application, env apisv1.CreateApplicationEnvbindingRequest) (*apisv1.EnvBinding, error) + BatchCreateEnvBinding(ctx context.Context, app *model.Application, env apisv1.EnvBindingList) error + UpdateEnvBinding(ctx context.Context, app *model.Application, envName string, diff apisv1.PutApplicationEnvBindingRequest) (*apisv1.DetailEnvBindingResponse, error) + DeleteEnvBinding(ctx context.Context, app *model.Application, envName string) error + BatchDeleteEnvBinding(ctx context.Context, app *model.Application) error + DetailEnvBinding(ctx context.Context, app *model.Application, envBinding *model.EnvBinding) (*apisv1.DetailEnvBindingResponse, error) + ApplicationEnvRecycle(ctx context.Context, appModel *model.Application, envBinding *model.EnvBinding) error +} + +type envBindingServiceImpl struct { + Store datastore.DataStore `inject:"datastore"` + WorkflowService WorkflowService `inject:""` + EnvService EnvService `inject:""` + DefinitionService DefinitionService `inject:""` + KubeClient client.Client `inject:"kubeClient"` +} + +// NewEnvBindingService new envBinding service +func NewEnvBindingService() EnvBindingService { + return &envBindingServiceImpl{} +} + +func (e *envBindingServiceImpl) GetEnvBindings(ctx context.Context, app *model.Application) ([]*apisv1.EnvBindingBase, error) { + full, err := repository.ListFullEnvBinding(ctx, e.Store, repository.EnvListOption{AppPrimaryKey: app.PrimaryKey(), ProjectName: app.Project}) + if err != nil { + log.Logger.Errorf("list envbinding for app %s err: %v\n", app.Name, err) + return nil, err + } + return full, nil +} + +func (e *envBindingServiceImpl) GetEnvBinding(ctx context.Context, app *model.Application, envName string) (*model.EnvBinding, error) { + envBinding, err := e.getBindingByEnv(ctx, app, envName) + if err != nil { + if errors.Is(err, datastore.ErrRecordNotExist) { + return nil, bcode.ErrEnvBindingsNotExist + } + return nil, err + } + return envBinding, nil +} + +// CheckAppEnvBindingsContainTarget check envbinding contain target +func CheckAppEnvBindingsContainTarget(envBindings []*apisv1.EnvBindingBase, targetName string) (bool, error) { + var filteredList []*apisv1.EnvBindingBase + for _, envBinding := range envBindings { + if utils.StringsContain(envBinding.TargetNames, targetName) { + filteredList = append(filteredList, envBinding) + } + } + return len(filteredList) > 0, nil +} + +func (e *envBindingServiceImpl) CreateEnvBinding(ctx context.Context, app *model.Application, envReq apisv1.CreateApplicationEnvbindingRequest) (*apisv1.EnvBinding, error) { + envBinding, err := e.getBindingByEnv(ctx, app, envReq.Name) + if err != nil { + if !errors.Is(err, datastore.ErrRecordNotExist) { + return nil, err + } + } + if envBinding != nil { + return nil, bcode.ErrEnvBindingExist + } + env, err := repository.GetEnv(ctx, e.Store, envReq.Name) + if err != nil { + return nil, err + } + envBindingModel := assembler.CreateEnvBindingModel(app, envReq) + err = e.createEnvWorkflow(ctx, app, env, false) + if err != nil { + return nil, err + } + if err := e.Store.Add(ctx, &envBindingModel); err != nil { + return nil, err + } + + return &envReq.EnvBinding, nil +} + +func (e *envBindingServiceImpl) BatchCreateEnvBinding(ctx context.Context, app *model.Application, envbindings apisv1.EnvBindingList) error { + for i := range envbindings { + envBindingModel := assembler.ConvertToEnvBindingModel(app, *envbindings[i]) + env, err := repository.GetEnv(ctx, e.Store, envBindingModel.Name) + if err != nil { + log.Logger.Errorf("get env failure %s", err.Error()) + continue + } + if err := e.Store.Add(ctx, envBindingModel); err != nil { + log.Logger.Errorf("add envbinding %s failure %s", utils2.Sanitize(envBindingModel.Name), err.Error()) + continue + } + err = e.createEnvWorkflow(ctx, app, env, i == 0) + if err != nil { + log.Logger.Errorf("create env workflow failure %s", err.Error()) + continue + } + } + return nil +} + +func (e *envBindingServiceImpl) getBindingByEnv(ctx context.Context, app *model.Application, envName string) (*model.EnvBinding, error) { + var envBinding = model.EnvBinding{ + AppPrimaryKey: app.PrimaryKey(), + Name: envName, + } + err := e.Store.Get(ctx, &envBinding) + if err != nil { + return nil, err + } + return &envBinding, nil +} + +func (e *envBindingServiceImpl) UpdateEnvBinding(ctx context.Context, app *model.Application, envName string, _ apisv1.PutApplicationEnvBindingRequest) (*apisv1.DetailEnvBindingResponse, error) { + envBinding, err := e.getBindingByEnv(ctx, app, envName) + if err != nil { + if errors.Is(err, datastore.ErrRecordNotExist) { + return nil, bcode.ErrEnvBindingNotExist + } + return nil, err + } + env, err := repository.GetEnv(ctx, e.Store, envName) + if err != nil { + return nil, err + } + // update env + if err := e.Store.Put(ctx, envBinding); err != nil { + return nil, err + } + // update env workflow + if err := repository.UpdateEnvWorkflow(ctx, e.KubeClient, e.Store, app, env); err != nil { + return nil, bcode.ErrEnvBindingUpdateWorkflow + } + return e.DetailEnvBinding(ctx, app, envBinding) +} + +func (e *envBindingServiceImpl) DeleteEnvBinding(ctx context.Context, appModel *model.Application, envName string) error { + envBinding, err := e.getBindingByEnv(ctx, appModel, envName) + if err != nil { + if errors.Is(err, datastore.ErrRecordNotExist) { + return bcode.ErrEnvBindingNotExist + } + return err + } + env, err := repository.GetEnv(ctx, e.Store, envName) + if err != nil && errors.Is(err, datastore.ErrRecordNotExist) { + return err + } + if env != nil { + var app v1beta1.Application + err = e.KubeClient.Get(ctx, types.NamespacedName{Namespace: env.Namespace, Name: appModel.Name}, &app) + if err == nil || !apierrors.IsNotFound(err) { + return bcode.ErrApplicationEnvRefusedDelete + } + if err := e.Store.Delete(ctx, &model.EnvBinding{AppPrimaryKey: appModel.PrimaryKey(), Name: envBinding.Name}); err != nil { + return err + } + } + // delete env workflow + if err := e.deleteEnvWorkflow(ctx, appModel, repository.ConvertWorkflowName(envBinding.Name)); err != nil { + return fmt.Errorf("fail to clear the workflow belong to the env %w", err) + } + + // delete the topology and env-bindings policies + if err := e.Store.Delete(ctx, &model.ApplicationPolicy{AppPrimaryKey: appModel.PrimaryKey(), EnvName: envName}); err != nil && !errors.Is(err, datastore.ErrRecordNotExist) { + return fmt.Errorf("fail to clear the policies belong to the env %w", err) + } + return nil +} + +func (e *envBindingServiceImpl) BatchDeleteEnvBinding(ctx context.Context, app *model.Application) error { + envBindings, err := e.GetEnvBindings(ctx, app) + if err != nil { + return err + } + for _, envBinding := range envBindings { + // delete env + if err := e.Store.Delete(ctx, &model.EnvBinding{AppPrimaryKey: app.PrimaryKey(), Name: envBinding.Name}); err != nil { + return err + } + // delete env workflow + err := e.deleteEnvWorkflow(ctx, app, repository.ConvertWorkflowName(envBinding.Name)) + if err != nil { + return err + } + } + return nil +} + +func (e *envBindingServiceImpl) createEnvWorkflow(ctx context.Context, app *model.Application, env *model.Env, isDefault bool) error { + steps, policies := repository.GenEnvWorkflowStepsAndPolicies(ctx, e.KubeClient, e.Store, env, app) + workflow := &model.Workflow{ + Steps: steps, + Name: repository.ConvertWorkflowName(env.Name), + Alias: fmt.Sprintf("%s Workflow", env.Alias), + Description: "Created automatically by envbinding.", + Default: &isDefault, + EnvName: env.Name, + AppPrimaryKey: app.PrimaryKey(), + } + log.Logger.Infof("create workflow %s for app %s", utils2.Sanitize(workflow.Name), utils2.Sanitize(app.PrimaryKey())) + if err := e.Store.Add(ctx, workflow); err != nil { + return err + } + err := e.Store.BatchAdd(ctx, policies) + if err != nil { + if err := e.WorkflowService.DeleteWorkflow(ctx, app, repository.ConvertWorkflowName(env.Name)); err != nil { + log.Logger.Errorf("fail to rollback the workflow after fail to create policies, %s", err.Error()) + } + return fmt.Errorf("fail to create policies %w", err) + } + return nil +} + +func (e *envBindingServiceImpl) deleteEnvWorkflow(ctx context.Context, app *model.Application, workflowName string) error { + if err := e.WorkflowService.DeleteWorkflow(ctx, app, workflowName); err != nil { + if !errors.Is(err, bcode.ErrWorkflowNotExist) { + return err + } + } + return nil +} + +func (e *envBindingServiceImpl) DetailEnvBinding(ctx context.Context, app *model.Application, envBinding *model.EnvBinding) (*apisv1.DetailEnvBindingResponse, error) { + targets, err := repository.ListTarget(ctx, e.Store, "", nil) + if err != nil { + return nil, err + } + env, err := repository.GetEnv(ctx, e.Store, envBinding.Name) + if err != nil { + return nil, err + } + return &apisv1.DetailEnvBindingResponse{ + EnvBindingBase: *assembler.ConvertEnvBindingModelToBase(envBinding, env, targets), + }, nil +} + +func (e *envBindingServiceImpl) ApplicationEnvRecycle(ctx context.Context, appModel *model.Application, envBinding *model.EnvBinding) error { + env, err := repository.GetEnv(ctx, e.Store, envBinding.Name) + if err != nil { + return err + } + var app v1beta1.Application + err = e.KubeClient.Get(ctx, types.NamespacedName{Namespace: env.Namespace, Name: appModel.Name}, &app) + if err != nil { + if apierrors.IsNotFound(err) { + return nil + } + return err + } + if err := e.KubeClient.Delete(ctx, &app); err != nil { + return err + } + + if err := resetRevisionsAndRecords(ctx, e.Store, appModel.Name, "", "", ""); err != nil { + return err + } + return nil +} diff --git a/pkg/apiserver/rest/usecase/envbinding_test.go b/pkg/apiserver/domain/service/envbinding_test.go similarity index 63% rename from pkg/apiserver/rest/usecase/envbinding_test.go rename to pkg/apiserver/domain/service/envbinding_test.go index feb92bd0e..8c524a71a 100644 --- a/pkg/apiserver/rest/usecase/envbinding_test.go +++ b/pkg/apiserver/domain/service/envbinding_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package usecase +package service import ( "context" @@ -23,18 +23,19 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/model" - apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/repository" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" + apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils" ) -var _ = Describe("Test envBindingUsecase functions", func() { +var _ = Describe("Test envBindingService functions", func() { var ( - envUsecase *envUsecaseImpl - envBindingUsecase *envBindingUsecaseImpl - workflowUsecase *workflowUsecaseImpl - definitionUsecase DefinitionUsecase + envService *envServiceImpl + envBindingService *envBindingServiceImpl + workflowService *workflowServiceImpl + definitionService DefinitionService envBindingDemo1 apisv1.EnvBinding envBindingDemo2 apisv1.EnvBinding testApp *model.Application @@ -49,12 +50,12 @@ var _ = Describe("Test envBindingUsecase functions", func() { Name: "test-app-env", Project: "default", } - rbacUsecase := &rbacUsecaseImpl{ds: ds} - projectUsecase := &projectUsecaseImpl{ds: ds, k8sClient: k8sClient, rbacUsecase: rbacUsecase} - envUsecase = &envUsecaseImpl{ds: ds, kubeClient: k8sClient, projectUsecase: projectUsecase} - workflowUsecase = &workflowUsecaseImpl{ds: ds, kubeClient: k8sClient, envUsecase: envUsecase} - definitionUsecase = &definitionUsecaseImpl{kubeClient: k8sClient, caches: utils.NewMemoryCacheStore(context.TODO())} - envBindingUsecase = &envBindingUsecaseImpl{ds: ds, workflowUsecase: workflowUsecase, definitionUsecase: definitionUsecase, kubeClient: k8sClient, envUsecase: envUsecase} + rbacService := &rbacServiceImpl{Store: ds} + projectService := &projectServiceImpl{Store: ds, K8sClient: k8sClient, RbacService: rbacService} + envService = &envServiceImpl{Store: ds, KubeClient: k8sClient, ProjectService: projectService} + workflowService = &workflowServiceImpl{Store: ds, KubeClient: k8sClient, EnvService: envService} + definitionService = &definitionServiceImpl{KubeClient: k8sClient, caches: utils.NewMemoryCacheStore(context.TODO())} + envBindingService = &envBindingServiceImpl{Store: ds, WorkflowService: workflowService, DefinitionService: definitionService, KubeClient: k8sClient, EnvService: envService} envBindingDemo1 = apisv1.EnvBinding{ Name: "envbinding-dev", } @@ -78,69 +79,69 @@ var _ = Describe("Test envBindingUsecase functions", func() { Cluster: &model.ClusterTarget{ClusterName: "local", Namespace: "prod-target"}}) Expect(err).Should(BeNil()) - _, err = envUsecase.CreateEnv(context.TODO(), apisv1.CreateEnvRequest{ + _, err = envService.CreateEnv(context.TODO(), apisv1.CreateEnvRequest{ Project: "default", Name: "envbinding-dev", Targets: []string{"dev-target"}}) Expect(err).Should(BeNil()) - _, err = envUsecase.CreateEnv(context.TODO(), apisv1.CreateEnvRequest{ + _, err = envService.CreateEnv(context.TODO(), apisv1.CreateEnvRequest{ Project: "default", Name: "envbinding-prod", Targets: []string{"prod-target"}}) Expect(err).Should(BeNil()) By("create two envbinding") req := apisv1.CreateApplicationEnvbindingRequest{EnvBinding: envBindingDemo1} - base, err := envBindingUsecase.CreateEnvBinding(context.TODO(), testApp, req) + base, err := envBindingService.CreateEnvBinding(context.TODO(), testApp, req) Expect(err).Should(BeNil()) Expect(cmp.Diff(base.Name, req.Name)).Should(BeEmpty()) req = apisv1.CreateApplicationEnvbindingRequest{EnvBinding: envBindingDemo2} - base, err = envBindingUsecase.CreateEnvBinding(context.TODO(), testApp, req) + base, err = envBindingService.CreateEnvBinding(context.TODO(), testApp, req) Expect(err).Should(BeNil()) Expect(cmp.Diff(base.Name, req.Name)).Should(BeEmpty()) By("auto create two workflow") - workflow, err := workflowUsecase.GetWorkflow(context.TODO(), testApp, convertWorkflowName("envbinding-dev")) + workflow, err := workflowService.GetWorkflow(context.TODO(), testApp, repository.ConvertWorkflowName("envbinding-dev")) Expect(err).Should(BeNil()) Expect(cmp.Diff(workflow.Steps[0].Name, "dev-target")).Should(BeEmpty()) - workflow, err = workflowUsecase.GetWorkflow(context.TODO(), testApp, convertWorkflowName("envbinding-prod")) + workflow, err = workflowService.GetWorkflow(context.TODO(), testApp, repository.ConvertWorkflowName("envbinding-prod")) Expect(err).Should(BeNil()) Expect(cmp.Diff(workflow.Steps[0].Name, "prod-target")).Should(BeEmpty()) }) It("Test GetApplication Envs function", func() { - envBindings, err := envBindingUsecase.GetEnvBindings(context.TODO(), testApp) + envBindings, err := envBindingService.GetEnvBindings(context.TODO(), testApp) Expect(err).Should(BeNil()) Expect(envBindings).ShouldNot(BeNil()) Expect(cmp.Diff(len(envBindings), 2)).Should(BeEmpty()) }) It("Test GetApplication Env function", func() { - envBinding, err := envBindingUsecase.GetEnvBinding(context.TODO(), testApp, "envbinding-dev") + envBinding, err := envBindingService.GetEnvBinding(context.TODO(), testApp, "envbinding-dev") Expect(err).Should(BeNil()) Expect(envBinding).ShouldNot(BeNil()) Expect(cmp.Diff(envBinding.Name, "envbinding-dev")).Should(BeEmpty()) }) It("Test Application UpdateEnv function", func() { - envBinding, err := envBindingUsecase.UpdateEnvBinding(context.TODO(), testApp, "envbinding-prod", apisv1.PutApplicationEnvBindingRequest{}) + envBinding, err := envBindingService.UpdateEnvBinding(context.TODO(), testApp, "envbinding-prod", apisv1.PutApplicationEnvBindingRequest{}) Expect(err).Should(BeNil()) Expect(envBinding).ShouldNot(BeNil()) Expect(cmp.Diff(envBinding.TargetNames[0], "prod-target")).Should(BeEmpty()) - workflow, err := workflowUsecase.GetWorkflow(context.TODO(), testApp, "workflow-envbinding-prod") + workflow, err := workflowService.GetWorkflow(context.TODO(), testApp, "workflow-envbinding-prod") Expect(err).Should(BeNil()) Expect(len(workflow.Steps)).Should(Equal(1)) Expect(cmp.Diff(workflow.Steps[0].Name, "prod-target")).Should(BeEmpty()) }) It("Test Application DeleteEnv function", func() { - err := envBindingUsecase.DeleteEnvBinding(context.TODO(), testApp, "envbinding-dev") + err := envBindingService.DeleteEnvBinding(context.TODO(), testApp, "envbinding-dev") Expect(err).Should(BeNil()) - _, err = workflowUsecase.GetWorkflow(context.TODO(), testApp, convertWorkflowName("envbinding-dev")) + _, err = workflowService.GetWorkflow(context.TODO(), testApp, repository.ConvertWorkflowName("envbinding-dev")) Expect(err).ShouldNot(BeNil()) - err = envBindingUsecase.DeleteEnvBinding(context.TODO(), testApp, "envbinding-prod") + err = envBindingService.DeleteEnvBinding(context.TODO(), testApp, "envbinding-prod") Expect(err).Should(BeNil()) - _, err = workflowUsecase.GetWorkflow(context.TODO(), testApp, convertWorkflowName("envbinding-prod")) + _, err = workflowService.GetWorkflow(context.TODO(), testApp, repository.ConvertWorkflowName("envbinding-prod")) Expect(err).ShouldNot(BeNil()) }) @@ -148,20 +149,20 @@ var _ = Describe("Test envBindingUsecase functions", func() { testBatchApp := &model.Application{ Name: "test-batch-createt", } - err := envBindingUsecase.BatchCreateEnvBinding(context.TODO(), testBatchApp, apisv1.EnvBindingList{&envBindingDemo1, &envBindingDemo2}) + err := envBindingService.BatchCreateEnvBinding(context.TODO(), testBatchApp, apisv1.EnvBindingList{&envBindingDemo1, &envBindingDemo2}) Expect(err).Should(BeNil()) - envBindings, err := envBindingUsecase.GetEnvBindings(context.TODO(), testBatchApp) + envBindings, err := envBindingService.GetEnvBindings(context.TODO(), testBatchApp) Expect(err).Should(BeNil()) Expect(cmp.Diff(len(envBindings), 2)).Should(BeEmpty()) - workflows, err := workflowUsecase.ListApplicationWorkflow(context.TODO(), testBatchApp) + workflows, err := workflowService.ListApplicationWorkflow(context.TODO(), testBatchApp) Expect(err).Should(BeNil()) Expect(cmp.Diff(len(workflows), 2)).Should(BeEmpty()) }) It("Test BatchDeleteEnvBinding function", func() { - err := envBindingUsecase.BatchDeleteEnvBinding(context.TODO(), testApp) + err := envBindingService.BatchDeleteEnvBinding(context.TODO(), testApp) Expect(err).Should(BeNil()) - envBindings, err := envBindingUsecase.GetEnvBindings(context.TODO(), testApp) + envBindings, err := envBindingService.GetEnvBindings(context.TODO(), testApp) Expect(err).Should(BeNil()) Expect(cmp.Diff(len(envBindings), 0)).Should(BeEmpty()) }) diff --git a/pkg/apiserver/rest/usecase/helm.go b/pkg/apiserver/domain/service/helm.go similarity index 75% rename from pkg/apiserver/rest/usecase/helm.go rename to pkg/apiserver/domain/service/helm.go index 01aa422a8..6f070dbc5 100644 --- a/pkg/apiserver/rest/usecase/helm.go +++ b/pkg/apiserver/domain/service/helm.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package usecase +package service import ( "context" @@ -23,10 +23,9 @@ import ( "github.com/oam-dev/kubevela/pkg/utils/config" "github.com/oam-dev/kubevela/apis/types" - "github.com/oam-dev/kubevela/pkg/apiserver/clients" - "github.com/oam-dev/kubevela/pkg/apiserver/log" - v1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + v1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/log" "github.com/oam-dev/kubevela/pkg/oam" "github.com/oam-dev/kubevela/pkg/utils" "github.com/oam-dev/kubevela/pkg/utils/common" @@ -39,39 +38,34 @@ import ( "helm.sh/helm/v3/pkg/repo" ) -// NewHelmUsecase return a helmHandler -func NewHelmUsecase() HelmHandler { - c, err := clients.GetKubeClient() - if err != nil { - log.Logger.Fatalf("get kube client failure %s", err.Error()) - } - return defaultHelmHandler{ - helper: helm.NewHelperWithCache(), - k8sClient: c, +// NewHelmService return a helmHandler +func NewHelmService() HelmService { + return &defaultHelmImpl{ + helper: helm.NewHelperWithCache(), } } -// HelmHandler responsible handle helm related interface -type HelmHandler interface { +// HelmService responsible handle helm related interface +type HelmService interface { ListChartNames(ctx context.Context, url string, secretName string, skipCache bool) ([]string, error) ListChartVersions(ctx context.Context, url string, chartName string, secretName string, skipCache bool) (repo.ChartVersions, error) GetChartValues(ctx context.Context, url string, chartName string, version string, secretName string, skipCache bool) (map[string]interface{}, error) ListChartRepo(ctx context.Context, projectName string) (*v1.ChartRepoResponseList, error) } -type defaultHelmHandler struct { +type defaultHelmImpl struct { helper *helm.Helper - k8sClient client.Client + K8sClient client.Client `inject:"kubeClient"` } -func (d defaultHelmHandler) ListChartNames(ctx context.Context, repoURL string, secretName string, skipCache bool) ([]string, error) { +func (d defaultHelmImpl) ListChartNames(ctx context.Context, repoURL string, secretName string, skipCache bool) ([]string, error) { if !utils.IsValidURL(repoURL) { return nil, bcode.ErrRepoInvalidURL } var opts *common.HTTPOption var err error if len(secretName) != 0 { - opts, err = helm.SetBasicAuthInfo(ctx, d.k8sClient, types2.NamespacedName{Namespace: types.DefaultKubeVelaNS, Name: secretName}) + opts, err = helm.SetBasicAuthInfo(ctx, d.K8sClient, types2.NamespacedName{Namespace: types.DefaultKubeVelaNS, Name: secretName}) if err != nil { return nil, bcode.ErrRepoBasicAuth } @@ -84,14 +78,14 @@ func (d defaultHelmHandler) ListChartNames(ctx context.Context, repoURL string, return charts, nil } -func (d defaultHelmHandler) ListChartVersions(ctx context.Context, repoURL string, chartName string, secretName string, skipCache bool) (repo.ChartVersions, error) { +func (d defaultHelmImpl) ListChartVersions(ctx context.Context, repoURL string, chartName string, secretName string, skipCache bool) (repo.ChartVersions, error) { if !utils.IsValidURL(repoURL) { return nil, bcode.ErrRepoInvalidURL } var opts *common.HTTPOption var err error if len(secretName) != 0 { - opts, err = helm.SetBasicAuthInfo(ctx, d.k8sClient, types2.NamespacedName{Namespace: types.DefaultKubeVelaNS, Name: secretName}) + opts, err = helm.SetBasicAuthInfo(ctx, d.K8sClient, types2.NamespacedName{Namespace: types.DefaultKubeVelaNS, Name: secretName}) if err != nil { return nil, bcode.ErrRepoBasicAuth } @@ -108,14 +102,14 @@ func (d defaultHelmHandler) ListChartVersions(ctx context.Context, repoURL strin return chartVersions, nil } -func (d defaultHelmHandler) GetChartValues(ctx context.Context, repoURL string, chartName string, version string, secretName string, skipCache bool) (map[string]interface{}, error) { +func (d defaultHelmImpl) GetChartValues(ctx context.Context, repoURL string, chartName string, version string, secretName string, skipCache bool) (map[string]interface{}, error) { if !utils.IsValidURL(repoURL) { return nil, bcode.ErrRepoInvalidURL } var opts *common.HTTPOption var err error if len(secretName) != 0 { - opts, err = helm.SetBasicAuthInfo(ctx, d.k8sClient, types2.NamespacedName{Namespace: types.DefaultKubeVelaNS, Name: secretName}) + opts, err = helm.SetBasicAuthInfo(ctx, d.K8sClient, types2.NamespacedName{Namespace: types.DefaultKubeVelaNS, Name: secretName}) if err != nil { return nil, bcode.ErrRepoBasicAuth } @@ -130,7 +124,7 @@ func (d defaultHelmHandler) GetChartValues(ctx context.Context, repoURL string, return res, nil } -func (d defaultHelmHandler) ListChartRepo(ctx context.Context, projectName string) (*v1.ChartRepoResponseList, error) { +func (d defaultHelmImpl) ListChartRepo(ctx context.Context, projectName string) (*v1.ChartRepoResponseList, error) { var res []*v1.ChartRepoResponse var err error @@ -139,7 +133,7 @@ func (d defaultHelmHandler) ListChartRepo(ctx context.Context, projectName strin client.MatchingLabels{oam.LabelConfigType: "config-helm-repository"}, client.InNamespace(types.DefaultKubeVelaNS), } - err = d.k8sClient.List(ctx, &projectSecrets, opts...) + err = d.K8sClient.List(ctx, &projectSecrets, opts...) if err != nil { return nil, err } diff --git a/pkg/apiserver/rest/usecase/helm_test.go b/pkg/apiserver/domain/service/helm_test.go similarity index 95% rename from pkg/apiserver/rest/usecase/helm_test.go rename to pkg/apiserver/domain/service/helm_test.go index 767c53bb2..290957574 100644 --- a/pkg/apiserver/rest/usecase/helm_test.go +++ b/pkg/apiserver/domain/service/helm_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package usecase +package service import ( "context" @@ -29,6 +29,7 @@ import ( . "github.com/onsi/gomega" "github.com/oam-dev/kubevela/pkg/oam/util" + "github.com/oam-dev/kubevela/pkg/utils/helm" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -51,6 +52,14 @@ func TestFlattenKeyFunc(t *testing.T) { assert.Equal(t, dstMap, res) } +// NewTestHelmService new helm service for test +func NewTestHelmService() HelmService { + return &defaultHelmImpl{ + helper: helm.NewHelperWithCache(), + K8sClient: k8sClient, + } +} + var _ = Describe("Test helm repo list", func() { ctx := context.Background() var pSec, gSec v1.Secret @@ -71,7 +80,7 @@ var _ = Describe("Test helm repo list", func() { }) It("Test list with project ", func() { - u := NewHelmUsecase() + u := NewTestHelmService() list, err := u.ListChartRepo(ctx, "my-project") Expect(err).Should(BeNil()) Expect(len(list.ChartRepoResponse)).Should(BeEquivalentTo(2)) @@ -90,7 +99,7 @@ var _ = Describe("Test helm repo list", func() { }) It("Test list func with not exist project", func() { - u := NewHelmUsecase() + u := NewTestHelmService() list, err := u.ListChartRepo(ctx, "not-exist-project") Expect(err).Should(BeNil()) Expect(len(list.ChartRepoResponse)).Should(BeEquivalentTo(1)) @@ -99,7 +108,7 @@ var _ = Describe("Test helm repo list", func() { }) It("Test list func without project", func() { - u := NewHelmUsecase() + u := NewTestHelmService() list, err := u.ListChartRepo(ctx, "") Expect(err).Should(BeNil()) Expect(len(list.ChartRepoResponse)).Should(BeEquivalentTo(1)) @@ -124,7 +133,7 @@ var _ = Describe("test helm usecasae", func() { Expect(k8sClient.Delete(ctx, &repoSec)).Should(BeNil()) }) - It("helm associated usecase interface test", func() { + It("helm associated service interface test", func() { var mockServer *httptest.Server handler := http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { @@ -162,7 +171,7 @@ var _ = Describe("test helm usecasae", func() { defer mockServer.Close() - u := NewHelmUsecase() + u := NewTestHelmService() charts, err := u.ListChartNames(ctx, mockServer.URL, "repo-secret", false) Expect(err).Should(BeNil()) Expect(len(charts)).Should(BeEquivalentTo(1)) @@ -180,7 +189,7 @@ var _ = Describe("test helm usecasae", func() { }) It("coverage not secret notExist error", func() { - u := NewHelmUsecase() + u := NewTestHelmService() _, err := u.ListChartNames(ctx, "http://127.0.0.1:8080", "repo-secret-notExist", false) Expect(err).ShouldNot(BeNil()) diff --git a/pkg/apiserver/rest/usecase/oam_application.go b/pkg/apiserver/domain/service/oam_application.go similarity index 67% rename from pkg/apiserver/rest/usecase/oam_application.go rename to pkg/apiserver/domain/service/oam_application.go index 6005c9f3d..3bf955a1b 100644 --- a/pkg/apiserver/rest/usecase/oam_application.go +++ b/pkg/apiserver/domain/service/oam_application.go @@ -14,13 +14,11 @@ limitations under the License. */ -package usecase +package service import ( "context" - "github.com/oam-dev/kubevela/pkg/apiserver/clients" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" v1 "k8s.io/api/core/v1" @@ -28,33 +26,32 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" - apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" + apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" ) -// OAMApplicationUsecase oam_application usecase -type OAMApplicationUsecase interface { +// OAMApplicationService oam_application service +type OAMApplicationService interface { CreateOrUpdateOAMApplication(context.Context, apisv1.ApplicationRequest, string, string) error GetOAMApplication(context.Context, string, string) (*apisv1.ApplicationResponse, error) DeleteOAMApplication(context.Context, string, string) error } -// NewOAMApplicationUsecase new oam_application usecase -func NewOAMApplicationUsecase() OAMApplicationUsecase { - kubeClient, _ := clients.GetKubeClient() - return &oamApplicationUsecaseImpl{kubeClient: kubeClient} +// NewOAMApplicationService new oam_application service +func NewOAMApplicationService() OAMApplicationService { + return &oamApplicationServiceImpl{} } -type oamApplicationUsecaseImpl struct { - kubeClient client.Client +type oamApplicationServiceImpl struct { + KubeClient client.Client `inject:"kubeClient"` } // CreateOrUpdateOAMApplication create or update application -func (o oamApplicationUsecaseImpl) CreateOrUpdateOAMApplication(ctx context.Context, request apisv1.ApplicationRequest, name, namespace string) error { +func (o oamApplicationServiceImpl) CreateOrUpdateOAMApplication(ctx context.Context, request apisv1.ApplicationRequest, name, namespace string) error { ns := new(v1.Namespace) - err := o.kubeClient.Get(ctx, client.ObjectKey{Name: namespace}, ns) + err := o.KubeClient.Get(ctx, client.ObjectKey{Name: namespace}, ns) if kerrors.IsNotFound(err) { ns.Name = namespace - if err = o.kubeClient.Create(ctx, ns); err != nil { + if err = o.KubeClient.Create(ctx, ns); err != nil { return err } } @@ -72,22 +69,22 @@ func (o oamApplicationUsecaseImpl) CreateOrUpdateOAMApplication(ctx context.Cont } existApp := new(v1beta1.Application) - err = o.kubeClient.Get(ctx, client.ObjectKey{Name: name, Namespace: namespace}, existApp) + err = o.KubeClient.Get(ctx, client.ObjectKey{Name: name, Namespace: namespace}, existApp) if err != nil { if kerrors.IsNotFound(err) { - return o.kubeClient.Create(ctx, app) + return o.KubeClient.Create(ctx, app) } return err } existApp.Spec = app.Spec - return o.kubeClient.Update(ctx, existApp) + return o.KubeClient.Update(ctx, existApp) } // GetOAMApplication get application -func (o oamApplicationUsecaseImpl) GetOAMApplication(ctx context.Context, name, namespace string) (*apisv1.ApplicationResponse, error) { +func (o oamApplicationServiceImpl) GetOAMApplication(ctx context.Context, name, namespace string) (*apisv1.ApplicationResponse, error) { app := new(v1beta1.Application) - if err := o.kubeClient.Get(ctx, client.ObjectKey{Name: name, Namespace: namespace}, app); err != nil { + if err := o.KubeClient.Get(ctx, client.ObjectKey{Name: name, Namespace: namespace}, app); err != nil { return nil, err } return &apisv1.ApplicationResponse{ @@ -99,8 +96,8 @@ func (o oamApplicationUsecaseImpl) GetOAMApplication(ctx context.Context, name, } // DeleteOAMApplication delete application -func (o oamApplicationUsecaseImpl) DeleteOAMApplication(ctx context.Context, name, namespace string) error { - return client.IgnoreNotFound(o.kubeClient.Delete(ctx, &v1beta1.Application{ +func (o oamApplicationServiceImpl) DeleteOAMApplication(ctx context.Context, name, namespace string) error { + return client.IgnoreNotFound(o.KubeClient.Delete(ctx, &v1beta1.Application{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, diff --git a/pkg/apiserver/rest/usecase/oam_application_test.go b/pkg/apiserver/domain/service/oam_application_test.go similarity index 88% rename from pkg/apiserver/rest/usecase/oam_application_test.go rename to pkg/apiserver/domain/service/oam_application_test.go index a8fff0087..533839c79 100644 --- a/pkg/apiserver/rest/usecase/oam_application_test.go +++ b/pkg/apiserver/domain/service/oam_application_test.go @@ -14,7 +14,7 @@ limitations under the License. */ -package usecase +package service import ( "context" @@ -29,13 +29,13 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" - apiv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" + apiv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" "github.com/oam-dev/kubevela/pkg/oam/util" "github.com/oam-dev/kubevela/pkg/utils/common" ) -var _ = Describe("Test oam application usecase function", func() { - var oamAppUsecase *oamApplicationUsecaseImpl +var _ = Describe("Test oam application service function", func() { + var oamAppService *oamApplicationServiceImpl var ctx context.Context var baseApp v1beta1.Application var ns corev1.Namespace @@ -45,8 +45,8 @@ var _ = Describe("Test oam application usecase function", func() { ctx = context.Background() namespace = randomNamespaceName("test-oam-app") ns = corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: namespace}} - oamAppUsecase = &oamApplicationUsecaseImpl{ - kubeClient: k8sClient, + oamAppService = &oamApplicationServiceImpl{ + KubeClient: k8sClient, } Expect(common.ReadYamlToObject("./testdata/example-app.yaml", &baseApp)).Should(BeNil()) @@ -77,7 +77,7 @@ var _ = Describe("Test oam application usecase function", func() { Policies: baseApp.Spec.Policies, Workflow: baseApp.Spec.Workflow, } - Expect(oamAppUsecase.CreateOrUpdateOAMApplication(ctx, req, appName, appNs)).Should(BeNil()) + Expect(oamAppService.CreateOrUpdateOAMApplication(ctx, req, appName, appNs)).Should(BeNil()) app := new(v1beta1.Application) Expect(k8sClient.Get(ctx, client.ObjectKey{Namespace: appNs, Name: appName}, app)).Should(BeNil()) @@ -89,7 +89,7 @@ var _ = Describe("Test oam application usecase function", func() { updateReq := apiv1.ApplicationRequest{ Components: baseApp.Spec.Components[1:], } - Expect(oamAppUsecase.CreateOrUpdateOAMApplication(ctx, updateReq, appName, appNs)).Should(BeNil()) + Expect(oamAppService.CreateOrUpdateOAMApplication(ctx, updateReq, appName, appNs)).Should(BeNil()) updatedApp := new(v1beta1.Application) Expect(k8sClient.Get(ctx, client.ObjectKey{Namespace: appNs, Name: appName}, updatedApp)).Should(BeNil()) @@ -100,7 +100,7 @@ var _ = Describe("Test oam application usecase function", func() { It("Test GetOAMApplication function", func() { By("test get an existed application") - resp, err := oamAppUsecase.GetOAMApplication(ctx, baseApp.Name, namespace) + resp, err := oamAppService.GetOAMApplication(ctx, baseApp.Name, namespace) Expect(err).Should(BeNil()) Expect(resp.Spec.Components).Should(Equal(baseApp.Spec.Components)) @@ -113,7 +113,7 @@ var _ = Describe("Test oam application usecase function", func() { app := new(v1beta1.Application) Expect(k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: baseApp.Name}, app)).Should(BeNil()) - Expect(oamAppUsecase.DeleteOAMApplication(ctx, baseApp.Name, namespace)).Should(BeNil()) + Expect(oamAppService.DeleteOAMApplication(ctx, baseApp.Name, namespace)).Should(BeNil()) err := k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: baseApp.Name}, app) Expect(kerrors.IsNotFound(err)).Should(BeTrue()) }) diff --git a/pkg/apiserver/rest/usecase/project.go b/pkg/apiserver/domain/service/project.go similarity index 80% rename from pkg/apiserver/rest/usecase/project.go rename to pkg/apiserver/domain/service/project.go index 9f4d5cf08..f86f14c01 100644 --- a/pkg/apiserver/rest/usecase/project.go +++ b/pkg/apiserver/domain/service/project.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package usecase +package service import ( "context" @@ -33,18 +33,18 @@ import ( "github.com/oam-dev/kubevela/apis/core.oam.dev/common" "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" "github.com/oam-dev/kubevela/apis/types" - "github.com/oam-dev/kubevela/pkg/apiserver/clients" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/log" - "github.com/oam-dev/kubevela/pkg/apiserver/model" - apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/repository" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" + apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/log" "github.com/oam-dev/kubevela/pkg/multicluster" image "github.com/oam-dev/kubevela/pkg/utils/imageregistry" ) -// ProjectUsecase project manage usecase. -type ProjectUsecase interface { +// ProjectService project manage service. +type ProjectService interface { GetProject(ctx context.Context, projectName string) (*model.Project, error) DetailProject(ctx context.Context, projectName string) (*apisv1.ProjectBase, error) ListProjects(ctx context.Context, page, pageSize int) (*apisv1.ListProjectResponse, error) @@ -61,32 +61,27 @@ type ProjectUsecase interface { ValidateImage(ctx context.Context, projectName, image string) (*apisv1.ImageResponse, error) } -type projectUsecaseImpl struct { - ds datastore.DataStore - k8sClient client.Client - rbacUsecase RBACUsecase +type projectServiceImpl struct { + Store datastore.DataStore `inject:"datastore"` + K8sClient client.Client `inject:"kubeClient"` + RbacService RBACService `inject:""` } -// NewProjectUsecase new project usecase -func NewProjectUsecase(ds datastore.DataStore, rbacUsecase RBACUsecase) ProjectUsecase { - k8sClient, err := clients.GetKubeClient() - if err != nil { - log.Logger.Fatalf("get k8sClient failure: %s", err.Error()) - } - p := &projectUsecaseImpl{ds: ds, k8sClient: k8sClient, rbacUsecase: rbacUsecase} - return p +// NewProjectService new project service +func NewProjectService() ProjectService { + return &projectServiceImpl{} } // Init init default data -func (p *projectUsecaseImpl) Init(ctx context.Context) error { +func (p *projectServiceImpl) Init(ctx context.Context) error { return p.InitDefaultProjectEnvTarget(ctx, model.DefaultInitNamespace) } // initDefaultProjectEnvTarget will initialize a default project with a default env that contain a default target // the default env and default target both using the `default` namespace in control plane cluster -func (p *projectUsecaseImpl) InitDefaultProjectEnvTarget(ctx context.Context, defaultNamespace string) error { +func (p *projectServiceImpl) InitDefaultProjectEnvTarget(ctx context.Context, defaultNamespace string) error { var project = model.Project{} - entities, err := p.ds.List(ctx, &project, &datastore.ListOptions{FilterOptions: datastore.FilterOptions{ + entities, err := p.Store.List(ctx, &project, &datastore.ListOptions{FilterOptions: datastore.FilterOptions{ IsNotExist: []datastore.IsNotExistQueryOption{ { Key: "owner", @@ -101,12 +96,12 @@ func (p *projectUsecaseImpl) InitDefaultProjectEnvTarget(ctx context.Context, de pro := project.(*model.Project) var init = pro.Owner == "" pro.Owner = model.DefaultAdminUserName - if err := p.ds.Put(ctx, pro); err != nil { + if err := p.Store.Put(ctx, pro); err != nil { return err } // owner is empty, it is old data if init { - if err := p.rbacUsecase.InitDefaultRoleAndUsersForProject(ctx, pro); err != nil { + if err := p.RbacService.InitDefaultRoleAndUsersForProject(ctx, pro); err != nil { return fmt.Errorf("init default role and users for project %s failure %w", pro.Name, err) } } @@ -114,17 +109,17 @@ func (p *projectUsecaseImpl) InitDefaultProjectEnvTarget(ctx context.Context, de return nil } - count, _ := p.ds.Count(ctx, &project, nil) + count, _ := p.Store.Count(ctx, &project, nil) if count > 0 { return nil } log.Logger.Info("no default project found, adding a default project with default env and target") - if err := createTargetNamespace(ctx, p.k8sClient, multicluster.ClusterLocalName, defaultNamespace, model.DefaultInitName); err != nil { + if err := repository.CreateTargetNamespace(ctx, p.K8sClient, multicluster.ClusterLocalName, defaultNamespace, model.DefaultInitName); err != nil { return fmt.Errorf("initialize default target namespace failed %w", err) } // initialize default target first - err = createTarget(ctx, p.ds, &model.Target{ + err = repository.CreateTarget(ctx, p.Store, &model.Target{ Name: model.DefaultInitName, Alias: "Default", Description: model.DefaultTargetDescription, @@ -139,7 +134,7 @@ func (p *projectUsecaseImpl) InitDefaultProjectEnvTarget(ctx context.Context, de } // initialize default target first - err = createEnv(ctx, p.k8sClient, p.ds, &model.Env{ + err = repository.CreateEnv(ctx, p.K8sClient, p.Store, &model.Env{ Name: model.DefaultInitName, Alias: "Default", Description: model.DefaultEnvDescription, @@ -149,7 +144,6 @@ func (p *projectUsecaseImpl) InitDefaultProjectEnvTarget(ctx context.Context, de }) // for idempotence, ignore default env already exist error if err != nil && errors.Is(err, bcode.ErrEnvAlreadyExists) { - return fmt.Errorf("initialize default environment failed %w", err) } @@ -166,9 +160,9 @@ func (p *projectUsecaseImpl) InitDefaultProjectEnvTarget(ctx context.Context, de } // GetProject get project -func (p *projectUsecaseImpl) GetProject(ctx context.Context, projectName string) (*model.Project, error) { +func (p *projectServiceImpl) GetProject(ctx context.Context, projectName string) (*model.Project, error) { project := &model.Project{Name: projectName} - if err := p.ds.Get(ctx, project); err != nil { + if err := p.Store.Get(ctx, project); err != nil { if errors.Is(err, datastore.ErrRecordNotExist) { return nil, bcode.ErrProjectIsNotExist } @@ -177,14 +171,14 @@ func (p *projectUsecaseImpl) GetProject(ctx context.Context, projectName string) return project, nil } -func (p *projectUsecaseImpl) DetailProject(ctx context.Context, projectName string) (*apisv1.ProjectBase, error) { +func (p *projectServiceImpl) DetailProject(ctx context.Context, projectName string) (*apisv1.ProjectBase, error) { project, err := p.GetProject(ctx, projectName) if err != nil { return nil, err } var user = &model.User{Name: project.Owner} if project.Owner != "" { - if err := p.ds.Get(ctx, user); err != nil { + if err := p.Store.Get(ctx, user); err != nil { log.Logger.Warnf("get project owner %s info failure %s", project.Owner, err.Error()) } } @@ -215,11 +209,11 @@ func listProjects(ctx context.Context, ds datastore.DataStore, page, pageSize in return &apisv1.ListProjectResponse{Projects: projects, Total: total}, nil } -func (p *projectUsecaseImpl) ListUserProjects(ctx context.Context, userName string) ([]*apisv1.ProjectBase, error) { +func (p *projectServiceImpl) ListUserProjects(ctx context.Context, userName string) ([]*apisv1.ProjectBase, error) { var projectUser = model.ProjectUser{ Username: userName, } - entities, err := p.ds.List(ctx, &projectUser, nil) + entities, err := p.Store.List(ctx, &projectUser, nil) if err != nil { return nil, err } @@ -230,7 +224,7 @@ func (p *projectUsecaseImpl) ListUserProjects(ctx context.Context, userName stri if len(projectNames) == 0 { return []*apisv1.ProjectBase{}, nil } - projectEntities, err := p.ds.List(ctx, &model.Project{}, &datastore.ListOptions{FilterOptions: datastore.FilterOptions{In: []datastore.InQueryOption{{ + projectEntities, err := p.Store.List(ctx, &model.Project{}, &datastore.ListOptions{FilterOptions: datastore.FilterOptions{In: []datastore.InQueryOption{{ Key: "name", Values: projectNames, }}}}) @@ -245,18 +239,18 @@ func (p *projectUsecaseImpl) ListUserProjects(ctx context.Context, userName stri } // ListProjects list projects -func (p *projectUsecaseImpl) ListProjects(ctx context.Context, page, pageSize int) (*apisv1.ListProjectResponse, error) { - return listProjects(ctx, p.ds, page, pageSize) +func (p *projectServiceImpl) ListProjects(ctx context.Context, page, pageSize int) (*apisv1.ListProjectResponse, error) { + return listProjects(ctx, p.Store, page, pageSize) } // DeleteProject delete a project -func (p *projectUsecaseImpl) DeleteProject(ctx context.Context, name string) error { +func (p *projectServiceImpl) DeleteProject(ctx context.Context, name string) error { _, err := p.GetProject(ctx, name) if err != nil { return err } - count, err := p.ds.Count(ctx, &model.Application{Project: name}, nil) + count, err := p.Store.Count(ctx, &model.Application{Project: name}, nil) if err != nil { return err } @@ -264,7 +258,7 @@ func (p *projectUsecaseImpl) DeleteProject(ctx context.Context, name string) err return bcode.ErrProjectDenyDeleteByApplication } - count, err = p.ds.Count(ctx, &model.Target{Project: name}, nil) + count, err = p.Store.Count(ctx, &model.Target{Project: name}, nil) if err != nil { return err } @@ -272,7 +266,7 @@ func (p *projectUsecaseImpl) DeleteProject(ctx context.Context, name string) err return bcode.ErrProjectDenyDeleteByTarget } - count, err = p.ds.Count(ctx, &model.Env{Project: name}, nil) + count, err = p.Store.Count(ctx, &model.Env{Project: name}, nil) if err != nil { return err } @@ -288,32 +282,32 @@ func (p *projectUsecaseImpl) DeleteProject(ctx context.Context, name string) err } } - roles, _ := p.rbacUsecase.ListRole(ctx, name, 0, 0) + roles, _ := p.RbacService.ListRole(ctx, name, 0, 0) for _, role := range roles.Roles { - err := p.rbacUsecase.DeleteRole(ctx, name, role.Name) + err := p.RbacService.DeleteRole(ctx, name, role.Name) if err != nil { return err } } - permissions, _ := p.rbacUsecase.ListPermissions(ctx, name) + permissions, _ := p.RbacService.ListPermissions(ctx, name) for _, perm := range permissions { - err := p.rbacUsecase.DeletePermission(ctx, name, perm.Name) + err := p.RbacService.DeletePermission(ctx, name, perm.Name) if err != nil { return err } } - if err := p.ds.Delete(ctx, &model.Project{Name: name}); err != nil { + if err := p.Store.Delete(ctx, &model.Project{Name: name}); err != nil { return err } // delete config-sync application - return destroySyncConfigsApp(ctx, p.k8sClient, name) + return destroySyncConfigsApp(ctx, p.K8sClient, name) } // CreateProject create project -func (p *projectUsecaseImpl) CreateProject(ctx context.Context, req apisv1.CreateProjectRequest) (*apisv1.ProjectBase, error) { +func (p *projectServiceImpl) CreateProject(ctx context.Context, req apisv1.CreateProjectRequest) (*apisv1.ProjectBase, error) { - exist, err := p.ds.IsExist(ctx, &model.Project{Name: req.Name}) + exist, err := p.Store.IsExist(ctx, &model.Project{Name: req.Name}) if err != nil { log.Logger.Errorf("check project name is exist failure %s", err.Error()) return nil, bcode.ErrProjectIsExist @@ -330,7 +324,7 @@ func (p *projectUsecaseImpl) CreateProject(ctx context.Context, req apisv1.Creat } var user = &model.User{Name: owner} if owner != "" { - if err := p.ds.Get(ctx, user); err != nil { + if err := p.Store.Get(ctx, user); err != nil { return nil, bcode.ErrProjectOwnerIsNotExist } } @@ -342,11 +336,11 @@ func (p *projectUsecaseImpl) CreateProject(ctx context.Context, req apisv1.Creat Owner: owner, } - if err := p.ds.Add(ctx, newProject); err != nil { + if err := p.Store.Add(ctx, newProject); err != nil { return nil, err } - if err := p.rbacUsecase.InitDefaultRoleAndUsersForProject(ctx, newProject); err != nil { + if err := p.RbacService.InitDefaultRoleAndUsersForProject(ctx, newProject); err != nil { log.Logger.Errorf("init default role and users for project failure %s", err.Error()) } @@ -354,7 +348,7 @@ func (p *projectUsecaseImpl) CreateProject(ctx context.Context, req apisv1.Creat } // UpdateProject update project -func (p *projectUsecaseImpl) UpdateProject(ctx context.Context, projectName string, req apisv1.UpdateProjectRequest) (*apisv1.ProjectBase, error) { +func (p *projectServiceImpl) UpdateProject(ctx context.Context, projectName string, req apisv1.UpdateProjectRequest) (*apisv1.ProjectBase, error) { project, err := p.GetProject(ctx, projectName) if err != nil { return nil, err @@ -363,7 +357,7 @@ func (p *projectUsecaseImpl) UpdateProject(ctx context.Context, projectName stri project.Description = req.Description var user = &model.User{Name: req.Owner} if req.Owner != "" { - if err := p.ds.Get(ctx, user); err != nil { + if err := p.Store.Get(ctx, user); err != nil { if errors.Is(err, datastore.ErrRecordNotExist) { return nil, bcode.ErrProjectOwnerIsNotExist } @@ -371,18 +365,18 @@ func (p *projectUsecaseImpl) UpdateProject(ctx context.Context, projectName stri } project.Owner = req.Owner } - err = p.ds.Put(ctx, project) + err = p.Store.Put(ctx, project) if err != nil { return nil, err } return ConvertProjectModel2Base(project, user), nil } -func (p *projectUsecaseImpl) ListProjectUser(ctx context.Context, projectName string, page, pageSize int) (*apisv1.ListProjectUsersResponse, error) { +func (p *projectServiceImpl) ListProjectUser(ctx context.Context, projectName string, page, pageSize int) (*apisv1.ListProjectUsersResponse, error) { var projectUser = model.ProjectUser{ ProjectName: projectName, } - entities, err := p.ds.List(ctx, &projectUser, &datastore.ListOptions{Page: page, PageSize: pageSize, SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderDescending}}}) + entities, err := p.Store.List(ctx, &projectUser, &datastore.ListOptions{Page: page, PageSize: pageSize, SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderDescending}}}) if err != nil { return nil, err } @@ -390,7 +384,7 @@ func (p *projectUsecaseImpl) ListProjectUser(ctx context.Context, projectName st for _, entity := range entities { res.Users = append(res.Users, ConvertProjectUserModel2Base(entity.(*model.ProjectUser))) } - count, err := p.ds.Count(ctx, &projectUser, nil) + count, err := p.Store.Count(ctx, &projectUser, nil) if err != nil { return nil, err } @@ -398,7 +392,7 @@ func (p *projectUsecaseImpl) ListProjectUser(ctx context.Context, projectName st return &res, nil } -func (p *projectUsecaseImpl) AddProjectUser(ctx context.Context, projectName string, req apisv1.AddProjectUserRequest) (*apisv1.ProjectUserBase, error) { +func (p *projectServiceImpl) AddProjectUser(ctx context.Context, projectName string, req apisv1.AddProjectUserRequest) (*apisv1.ProjectUserBase, error) { project, err := p.GetProject(ctx, projectName) if err != nil { return nil, err @@ -409,7 +403,7 @@ func (p *projectUsecaseImpl) AddProjectUser(ctx context.Context, projectName str Name: role, Project: projectName, } - if err := p.ds.Get(ctx, &projectUser); err != nil { + if err := p.Store.Get(ctx, &projectUser); err != nil { return nil, bcode.ErrProjectRoleCheckFailure } if projectUser.Project != "" && projectUser.Project != projectName { @@ -421,7 +415,7 @@ func (p *projectUsecaseImpl) AddProjectUser(ctx context.Context, projectName str ProjectName: project.Name, UserRoles: req.UserRoles, } - if err := p.ds.Add(ctx, &projectUser); err != nil { + if err := p.Store.Add(ctx, &projectUser); err != nil { if errors.Is(err, datastore.ErrRecordExist) { return nil, bcode.ErrProjectUserExist } @@ -430,7 +424,7 @@ func (p *projectUsecaseImpl) AddProjectUser(ctx context.Context, projectName str return ConvertProjectUserModel2Base(&projectUser), nil } -func (p *projectUsecaseImpl) DeleteProjectUser(ctx context.Context, projectName string, userName string) error { +func (p *projectServiceImpl) DeleteProjectUser(ctx context.Context, projectName string, userName string) error { project, err := p.GetProject(ctx, projectName) if err != nil { return err @@ -439,7 +433,7 @@ func (p *projectUsecaseImpl) DeleteProjectUser(ctx context.Context, projectName Username: userName, ProjectName: project.Name, } - if err := p.ds.Delete(ctx, &projectUser); err != nil { + if err := p.Store.Delete(ctx, &projectUser); err != nil { if errors.Is(err, datastore.ErrRecordExist) { return bcode.ErrProjectUserExist } @@ -448,7 +442,7 @@ func (p *projectUsecaseImpl) DeleteProjectUser(ctx context.Context, projectName return nil } -func (p *projectUsecaseImpl) UpdateProjectUser(ctx context.Context, projectName string, userName string, req apisv1.UpdateProjectUserRequest) (*apisv1.ProjectUserBase, error) { +func (p *projectServiceImpl) UpdateProjectUser(ctx context.Context, projectName string, userName string, req apisv1.UpdateProjectUserRequest) (*apisv1.ProjectUserBase, error) { project, err := p.GetProject(ctx, projectName) if err != nil { return nil, err @@ -459,7 +453,7 @@ func (p *projectUsecaseImpl) UpdateProjectUser(ctx context.Context, projectName Name: role, Project: projectName, } - if err := p.ds.Get(ctx, &projectUser); err != nil { + if err := p.Store.Get(ctx, &projectUser); err != nil { return nil, bcode.ErrProjectRoleCheckFailure } if projectUser.Project != "" && projectUser.Project != projectName { @@ -470,26 +464,26 @@ func (p *projectUsecaseImpl) UpdateProjectUser(ctx context.Context, projectName Username: userName, ProjectName: project.Name, } - if err := p.ds.Get(ctx, &projectUser); err != nil { + if err := p.Store.Get(ctx, &projectUser); err != nil { if errors.Is(err, datastore.ErrRecordExist) { return nil, bcode.ErrProjectUserExist } return nil, err } projectUser.UserRoles = req.UserRoles - if err := p.ds.Put(ctx, &projectUser); err != nil { + if err := p.Store.Put(ctx, &projectUser); err != nil { return nil, err } return ConvertProjectUserModel2Base(&projectUser), nil } -func (p *projectUsecaseImpl) GetConfigs(ctx context.Context, projectName, configType string) ([]*apisv1.Config, error) { +func (p *projectServiceImpl) GetConfigs(ctx context.Context, projectName, configType string) ([]*apisv1.Config, error) { var ( configs []*apisv1.Config legacyTerraformProviders []*apisv1.Config apps = &v1beta1.ApplicationList{} ) - if err := p.k8sClient.List(ctx, apps, client.InNamespace(types.DefaultKubeVelaNS), + if err := p.K8sClient.List(ctx, apps, client.InNamespace(types.DefaultKubeVelaNS), client.MatchingLabels{ model.LabelSourceOfTruth: model.FromInner, types.LabelConfigCatalog: types.VelaCoreConfig, @@ -500,7 +494,7 @@ func (p *projectUsecaseImpl) GetConfigs(ctx context.Context, projectName, config if configType == types.TerraformProvider || configType == "" { // legacy providers var providers = &terraformapi.ProviderList{} - if err := p.k8sClient.List(ctx, providers, client.InNamespace(types.DefaultAppNamespace)); err != nil { + if err := p.K8sClient.List(ctx, providers, client.InNamespace(types.DefaultAppNamespace)); err != nil { return nil, err } for _, p := range providers.Items { @@ -559,7 +553,7 @@ func (p *projectUsecaseImpl) GetConfigs(ctx context.Context, projectName, config for i, c := range configs { if c.ConfigType != "" { d := &v1beta1.ComponentDefinition{} - err := p.k8sClient.Get(ctx, client.ObjectKey{Namespace: types.DefaultKubeVelaNS, Name: c.ConfigType}, d) + err := p.K8sClient.Get(ctx, client.ObjectKey{Namespace: types.DefaultKubeVelaNS, Name: c.ConfigType}, d) if err != nil { klog.InfoS("failed to get component definition", "ComponentDefinition", configType, "err", err) } else { @@ -619,8 +613,8 @@ func retrieveConfigFromApplication(a v1beta1.Application, project string) *apisv } } -func (p *projectUsecaseImpl) ValidateImage(ctx context.Context, projectName, image string) (*apisv1.ImageResponse, error) { - return validateImage(ctx, p.k8sClient, projectName, image) +func (p *projectServiceImpl) ValidateImage(ctx context.Context, projectName, image string) (*apisv1.ImageResponse, error) { + return validateImage(ctx, p.K8sClient, projectName, image) } func validateImage(ctx context.Context, k8sClient client.Client, project, imageName string) (*apisv1.ImageResponse, error) { diff --git a/pkg/apiserver/rest/usecase/project_test.go b/pkg/apiserver/domain/service/project_test.go similarity index 86% rename from pkg/apiserver/rest/usecase/project_test.go rename to pkg/apiserver/domain/service/project_test.go index 4a7fb55fa..04768cc39 100644 --- a/pkg/apiserver/rest/usecase/project_test.go +++ b/pkg/apiserver/domain/service/project_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package usecase +package service import ( "context" @@ -36,42 +36,42 @@ import ( "github.com/oam-dev/kubevela/apis/core.oam.dev/common" "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" velatypes "github.com/oam-dev/kubevela/apis/types" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/model" - apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" + apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" "github.com/oam-dev/kubevela/pkg/multicluster" "github.com/oam-dev/kubevela/pkg/oam" "github.com/oam-dev/kubevela/pkg/oam/util" ) -var _ = Describe("Test project usecase functions", func() { +var _ = Describe("Test project service functions", func() { var ( - projectUsecase *projectUsecaseImpl - envImpl *envUsecaseImpl - userUsecase *userUsecaseImpl - targetImpl *targetUsecaseImpl + projectService *projectServiceImpl + envImpl *envServiceImpl + userService *userServiceImpl + targetImpl *targetServiceImpl defaultNamespace = "project-default-ns1-test" ) BeforeEach(func() { ds, err := NewDatastore(datastore.Config{Type: "kubeapi", Database: "target-test-kubevela"}) Expect(ds).ToNot(BeNil()) Expect(err).Should(BeNil()) - userUsecase = &userUsecaseImpl{ds: ds, k8sClient: k8sClient} + userService = &userServiceImpl{Store: ds, K8sClient: k8sClient} var ns = corev1.Namespace{} ns.Name = defaultNamespace err = k8sClient.Create(context.TODO(), &ns) Expect(err).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{})) - projectUsecase = &projectUsecaseImpl{k8sClient: k8sClient, ds: ds, rbacUsecase: &rbacUsecaseImpl{ds: ds}} - pp, err := projectUsecase.ListProjects(context.TODO(), 0, 0) + projectService = &projectServiceImpl{K8sClient: k8sClient, Store: ds, RbacService: &rbacServiceImpl{Store: ds}} + pp, err := projectService.ListProjects(context.TODO(), 0, 0) Expect(err).Should(BeNil()) // reset all projects for _, p := range pp.Projects { - _ = projectUsecase.DeleteProject(context.TODO(), p.Name) + _ = projectService.DeleteProject(context.TODO(), p.Name) } - envImpl = &envUsecaseImpl{kubeClient: k8sClient, ds: ds, projectUsecase: projectUsecase} + envImpl = &envServiceImpl{KubeClient: k8sClient, Store: ds, ProjectService: projectService} ctx := context.WithValue(context.TODO(), &apisv1.CtxKeyUser, "admin") envs, err := envImpl.ListEnvs(ctx, 0, 0, apisv1.ListEnvOptions{}) Expect(err).Should(BeNil()) @@ -79,7 +79,7 @@ var _ = Describe("Test project usecase functions", func() { for _, e := range envs.Envs { _ = envImpl.DeleteEnv(context.TODO(), e.Name) } - targetImpl = &targetUsecaseImpl{k8sClient: k8sClient, ds: ds} + targetImpl = &targetServiceImpl{K8sClient: k8sClient, Store: ds} targets, err := targetImpl.ListTargets(context.TODO(), 0, 0, "") Expect(err).Should(BeNil()) // reset all projects @@ -94,11 +94,11 @@ var _ = Describe("Test project usecase functions", func() { ns.Name = velatypes.DefaultKubeVelaNS err := k8sClient.Create(context.TODO(), &ns) Expect(err).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{})) - err = userUsecase.Init(context.TODO()) + err = userService.Init(context.TODO()) Expect(err).Should(BeNil()) // init default project - err = projectUsecase.InitDefaultProjectEnvTarget(context.WithValue(context.TODO(), &apisv1.CtxKeyUser, model.DefaultAdminUserName), defaultNamespace) + err = projectService.InitDefaultProjectEnvTarget(context.WithValue(context.TODO(), &apisv1.CtxKeyUser, model.DefaultAdminUserName), defaultNamespace) Expect(err).Should(BeNil()) By("test env created") var namespace corev1.Namespace @@ -112,7 +112,7 @@ var _ = Describe("Test project usecase functions", func() { Expect(cmp.Diff(namespace.Labels[oam.LabelRuntimeNamespaceUsage], oam.VelaNamespaceUsageTarget)).Should(BeEmpty()) By("check project created") - dp, err := projectUsecase.GetProject(context.TODO(), model.DefaultInitName) + dp, err := projectService.GetProject(context.TODO(), model.DefaultInitName) Expect(err).Should(BeNil()) Expect(dp.Alias).Should(BeEquivalentTo("Default")) Expect(dp.Description).Should(BeEquivalentTo(model.DefaultProjectDescription)) @@ -151,12 +151,12 @@ var _ = Describe("Test project usecase functions", func() { Name: "test-project", Description: "this is a project description", } - base, err := projectUsecase.CreateProject(context.TODO(), req) + base, err := projectService.CreateProject(context.TODO(), req) Expect(err).Should(BeNil()) Expect(cmp.Diff(base.Description, req.Description)).Should(BeEmpty()) - _, err = projectUsecase.ListProjects(context.TODO(), 0, 0) + _, err = projectService.ListProjects(context.TODO(), 0, 0) Expect(err).Should(BeNil()) - projectUsecase.DeleteProject(context.TODO(), "test-project") + projectService.DeleteProject(context.TODO(), "test-project") }) It("Test Update project function", func() { @@ -176,10 +176,10 @@ var _ = Describe("Test project usecase functions", func() { }, } Expect(k8sClient.Create(context.TODO(), app1)).Should(BeNil()) - _, err := projectUsecase.CreateProject(context.TODO(), req) + _, err := projectService.CreateProject(context.TODO(), req) Expect(err).Should(BeNil()) - base, err := projectUsecase.UpdateProject(context.TODO(), "test-project", apisv1.UpdateProjectRequest{ + base, err := projectService.UpdateProject(context.TODO(), "test-project", apisv1.UpdateProjectRequest{ Alias: "Change alias", Description: "Change description", Owner: "admin", @@ -189,13 +189,13 @@ var _ = Describe("Test project usecase functions", func() { Expect(base.Description).Should(BeEquivalentTo("Change description")) Expect(base.Owner.Alias).Should(BeEquivalentTo("Administrator")) - _, err = projectUsecase.UpdateProject(context.TODO(), "test-project", apisv1.UpdateProjectRequest{ + _, err = projectService.UpdateProject(context.TODO(), "test-project", apisv1.UpdateProjectRequest{ Alias: "Change alias", Description: "Change description", Owner: "admin-error", }) Expect(err).Should(BeEquivalentTo(bcode.ErrProjectOwnerIsNotExist)) - err = projectUsecase.DeleteProject(context.TODO(), "test-project") + err = projectService.DeleteProject(context.TODO(), "test-project") Expect(err).Should(BeNil()) }) @@ -204,10 +204,10 @@ var _ = Describe("Test project usecase functions", func() { Name: "test-project", Description: "this is a project description", } - _, err := projectUsecase.CreateProject(context.TODO(), req) + _, err := projectService.CreateProject(context.TODO(), req) Expect(err).Should(BeNil()) - _, err = projectUsecase.AddProjectUser(context.TODO(), "test-project", apisv1.AddProjectUserRequest{ + _, err = projectService.AddProjectUser(context.TODO(), "test-project", apisv1.AddProjectUserRequest{ UserName: "admin", UserRoles: []string{"project-admin"}, }) @@ -219,21 +219,21 @@ var _ = Describe("Test project usecase functions", func() { Name: "test-project", Description: "this is a project description", } - _, err := projectUsecase.CreateProject(context.TODO(), req) + _, err := projectService.CreateProject(context.TODO(), req) Expect(err).Should(BeNil()) - _, err = projectUsecase.AddProjectUser(context.TODO(), "test-project", apisv1.AddProjectUserRequest{ + _, err = projectService.AddProjectUser(context.TODO(), "test-project", apisv1.AddProjectUserRequest{ UserName: "admin", UserRoles: []string{"project-admin"}, }) Expect(err).Should(BeNil()) - _, err = projectUsecase.UpdateProjectUser(context.TODO(), "test-project", "admin", apisv1.UpdateProjectUserRequest{ + _, err = projectService.UpdateProjectUser(context.TODO(), "test-project", "admin", apisv1.UpdateProjectUserRequest{ UserRoles: []string{"project-admin", "app-developer"}, }) Expect(err).Should(BeNil()) - _, err = projectUsecase.UpdateProjectUser(context.TODO(), "test-project", "admin", apisv1.UpdateProjectUserRequest{ + _, err = projectService.UpdateProjectUser(context.TODO(), "test-project", "admin", apisv1.UpdateProjectUserRequest{ UserRoles: []string{"project-admin", "app-developer", "xxx"}, }) Expect(err).Should(BeEquivalentTo(bcode.ErrProjectRoleCheckFailure)) @@ -257,23 +257,23 @@ var _ = Describe("Test project usecase functions", func() { } Expect(k8sClient.Create(context.TODO(), app1)).Should(BeNil()) - _, err := projectUsecase.CreateProject(context.TODO(), req) + _, err := projectService.CreateProject(context.TODO(), req) Expect(err).Should(BeNil()) - _, err = projectUsecase.AddProjectUser(context.TODO(), "test-project", apisv1.AddProjectUserRequest{ + _, err = projectService.AddProjectUser(context.TODO(), "test-project", apisv1.AddProjectUserRequest{ UserName: "admin", UserRoles: []string{"project-admin"}, }) Expect(err).Should(BeNil()) - err = projectUsecase.DeleteProjectUser(context.TODO(), "test-project", "admin") + err = projectService.DeleteProjectUser(context.TODO(), "test-project", "admin") Expect(err).Should(BeNil()) - err = projectUsecase.DeleteProject(context.TODO(), "test-project") + err = projectService.DeleteProject(context.TODO(), "test-project") Expect(err).Should(BeNil()) - perms, err := projectUsecase.rbacUsecase.ListPermissions(context.TODO(), "test-project") + perms, err := projectService.RbacService.ListPermissions(context.TODO(), "test-project") Expect(err).Should(BeNil()) Expect(len(perms)).Should(BeEquivalentTo(0)) - roles, err := projectUsecase.rbacUsecase.ListRole(context.TODO(), "test-project", 0, 0) + roles, err := projectService.RbacService.ListRole(context.TODO(), "test-project", 0, 0) Expect(err).Should(BeNil()) Expect(roles.Total).Should(BeEquivalentTo(0)) }) @@ -357,12 +357,12 @@ func TestProjectGetConfigs(t *testing.T) { k8sClient := fake.NewClientBuilder().WithScheme(s).WithObjects(app1, app2, app3, provider1, provider2).Build() - h := &projectUsecaseImpl{k8sClient: k8sClient} + h := &projectServiceImpl{K8sClient: k8sClient} type args struct { projectName string configType string - h ProjectUsecase + h ProjectService } type want struct { @@ -520,7 +520,7 @@ func TestValidateImage(t *testing.T) { }, } k8sClient1 := fake.NewClientBuilder().WithScheme(s).WithObjects(s1).Build() - h1 := &projectUsecaseImpl{k8sClient: k8sClient1} + h1 := &projectServiceImpl{K8sClient: k8sClient1} s2 := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ @@ -539,12 +539,12 @@ func TestValidateImage(t *testing.T) { } k8sClient2 := fake.NewClientBuilder().WithScheme(s).WithObjects(s2).Build() - h2 := &projectUsecaseImpl{k8sClient: k8sClient2} + h2 := &projectServiceImpl{K8sClient: k8sClient2} type args struct { project string imageName string - h ProjectUsecase + h ProjectService } type want struct { diff --git a/pkg/apiserver/rest/usecase/rbac.go b/pkg/apiserver/domain/service/rbac.go similarity index 88% rename from pkg/apiserver/rest/usecase/rbac.go rename to pkg/apiserver/domain/service/rbac.go index 1d73fa99d..11c65812d 100644 --- a/pkg/apiserver/rest/usecase/rbac.go +++ b/pkg/apiserver/domain/service/rbac.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package usecase +package service import ( "context" @@ -26,11 +26,11 @@ import ( "github.com/emicklei/go-restful/v3" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/log" - "github.com/oam-dev/kubevela/pkg/apiserver/model" - apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" + apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/log" "github.com/oam-dev/kubevela/pkg/utils" ) @@ -311,12 +311,12 @@ func registerResourceAction(resource string, actions ...string) { } } -type rbacUsecaseImpl struct { - ds datastore.DataStore +type rbacServiceImpl struct { + Store datastore.DataStore `inject:"datastore"` } -// RBACUsecase implement RBAC-related business logic. -type RBACUsecase interface { +// RBACService implement RBAC-related business logic. +type RBACService interface { CheckPerm(resource string, actions ...string) func(req *restful.Request, res *restful.Response, chain *restful.FilterChain) GetUserPermissions(ctx context.Context, user *model.User, projectName string, withPlatform bool) ([]*model.Permission, error) CreateRole(ctx context.Context, projectName string, req apisv1.CreateRoleRequest) (*apisv1.RoleBase, error) @@ -330,16 +330,14 @@ type RBACUsecase interface { Init(ctx context.Context) error } -// NewRBACUsecase is the usecase service of RBAC -func NewRBACUsecase(ds datastore.DataStore) RBACUsecase { - rbacUsecase := &rbacUsecaseImpl{ - ds: ds, - } - return rbacUsecase +// NewRBACService is the service service of RBAC +func NewRBACService() RBACService { + rbacService := &rbacServiceImpl{} + return rbacService } -func (p *rbacUsecaseImpl) Init(ctx context.Context) error { - count, _ := p.ds.Count(ctx, &model.Permission{}, &datastore.FilterOptions{ +func (p *rbacServiceImpl) Init(ctx context.Context) error { + count, _ := p.Store.Count(ctx, &model.Permission{}, &datastore.FilterOptions{ IsNotExist: []datastore.IsNotExistQueryOption{ { Key: "project", @@ -364,18 +362,18 @@ func (p *rbacUsecaseImpl) Init(ctx context.Context) error { Alias: "Admin", Permissions: []string{"admin"}, }) - if err := p.ds.BatchAdd(context.Background(), batchData); err != nil { + if err := p.Store.BatchAdd(ctx, batchData); err != nil { return fmt.Errorf("init the platform perm policies failure %w", err) } return nil } // GetUserPermissions get user permission policies, if projectName is empty, will only get the platform permission policies -func (p *rbacUsecaseImpl) GetUserPermissions(ctx context.Context, user *model.User, projectName string, withPlatform bool) ([]*model.Permission, error) { +func (p *rbacServiceImpl) GetUserPermissions(ctx context.Context, user *model.User, projectName string, withPlatform bool) ([]*model.Permission, error) { var permissionNames []string var perms []*model.Permission if withPlatform && len(user.UserRoles) > 0 { - entities, err := p.ds.List(ctx, &model.Role{}, &datastore.ListOptions{FilterOptions: datastore.FilterOptions{ + entities, err := p.Store.List(ctx, &model.Role{}, &datastore.ListOptions{FilterOptions: datastore.FilterOptions{ In: []datastore.InQueryOption{ { Key: "name", @@ -405,11 +403,11 @@ func (p *rbacUsecaseImpl) GetUserPermissions(ctx context.Context, user *model.Us Username: user.Name, } var roles []string - if err := p.ds.Get(ctx, &projectUser); err == nil { + if err := p.Store.Get(ctx, &projectUser); err == nil { roles = append(roles, projectUser.UserRoles...) } if len(roles) > 0 { - entities, err := p.ds.List(ctx, &model.Role{Project: projectName}, &datastore.ListOptions{FilterOptions: datastore.FilterOptions{In: []datastore.InQueryOption{ + entities, err := p.Store.List(ctx, &model.Role{Project: projectName}, &datastore.ListOptions{FilterOptions: datastore.FilterOptions{In: []datastore.InQueryOption{ { Key: "name", Values: roles, @@ -431,12 +429,12 @@ func (p *rbacUsecaseImpl) GetUserPermissions(ctx context.Context, user *model.Us return perms, nil } -func (p *rbacUsecaseImpl) UpdatePermission(ctx context.Context, projetName string, permissionName string, req *apisv1.UpdatePermissionRequest) (*apisv1.PermissionBase, error) { +func (p *rbacServiceImpl) UpdatePermission(ctx context.Context, projetName string, permissionName string, req *apisv1.UpdatePermissionRequest) (*apisv1.PermissionBase, error) { perm := &model.Permission{ Project: projetName, Name: permissionName, } - err := p.ds.Get(ctx, perm) + err := p.Store.Get(ctx, perm) if err != nil { if errors.Is(err, datastore.ErrRecordNotExist) { return nil, bcode.ErrPermissionNotExist @@ -447,7 +445,7 @@ func (p *rbacUsecaseImpl) UpdatePermission(ctx context.Context, projetName strin perm.Alias = req.Alias perm.Resources = req.Resources perm.Effect = req.Effect - if err := p.ds.Put(ctx, perm); err != nil { + if err := p.Store.Put(ctx, perm); err != nil { return nil, err } return &apisv1.PermissionBase{ @@ -461,7 +459,7 @@ func (p *rbacUsecaseImpl) UpdatePermission(ctx context.Context, projetName strin }, nil } -func (p *rbacUsecaseImpl) listPermPolices(ctx context.Context, projectName string, permissionNames []string) ([]*model.Permission, error) { +func (p *rbacServiceImpl) listPermPolices(ctx context.Context, projectName string, permissionNames []string) ([]*model.Permission, error) { if len(permissionNames) == 0 { return []*model.Permission{}, nil } @@ -476,7 +474,7 @@ func (p *rbacUsecaseImpl) listPermPolices(ctx context.Context, projectName strin Key: "project", }) } - permEntities, err := p.ds.List(ctx, &model.Permission{Project: projectName}, &datastore.ListOptions{FilterOptions: filter}) + permEntities, err := p.Store.List(ctx, &model.Permission{Project: projectName}, &datastore.ListOptions{FilterOptions: filter}) if err != nil { return nil, err } @@ -487,7 +485,7 @@ func (p *rbacUsecaseImpl) listPermPolices(ctx context.Context, projectName strin return perms, nil } -func (p *rbacUsecaseImpl) CheckPerm(resource string, actions ...string) func(req *restful.Request, res *restful.Response, chain *restful.FilterChain) { +func (p *rbacServiceImpl) CheckPerm(resource string, actions ...string) func(req *restful.Request, res *restful.Response, chain *restful.FilterChain) { registerResourceAction(resource, actions...) f := func(req *restful.Request, res *restful.Response, chain *restful.FilterChain) { // get login user info @@ -497,7 +495,7 @@ func (p *rbacUsecaseImpl) CheckPerm(resource string, actions ...string) func(req return } user := &model.User{Name: userName} - if err := p.ds.Get(req.Request.Context(), user); err != nil { + if err := p.Store.Get(req.Request.Context(), user); err != nil { bcode.ReturnError(req, res, bcode.ErrUnauthorized) return } @@ -521,13 +519,13 @@ func (p *rbacUsecaseImpl) CheckPerm(resource string, actions ...string) func(req } if appName := req.PathParameter(ResourceMaps["project"].subResources["application"].pathName); appName != "" { app := &model.Application{Name: appName} - if err := p.ds.Get(req.Request.Context(), app); err == nil { + if err := p.Store.Get(req.Request.Context(), app); err == nil { return app.Project } } if envName := req.PathParameter(ResourceMaps["project"].subResources["environment"].pathName); envName != "" { env := &model.Env{Name: envName} - if err := p.ds.Get(req.Request.Context(), env); err == nil { + if err := p.Store.Get(req.Request.Context(), env); err == nil { return env.Project } } @@ -559,12 +557,12 @@ func (p *rbacUsecaseImpl) CheckPerm(resource string, actions ...string) func(req return f } -func (p *rbacUsecaseImpl) CreateRole(ctx context.Context, projectName string, req apisv1.CreateRoleRequest) (*apisv1.RoleBase, error) { +func (p *rbacServiceImpl) CreateRole(ctx context.Context, projectName string, req apisv1.CreateRoleRequest) (*apisv1.RoleBase, error) { if projectName != "" { var project = model.Project{ Name: projectName, } - if err := p.ds.Get(ctx, &project); err != nil { + if err := p.Store.Get(ctx, &project); err != nil { return nil, bcode.ErrProjectIsNotExist } } @@ -581,7 +579,7 @@ func (p *rbacUsecaseImpl) CreateRole(ctx context.Context, projectName string, re Project: projectName, Permissions: req.Permissions, } - if err := p.ds.Add(ctx, &role); err != nil { + if err := p.Store.Add(ctx, &role); err != nil { if errors.Is(err, datastore.ErrRecordExist) { return nil, bcode.ErrRoleIsExist } @@ -590,12 +588,12 @@ func (p *rbacUsecaseImpl) CreateRole(ctx context.Context, projectName string, re return ConvertRole2Model(&role, policies), nil } -func (p *rbacUsecaseImpl) DeleteRole(ctx context.Context, projectName, roleName string) error { +func (p *rbacServiceImpl) DeleteRole(ctx context.Context, projectName, roleName string) error { var role = model.Role{ Name: roleName, Project: projectName, } - if err := p.ds.Delete(ctx, &role); err != nil { + if err := p.Store.Delete(ctx, &role); err != nil { if errors.Is(err, datastore.ErrRecordNotExist) { return bcode.ErrRoleIsNotExist } @@ -604,12 +602,12 @@ func (p *rbacUsecaseImpl) DeleteRole(ctx context.Context, projectName, roleName return nil } -func (p *rbacUsecaseImpl) DeletePermission(ctx context.Context, projectName, permName string) error { +func (p *rbacServiceImpl) DeletePermission(ctx context.Context, projectName, permName string) error { var perm = model.Permission{ Name: permName, Project: projectName, } - if err := p.ds.Delete(ctx, &perm); err != nil { + if err := p.Store.Delete(ctx, &perm); err != nil { if errors.Is(err, datastore.ErrRecordNotExist) { return bcode.ErrRoleIsNotExist } @@ -618,12 +616,12 @@ func (p *rbacUsecaseImpl) DeletePermission(ctx context.Context, projectName, per return nil } -func (p *rbacUsecaseImpl) UpdateRole(ctx context.Context, projectName, roleName string, req apisv1.UpdateRoleRequest) (*apisv1.RoleBase, error) { +func (p *rbacServiceImpl) UpdateRole(ctx context.Context, projectName, roleName string, req apisv1.UpdateRoleRequest) (*apisv1.RoleBase, error) { if projectName != "" { var project = model.Project{ Name: projectName, } - if err := p.ds.Get(ctx, &project); err != nil { + if err := p.Store.Get(ctx, &project); err != nil { return nil, bcode.ErrProjectIsNotExist } } @@ -638,7 +636,7 @@ func (p *rbacUsecaseImpl) UpdateRole(ctx context.Context, projectName, roleName Name: roleName, Project: projectName, } - if err := p.ds.Get(ctx, &role); err != nil { + if err := p.Store.Get(ctx, &role); err != nil { if errors.Is(err, datastore.ErrRecordNotExist) { return nil, bcode.ErrRoleIsNotExist } @@ -646,13 +644,13 @@ func (p *rbacUsecaseImpl) UpdateRole(ctx context.Context, projectName, roleName } role.Alias = req.Alias role.Permissions = req.Permissions - if err := p.ds.Put(ctx, &role); err != nil { + if err := p.Store.Put(ctx, &role); err != nil { return nil, err } return ConvertRole2Model(&role, policies), nil } -func (p *rbacUsecaseImpl) ListRole(ctx context.Context, projectName string, page, pageSize int) (*apisv1.ListRolesResponse, error) { +func (p *rbacServiceImpl) ListRole(ctx context.Context, projectName string, page, pageSize int) (*apisv1.ListRolesResponse, error) { var role = model.Role{ Project: projectName, } @@ -662,7 +660,7 @@ func (p *rbacUsecaseImpl) ListRole(ctx context.Context, projectName string, page Key: "project", }) } - entities, err := p.ds.List(ctx, &role, &datastore.ListOptions{FilterOptions: filter, Page: page, PageSize: pageSize, SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderDescending}}}) + entities, err := p.Store.List(ctx, &role, &datastore.ListOptions{FilterOptions: filter, Page: page, PageSize: pageSize, SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderDescending}}}) if err != nil { return nil, err } @@ -690,7 +688,7 @@ func (p *rbacUsecaseImpl) ListRole(ctx context.Context, projectName string, page } res.Roles = append(res.Roles, ConvertRole2Model(entity.(*model.Role), rolePolicies)) } - count, err := p.ds.Count(ctx, &role, &filter) + count, err := p.Store.Count(ctx, &role, &filter) if err != nil { return nil, err } @@ -699,18 +697,18 @@ func (p *rbacUsecaseImpl) ListRole(ctx context.Context, projectName string, page } // ListPermissionTemplate TODO: -func (p *rbacUsecaseImpl) ListPermissionTemplate(ctx context.Context, projectName string) ([]apisv1.PermissionTemplateBase, error) { +func (p *rbacServiceImpl) ListPermissionTemplate(ctx context.Context, projectName string) ([]apisv1.PermissionTemplateBase, error) { return nil, nil } -func (p *rbacUsecaseImpl) ListPermissions(ctx context.Context, projectName string) ([]apisv1.PermissionBase, error) { +func (p *rbacServiceImpl) ListPermissions(ctx context.Context, projectName string) ([]apisv1.PermissionBase, error) { var filter datastore.FilterOptions if projectName == "" { filter.IsNotExist = append(filter.IsNotExist, datastore.IsNotExistQueryOption{ Key: "project", }) } - permEntities, err := p.ds.List(ctx, &model.Permission{Project: projectName}, &datastore.ListOptions{FilterOptions: filter}) + permEntities, err := p.Store.List(ctx, &model.Permission{Project: projectName}, &datastore.ListOptions{FilterOptions: filter}) if err != nil { return nil, err } @@ -730,7 +728,7 @@ func (p *rbacUsecaseImpl) ListPermissions(ctx context.Context, projectName strin return perms, nil } -func (p *rbacUsecaseImpl) InitDefaultRoleAndUsersForProject(ctx context.Context, project *model.Project) error { +func (p *rbacServiceImpl) InitDefaultRoleAndUsersForProject(ctx context.Context, project *model.Project) error { var batchData []datastore.Entity for _, permissionTemp := range defaultProjectPermissionTemplate { var rra = RequestResourceAction{} @@ -772,7 +770,7 @@ func (p *rbacUsecaseImpl) InitDefaultRoleAndUsersForProject(ctx context.Context, } batchData = append(batchData, projectUser) } - return p.ds.BatchAdd(ctx, batchData) + return p.Store.BatchAdd(ctx, batchData) } // ConvertRole2Model convert role model to role base struct diff --git a/pkg/apiserver/rest/usecase/rbac_test.go b/pkg/apiserver/domain/service/rbac_test.go similarity index 91% rename from pkg/apiserver/rest/usecase/rbac_test.go rename to pkg/apiserver/domain/service/rbac_test.go index 9b99183ba..b706ec7a3 100644 --- a/pkg/apiserver/rest/usecase/rbac_test.go +++ b/pkg/apiserver/domain/service/rbac_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package usecase +package service import ( "context" @@ -28,10 +28,10 @@ import ( . "github.com/onsi/gomega" "github.com/stretchr/testify/assert" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/model" - apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" + apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" ) var _ = Describe("Test rbac service", func() { @@ -89,10 +89,10 @@ var _ = Describe("Test rbac service", func() { }) It("Test init and list platform permissions", func() { - rbacUsecase := rbacUsecaseImpl{ds: ds} - err := rbacUsecase.Init(context.TODO()) + rbacService := rbacServiceImpl{Store: ds} + err := rbacService.Init(context.TODO()) Expect(err).Should(BeNil()) - policies, err := rbacUsecase.ListPermissions(context.TODO(), "") + policies, err := rbacService.ListPermissions(context.TODO(), "") Expect(err).Should(BeNil()) Expect(len(policies)).Should(BeEquivalentTo(int64(7))) }) @@ -102,7 +102,7 @@ var _ = Describe("Test rbac service", func() { err := ds.Add(context.TODO(), &model.User{Name: "admin", UserRoles: []string{"admin"}}) Expect(err).Should(BeNil()) - rbac := rbacUsecaseImpl{ds: ds} + rbac := rbacServiceImpl{Store: ds} req := &http.Request{} req = req.WithContext(context.WithValue(req.Context(), &apisv1.CtxKeyUser, "admin")) res := &restful.Response{} @@ -137,7 +137,7 @@ var _ = Describe("Test rbac service", func() { err = ds.Add(context.TODO(), &model.Permission{Project: projectName, Name: "application-manage", Resources: []string{"project:test-app-project/application:*"}, Actions: []string{"*"}}) Expect(err).Should(BeNil()) - rbac := rbacUsecaseImpl{ds: ds} + rbac := rbacServiceImpl{Store: ds} header := http.Header{} header.Set("Accept", "application/json") header.Set("Content-Type", "application/json") @@ -165,7 +165,7 @@ var _ = Describe("Test rbac service", func() { Expect(res.StatusCode()).Should(Equal(int(bcode.ErrForbidden.HTTPCode))) // add list application permission to role - // err = ds.Add(context.TODO(), &model.Permission{Project: projectName, Name: "application-list", Resources: []string{"project:*/application:*"}, Actions: []string{"list"}}) + // err = Store.Add(context.TODO(), &model.Permission{Project: projectName, Name: "application-list", Resources: []string{"project:*/application:*"}, Actions: []string{"list"}}) // Expect(err).Should(BeNil()) // _, err = rbac.UpdateRole(context.TODO(), projectName, "application-admin", apisv1.UpdateRoleRequest{ // Permissions: []string{"application-list", "application-manage"}, @@ -179,27 +179,27 @@ var _ = Describe("Test rbac service", func() { }) It("Test initDefaultRoleAndUsersForProject", func() { - rbacUsecase := rbacUsecaseImpl{ds: ds} + rbacService := rbacServiceImpl{Store: ds} err := ds.Add(context.TODO(), &model.User{Name: "test-user"}) Expect(err).Should(BeNil()) err = ds.Add(context.TODO(), &model.Project{Name: "init-test", Owner: "test-user"}) Expect(err).Should(BeNil()) - err = rbacUsecase.InitDefaultRoleAndUsersForProject(context.TODO(), &model.Project{Name: "init-test"}) + err = rbacService.InitDefaultRoleAndUsersForProject(context.TODO(), &model.Project{Name: "init-test"}) Expect(err).Should(BeNil()) - roles, err := rbacUsecase.ListRole(context.TODO(), "init-test", 0, 0) + roles, err := rbacService.ListRole(context.TODO(), "init-test", 0, 0) Expect(err).Should(BeNil()) Expect(roles.Total).Should(BeEquivalentTo(int64(2))) - policies, err := rbacUsecase.ListPermissions(context.TODO(), "init-test") + policies, err := rbacService.ListPermissions(context.TODO(), "init-test") Expect(err).Should(BeNil()) Expect(len(policies)).Should(BeEquivalentTo(int64(4))) }) It("Test UpdatePermission", func() { - rbacUsecase := rbacUsecaseImpl{ds: ds} - base, err := rbacUsecase.UpdatePermission(context.TODO(), "test-app-project", "application-manage", &apisv1.UpdatePermissionRequest{ + rbacService := rbacServiceImpl{Store: ds} + base, err := rbacService.UpdatePermission(context.TODO(), "test-app-project", "application-manage", &apisv1.UpdatePermissionRequest{ Resources: []string{"project:{projectName}/application:*/*"}, Actions: []string{"*"}, Alias: "App Management Update", diff --git a/pkg/apiserver/domain/service/service.go b/pkg/apiserver/domain/service/service.go new file mode 100644 index 000000000..90fa584f1 --- /dev/null +++ b/pkg/apiserver/domain/service/service.go @@ -0,0 +1,70 @@ +/* +Copyright 2022 The KubeVela Authors. + +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 service + +import ( + "context" + "fmt" + + "github.com/oam-dev/kubevela/pkg/apiserver/config" +) + +// needInitData register the service that need to init data +var needInitData []DataInit + +// InitServiceBean init all service instance +func InitServiceBean(c config.Config) []interface{} { + clusterService := NewClusterService() + rbacService := NewRBACService() + projectService := NewProjectService() + envService := NewEnvService() + targetService := NewTargetService() + workflowService := NewWorkflowService() + oamApplicationService := NewOAMApplicationService() + velaQLService := NewVelaQLService() + definitionService := NewDefinitionService() + addonService := NewAddonService(c.AddonCacheTime) + envBindingService := NewEnvBindingService() + systemInfoService := NewSystemInfoService() + helmService := NewHelmService() + userService := NewUserService() + authenticationService := NewAuthenticationService() + configService := NewConfigService() + applicationService := NewApplicationService() + webhookService := NewWebhookService() + needInitData = []DataInit{clusterService, userService, rbacService, projectService, targetService, systemInfoService} + return []interface{}{ + clusterService, rbacService, projectService, envService, targetService, workflowService, oamApplicationService, + velaQLService, definitionService, addonService, envBindingService, systemInfoService, helmService, userService, + authenticationService, configService, applicationService, webhookService, + } +} + +// DataInit the service set that needs init data +type DataInit interface { + Init(ctx context.Context) error +} + +// InitData init data +func InitData(ctx context.Context) error { + for _, init := range needInitData { + if err := init.Init(ctx); err != nil { + return fmt.Errorf("database init failure %w", err) + } + } + return nil +} diff --git a/pkg/apiserver/rest/usecase/suite_test.go b/pkg/apiserver/domain/service/suite_test.go similarity index 91% rename from pkg/apiserver/rest/usecase/suite_test.go rename to pkg/apiserver/domain/service/suite_test.go index 1c3b1fb76..8a2e9f909 100644 --- a/pkg/apiserver/rest/usecase/suite_test.go +++ b/pkg/apiserver/domain/service/suite_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package usecase +package service import ( "context" @@ -39,10 +39,10 @@ import ( "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" "github.com/oam-dev/kubevela/apis/types" - "github.com/oam-dev/kubevela/pkg/apiserver/clients" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore/kubeapi" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore/mongodb" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/clients" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore/kubeapi" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore/mongodb" "github.com/oam-dev/kubevela/pkg/utils/common" ) @@ -50,9 +50,9 @@ var cfg *rest.Config var k8sClient client.Client var testEnv *envtest.Environment -func TestUsecase(t *testing.T) { +func TestService(t *testing.T) { RegisterFailHandler(Fail) - RunSpecs(t, "Usecase Suite") + RunSpecs(t, "Service Suite") } var _ = BeforeSuite(func(done Done) { diff --git a/pkg/apiserver/rest/usecase/system_info.go b/pkg/apiserver/domain/service/system_info.go similarity index 73% rename from pkg/apiserver/rest/usecase/system_info.go rename to pkg/apiserver/domain/service/system_info.go index 6390b69ee..f8acafe08 100644 --- a/pkg/apiserver/rest/usecase/system_info.go +++ b/pkg/apiserver/domain/service/system_info.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package usecase +package service import ( "context" @@ -23,42 +23,36 @@ import ( "k8s.io/apimachinery/pkg/util/rand" "sigs.k8s.io/controller-runtime/pkg/client" - "github.com/oam-dev/kubevela/pkg/apiserver/clients" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/log" - "github.com/oam-dev/kubevela/pkg/apiserver/model" - v1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" + v1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" "github.com/oam-dev/kubevela/version" ) -// SystemInfoUsecase is usecase for systemInfoCollection -type SystemInfoUsecase interface { +// SystemInfoService is service for systemInfoCollection +type SystemInfoService interface { Get(ctx context.Context) (*model.SystemInfo, error) GetSystemInfo(ctx context.Context) (*v1.SystemInfoResponse, error) UpdateSystemInfo(ctx context.Context, sysInfo v1.SystemInfoRequest) (*v1.SystemInfoResponse, error) Init(ctx context.Context) error } -type systemInfoUsecaseImpl struct { - ds datastore.DataStore - kubeClient client.Client +type systemInfoServiceImpl struct { + Store datastore.DataStore `inject:"datastore"` + KubeClient client.Client `inject:"kubeClient"` } -// NewSystemInfoUsecase return a systemInfoCollectionUsecase -func NewSystemInfoUsecase(ds datastore.DataStore) SystemInfoUsecase { - kubecli, err := clients.GetKubeClient() - if err != nil { - log.Logger.Fatalf("failed to get kube client: %s", err.Error()) - } - return &systemInfoUsecaseImpl{ds: ds, kubeClient: kubecli} +// NewSystemInfoService return a systemInfoCollectionService +func NewSystemInfoService() SystemInfoService { + return &systemInfoServiceImpl{} } -func (u systemInfoUsecaseImpl) Get(ctx context.Context) (*model.SystemInfo, error) { +func (u systemInfoServiceImpl) Get(ctx context.Context) (*model.SystemInfo, error) { // first get request will init systemInfoCollection{installId: {random}, enableCollection: true} info := &model.SystemInfo{} - entities, err := u.ds.List(ctx, info, &datastore.ListOptions{}) + entities, err := u.Store.List(ctx, info, &datastore.ListOptions{}) if err != nil { return nil, err } @@ -74,14 +68,14 @@ func (u systemInfoUsecaseImpl) Get(ctx context.Context) (*model.SystemInfo, erro info.EnableCollection = true info.LoginType = model.LoginTypeLocal info.BaseModel = model.BaseModel{CreateTime: time.Now()} - err = u.ds.Add(ctx, info) + err = u.Store.Add(ctx, info) if err != nil { return nil, err } return info, nil } -func (u systemInfoUsecaseImpl) GetSystemInfo(ctx context.Context) (*v1.SystemInfoResponse, error) { +func (u systemInfoServiceImpl) GetSystemInfo(ctx context.Context) (*v1.SystemInfoResponse, error) { // first get request will init systemInfoCollection{installId: {random}, enableCollection: true} info, err := u.Get(ctx) if err != nil { @@ -106,7 +100,7 @@ func (u systemInfoUsecaseImpl) GetSystemInfo(ctx context.Context) (*v1.SystemInf }, nil } -func (u systemInfoUsecaseImpl) UpdateSystemInfo(ctx context.Context, sysInfo v1.SystemInfoRequest) (*v1.SystemInfoResponse, error) { +func (u systemInfoServiceImpl) UpdateSystemInfo(ctx context.Context, sysInfo v1.SystemInfoRequest) (*v1.SystemInfoResponse, error) { info, err := u.Get(ctx) if err != nil { return nil, err @@ -124,27 +118,27 @@ func (u systemInfoUsecaseImpl) UpdateSystemInfo(ctx context.Context, sysInfo v1. if sysInfo.LoginType == model.LoginTypeDex { admin := &model.User{Name: model.DefaultAdminUserName} - if err := u.ds.Get(ctx, admin); err != nil { + if err := u.Store.Get(ctx, admin); err != nil { return nil, err } if admin.Email == "" { return nil, bcode.ErrEmptyAdminEmail } - connectors, err := utils.GetDexConnectors(ctx, u.kubeClient) + connectors, err := utils.GetDexConnectors(ctx, u.KubeClient) if err != nil { return nil, err } if len(connectors) < 1 { return nil, bcode.ErrNoDexConnector } - if err := generateDexConfig(ctx, u.kubeClient, &model.UpdateDexConfig{ + if err := generateDexConfig(ctx, u.KubeClient, &model.UpdateDexConfig{ VelaAddress: sysInfo.VelaAddress, Connectors: connectors, }); err != nil { return nil, err } } - err = u.ds.Put(ctx, &modifiedInfo) + err = u.Store.Put(ctx, &modifiedInfo) if err != nil { return nil, err } @@ -160,13 +154,13 @@ func (u systemInfoUsecaseImpl) UpdateSystemInfo(ctx context.Context, sysInfo v1. }, nil } -func (u systemInfoUsecaseImpl) Init(ctx context.Context) error { +func (u systemInfoServiceImpl) Init(ctx context.Context) error { info, err := u.Get(ctx) if err != nil { return err } signedKey = info.InstallID - _, err = initDexConfig(ctx, u.kubeClient, "http://velaux.com") + _, err = initDexConfig(ctx, u.KubeClient, "http://velaux.com") return err } diff --git a/pkg/apiserver/rest/usecase/target.go b/pkg/apiserver/domain/service/target.go similarity index 69% rename from pkg/apiserver/rest/usecase/target.go rename to pkg/apiserver/domain/service/target.go index 28a8ca7c9..2bbadb707 100644 --- a/pkg/apiserver/rest/usecase/target.go +++ b/pkg/apiserver/domain/service/target.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package usecase +package service import ( "context" @@ -23,17 +23,17 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" - "github.com/oam-dev/kubevela/pkg/apiserver/clients" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/log" - "github.com/oam-dev/kubevela/pkg/apiserver/model" - apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/repository" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" + apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/log" "github.com/oam-dev/kubevela/pkg/multicluster" ) -// TargetUsecase Target manage api -type TargetUsecase interface { +// TargetService Target manage api +type TargetService interface { GetTarget(ctx context.Context, TargetName string) (*model.Target, error) DetailTarget(ctx context.Context, Target *model.Target) (*apisv1.DetailTargetResponse, error) DeleteTarget(ctx context.Context, TargetName string) error @@ -44,24 +44,18 @@ type TargetUsecase interface { Init(ctx context.Context) error } -type targetUsecaseImpl struct { - ds datastore.DataStore - k8sClient client.Client +type targetServiceImpl struct { + Store datastore.DataStore `inject:"datastore"` + K8sClient client.Client `inject:"kubeClient"` } -// NewTargetUsecase new Target usecase -func NewTargetUsecase(ds datastore.DataStore) TargetUsecase { - k8sClient, err := clients.GetKubeClient() - if err != nil { - log.Logger.Fatalf("get k8sClient failure: %s", err.Error()) - } - return &targetUsecaseImpl{ - k8sClient: k8sClient, - ds: ds, - } +// NewTargetService new Target service +func NewTargetService() TargetService { + return &targetServiceImpl{} } -func (dt *targetUsecaseImpl) Init(ctx context.Context) error { - targets, err := dt.ds.List(ctx, &model.Target{}, &datastore.ListOptions{FilterOptions: datastore.FilterOptions{ + +func (dt *targetServiceImpl) Init(ctx context.Context) error { + targets, err := dt.Store.List(ctx, &model.Target{}, &datastore.ListOptions{FilterOptions: datastore.FilterOptions{ IsNotExist: []datastore.IsNotExistQueryOption{ { Key: "project", @@ -74,14 +68,14 @@ func (dt *targetUsecaseImpl) Init(ctx context.Context) error { for _, target := range targets { t := target.(*model.Target) t.Project = model.DefaultInitName - if err := dt.ds.Put(ctx, t); err != nil { + if err := dt.Store.Put(ctx, t); err != nil { return err } } return nil } -func (dt *targetUsecaseImpl) ListTargets(ctx context.Context, page, pageSize int, projectName string) (*apisv1.ListTargetResponse, error) { - targets, err := listTarget(ctx, dt.ds, projectName, &datastore.ListOptions{ +func (dt *targetServiceImpl) ListTargets(ctx context.Context, page, pageSize int, projectName string) (*apisv1.ListTargetResponse, error) { + targets, err := repository.ListTarget(ctx, dt.Store, projectName, &datastore.ListOptions{ Page: page, PageSize: pageSize, SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderDescending}}, @@ -95,7 +89,7 @@ func (dt *targetUsecaseImpl) ListTargets(ctx context.Context, page, pageSize int for _, raw := range targets { resp.Targets = append(resp.Targets, *(dt.convertFromTargetModel(ctx, raw))) } - count, err := dt.ds.Count(ctx, &model.Target{Project: projectName}, nil) + count, err := dt.Store.Count(ctx, &model.Target{Project: projectName}, nil) if err != nil { return nil, err } @@ -104,12 +98,12 @@ func (dt *targetUsecaseImpl) ListTargets(ctx context.Context, page, pageSize int return resp, nil } -func (dt *targetUsecaseImpl) ListTargetCount(ctx context.Context, projectName string) (int64, error) { - return dt.ds.Count(ctx, &model.Target{Project: projectName}, nil) +func (dt *targetServiceImpl) ListTargetCount(ctx context.Context, projectName string) (int64, error) { + return dt.Store.Count(ctx, &model.Target{Project: projectName}, nil) } // DeleteTarget delete application Target -func (dt *targetUsecaseImpl) DeleteTarget(ctx context.Context, targetName string) error { +func (dt *targetServiceImpl) DeleteTarget(ctx context.Context, targetName string) error { target := &model.Target{ Name: targetName, } @@ -120,10 +114,10 @@ func (dt *targetUsecaseImpl) DeleteTarget(ctx context.Context, targetName string if err != nil { return err } - if err = deleteTargetNamespace(ctx, dt.k8sClient, ddt.Cluster.ClusterName, ddt.Cluster.Namespace, targetName); err != nil { + if err = repository.DeleteTargetNamespace(ctx, dt.K8sClient, ddt.Cluster.ClusterName, ddt.Cluster.Namespace, targetName); err != nil { return err } - if err = dt.ds.Delete(ctx, target); err != nil { + if err = dt.Store.Delete(ctx, target); err != nil { if errors.Is(err, datastore.ErrRecordNotExist) { return bcode.ErrTargetNotExist } @@ -134,48 +128,48 @@ func (dt *targetUsecaseImpl) DeleteTarget(ctx context.Context, targetName string // CreateTarget will create a delivery target binding with a cluster and namespace, by default, it will use local cluster and namespace align with targetName // TODO(@wonderflow): we should support empty target in the future which only delivery cloud resources -func (dt *targetUsecaseImpl) CreateTarget(ctx context.Context, req apisv1.CreateTargetRequest) (*apisv1.DetailTargetResponse, error) { +func (dt *targetServiceImpl) CreateTarget(ctx context.Context, req apisv1.CreateTargetRequest) (*apisv1.DetailTargetResponse, error) { var project = model.Project{ Name: req.Project, } - if err := dt.ds.Get(ctx, &project); err != nil { + if err := dt.Store.Get(ctx, &project); err != nil { return nil, bcode.ErrProjectIsNotExist } target := convertCreateReqToTargetModel(req) if req.Cluster == nil { req.Cluster = &apisv1.ClusterTarget{ClusterName: multicluster.ClusterLocalName, Namespace: req.Name} } - if err := createTargetNamespace(ctx, dt.k8sClient, req.Cluster.ClusterName, req.Cluster.Namespace, req.Name); err != nil { + if err := repository.CreateTargetNamespace(ctx, dt.K8sClient, req.Cluster.ClusterName, req.Cluster.Namespace, req.Name); err != nil { return nil, err } - err := createTarget(ctx, dt.ds, &target) + err := repository.CreateTarget(ctx, dt.Store, &target) if err != nil { return nil, err } return dt.DetailTarget(ctx, &target) } -func (dt *targetUsecaseImpl) UpdateTarget(ctx context.Context, target *model.Target, req apisv1.UpdateTargetRequest) (*apisv1.DetailTargetResponse, error) { +func (dt *targetServiceImpl) UpdateTarget(ctx context.Context, target *model.Target, req apisv1.UpdateTargetRequest) (*apisv1.DetailTargetResponse, error) { targetModel := convertUpdateReqToTargetModel(target, req) - if err := dt.ds.Put(ctx, targetModel); err != nil { + if err := dt.Store.Put(ctx, targetModel); err != nil { return nil, err } return dt.DetailTarget(ctx, targetModel) } // DetailTarget detail Target -func (dt *targetUsecaseImpl) DetailTarget(ctx context.Context, target *model.Target) (*apisv1.DetailTargetResponse, error) { +func (dt *targetServiceImpl) DetailTarget(ctx context.Context, target *model.Target) (*apisv1.DetailTargetResponse, error) { return &apisv1.DetailTargetResponse{ TargetBase: *dt.convertFromTargetModel(ctx, target), }, nil } // GetTarget get Target model -func (dt *targetUsecaseImpl) GetTarget(ctx context.Context, targetName string) (*model.Target, error) { +func (dt *targetServiceImpl) GetTarget(ctx context.Context, targetName string) (*model.Target, error) { Target := &model.Target{ Name: targetName, } - if err := dt.ds.Get(ctx, Target); err != nil { + if err := dt.Store.Get(ctx, Target); err != nil { return nil, err } return Target, nil @@ -200,7 +194,7 @@ func convertCreateReqToTargetModel(req apisv1.CreateTargetRequest) model.Target return target } -func (dt *targetUsecaseImpl) convertFromTargetModel(ctx context.Context, target *model.Target) *apisv1.TargetBase { +func (dt *targetServiceImpl) convertFromTargetModel(ctx context.Context, target *model.Target) *apisv1.TargetBase { var appNum int64 = 0 // TODO: query app num in target targetBase := &apisv1.TargetBase{ @@ -217,13 +211,13 @@ func (dt *targetUsecaseImpl) convertFromTargetModel(ctx context.Context, target var project = model.Project{ Name: target.Project, } - if err := dt.ds.Get(ctx, &project); err != nil { + if err := dt.Store.Get(ctx, &project); err != nil { log.Logger.Errorf("get project failure %s", err.Error()) } targetBase.Project = apisv1.NameAlias{Name: project.Name, Alias: project.Alias} } if targetBase.Cluster != nil && targetBase.Cluster.ClusterName != "" { - cluster, err := _getClusterFromDataStore(ctx, dt.ds, target.Cluster.ClusterName) + cluster, err := _getClusterFromDataStore(ctx, dt.Store, target.Cluster.ClusterName) if err != nil { log.Logger.Errorf("query cluster info failure %s", err.Error()) } diff --git a/pkg/apiserver/rest/usecase/target_test.go b/pkg/apiserver/domain/service/target_test.go similarity index 67% rename from pkg/apiserver/rest/usecase/target_test.go rename to pkg/apiserver/domain/service/target_test.go index 0428b5c56..fc9dc0f6e 100644 --- a/pkg/apiserver/rest/usecase/target_test.go +++ b/pkg/apiserver/domain/service/target_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package usecase +package service import ( "context" @@ -23,27 +23,27 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/model" - apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" + apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" ) -var _ = Describe("Test target usecase functions", func() { +var _ = Describe("Test target service functions", func() { var ( - targetUsecase *targetUsecaseImpl - projectUsecase *projectUsecaseImpl + targetService *targetServiceImpl + projectService *projectServiceImpl testProject = "target-project" ) BeforeEach(func() { ds, err := NewDatastore(datastore.Config{Type: "kubeapi", Database: "target-test-kubevela"}) Expect(ds).ToNot(BeNil()) Expect(err).Should(BeNil()) - rbacUsecase := &rbacUsecaseImpl{ds: ds} - projectUsecase = &projectUsecaseImpl{ds: ds, k8sClient: k8sClient, rbacUsecase: rbacUsecase} - targetUsecase = &targetUsecaseImpl{ds: ds, k8sClient: k8sClient} + rbacService := &rbacServiceImpl{Store: ds} + projectService = &projectServiceImpl{Store: ds, K8sClient: k8sClient, RbacService: rbacService} + targetService = &targetServiceImpl{Store: ds, K8sClient: k8sClient} }) It("Test CreateTarget function", func() { - _, err := projectUsecase.CreateProject(context.TODO(), apisv1.CreateProjectRequest{Name: testProject}) + _, err := projectService.CreateProject(context.TODO(), apisv1.CreateProjectRequest{Name: testProject}) Expect(err).Should(BeNil()) req := apisv1.CreateTargetRequest{ @@ -54,25 +54,25 @@ var _ = Describe("Test target usecase functions", func() { Cluster: &apisv1.ClusterTarget{ClusterName: "cluster-dev", Namespace: "dev"}, Variable: map[string]interface{}{"terraform-provider": "provider", "region": "us-1"}, } - base, err := targetUsecase.CreateTarget(context.TODO(), req) + base, err := targetService.CreateTarget(context.TODO(), req) Expect(err).Should(BeNil()) Expect(cmp.Diff(base.Name, req.Name)).Should(BeEmpty()) - Expect(targetUsecase.ds.Add(context.TODO(), &model.Cluster{Name: "cluster-dev", Alias: "dev-alias"})).Should(Succeed()) + Expect(targetService.Store.Add(context.TODO(), &model.Cluster{Name: "cluster-dev", Alias: "dev-alias"})).Should(Succeed()) By("Test GetTarget function") - Target, err := targetUsecase.GetTarget(context.TODO(), "test--target") + Target, err := targetService.GetTarget(context.TODO(), "test--target") Expect(err).Should(BeNil()) Expect(Target).ShouldNot(BeNil()) Expect(cmp.Diff(Target.Name, "test--target")).Should(BeEmpty()) By("Test ListTargets function") - resp, err := targetUsecase.ListTargets(context.TODO(), 1, 1, "") + resp, err := targetService.ListTargets(context.TODO(), 1, 1, "") Expect(err).Should(BeNil()) Expect(resp.Targets[0].ClusterAlias).Should(Equal("dev-alias")) By("Test DetailTarget function") - detail, err := targetUsecase.DetailTarget(context.TODO(), + detail, err := targetService.DetailTarget(context.TODO(), &model.Target{ Name: "test--target", Alias: "test-alias", @@ -83,7 +83,7 @@ var _ = Describe("Test target usecase functions", func() { Expect(detail.Name).Should(Equal("test--target")) By("Test Delete target") - err = targetUsecase.DeleteTarget(context.TODO(), "test--target") + err = targetService.DeleteTarget(context.TODO(), "test--target") Expect(err).Should(BeNil()) }) }) diff --git a/pkg/apiserver/rest/usecase/testdata/addon-uischema-test.yaml b/pkg/apiserver/domain/service/testdata/addon-uischema-test.yaml similarity index 100% rename from pkg/apiserver/rest/usecase/testdata/addon-uischema-test.yaml rename to pkg/apiserver/domain/service/testdata/addon-uischema-test.yaml diff --git a/pkg/apiserver/rest/usecase/testdata/api-schema.json b/pkg/apiserver/domain/service/testdata/api-schema.json similarity index 100% rename from pkg/apiserver/rest/usecase/testdata/api-schema.json rename to pkg/apiserver/domain/service/testdata/api-schema.json diff --git a/pkg/apiserver/rest/usecase/testdata/apply-application-hide.yaml b/pkg/apiserver/domain/service/testdata/apply-application-hide.yaml similarity index 100% rename from pkg/apiserver/rest/usecase/testdata/apply-application-hide.yaml rename to pkg/apiserver/domain/service/testdata/apply-application-hide.yaml diff --git a/pkg/apiserver/rest/usecase/testdata/apply-object.yaml b/pkg/apiserver/domain/service/testdata/apply-object.yaml similarity index 100% rename from pkg/apiserver/rest/usecase/testdata/apply-object.yaml rename to pkg/apiserver/domain/service/testdata/apply-object.yaml diff --git a/pkg/apiserver/rest/usecase/testdata/applyapplication-sd.yaml b/pkg/apiserver/domain/service/testdata/applyapplication-sd.yaml similarity index 100% rename from pkg/apiserver/rest/usecase/testdata/applyapplication-sd.yaml rename to pkg/apiserver/domain/service/testdata/applyapplication-sd.yaml diff --git a/pkg/apiserver/rest/usecase/testdata/deploy.yaml b/pkg/apiserver/domain/service/testdata/deploy.yaml similarity index 100% rename from pkg/apiserver/rest/usecase/testdata/deploy.yaml rename to pkg/apiserver/domain/service/testdata/deploy.yaml diff --git a/pkg/apiserver/rest/usecase/testdata/dex-config-def.yaml b/pkg/apiserver/domain/service/testdata/dex-config-def.yaml similarity index 100% rename from pkg/apiserver/rest/usecase/testdata/dex-config-def.yaml rename to pkg/apiserver/domain/service/testdata/dex-config-def.yaml diff --git a/pkg/apiserver/rest/usecase/testdata/example-app-error.yaml b/pkg/apiserver/domain/service/testdata/example-app-error.yaml similarity index 100% rename from pkg/apiserver/rest/usecase/testdata/example-app-error.yaml rename to pkg/apiserver/domain/service/testdata/example-app-error.yaml diff --git a/pkg/apiserver/rest/usecase/testdata/example-app.yaml b/pkg/apiserver/domain/service/testdata/example-app.yaml similarity index 100% rename from pkg/apiserver/rest/usecase/testdata/example-app.yaml rename to pkg/apiserver/domain/service/testdata/example-app.yaml diff --git a/pkg/apiserver/rest/usecase/testdata/helm/index.yaml b/pkg/apiserver/domain/service/testdata/helm/index.yaml similarity index 100% rename from pkg/apiserver/rest/usecase/testdata/helm/index.yaml rename to pkg/apiserver/domain/service/testdata/helm/index.yaml diff --git a/pkg/apiserver/rest/usecase/testdata/helm/mysql-8.8.23.tgz b/pkg/apiserver/domain/service/testdata/helm/mysql-8.8.23.tgz similarity index 100% rename from pkg/apiserver/rest/usecase/testdata/helm/mysql-8.8.23.tgz rename to pkg/apiserver/domain/service/testdata/helm/mysql-8.8.23.tgz diff --git a/pkg/apiserver/rest/usecase/testdata/myingress-td.yaml b/pkg/apiserver/domain/service/testdata/myingress-td.yaml similarity index 100% rename from pkg/apiserver/rest/usecase/testdata/myingress-td.yaml rename to pkg/apiserver/domain/service/testdata/myingress-td.yaml diff --git a/pkg/apiserver/rest/usecase/testdata/scaler.yaml b/pkg/apiserver/domain/service/testdata/scaler.yaml similarity index 100% rename from pkg/apiserver/rest/usecase/testdata/scaler.yaml rename to pkg/apiserver/domain/service/testdata/scaler.yaml diff --git a/pkg/apiserver/rest/usecase/testdata/terraform-alibaba-rds.yaml b/pkg/apiserver/domain/service/testdata/terraform-alibaba-rds.yaml similarity index 100% rename from pkg/apiserver/rest/usecase/testdata/terraform-alibaba-rds.yaml rename to pkg/apiserver/domain/service/testdata/terraform-alibaba-rds.yaml diff --git a/pkg/apiserver/rest/usecase/testdata/ui-custom-schema.yaml b/pkg/apiserver/domain/service/testdata/ui-custom-schema.yaml similarity index 100% rename from pkg/apiserver/rest/usecase/testdata/ui-custom-schema.yaml rename to pkg/apiserver/domain/service/testdata/ui-custom-schema.yaml diff --git a/pkg/apiserver/rest/usecase/testdata/ui-default-schema.yaml b/pkg/apiserver/domain/service/testdata/ui-default-schema.yaml similarity index 100% rename from pkg/apiserver/rest/usecase/testdata/ui-default-schema.yaml rename to pkg/apiserver/domain/service/testdata/ui-default-schema.yaml diff --git a/pkg/apiserver/rest/usecase/testdata/ui-schema.yaml b/pkg/apiserver/domain/service/testdata/ui-schema.yaml similarity index 100% rename from pkg/apiserver/rest/usecase/testdata/ui-schema.yaml rename to pkg/apiserver/domain/service/testdata/ui-schema.yaml diff --git a/pkg/apiserver/rest/usecase/testdata/webserver-cd.yaml b/pkg/apiserver/domain/service/testdata/webserver-cd.yaml similarity index 100% rename from pkg/apiserver/rest/usecase/testdata/webserver-cd.yaml rename to pkg/apiserver/domain/service/testdata/webserver-cd.yaml diff --git a/pkg/apiserver/rest/usecase/testdata/webservice.yaml b/pkg/apiserver/domain/service/testdata/webservice.yaml similarity index 100% rename from pkg/apiserver/rest/usecase/testdata/webservice.yaml rename to pkg/apiserver/domain/service/testdata/webservice.yaml diff --git a/pkg/apiserver/rest/usecase/testdata/workflowstep-apply-object.yaml b/pkg/apiserver/domain/service/testdata/workflowstep-apply-object.yaml similarity index 100% rename from pkg/apiserver/rest/usecase/testdata/workflowstep-apply-object.yaml rename to pkg/apiserver/domain/service/testdata/workflowstep-apply-object.yaml diff --git a/pkg/apiserver/rest/usecase/user.go b/pkg/apiserver/domain/service/user.go similarity index 76% rename from pkg/apiserver/rest/usecase/user.go rename to pkg/apiserver/domain/service/user.go index 1431bf88c..a1fa92e95 100644 --- a/pkg/apiserver/rest/usecase/user.go +++ b/pkg/apiserver/domain/service/user.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package usecase +package service import ( "context" @@ -24,12 +24,11 @@ import ( "helm.sh/helm/v3/pkg/time" "sigs.k8s.io/controller-runtime/pkg/client" - "github.com/oam-dev/kubevela/pkg/apiserver/clients" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/log" - "github.com/oam-dev/kubevela/pkg/apiserver/model" - apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" + apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/log" utils2 "github.com/oam-dev/kubevela/pkg/utils" ) @@ -37,8 +36,8 @@ const ( initAdminPassword = "VelaUX12345" ) -// UserUsecase User manage api -type UserUsecase interface { +// UserService User manage api +type UserService interface { GetUser(ctx context.Context, username string) (*model.User, error) DetailUser(ctx context.Context, user *model.User) (*apisv1.DetailUserResponse, error) DeleteUser(ctx context.Context, username string) error @@ -52,32 +51,22 @@ type UserUsecase interface { Init(ctx context.Context) error } -type userUsecaseImpl struct { - ds datastore.DataStore - k8sClient client.Client - projectUsecase ProjectUsecase - rbacUsecase RBACUsecase - sysUsecase SystemInfoUsecase +type userServiceImpl struct { + Store datastore.DataStore `inject:"datastore"` + K8sClient client.Client `inject:"kubeClient"` + ProjectService ProjectService `inject:""` + RbacService RBACService `inject:""` + SysService SystemInfoService `inject:""` } -// NewUserUsecase new User usecase -func NewUserUsecase(ds datastore.DataStore, projectUsecase ProjectUsecase, sysUsecase SystemInfoUsecase, rbacUsecase RBACUsecase) UserUsecase { - k8sClient, err := clients.GetKubeClient() - if err != nil { - log.Logger.Fatalf("get k8sClient failure: %s", err.Error()) - } - return &userUsecaseImpl{ - k8sClient: k8sClient, - ds: ds, - projectUsecase: projectUsecase, - sysUsecase: sysUsecase, - rbacUsecase: rbacUsecase, - } +// NewUserService new User service +func NewUserService() UserService { + return &userServiceImpl{} } -func (u *userUsecaseImpl) Init(ctx context.Context) error { +func (u *userServiceImpl) Init(ctx context.Context) error { admin := model.DefaultAdminUserName - if err := u.ds.Get(ctx, &model.User{ + if err := u.Store.Get(ctx, &model.User{ Name: admin, }); err != nil { if errors.Is(err, datastore.ErrRecordNotExist) { @@ -85,7 +74,7 @@ func (u *userUsecaseImpl) Init(ctx context.Context) error { if err != nil { return err } - if err := u.ds.Add(ctx, &model.User{ + if err := u.Store.Add(ctx, &model.User{ Name: admin, Alias: model.DefaultAdminUserAlias, Password: encrypted, @@ -104,19 +93,19 @@ func (u *userUsecaseImpl) Init(ctx context.Context) error { } // GetUser get user -func (u *userUsecaseImpl) GetUser(ctx context.Context, username string) (*model.User, error) { +func (u *userServiceImpl) GetUser(ctx context.Context, username string) (*model.User, error) { user := &model.User{ Name: username, } - if err := u.ds.Get(ctx, user); err != nil { + if err := u.Store.Get(ctx, user); err != nil { return nil, err } return user, nil } // DetailUser return user detail -func (u *userUsecaseImpl) DetailUser(ctx context.Context, user *model.User) (*apisv1.DetailUserResponse, error) { - roles, err := u.rbacUsecase.ListRole(ctx, "", 0, 0) +func (u *userServiceImpl) DetailUser(ctx context.Context, user *model.User) (*apisv1.DetailUserResponse, error) { + roles, err := u.RbacService.ListRole(ctx, "", 0, 0) if err != nil { log.Logger.Warnf("list platform roles failure %s", err.Error()) } @@ -124,7 +113,7 @@ func (u *userUsecaseImpl) DetailUser(ctx context.Context, user *model.User) (*ap pUser := &model.ProjectUser{ Username: user.Name, } - projectUsers, err := u.ds.List(ctx, pUser, &datastore.ListOptions{ + projectUsers, err := u.Store.List(ctx, pUser, &datastore.ListOptions{ SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderDescending}}, }) if err != nil { @@ -133,7 +122,7 @@ func (u *userUsecaseImpl) DetailUser(ctx context.Context, user *model.User) (*ap for _, v := range projectUsers { pu, ok := v.(*model.ProjectUser) if ok { - project, err := u.projectUsecase.DetailProject(ctx, pu.ProjectName) + project, err := u.ProjectService.DetailProject(ctx, pu.ProjectName) if err != nil { log.Logger.Errorf("failed to delete project(%s) info: %s", pu.ProjectName, err.Error()) continue @@ -145,22 +134,22 @@ func (u *userUsecaseImpl) DetailUser(ctx context.Context, user *model.User) (*ap } // DeleteUser delete user -func (u *userUsecaseImpl) DeleteUser(ctx context.Context, username string) error { +func (u *userServiceImpl) DeleteUser(ctx context.Context, username string) error { pUser := &model.ProjectUser{ Username: username, } - projectUsers, err := u.ds.List(ctx, pUser, &datastore.ListOptions{}) + projectUsers, err := u.Store.List(ctx, pUser, &datastore.ListOptions{}) if err != nil { return err } for _, v := range projectUsers { pu := v.(*model.ProjectUser) - if err := u.ds.Delete(ctx, pu); err != nil { + if err := u.Store.Delete(ctx, pu); err != nil { log.Logger.Errorf("failed to delete project user %s: %s", pu.PrimaryKey(), err.Error()) } } - if err := u.ds.Delete(ctx, &model.User{Name: username}); err != nil { + if err := u.Store.Delete(ctx, &model.User{Name: username}); err != nil { log.Logger.Errorf("failed to delete user %s %v", utils2.Sanitize(username), err.Error()) return err } @@ -168,8 +157,8 @@ func (u *userUsecaseImpl) DeleteUser(ctx context.Context, username string) error } // CreateUser create user -func (u *userUsecaseImpl) CreateUser(ctx context.Context, req apisv1.CreateUserRequest) (*apisv1.UserBase, error) { - sysInfo, err := u.sysUsecase.Get(ctx) +func (u *userServiceImpl) CreateUser(ctx context.Context, req apisv1.CreateUserRequest) (*apisv1.UserBase, error) { + sysInfo, err := u.SysService.Get(ctx) if err != nil { return nil, err } @@ -189,15 +178,15 @@ func (u *userUsecaseImpl) CreateUser(ctx context.Context, req apisv1.CreateUserR Password: hash, Disabled: false, } - if err := u.ds.Add(ctx, user); err != nil { + if err := u.Store.Add(ctx, user); err != nil { return nil, err } return convertUserBase(user), nil } // UpdateUser update user -func (u *userUsecaseImpl) UpdateUser(ctx context.Context, user *model.User, req apisv1.UpdateUserRequest) (*apisv1.UserBase, error) { - sysInfo, err := u.sysUsecase.Get(ctx) +func (u *userServiceImpl) UpdateUser(ctx context.Context, user *model.User, req apisv1.UpdateUserRequest) (*apisv1.UserBase, error) { + sysInfo, err := u.SysService.Get(ctx) if err != nil { return nil, err } @@ -224,11 +213,11 @@ func (u *userUsecaseImpl) UpdateUser(ctx context.Context, user *model.User, req if req.Roles != nil { user.UserRoles = *req.Roles } - if err := u.ds.Put(ctx, user); err != nil { + if err := u.Store.Put(ctx, user); err != nil { return nil, err } if user.Name == model.DefaultAdminUserName { - if err := generateDexConfig(ctx, u.k8sClient, &model.UpdateDexConfig{ + if err := generateDexConfig(ctx, u.K8sClient, &model.UpdateDexConfig{ StaticPasswords: []model.StaticPassword{ { Email: user.Email, @@ -244,7 +233,7 @@ func (u *userUsecaseImpl) UpdateUser(ctx context.Context, user *model.User, req } // ListUsers list users -func (u *userUsecaseImpl) ListUsers(ctx context.Context, page, pageSize int, listOptions apisv1.ListUserOptions) (*apisv1.ListUserResponse, error) { +func (u *userServiceImpl) ListUsers(ctx context.Context, page, pageSize int, listOptions apisv1.ListUserOptions) (*apisv1.ListUserResponse, error) { user := &model.User{} var queries []datastore.FuzzyQueryOption if listOptions.Name != "" { @@ -259,7 +248,7 @@ func (u *userUsecaseImpl) ListUsers(ctx context.Context, page, pageSize int, lis fo := datastore.FilterOptions{Queries: queries} var userList []*apisv1.DetailUserResponse - users, err := u.ds.List(ctx, user, &datastore.ListOptions{ + users, err := u.Store.List(ctx, user, &datastore.ListOptions{ Page: page, PageSize: pageSize, SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderDescending}}, @@ -268,7 +257,7 @@ func (u *userUsecaseImpl) ListUsers(ctx context.Context, page, pageSize int, lis if err != nil { return nil, err } - roles, err := u.rbacUsecase.ListRole(ctx, "", 0, 0) + roles, err := u.RbacService.ListRole(ctx, "", 0, 0) if err != nil { log.Logger.Warnf("list platform roles failure %s", err.Error()) } @@ -278,7 +267,7 @@ func (u *userUsecaseImpl) ListUsers(ctx context.Context, page, pageSize int, lis userList = append(userList, convertUserModel(user, roles)) } } - count, err := u.ds.Count(ctx, user, &fo) + count, err := u.Store.Count(ctx, user, &fo) if err != nil { return nil, err } @@ -290,31 +279,31 @@ func (u *userUsecaseImpl) ListUsers(ctx context.Context, page, pageSize int, lis } // DisableUser disable user -func (u *userUsecaseImpl) DisableUser(ctx context.Context, user *model.User) error { +func (u *userServiceImpl) DisableUser(ctx context.Context, user *model.User) error { if user.Disabled { return bcode.ErrUserAlreadyDisabled } user.Disabled = true - return u.ds.Put(ctx, user) + return u.Store.Put(ctx, user) } // EnableUser disable user -func (u *userUsecaseImpl) EnableUser(ctx context.Context, user *model.User) error { +func (u *userServiceImpl) EnableUser(ctx context.Context, user *model.User) error { if !user.Disabled { return bcode.ErrUserAlreadyEnabled } user.Disabled = false - return u.ds.Put(ctx, user) + return u.Store.Put(ctx, user) } // UpdateUserLoginTime update user login time -func (u *userUsecaseImpl) UpdateUserLoginTime(ctx context.Context, user *model.User) error { +func (u *userServiceImpl) UpdateUserLoginTime(ctx context.Context, user *model.User) error { user.LastLoginTime = time.Now().Time - return u.ds.Put(ctx, user) + return u.Store.Put(ctx, user) } // DetailLoginUserInfo get projects and permission policies of login user -func (u *userUsecaseImpl) DetailLoginUserInfo(ctx context.Context) (*apisv1.LoginUserInfoResponse, error) { +func (u *userServiceImpl) DetailLoginUserInfo(ctx context.Context) (*apisv1.LoginUserInfoResponse, error) { userName, ok := ctx.Value(&apisv1.CtxKeyUser).(string) if !ok { return nil, bcode.ErrUnauthorized @@ -324,13 +313,13 @@ func (u *userUsecaseImpl) DetailLoginUserInfo(ctx context.Context) (*apisv1.Logi log.Logger.Errorf("get login user model failure %s", err.Error()) return nil, bcode.ErrUnauthorized } - projects, err := u.projectUsecase.ListUserProjects(ctx, userName) + projects, err := u.ProjectService.ListUserProjects(ctx, userName) if err != nil { return nil, err } var projectPermissions = make(map[string][]apisv1.PermissionBase) for _, project := range projects { - perms, err := u.rbacUsecase.GetUserPermissions(ctx, user, project.Name, false) + perms, err := u.RbacService.GetUserPermissions(ctx, user, project.Name, false) if err != nil { log.Logger.Errorf("list user %s perm policies from project %s failure %s", user.Name, project.Name, err.Error()) continue @@ -350,7 +339,7 @@ func (u *userUsecaseImpl) DetailLoginUserInfo(ctx context.Context) (*apisv1.Logi return }() } - perms, err := u.rbacUsecase.GetUserPermissions(ctx, user, "", true) + perms, err := u.RbacService.GetUserPermissions(ctx, user, "", true) if err != nil { log.Logger.Errorf("list user %s platform perm policies failure %s", user.Name, err.Error()) } diff --git a/pkg/apiserver/rest/usecase/user_test.go b/pkg/apiserver/domain/service/user_test.go similarity index 81% rename from pkg/apiserver/rest/usecase/user_test.go rename to pkg/apiserver/domain/service/user_test.go index 6cf07fa19..f3b83dab6 100644 --- a/pkg/apiserver/rest/usecase/user_test.go +++ b/pkg/apiserver/domain/service/user_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package usecase +package service import ( "context" @@ -29,16 +29,16 @@ import ( "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/yaml" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/model" - apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" + apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" "github.com/oam-dev/kubevela/pkg/oam/util" ) -var _ = Describe("Test authentication usecase functions", func() { +var _ = Describe("Test authentication service functions", func() { var ( - userUsecase *userUsecaseImpl + userService *userServiceImpl ds datastore.DataStore db string ) @@ -49,17 +49,17 @@ var _ = Describe("Test authentication usecase functions", func() { ds, err = NewDatastore(datastore.Config{Type: "kubeapi", Database: db}) Expect(ds).ToNot(BeNil()) Expect(err).Should(BeNil()) - rbacUsecase := &rbacUsecaseImpl{ds: ds} - projectUsecase := &projectUsecaseImpl{k8sClient: k8sClient, ds: ds, rbacUsecase: rbacUsecase} - sysUsecase := &systemInfoUsecaseImpl{ds: ds} - userUsecase = &userUsecaseImpl{ds: ds, k8sClient: k8sClient, projectUsecase: projectUsecase, sysUsecase: sysUsecase, rbacUsecase: rbacUsecase} + rbacService := &rbacServiceImpl{Store: ds} + projectService := &projectServiceImpl{K8sClient: k8sClient, Store: ds, RbacService: rbacService} + sysService := &systemInfoServiceImpl{Store: ds} + userService = &userServiceImpl{Store: ds, K8sClient: k8sClient, ProjectService: projectService, SysService: sysService, RbacService: rbacService} }) AfterEach(func() { err := k8sClient.Delete(context.Background(), &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: db}}) Expect(err).Should(BeNil()) }) It("Test create user", func() { - user, err := userUsecase.CreateUser(context.Background(), apisv1.CreateUserRequest{ + user, err := userService.CreateUser(context.Background(), apisv1.CreateUserRequest{ Name: "name", Alias: "alias", Email: "email@example.com", @@ -110,7 +110,7 @@ var _ = Describe("Test authentication usecase functions", func() { } err = ds.Get(ctx, userModel) Expect(err).Should(BeNil()) - user, err := userUsecase.DetailUser(ctx, userModel) + user, err := userService.DetailUser(ctx, userModel) Expect(err).Should(BeNil()) Expect(user.Name).Should(Equal("name")) Expect(user.Alias).Should(Equal("alias")) @@ -126,11 +126,11 @@ var _ = Describe("Test authentication usecase functions", func() { }) Expect(err).Should(BeNil()) } - users, err := userUsecase.ListUsers(ctx, 0, 10, apisv1.ListUserOptions{Name: "1"}) + users, err := userService.ListUsers(ctx, 0, 10, apisv1.ListUserOptions{Name: "1"}) Expect(err).Should(BeNil()) Expect(users.Total).Should(Equal(int64(1))) - users, err = userUsecase.ListUsers(ctx, 0, 10, apisv1.ListUserOptions{}) + users, err = userService.ListUsers(ctx, 0, 10, apisv1.ListUserOptions{}) Expect(err).Should(BeNil()) Expect(users.Total).Should(Equal(int64(2))) }) @@ -144,13 +144,13 @@ var _ = Describe("Test authentication usecase functions", func() { Password: "password", }) Expect(err).Should(BeNil()) - users, err := userUsecase.ListUsers(ctx, 0, 10, apisv1.ListUserOptions{}) + users, err := userService.ListUsers(ctx, 0, 10, apisv1.ListUserOptions{}) Expect(err).Should(BeNil()) Expect(users.Total).Should(Equal(int64(1))) - err = userUsecase.DeleteUser(ctx, "name") + err = userService.DeleteUser(ctx, "name") Expect(err).Should(BeNil()) - users, err = userUsecase.ListUsers(ctx, 0, 10, apisv1.ListUserOptions{}) + users, err = userService.ListUsers(ctx, 0, 10, apisv1.ListUserOptions{}) Expect(err).Should(BeNil()) Expect(users.Total).Should(Equal(int64(0))) }) @@ -172,7 +172,7 @@ var _ = Describe("Test authentication usecase functions", func() { }, }) Expect(err).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{})) - _, err = userUsecase.UpdateUser(ctx, userModel, apisv1.UpdateUserRequest{ + _, err = userService.UpdateUser(ctx, userModel, apisv1.UpdateUserRequest{ Alias: "new-alias", Password: "new-password", }) @@ -205,13 +205,13 @@ var _ = Describe("Test authentication usecase functions", func() { err := ds.Add(ctx, userModel) Expect(err).Should(BeNil()) - err = userUsecase.DisableUser(ctx, userModel) + err = userService.DisableUser(ctx, userModel) Expect(err).Should(Equal(bcode.ErrUserAlreadyDisabled)) userModel.Disabled = false err = ds.Put(ctx, userModel) Expect(err).Should(BeNil()) - err = userUsecase.DisableUser(ctx, userModel) + err = userService.DisableUser(ctx, userModel) Expect(err).Should(BeNil()) newUser := &model.User{ @@ -231,13 +231,13 @@ var _ = Describe("Test authentication usecase functions", func() { err := ds.Add(ctx, userModel) Expect(err).Should(BeNil()) - err = userUsecase.EnableUser(ctx, userModel) + err = userService.EnableUser(ctx, userModel) Expect(err).Should(Equal(bcode.ErrUserAlreadyEnabled)) userModel.Disabled = true err = ds.Put(ctx, userModel) Expect(err).Should(BeNil()) - err = userUsecase.EnableUser(ctx, userModel) + err = userService.EnableUser(ctx, userModel) Expect(err).Should(BeNil()) newUser := &model.User{ diff --git a/pkg/apiserver/rest/usecase/velaql.go b/pkg/apiserver/domain/service/velaql.go similarity index 80% rename from pkg/apiserver/rest/usecase/velaql.go rename to pkg/apiserver/domain/service/velaql.go index 166fc7852..826aa47fc 100644 --- a/pkg/apiserver/rest/usecase/velaql.go +++ b/pkg/apiserver/domain/service/velaql.go @@ -14,7 +14,7 @@ limitations under the License. */ -package usecase +package service import ( "context" @@ -22,29 +22,29 @@ import ( "k8s.io/client-go/rest" "sigs.k8s.io/controller-runtime/pkg/client" - "github.com/oam-dev/kubevela/pkg/apiserver/clients" - "github.com/oam-dev/kubevela/pkg/apiserver/log" - apis "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/clients" + apis "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/log" "github.com/oam-dev/kubevela/pkg/cue/packages" "github.com/oam-dev/kubevela/pkg/oam/discoverymapper" "github.com/oam-dev/kubevela/pkg/velaql" ) -// VelaQLUsecase velaQL usecase -type VelaQLUsecase interface { +// VelaQLService velaQL service +type VelaQLService interface { QueryView(context.Context, string) (*apis.VelaQLViewResponse, error) } -type velaQLUsecaseImpl struct { +type velaQLServiceImpl struct { kubeClient client.Client kubeConfig *rest.Config dm discoverymapper.DiscoveryMapper pd *packages.PackageDiscover } -// NewVelaQLUsecase new velaQL usecase -func NewVelaQLUsecase() VelaQLUsecase { +// NewVelaQLService new velaQL service +func NewVelaQLService() VelaQLService { k8sClient, err := clients.GetKubeClient() if err != nil { log.Logger.Fatalf("get kubeclient failure %s", err.Error()) @@ -64,7 +64,7 @@ func NewVelaQLUsecase() VelaQLUsecase { if err != nil { log.Logger.Fatalf("get package discover failure %s", err.Error()) } - return &velaQLUsecaseImpl{ + return &velaQLServiceImpl{ kubeClient: k8sClient, kubeConfig: kubeConfig, dm: dm, @@ -73,7 +73,7 @@ func NewVelaQLUsecase() VelaQLUsecase { } // QueryView get the view query results -func (v *velaQLUsecaseImpl) QueryView(ctx context.Context, velaQL string) (*apis.VelaQLViewResponse, error) { +func (v *velaQLServiceImpl) QueryView(ctx context.Context, velaQL string) (*apis.VelaQLViewResponse, error) { query, err := velaql.ParseVelaQL(velaQL) if err != nil { return nil, bcode.ErrParseVelaQL diff --git a/pkg/apiserver/rest/usecase/webhook.go b/pkg/apiserver/domain/service/webhook.go similarity index 86% rename from pkg/apiserver/rest/usecase/webhook.go rename to pkg/apiserver/domain/service/webhook.go index 8ba2159be..3976cff7e 100644 --- a/pkg/apiserver/rest/usecase/webhook.go +++ b/pkg/apiserver/domain/service/webhook.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package usecase +package service import ( "context" @@ -25,36 +25,31 @@ import ( "github.com/emicklei/go-restful/v3" "k8s.io/apimachinery/pkg/runtime" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/log" - "github.com/oam-dev/kubevela/pkg/apiserver/model" - apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" + apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/log" "github.com/oam-dev/kubevela/pkg/policy/envbinding" ) -// WebhookUsecase webhook usecase -type WebhookUsecase interface { +// WebhookService webhook service +type WebhookService interface { HandleApplicationWebhook(ctx context.Context, token string, req *restful.Request) (interface{}, error) } -type webhookUsecaseImpl struct { - ds datastore.DataStore - applicationUsecase ApplicationUsecase +type webhookServiceImpl struct { + Store datastore.DataStore `inject:"datastore"` + ApplicationService ApplicationService `inject:""` } // WebhookHandlers is the webhook handlers var WebhookHandlers []string -// NewWebhookUsecase new webhook usecase -func NewWebhookUsecase(ds datastore.DataStore, - applicationUsecase ApplicationUsecase, -) WebhookUsecase { +// NewWebhookService new webhook service +func NewWebhookService() WebhookService { registerHandlers() - return &webhookUsecaseImpl{ - ds: ds, - applicationUsecase: applicationUsecase, - } + return &webhookServiceImpl{} } func registerHandlers() { @@ -72,20 +67,20 @@ type webhookHandler interface { type customHandlerImpl struct { req apisv1.HandleApplicationTriggerWebhookRequest - w *webhookUsecaseImpl + w *webhookServiceImpl } type acrHandlerImpl struct { req apisv1.HandleApplicationTriggerACRRequest - w *webhookUsecaseImpl + w *webhookServiceImpl } type dockerHubHandlerImpl struct { req apisv1.HandleApplicationTriggerDockerHubRequest - w *webhookUsecaseImpl + w *webhookServiceImpl } -func (c *webhookUsecaseImpl) newCustomHandler(req *restful.Request) (webhookHandler, error) { +func (c *webhookServiceImpl) newCustomHandler(req *restful.Request) (webhookHandler, error) { var webhookReq apisv1.HandleApplicationTriggerWebhookRequest if err := req.ReadEntity(&webhookReq); err != nil { return nil, bcode.ErrInvalidWebhookPayloadBody @@ -96,7 +91,7 @@ func (c *webhookUsecaseImpl) newCustomHandler(req *restful.Request) (webhookHand }, nil } -func (c *webhookUsecaseImpl) newACRHandler(req *restful.Request) (webhookHandler, error) { +func (c *webhookServiceImpl) newACRHandler(req *restful.Request) (webhookHandler, error) { var acrReq apisv1.HandleApplicationTriggerACRRequest if err := req.ReadEntity(&acrReq); err != nil { return nil, bcode.ErrInvalidWebhookPayloadBody @@ -107,7 +102,7 @@ func (c *webhookUsecaseImpl) newACRHandler(req *restful.Request) (webhookHandler }, nil } -func (c *webhookUsecaseImpl) newDockerHubHandler(req *restful.Request) (webhookHandler, error) { +func (c *webhookServiceImpl) newDockerHubHandler(req *restful.Request) (webhookHandler, error) { var dockerHubReq apisv1.HandleApplicationTriggerDockerHubRequest if err := req.ReadEntity(&dockerHubReq); err != nil { return nil, bcode.ErrInvalidWebhookPayloadBody @@ -118,11 +113,11 @@ func (c *webhookUsecaseImpl) newDockerHubHandler(req *restful.Request) (webhookH }, nil } -func (c *webhookUsecaseImpl) HandleApplicationWebhook(ctx context.Context, token string, req *restful.Request) (interface{}, error) { +func (c *webhookServiceImpl) HandleApplicationWebhook(ctx context.Context, token string, req *restful.Request) (interface{}, error) { webhookTrigger := &model.ApplicationTrigger{ Token: token, } - if err := c.ds.Get(ctx, webhookTrigger); err != nil { + if err := c.Store.Get(ctx, webhookTrigger); err != nil { if errors.Is(err, datastore.ErrRecordNotExist) { return nil, bcode.ErrInvalidWebhookToken } @@ -131,7 +126,7 @@ func (c *webhookUsecaseImpl) HandleApplicationWebhook(ctx context.Context, token app := &model.Application{ Name: webhookTrigger.AppPrimaryKey, } - if err := c.ds.Get(ctx, app); err != nil { + if err := c.Store.Get(ctx, app); err != nil { if errors.Is(err, datastore.ErrRecordNotExist) { return nil, bcode.ErrApplicationNotExist } @@ -173,7 +168,7 @@ func (c *webhookUsecaseImpl) HandleApplicationWebhook(ctx context.Context, token return handler.handle(ctx, webhookTrigger, app) } -func (c *webhookUsecaseImpl) patchComponentProperties(ctx context.Context, component *model.ApplicationComponent, patch *runtime.RawExtension) error { +func (c *webhookServiceImpl) patchComponentProperties(ctx context.Context, component *model.ApplicationComponent, patch *runtime.RawExtension) error { merge, err := envbinding.MergeRawExtension(component.Properties.RawExtension(), patch) if err != nil { return err @@ -183,7 +178,7 @@ func (c *webhookUsecaseImpl) patchComponentProperties(ctx context.Context, compo return err } component.Properties = prop - if err := c.ds.Put(ctx, component); err != nil { + if err := c.Store.Put(ctx, component); err != nil { return err } return nil @@ -195,7 +190,7 @@ func (c *customHandlerImpl) handle(ctx context.Context, webhookTrigger *model.Ap AppPrimaryKey: webhookTrigger.AppPrimaryKey, Name: comp, } - if err := c.w.ds.Get(ctx, component); err != nil { + if err := c.w.Store.Get(ctx, component); err != nil { if errors.Is(err, datastore.ErrRecordNotExist) { return nil, bcode.ErrApplicationComponentNotExist } @@ -205,7 +200,7 @@ func (c *customHandlerImpl) handle(ctx context.Context, webhookTrigger *model.Ap return nil, err } } - return c.w.applicationUsecase.Deploy(ctx, app, apisv1.ApplicationDeployRequest{ + return c.w.ApplicationService.Deploy(ctx, app, apisv1.ApplicationDeployRequest{ WorkflowName: webhookTrigger.WorkflowName, Note: "triggered by webhook custom", TriggerType: apisv1.TriggerTypeWebhook, @@ -220,7 +215,7 @@ func (c *customHandlerImpl) install() { func (c *acrHandlerImpl) handle(ctx context.Context, webhookTrigger *model.ApplicationTrigger, app *model.Application) (interface{}, error) { - component, err := getComponent(ctx, c.w.ds, webhookTrigger) + component, err := getComponent(ctx, c.w.Store, webhookTrigger) if err != nil { return nil, err } @@ -232,7 +227,7 @@ func (c *acrHandlerImpl) handle(ctx context.Context, webhookTrigger *model.Appli return nil, err } - return c.w.applicationUsecase.Deploy(ctx, app, apisv1.ApplicationDeployRequest{ + return c.w.ApplicationService.Deploy(ctx, app, apisv1.ApplicationDeployRequest{ WorkflowName: webhookTrigger.WorkflowName, Note: "triggered by webhook acr", TriggerType: apisv1.TriggerTypeWebhook, @@ -270,7 +265,7 @@ func (c dockerHubHandlerImpl) handle(ctx context.Context, trigger *model.Applica Description: "not create event", }, nil } - component, err := getComponent(ctx, c.w.ds, trigger) + component, err := getComponent(ctx, c.w.Store, trigger) if err != nil { return nil, err } @@ -286,7 +281,7 @@ func (c dockerHubHandlerImpl) handle(ctx context.Context, trigger *model.Applica repositoryType = "private" } - if _, err = c.w.applicationUsecase.Deploy(ctx, app, apisv1.ApplicationDeployRequest{ + if _, err = c.w.ApplicationService.Deploy(ctx, app, apisv1.ApplicationDeployRequest{ WorkflowName: trigger.WorkflowName, Note: "triggered by webhook dockerhub", TriggerType: apisv1.TriggerTypeWebhook, @@ -340,10 +335,10 @@ func parseTimeString(t string) time.Time { type harborHandlerImpl struct { req apisv1.HandleApplicationHarborReq - w *webhookUsecaseImpl + w *webhookServiceImpl } -func (c *webhookUsecaseImpl) newHarborHandler(req *restful.Request) (webhookHandler, error) { +func (c *webhookServiceImpl) newHarborHandler(req *restful.Request) (webhookHandler, error) { var harborReq apisv1.HandleApplicationHarborReq if err := req.ReadEntity(&harborReq); err != nil { return nil, bcode.ErrInvalidWebhookPayloadBody @@ -369,7 +364,7 @@ func (c *harborHandlerImpl) handle(ctx context.Context, webhookTrigger *model.Ap imageURL := resources[0].ResourceURL digest := resources[0].Digest tag := resources[0].Tag - component, err := getComponent(ctx, c.w.ds, webhookTrigger) + component, err := getComponent(ctx, c.w.Store, webhookTrigger) if err != nil { return nil, err } @@ -379,7 +374,7 @@ func (c *harborHandlerImpl) handle(ctx context.Context, webhookTrigger *model.Ap }); err != nil { return nil, err } - return c.w.applicationUsecase.Deploy(ctx, app, apisv1.ApplicationDeployRequest{ + return c.w.ApplicationService.Deploy(ctx, app, apisv1.ApplicationDeployRequest{ WorkflowName: webhookTrigger.WorkflowName, Note: "triggered by webhook harbor", TriggerType: apisv1.TriggerTypeWebhook, @@ -405,10 +400,10 @@ func (c *harborHandlerImpl) handle(ctx context.Context, webhookTrigger *model.Ap type jfrogHandlerImpl struct { req apisv1.HandleApplicationTriggerJFrogRequest - w *webhookUsecaseImpl + w *webhookServiceImpl } -func (c *webhookUsecaseImpl) newJFrogHandler(req *restful.Request) (webhookHandler, error) { +func (c *webhookServiceImpl) newJFrogHandler(req *restful.Request) (webhookHandler, error) { var jfrogReq apisv1.HandleApplicationTriggerJFrogRequest if err := req.ReadEntity(&jfrogReq); err != nil { return nil, bcode.ErrInvalidWebhookPayloadBody @@ -426,7 +421,7 @@ func (c *webhookUsecaseImpl) newJFrogHandler(req *restful.Request) (webhookHandl func (j *jfrogHandlerImpl) handle(ctx context.Context, webhookTrigger *model.ApplicationTrigger, app *model.Application) (interface{}, error) { jfrogReq := j.req - component, err := getComponent(ctx, j.w.ds, webhookTrigger) + component, err := getComponent(ctx, j.w.Store, webhookTrigger) if err != nil { return nil, err } @@ -440,7 +435,7 @@ func (j *jfrogHandlerImpl) handle(ctx context.Context, webhookTrigger *model.App return nil, err } - return j.w.applicationUsecase.Deploy(ctx, app, apisv1.ApplicationDeployRequest{ + return j.w.ApplicationService.Deploy(ctx, app, apisv1.ApplicationDeployRequest{ WorkflowName: webhookTrigger.WorkflowName, Note: "triggered by webhook jfrog", TriggerType: apisv1.TriggerTypeWebhook, diff --git a/pkg/apiserver/rest/usecase/webhook_test.go b/pkg/apiserver/domain/service/webhook_test.go similarity index 73% rename from pkg/apiserver/rest/usecase/webhook_test.go rename to pkg/apiserver/domain/service/webhook_test.go index ffb952053..62669054a 100644 --- a/pkg/apiserver/rest/usecase/webhook_test.go +++ b/pkg/apiserver/domain/service/webhook_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package usecase +package service import ( "bytes" @@ -28,51 +28,51 @@ import ( corev1 "k8s.io/api/core/v1" "github.com/oam-dev/kubevela/apis/types" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/model" - apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" + apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" "github.com/oam-dev/kubevela/pkg/oam/util" "github.com/oam-dev/kubevela/pkg/utils/apply" ) -var _ = Describe("Test application usecase function", func() { +var _ = Describe("Test application service function", func() { var ( - appUsecase *applicationUsecaseImpl - workflowUsecase *workflowUsecaseImpl - envUsecase *envUsecaseImpl - envBindingUsecase *envBindingUsecaseImpl - targetUsecase *targetUsecaseImpl - definitionUsecase *definitionUsecaseImpl - projectUsecase *projectUsecaseImpl - webhookUsecase *webhookUsecaseImpl + appService *applicationServiceImpl + workflowService *workflowServiceImpl + envService *envServiceImpl + envBindingService *envBindingServiceImpl + targetService *targetServiceImpl + definitionService *definitionServiceImpl + projectService *projectServiceImpl + webhookService *webhookServiceImpl ) BeforeEach(func() { ds, err := NewDatastore(datastore.Config{Type: "kubeapi", Database: "app-test-kubevela"}) Expect(ds).ToNot(BeNil()) Expect(err).Should(BeNil()) - workflowUsecase = &workflowUsecaseImpl{ds: ds, envUsecase: envUsecase} - definitionUsecase = &definitionUsecaseImpl{kubeClient: k8sClient} - envBindingUsecase = &envBindingUsecaseImpl{ds: ds, envUsecase: envUsecase, workflowUsecase: workflowUsecase, kubeClient: k8sClient, definitionUsecase: definitionUsecase} - targetUsecase = &targetUsecaseImpl{ds: ds, k8sClient: k8sClient} - rbacUsecase := &rbacUsecaseImpl{ds: ds} - projectUsecase = &projectUsecaseImpl{ds: ds, k8sClient: k8sClient, rbacUsecase: rbacUsecase} - envUsecase = &envUsecaseImpl{ds: ds, kubeClient: k8sClient, projectUsecase: projectUsecase} - appUsecase = &applicationUsecaseImpl{ - ds: ds, - workflowUsecase: workflowUsecase, - apply: apply.NewAPIApplicator(k8sClient), - kubeClient: k8sClient, - envBindingUsecase: envBindingUsecase, - envUsecase: envUsecase, - definitionUsecase: definitionUsecase, - targetUsecase: targetUsecase, - projectUsecase: projectUsecase, + workflowService = &workflowServiceImpl{Store: ds, EnvService: envService} + definitionService = &definitionServiceImpl{KubeClient: k8sClient} + envBindingService = &envBindingServiceImpl{Store: ds, EnvService: envService, WorkflowService: workflowService, KubeClient: k8sClient, DefinitionService: definitionService} + targetService = &targetServiceImpl{Store: ds, K8sClient: k8sClient} + rbacService := &rbacServiceImpl{Store: ds} + projectService = &projectServiceImpl{Store: ds, K8sClient: k8sClient, RbacService: rbacService} + envService = &envServiceImpl{Store: ds, KubeClient: k8sClient, ProjectService: projectService} + appService = &applicationServiceImpl{ + Store: ds, + WorkflowService: workflowService, + Apply: apply.NewAPIApplicator(k8sClient), + KubeClient: k8sClient, + EnvBindingService: envBindingService, + EnvService: envService, + DefinitionService: definitionService, + TargetService: targetService, + ProjectService: projectService, } - webhookUsecase = &webhookUsecaseImpl{ - ds: ds, - applicationUsecase: appUsecase, + webhookService = &webhookServiceImpl{ + Store: ds, + ApplicationService: appService, } }) @@ -82,13 +82,13 @@ var _ = Describe("Test application usecase function", func() { err := k8sClient.Create(context.TODO(), &ns) Expect(err).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{})) - _, err = projectUsecase.CreateProject(context.TODO(), apisv1.CreateProjectRequest{Name: "project-webhook"}) + _, err = projectService.CreateProject(context.TODO(), apisv1.CreateProjectRequest{Name: "project-webhook"}) Expect(err).Should(BeNil()) - _, err = targetUsecase.CreateTarget(context.TODO(), apisv1.CreateTargetRequest{Name: "dev-target-webhook", Project: "project-webhook"}) + _, err = targetService.CreateTarget(context.TODO(), apisv1.CreateTargetRequest{Name: "dev-target-webhook", Project: "project-webhook"}) Expect(err).Should(BeNil()) - _, err = envUsecase.CreateEnv(context.TODO(), apisv1.CreateEnvRequest{Name: "webhook-dev", Namespace: "webhook-dev", Targets: []string{"dev-target-webhook"}, Project: "project-webhook"}) + _, err = envService.CreateEnv(context.TODO(), apisv1.CreateEnvRequest{Name: "webhook-dev", Namespace: "webhook-dev", Targets: []string{"dev-target-webhook"}, Project: "project-webhook"}) Expect(err).Should(BeNil()) Expect(err).Should(BeNil()) @@ -104,22 +104,22 @@ var _ = Describe("Test application usecase function", func() { ComponentType: "webservice", }, } - _, err = appUsecase.CreateApplication(context.TODO(), req) + _, err = appService.CreateApplication(context.TODO(), req) Expect(err).Should(BeNil()) - appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-webhook") + appModel, err := appService.GetApplication(context.TODO(), "test-app-webhook") Expect(err).Should(BeNil()) - _, err = webhookUsecase.HandleApplicationWebhook(context.TODO(), "invalid-token", nil) + _, err = webhookService.HandleApplicationWebhook(context.TODO(), "invalid-token", nil) Expect(err).Should(Equal(bcode.ErrInvalidWebhookToken)) - triggers, err := appUsecase.ListApplicationTriggers(context.TODO(), appModel) + triggers, err := appService.ListApplicationTriggers(context.TODO(), appModel) Expect(err).Should(BeNil()) invalidReq, err := http.NewRequest("post", "/", bytes.NewBuffer([]byte(`{"upgrade": "test"}`))) invalidReq.Header.Add(restful.HEADER_ContentType, "application/json") Expect(err).Should(BeNil()) - _, err = webhookUsecase.HandleApplicationWebhook(context.TODO(), triggers[0].Token, restful.NewRequest(invalidReq)) + _, err = webhookService.HandleApplicationWebhook(context.TODO(), triggers[0].Token, restful.NewRequest(invalidReq)) Expect(err).Should(Equal(bcode.ErrInvalidWebhookPayloadBody)) By("Test HandleApplicationWebhook function with custom payload") @@ -143,10 +143,10 @@ var _ = Describe("Test application usecase function", func() { httpreq, err := http.NewRequest("post", "/", bytes.NewBuffer(body)) httpreq.Header.Add(restful.HEADER_ContentType, "application/json") Expect(err).Should(BeNil()) - res, err := webhookUsecase.HandleApplicationWebhook(context.TODO(), triggers[0].Token, restful.NewRequest(httpreq)) + res, err := webhookService.HandleApplicationWebhook(context.TODO(), triggers[0].Token, restful.NewRequest(httpreq)) Expect(err).Should(BeNil()) appDeployRes := res.(*apisv1.ApplicationDeployResponse) - comp, err := appUsecase.GetApplicationComponent(context.TODO(), appModel, "component-name-webhook") + comp, err := appService.GetApplicationComponent(context.TODO(), appModel, "component-name-webhook") Expect(err).Should(BeNil()) Expect((*comp.Properties)["image"]).Should(Equal("test-image")) Expect((*comp.Properties)["test1"]).Should(Equal(map[string]interface{}{ @@ -157,14 +157,14 @@ var _ = Describe("Test application usecase function", func() { AppPrimaryKey: "test-app-webhook", Version: appDeployRes.Version, } - err = webhookUsecase.ds.Get(context.TODO(), revision) + err = webhookService.Store.Get(context.TODO(), revision) Expect(err).Should(BeNil()) Expect(revision.CodeInfo.Commit).Should(Equal("test-commit")) Expect(revision.CodeInfo.Branch).Should(Equal("test-branch")) Expect(revision.CodeInfo.User).Should(Equal("test-user")) By("Test HandleApplicationWebhook function with ACR payload") - acrTrigger, err := appUsecase.CreateApplicationTrigger(context.TODO(), appModel, apisv1.CreateApplicationTriggerRequest{ + acrTrigger, err := appService.CreateApplicationTrigger(context.TODO(), appModel, apisv1.CreateApplicationTriggerRequest{ Name: "test-acr", PayloadType: "acr", Type: "webhook", @@ -190,14 +190,14 @@ var _ = Describe("Test application usecase function", func() { httpreq, err = http.NewRequest("post", "/", bytes.NewBuffer(body)) httpreq.Header.Add(restful.HEADER_ContentType, "application/json") Expect(err).Should(BeNil()) - _, err = webhookUsecase.HandleApplicationWebhook(context.TODO(), acrTrigger.Token, restful.NewRequest(httpreq)) + _, err = webhookService.HandleApplicationWebhook(context.TODO(), acrTrigger.Token, restful.NewRequest(httpreq)) Expect(err).Should(BeNil()) - comp, err = appUsecase.GetApplicationComponent(context.TODO(), appModel, "component-name-webhook") + comp, err = appService.GetApplicationComponent(context.TODO(), appModel, "component-name-webhook") Expect(err).Should(BeNil()) Expect((*comp.Properties)["image"]).Should(Equal("registry.test-region.aliyuncs.com/test-namespace/test-repo:test-tag")) By("Test HandleApplicationWebhook function with harbor payload") - harborTrigger, err := appUsecase.CreateApplicationTrigger(context.TODO(), appModel, apisv1.CreateApplicationTriggerRequest{ + harborTrigger, err := appService.CreateApplicationTrigger(context.TODO(), appModel, apisv1.CreateApplicationTriggerRequest{ Name: "test-harbor", PayloadType: "harbor", Type: "webhook", @@ -228,14 +228,14 @@ var _ = Describe("Test application usecase function", func() { httpreq, err = http.NewRequest("post", "/", bytes.NewBuffer(body)) httpreq.Header.Add(restful.HEADER_ContentType, "application/json") Expect(err).Should(BeNil()) - _, err = webhookUsecase.HandleApplicationWebhook(context.TODO(), harborTrigger.Token, restful.NewRequest(httpreq)) + _, err = webhookService.HandleApplicationWebhook(context.TODO(), harborTrigger.Token, restful.NewRequest(httpreq)) Expect(err).Should(BeNil()) - comp, err = appUsecase.GetApplicationComponent(context.TODO(), appModel, "component-name-webhook") + comp, err = appService.GetApplicationComponent(context.TODO(), appModel, "component-name-webhook") Expect(err).Should(BeNil()) Expect((*comp.Properties)["image"]).Should(Equal("harbor.server/test-pro/test-repo:test-tag")) By("Test HandleApplicationWebhook function with dockerhub payload") - dockerhubTrigger, err := appUsecase.CreateApplicationTrigger(context.TODO(), appModel, apisv1.CreateApplicationTriggerRequest{ + dockerhubTrigger, err := appService.CreateApplicationTrigger(context.TODO(), appModel, apisv1.CreateApplicationTriggerRequest{ Name: "test-dockerhub", PayloadType: "dockerhub", Type: "webhook", @@ -260,14 +260,14 @@ var _ = Describe("Test application usecase function", func() { httpreq, err = http.NewRequest("post", "/", bytes.NewBuffer(body)) httpreq.Header.Add(restful.HEADER_ContentType, "application/json") Expect(err).Should(BeNil()) - _, err = webhookUsecase.HandleApplicationWebhook(context.TODO(), dockerhubTrigger.Token, restful.NewRequest(httpreq)) + _, err = webhookService.HandleApplicationWebhook(context.TODO(), dockerhubTrigger.Token, restful.NewRequest(httpreq)) Expect(err).Should(BeNil()) - comp, err = appUsecase.GetApplicationComponent(context.TODO(), appModel, "component-name-webhook") + comp, err = appService.GetApplicationComponent(context.TODO(), appModel, "component-name-webhook") Expect(err).Should(BeNil()) Expect((*comp.Properties)["image"]).Should(Equal("docker.io/test-namespace/test-repo:test-tag")) By("Test HandleApplicationWebhook function with jfrog payload without header of X-JFrogURL") - jfrogTrigger, err := appUsecase.CreateApplicationTrigger(context.TODO(), appModel, apisv1.CreateApplicationTriggerRequest{ + jfrogTrigger, err := appService.CreateApplicationTrigger(context.TODO(), appModel, apisv1.CreateApplicationTriggerRequest{ Name: "test-jfrog", PayloadType: "jfrog", Type: "webhook", @@ -289,9 +289,9 @@ var _ = Describe("Test application usecase function", func() { httpreq, err = http.NewRequest("post", "/", bytes.NewBuffer(body)) httpreq.Header.Add(restful.HEADER_ContentType, "application/json") Expect(err).Should(BeNil()) - _, err = webhookUsecase.HandleApplicationWebhook(context.TODO(), jfrogTrigger.Token, restful.NewRequest(httpreq)) + _, err = webhookService.HandleApplicationWebhook(context.TODO(), jfrogTrigger.Token, restful.NewRequest(httpreq)) Expect(err).Should(BeNil()) - comp, err = appUsecase.GetApplicationComponent(context.TODO(), appModel, "component-name-webhook") + comp, err = appService.GetApplicationComponent(context.TODO(), appModel, "component-name-webhook") Expect(err).Should(BeNil()) Expect((*comp.Properties)["image"]).Should(Equal("test-repo/test-image:test-tag")) @@ -300,9 +300,9 @@ var _ = Describe("Test application usecase function", func() { Expect(err).Should(BeNil()) httpreq.Header.Add(restful.HEADER_ContentType, "application/json") httpreq.Header.Add("X-JFrogURL", "test-addr") - _, err = webhookUsecase.HandleApplicationWebhook(context.TODO(), jfrogTrigger.Token, restful.NewRequest(httpreq)) + _, err = webhookService.HandleApplicationWebhook(context.TODO(), jfrogTrigger.Token, restful.NewRequest(httpreq)) Expect(err).Should(BeNil()) - comp, err = appUsecase.GetApplicationComponent(context.TODO(), appModel, "component-name-webhook") + comp, err = appService.GetApplicationComponent(context.TODO(), appModel, "component-name-webhook") Expect(err).Should(BeNil()) Expect((*comp.Properties)["image"]).Should(Equal("test-addr/test-repo/test-image:test-tag")) }) diff --git a/pkg/apiserver/rest/usecase/workflow.go b/pkg/apiserver/domain/service/workflow.go similarity index 76% rename from pkg/apiserver/rest/usecase/workflow.go rename to pkg/apiserver/domain/service/workflow.go index 42101393b..9befe37ed 100644 --- a/pkg/apiserver/rest/usecase/workflow.go +++ b/pkg/apiserver/domain/service/workflow.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package usecase +package service import ( "context" @@ -31,21 +31,22 @@ import ( "github.com/oam-dev/kubevela/apis/core.oam.dev/common" "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" - "github.com/oam-dev/kubevela/pkg/apiserver/clients" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/log" - "github.com/oam-dev/kubevela/pkg/apiserver/model" - apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/repository" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" + assembler "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/assembler/v1" + apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/log" "github.com/oam-dev/kubevela/pkg/oam" "github.com/oam-dev/kubevela/pkg/oam/util" utils2 "github.com/oam-dev/kubevela/pkg/utils" "github.com/oam-dev/kubevela/pkg/utils/apply" ) -// WorkflowUsecase workflow manage api -type WorkflowUsecase interface { +// WorkflowService workflow manage api +type WorkflowService interface { ListApplicationWorkflow(ctx context.Context, app *model.Application) ([]*apisv1.WorkflowBase, error) GetWorkflow(ctx context.Context, app *model.Application, workflowName string) (*model.Workflow, error) DetailWorkflow(ctx context.Context, workflow *model.Workflow) (*apisv1.DetailWorkflowResponse, error) @@ -64,29 +65,20 @@ type WorkflowUsecase interface { CountWorkflow(ctx context.Context, app *model.Application) int64 } -// NewWorkflowUsecase new workflow usecase -func NewWorkflowUsecase(ds datastore.DataStore, envUsecase EnvUsecase) WorkflowUsecase { - kubecli, err := clients.GetKubeClient() - if err != nil { - log.Logger.Fatalf("get kubeclient failure %s", err.Error()) - } - return &workflowUsecaseImpl{ - ds: ds, - kubeClient: kubecli, - apply: apply.NewAPIApplicator(kubecli), - envUsecase: envUsecase, - } +// NewWorkflowService new workflow service +func NewWorkflowService() WorkflowService { + return &workflowServiceImpl{} } -type workflowUsecaseImpl struct { - ds datastore.DataStore - kubeClient client.Client - apply apply.Applicator - envUsecase EnvUsecase +type workflowServiceImpl struct { + Store datastore.DataStore `inject:"datastore"` + KubeClient client.Client `inject:"kubeClient"` + Apply apply.Applicator `inject:"apply"` + EnvService EnvService `inject:""` } // DeleteWorkflow delete application workflow -func (w *workflowUsecaseImpl) DeleteWorkflow(ctx context.Context, app *model.Application, workflowName string) error { +func (w *workflowServiceImpl) DeleteWorkflow(ctx context.Context, app *model.Application, workflowName string) error { var workflow = &model.Workflow{ Name: workflowName, AppPrimaryKey: app.PrimaryKey(), @@ -95,16 +87,16 @@ func (w *workflowUsecaseImpl) DeleteWorkflow(ctx context.Context, app *model.App AppPrimaryKey: workflow.AppPrimaryKey, WorkflowName: workflow.Name, } - records, err := w.ds.List(ctx, &record, &datastore.ListOptions{}) + records, err := w.Store.List(ctx, &record, &datastore.ListOptions{}) if err != nil { log.Logger.Errorf("list workflow %s record failure %s", utils2.Sanitize(workflow.PrimaryKey()), err.Error()) } for _, record := range records { - if err := w.ds.Delete(ctx, record); err != nil { + if err := w.Store.Delete(ctx, record); err != nil { log.Logger.Errorf("delete workflow record %s failure %s", record.PrimaryKey(), err.Error()) } } - if err := w.ds.Delete(ctx, workflow); err != nil { + if err := w.Store.Delete(ctx, workflow); err != nil { if errors.Is(err, datastore.ErrRecordNotExist) { return bcode.ErrWorkflowNotExist } @@ -113,12 +105,12 @@ func (w *workflowUsecaseImpl) DeleteWorkflow(ctx context.Context, app *model.App return nil } -func (w *workflowUsecaseImpl) DeleteWorkflowByApp(ctx context.Context, app *model.Application) error { +func (w *workflowServiceImpl) DeleteWorkflowByApp(ctx context.Context, app *model.Application) error { var workflow = &model.Workflow{ AppPrimaryKey: app.PrimaryKey(), } - workflows, err := w.ds.List(ctx, workflow, &datastore.ListOptions{}) + workflows, err := w.Store.List(ctx, workflow, &datastore.ListOptions{}) if err != nil { if errors.Is(err, datastore.ErrRecordNotExist) { return nil @@ -131,23 +123,23 @@ func (w *workflowUsecaseImpl) DeleteWorkflowByApp(ctx context.Context, app *mode AppPrimaryKey: workflow.AppPrimaryKey, WorkflowName: workflow.Name, } - records, err := w.ds.List(ctx, &record, &datastore.ListOptions{}) + records, err := w.Store.List(ctx, &record, &datastore.ListOptions{}) if err != nil { log.Logger.Errorf("list workflow %s record failure %s", workflow.PrimaryKey(), err.Error()) } for _, record := range records { - if err := w.ds.Delete(ctx, record); err != nil { + if err := w.Store.Delete(ctx, record); err != nil { log.Logger.Errorf("delete workflow record %s failure %s", record.PrimaryKey(), err.Error()) } } - if err := w.ds.Delete(ctx, workflow); err != nil { + if err := w.Store.Delete(ctx, workflow); err != nil { log.Logger.Errorf("delete workflow %s failure %s", workflow.PrimaryKey(), err.Error()) } } return nil } -func (w *workflowUsecaseImpl) CreateOrUpdateWorkflow(ctx context.Context, app *model.Application, req apisv1.CreateWorkflowRequest) (*apisv1.DetailWorkflowResponse, error) { +func (w *workflowServiceImpl) CreateOrUpdateWorkflow(ctx context.Context, app *model.Application, req apisv1.CreateWorkflowRequest) (*apisv1.DetailWorkflowResponse, error) { if req.EnvName == "" { return nil, bcode.ErrWorkflowNoEnv } @@ -178,7 +170,7 @@ func (w *workflowUsecaseImpl) CreateOrUpdateWorkflow(ctx context.Context, app *m workflow.Alias = req.Alias workflow.Description = req.Description workflow.Default = req.Default - if err := w.ds.Put(ctx, workflow); err != nil { + if err := w.Store.Put(ctx, workflow); err != nil { return nil, err } } else { @@ -193,21 +185,15 @@ func (w *workflowUsecaseImpl) CreateOrUpdateWorkflow(ctx context.Context, app *m AppPrimaryKey: app.PrimaryKey(), } log.Logger.Infof("create workflow %s for app %s", utils2.Sanitize(req.Name), utils2.Sanitize(app.PrimaryKey())) - if err := w.ds.Add(ctx, workflow); err != nil { + if err := w.Store.Add(ctx, workflow); err != nil { return nil, err } } return w.DetailWorkflow(ctx, workflow) } -// updateWorkflowSteps will update workflow with new steps -func updateWorkflowSteps(ctx context.Context, ds datastore.DataStore, workflow *model.Workflow, steps []model.WorkflowStep) error { - workflow.Steps = steps - return ds.Put(ctx, workflow) -} - -func (w *workflowUsecaseImpl) UpdateWorkflow(ctx context.Context, workflow *model.Workflow, req apisv1.UpdateWorkflowRequest) (*apisv1.DetailWorkflowResponse, error) { - modeSteps, err := convertAPIStep2ModelStep(req.Steps) +func (w *workflowServiceImpl) UpdateWorkflow(ctx context.Context, workflow *model.Workflow, req apisv1.UpdateWorkflowRequest) (*apisv1.DetailWorkflowResponse, error) { + modeSteps, err := assembler.CreateWorkflowStepModel(req.Steps) if err != nil { return nil, err } @@ -216,50 +202,50 @@ func (w *workflowUsecaseImpl) UpdateWorkflow(ctx context.Context, workflow *mode if req.Default != nil { workflow.Default = req.Default } - if err := updateWorkflowSteps(ctx, w.ds, workflow, modeSteps); err != nil { + if err := repository.UpdateWorkflowSteps(ctx, w.Store, workflow, modeSteps); err != nil { return nil, err } return w.DetailWorkflow(ctx, workflow) } // DetailWorkflow detail workflow -func (w *workflowUsecaseImpl) DetailWorkflow(ctx context.Context, workflow *model.Workflow) (*apisv1.DetailWorkflowResponse, error) { +func (w *workflowServiceImpl) DetailWorkflow(ctx context.Context, workflow *model.Workflow) (*apisv1.DetailWorkflowResponse, error) { return &apisv1.DetailWorkflowResponse{ - WorkflowBase: convertWorkflowBase(workflow), + WorkflowBase: assembler.ConvertWorkflowBase(workflow), }, nil } // GetWorkflow get workflow model -func (w *workflowUsecaseImpl) GetWorkflow(ctx context.Context, app *model.Application, workflowName string) (*model.Workflow, error) { - return getWorkflowForApp(ctx, w.ds, app, workflowName) +func (w *workflowServiceImpl) GetWorkflow(ctx context.Context, app *model.Application, workflowName string) (*model.Workflow, error) { + return repository.GetWorkflowForApp(ctx, w.Store, app, workflowName) } // ListApplicationWorkflow list application workflows -func (w *workflowUsecaseImpl) ListApplicationWorkflow(ctx context.Context, app *model.Application) ([]*apisv1.WorkflowBase, error) { +func (w *workflowServiceImpl) ListApplicationWorkflow(ctx context.Context, app *model.Application) ([]*apisv1.WorkflowBase, error) { var workflow = model.Workflow{ AppPrimaryKey: app.PrimaryKey(), } - workflows, err := w.ds.List(ctx, &workflow, &datastore.ListOptions{}) + workflows, err := w.Store.List(ctx, &workflow, &datastore.ListOptions{}) if err != nil { return nil, err } var list []*apisv1.WorkflowBase for _, workflow := range workflows { wm := workflow.(*model.Workflow) - base := convertWorkflowBase(wm) + base := assembler.ConvertWorkflowBase(wm) list = append(list, &base) } return list, nil } // GetApplicationDefaultWorkflow get application default workflow -func (w *workflowUsecaseImpl) GetApplicationDefaultWorkflow(ctx context.Context, app *model.Application) (*model.Workflow, error) { +func (w *workflowServiceImpl) GetApplicationDefaultWorkflow(ctx context.Context, app *model.Application) (*model.Workflow, error) { var defaultEnable = true var workflow = model.Workflow{ AppPrimaryKey: app.PrimaryKey(), Default: &defaultEnable, } - workflows, err := w.ds.List(ctx, &workflow, &datastore.ListOptions{}) + workflows, err := w.Store.List(ctx, &workflow, &datastore.ListOptions{}) if err != nil { return nil, err } @@ -270,12 +256,12 @@ func (w *workflowUsecaseImpl) GetApplicationDefaultWorkflow(ctx context.Context, } // ListWorkflowRecords list workflow record -func (w *workflowUsecaseImpl) ListWorkflowRecords(ctx context.Context, workflow *model.Workflow, page, pageSize int) (*apisv1.ListWorkflowRecordsResponse, error) { +func (w *workflowServiceImpl) ListWorkflowRecords(ctx context.Context, workflow *model.Workflow, page, pageSize int) (*apisv1.ListWorkflowRecordsResponse, error) { var record = model.WorkflowRecord{ AppPrimaryKey: workflow.AppPrimaryKey, WorkflowName: workflow.Name, } - records, err := w.ds.List(ctx, &record, &datastore.ListOptions{Page: page, PageSize: pageSize}) + records, err := w.Store.List(ctx, &record, &datastore.ListOptions{Page: page, PageSize: pageSize}) if err != nil { return nil, err } @@ -286,10 +272,10 @@ func (w *workflowUsecaseImpl) ListWorkflowRecords(ctx context.Context, workflow for _, raw := range records { record, ok := raw.(*model.WorkflowRecord) if ok { - resp.Records = append(resp.Records, *convertFromRecordModel(record)) + resp.Records = append(resp.Records, *assembler.ConvertFromRecordModel(record)) } } - count, err := w.ds.Count(ctx, &record, nil) + count, err := w.Store.Count(ctx, &record, nil) if err != nil { return nil, err } @@ -299,13 +285,13 @@ func (w *workflowUsecaseImpl) ListWorkflowRecords(ctx context.Context, workflow } // DetailWorkflowRecord get workflow record detail with name -func (w *workflowUsecaseImpl) DetailWorkflowRecord(ctx context.Context, workflow *model.Workflow, recordName string) (*apisv1.DetailWorkflowRecordResponse, error) { +func (w *workflowServiceImpl) DetailWorkflowRecord(ctx context.Context, workflow *model.Workflow, recordName string) (*apisv1.DetailWorkflowRecordResponse, error) { var record = model.WorkflowRecord{ AppPrimaryKey: workflow.AppPrimaryKey, WorkflowName: workflow.Name, Name: recordName, } - err := w.ds.Get(ctx, &record) + err := w.Store.Get(ctx, &record) if err != nil { if errors.Is(err, datastore.ErrRecordNotExist) { return nil, bcode.ErrWorkflowRecordNotExist @@ -317,7 +303,7 @@ func (w *workflowUsecaseImpl) DetailWorkflowRecord(ctx context.Context, workflow AppPrimaryKey: record.AppPrimaryKey, Version: record.RevisionPrimaryKey, } - err = w.ds.Get(ctx, &revision) + err = w.Store.Get(ctx, &revision) if err != nil { if errors.Is(err, datastore.ErrRecordNotExist) { return nil, bcode.ErrApplicationRevisionNotExist @@ -326,7 +312,7 @@ func (w *workflowUsecaseImpl) DetailWorkflowRecord(ctx context.Context, workflow } return &apisv1.DetailWorkflowRecordResponse{ - WorkflowRecord: *convertFromRecordModel(&record), + WorkflowRecord: *assembler.ConvertFromRecordModel(&record), DeployTime: revision.CreateTime, DeployUser: revision.DeployUser, Note: revision.Note, @@ -334,12 +320,12 @@ func (w *workflowUsecaseImpl) DetailWorkflowRecord(ctx context.Context, workflow }, nil } -func (w *workflowUsecaseImpl) SyncWorkflowRecord(ctx context.Context) error { +func (w *workflowServiceImpl) SyncWorkflowRecord(ctx context.Context) error { var record = model.WorkflowRecord{ Finished: "false", } // list all unfinished workflow records - records, err := w.ds.List(ctx, &record, &datastore.ListOptions{}) + records, err := w.Store.List(ctx, &record, &datastore.ListOptions{}) if err != nil { return err } @@ -351,12 +337,12 @@ func (w *workflowUsecaseImpl) SyncWorkflowRecord(ctx context.Context) error { Name: record.WorkflowName, AppPrimaryKey: record.AppPrimaryKey, } - if err := w.ds.Get(ctx, workflow); err != nil { + if err := w.Store.Get(ctx, workflow); err != nil { klog.ErrorS(err, "failed to get workflow", "app name", record.AppPrimaryKey, "workflow name", record.WorkflowName, "record name", record.Name) continue } appName := record.AppPrimaryKey - if err := w.kubeClient.Get(ctx, types.NamespacedName{ + if err := w.KubeClient.Get(ctx, types.NamespacedName{ Name: appName, Namespace: record.Namespace, }, app); err != nil { @@ -374,7 +360,7 @@ func (w *workflowUsecaseImpl) SyncWorkflowRecord(ctx context.Context) error { // try to sync the status from the controller revision cr := &appsv1.ControllerRevision{} - if err := w.kubeClient.Get(ctx, types.NamespacedName{ + if err := w.KubeClient.Get(ctx, types.NamespacedName{ Name: fmt.Sprintf("record-%s-%s", appName, record.Name), Namespace: record.Namespace, }, cr); err != nil { @@ -396,12 +382,12 @@ func (w *workflowUsecaseImpl) SyncWorkflowRecord(ctx context.Context) error { return nil } -func (w *workflowUsecaseImpl) syncWorkflowStatus(ctx context.Context, app *v1beta1.Application, recordName, source string) error { +func (w *workflowServiceImpl) syncWorkflowStatus(ctx context.Context, app *v1beta1.Application, recordName, source string) error { var record = &model.WorkflowRecord{ AppPrimaryKey: app.Annotations[oam.AnnotationAppName], Name: recordName, } - if err := w.ds.Get(ctx, record); err != nil { + if err := w.Store.Get(ctx, record); err != nil { if errors.Is(err, datastore.ErrRecordNotExist) { return bcode.ErrWorkflowRecordNotExist } @@ -412,7 +398,7 @@ func (w *workflowUsecaseImpl) syncWorkflowStatus(ctx context.Context, app *v1bet Version: record.RevisionPrimaryKey, } - if err := w.ds.Get(ctx, revision); err != nil { + if err := w.Store.Get(ctx, revision); err != nil { if errors.Is(err, datastore.ErrRecordNotExist) { return bcode.ErrApplicationRevisionNotExist } @@ -445,12 +431,12 @@ func (w *workflowUsecaseImpl) syncWorkflowStatus(ctx context.Context, app *v1bet } record.Finished = strconv.FormatBool(status.Finished) - if err := w.ds.Put(ctx, record); err != nil { + if err := w.Store.Put(ctx, record); err != nil { return err } revision.Status = summaryStatus - if err := w.ds.Put(ctx, revision); err != nil { + if err := w.Store.Put(ctx, revision); err != nil { return err } } @@ -462,7 +448,7 @@ func (w *workflowUsecaseImpl) syncWorkflowStatus(ctx context.Context, app *v1bet return nil } -func (w *workflowUsecaseImpl) CreateWorkflowRecord(ctx context.Context, appModel *model.Application, app *v1beta1.Application, workflow *model.Workflow) error { +func (w *workflowServiceImpl) CreateWorkflowRecord(ctx context.Context, appModel *model.Application, app *v1beta1.Application, workflow *model.Workflow) error { if app.Annotations == nil { return fmt.Errorf("empty annotations in application") } @@ -481,7 +467,7 @@ func (w *workflowUsecaseImpl) CreateWorkflowRecord(ctx context.Context, appModel } } - if err := w.ds.Add(ctx, &model.WorkflowRecord{ + if err := w.Store.Add(ctx, &model.WorkflowRecord{ WorkflowName: workflow.Name, WorkflowAlias: workflow.Alias, AppPrimaryKey: appModel.PrimaryKey(), @@ -496,7 +482,7 @@ func (w *workflowUsecaseImpl) CreateWorkflowRecord(ctx context.Context, appModel return err } - if err := resetRevisionsAndRecords(ctx, w.ds, appModel.PrimaryKey(), workflow.Name, app.Annotations[oam.AnnotationDeployVersion], app.Annotations[oam.AnnotationPublishVersion]); err != nil { + if err := resetRevisionsAndRecords(ctx, w.Store, appModel.PrimaryKey(), workflow.Name, app.Annotations[oam.AnnotationDeployVersion], app.Annotations[oam.AnnotationPublishVersion]); err != nil { return err } @@ -560,22 +546,22 @@ func resetRevisionsAndRecords(ctx context.Context, ds datastore.DataStore, appNa return nil } -func (w *workflowUsecaseImpl) CountWorkflow(ctx context.Context, app *model.Application) int64 { - count, err := w.ds.Count(ctx, &model.Workflow{AppPrimaryKey: app.PrimaryKey()}, &datastore.FilterOptions{}) +func (w *workflowServiceImpl) CountWorkflow(ctx context.Context, app *model.Application) int64 { + count, err := w.Store.Count(ctx, &model.Workflow{AppPrimaryKey: app.PrimaryKey()}, &datastore.FilterOptions{}) if err != nil { log.Logger.Errorf("count app %s workflow failure %s", app.PrimaryKey(), err.Error()) } return count } -func (w *workflowUsecaseImpl) ResumeRecord(ctx context.Context, appModel *model.Application, workflow *model.Workflow, recordName string) error { +func (w *workflowServiceImpl) ResumeRecord(ctx context.Context, appModel *model.Application, workflow *model.Workflow, recordName string) error { oamApp, err := w.checkRecordRunning(ctx, appModel, workflow.EnvName) if err != nil { return err } oamApp.Status.Workflow.Suspend = false - if err := w.kubeClient.Status().Patch(ctx, oamApp, client.Merge); err != nil { + if err := w.KubeClient.Status().Patch(ctx, oamApp, client.Merge); err != nil { return err } if err := w.syncWorkflowStatus(ctx, oamApp, recordName, oamApp.Name); err != nil { @@ -585,14 +571,14 @@ func (w *workflowUsecaseImpl) ResumeRecord(ctx context.Context, appModel *model. return nil } -func (w *workflowUsecaseImpl) TerminateRecord(ctx context.Context, appModel *model.Application, workflow *model.Workflow, recordName string) error { +func (w *workflowServiceImpl) TerminateRecord(ctx context.Context, appModel *model.Application, workflow *model.Workflow, recordName string) error { oamApp, err := w.checkRecordRunning(ctx, appModel, workflow.EnvName) if err != nil { return err } oamApp.Status.Workflow.Terminated = true - if err := w.kubeClient.Status().Patch(ctx, oamApp, client.Merge); err != nil { + if err := w.KubeClient.Status().Patch(ctx, oamApp, client.Merge); err != nil { return err } if err := w.syncWorkflowStatus(ctx, oamApp, recordName, oamApp.Name); err != nil { @@ -602,7 +588,7 @@ func (w *workflowUsecaseImpl) TerminateRecord(ctx context.Context, appModel *mod return nil } -func (w *workflowUsecaseImpl) RollbackRecord(ctx context.Context, appModel *model.Application, workflow *model.Workflow, recordName, revisionVersion string) error { +func (w *workflowServiceImpl) RollbackRecord(ctx context.Context, appModel *model.Application, workflow *model.Workflow, recordName, revisionVersion string) error { if revisionVersion == "" { // find the latest complete revision version var revision = model.ApplicationRevision{ @@ -611,7 +597,7 @@ func (w *workflowUsecaseImpl) RollbackRecord(ctx context.Context, appModel *mode WorkflowName: workflow.Name, EnvName: workflow.EnvName, } - revisions, err := w.ds.List(ctx, &revision, &datastore.ListOptions{ + revisions, err := w.Store.List(ctx, &revision, &datastore.ListOptions{ Page: 1, PageSize: 1, SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderDescending}}, @@ -630,7 +616,7 @@ func (w *workflowUsecaseImpl) RollbackRecord(ctx context.Context, appModel *mode AppPrimaryKey: appModel.PrimaryKey(), Name: recordName, } - if err := w.ds.Get(ctx, record); err != nil { + if err := w.Store.Get(ctx, record); err != nil { return err } @@ -643,7 +629,7 @@ func (w *workflowUsecaseImpl) RollbackRecord(ctx context.Context, appModel *mode AppPrimaryKey: appModel.Name, Version: record.RevisionPrimaryKey, } - if err := w.ds.Get(ctx, originalRevision); err != nil { + if err := w.Store.Get(ctx, originalRevision); err != nil { return err } @@ -651,7 +637,7 @@ func (w *workflowUsecaseImpl) RollbackRecord(ctx context.Context, appModel *mode AppPrimaryKey: appModel.Name, Version: revisionVersion, } - if err := w.ds.Get(ctx, rollbackRevision); err != nil { + if err := w.Store.Get(ctx, rollbackRevision); err != nil { return err } @@ -659,7 +645,7 @@ func (w *workflowUsecaseImpl) RollbackRecord(ctx context.Context, appModel *mode originalRevision.Status = model.RevisionStatusRollback originalRevision.RollbackVersion = revisionVersion originalRevision.UpdateTime = time.Now().Time - if err := w.ds.Put(ctx, originalRevision); err != nil { + if err := w.Store.Put(ctx, originalRevision); err != nil { return err } @@ -681,9 +667,9 @@ func (w *workflowUsecaseImpl) RollbackRecord(ctx context.Context, appModel *mode return err } - if err := w.apply.Apply(ctx, oamApp); err != nil { + if err := w.Apply.Apply(ctx, oamApp); err != nil { // rollback error case - if err := w.ds.Delete(ctx, &model.WorkflowRecord{Name: newRecordName}); err != nil { + if err := w.Store.Delete(ctx, &model.WorkflowRecord{Name: newRecordName}); err != nil { klog.Error(err, "failed to delete record", newRecordName) } return err @@ -692,13 +678,13 @@ func (w *workflowUsecaseImpl) RollbackRecord(ctx context.Context, appModel *mode return nil } -func (w *workflowUsecaseImpl) checkRecordRunning(ctx context.Context, appModel *model.Application, envName string) (*v1beta1.Application, error) { +func (w *workflowServiceImpl) checkRecordRunning(ctx context.Context, appModel *model.Application, envName string) (*v1beta1.Application, error) { oamApp := &v1beta1.Application{} - env, err := w.envUsecase.GetEnv(ctx, envName) + env, err := w.EnvService.GetEnv(ctx, envName) if err != nil { return nil, err } - if err := w.kubeClient.Get(ctx, types.NamespacedName{Name: appModel.Name, Namespace: env.Namespace}, oamApp); err != nil { + if err := w.KubeClient.Get(ctx, types.NamespacedName{Name: appModel.Name, Namespace: env.Namespace}, oamApp); err != nil { return nil, err } if oamApp.Status.Workflow != nil && !oamApp.Status.Workflow.Suspend && !oamApp.Status.Workflow.Terminated && !oamApp.Status.Workflow.Finished { @@ -708,40 +694,3 @@ func (w *workflowUsecaseImpl) checkRecordRunning(ctx context.Context, appModel * oamApp.SetGroupVersionKind(v1beta1.ApplicationKindVersionKind) return oamApp, nil } - -func convertFromRecordModel(record *model.WorkflowRecord) *apisv1.WorkflowRecord { - return &apisv1.WorkflowRecord{ - Name: record.Name, - Namespace: record.Namespace, - WorkflowName: record.WorkflowName, - WorkflowAlias: record.WorkflowAlias, - ApplicationRevision: record.RevisionPrimaryKey, - StartTime: record.StartTime, - Status: record.Status, - Steps: record.Steps, - } -} - -func convertFromWorkflowStepModel(step model.WorkflowStep) apisv1.WorkflowStep { - apiStep := apisv1.WorkflowStep{ - Name: step.Name, - Type: step.Type, - Alias: step.Alias, - Description: step.Description, - Inputs: step.Inputs, - Outputs: step.Outputs, - Properties: step.Properties.JSON(), - DependsOn: step.DependsOn, - } - if step.Properties != nil { - apiStep.Properties = step.Properties.JSON() - } - return apiStep -} - -func convertBool(b *bool) bool { - if b == nil { - return false - } - return *b -} diff --git a/pkg/apiserver/rest/usecase/workflow_test.go b/pkg/apiserver/domain/service/workflow_test.go similarity index 76% rename from pkg/apiserver/rest/usecase/workflow_test.go rename to pkg/apiserver/domain/service/workflow_test.go index ff0b05486..cd8b7ffac 100644 --- a/pkg/apiserver/rest/usecase/workflow_test.go +++ b/pkg/apiserver/domain/service/workflow_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package usecase +package service import ( "context" @@ -34,20 +34,20 @@ import ( "github.com/oam-dev/kubevela/apis/core.oam.dev/common" "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/model" - apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" + apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" "github.com/oam-dev/kubevela/pkg/oam" "github.com/oam-dev/kubevela/pkg/utils/apply" ) var appName = "app-workflow" -var _ = Describe("Test workflow usecase functions", func() { +var _ = Describe("Test workflow service functions", func() { var ( - workflowUsecase *workflowUsecaseImpl - appUsecase *applicationUsecaseImpl - projectUsecase *projectUsecaseImpl - envUsecase *envUsecaseImpl + workflowService *workflowServiceImpl + appService *applicationServiceImpl + projectService *projectServiceImpl + envService *envServiceImpl testProject = "workflow-project" ds datastore.DataStore ) @@ -57,26 +57,26 @@ var _ = Describe("Test workflow usecase functions", func() { ds, err = NewDatastore(datastore.Config{Type: "kubeapi", Database: "workflow-test-" + strconv.FormatInt(time.Now().UnixNano(), 10)}) Expect(ds).ToNot(BeNil()) Expect(err).Should(BeNil()) - rbacUsecase := &rbacUsecaseImpl{ds: ds} - projectUsecase = &projectUsecaseImpl{ds: ds, rbacUsecase: rbacUsecase} - envUsecase = &envUsecaseImpl{ds: ds, kubeClient: k8sClient, projectUsecase: projectUsecase} - workflowUsecase = &workflowUsecaseImpl{ - ds: ds, - kubeClient: k8sClient, - apply: apply.NewAPIApplicator(k8sClient), - envUsecase: envUsecase} - appUsecase = &applicationUsecaseImpl{ds: ds, kubeClient: k8sClient, - apply: apply.NewAPIApplicator(k8sClient), - projectUsecase: projectUsecase, - envUsecase: envUsecase, - envBindingUsecase: &envBindingUsecaseImpl{ - ds: ds, - workflowUsecase: workflowUsecase, - envUsecase: envUsecase, + rbacService := &rbacServiceImpl{Store: ds} + projectService = &projectServiceImpl{Store: ds, RbacService: rbacService} + envService = &envServiceImpl{Store: ds, KubeClient: k8sClient, ProjectService: projectService} + workflowService = &workflowServiceImpl{ + Store: ds, + KubeClient: k8sClient, + Apply: apply.NewAPIApplicator(k8sClient), + EnvService: envService} + appService = &applicationServiceImpl{Store: ds, KubeClient: k8sClient, + Apply: apply.NewAPIApplicator(k8sClient), + ProjectService: projectService, + EnvService: envService, + EnvBindingService: &envBindingServiceImpl{ + Store: ds, + WorkflowService: workflowService, + EnvService: envService, }} }) It("Test CreateWorkflow function", func() { - _, err := projectUsecase.CreateProject(context.TODO(), apisv1.CreateProjectRequest{Name: testProject}) + _, err := projectService.CreateProject(context.TODO(), apisv1.CreateProjectRequest{Name: testProject}) Expect(err).Should(BeNil()) reqApp := apisv1.CreateApplicationRequest{ Name: appName, @@ -86,7 +86,7 @@ var _ = Describe("Test workflow usecase functions", func() { Name: "dev", }}, } - _, err = appUsecase.CreateApplication(context.TODO(), reqApp) + _, err = appService.CreateApplication(context.TODO(), reqApp) Expect(err).Should(BeNil()) req := apisv1.CreateWorkflowRequest{ Name: "test-workflow-1", @@ -94,7 +94,7 @@ var _ = Describe("Test workflow usecase functions", func() { EnvName: "dev", } - base, err := workflowUsecase.CreateOrUpdateWorkflow(context.TODO(), &model.Application{ + base, err := workflowService.CreateOrUpdateWorkflow(context.TODO(), &model.Application{ Name: appName, }, req) Expect(err).Should(BeNil()) @@ -106,7 +106,7 @@ var _ = Describe("Test workflow usecase functions", func() { EnvName: "dev2", } - base, err = workflowUsecase.CreateOrUpdateWorkflow(context.TODO(), &model.Application{ + base, err = workflowService.CreateOrUpdateWorkflow(context.TODO(), &model.Application{ Name: appName, }, req2) Expect(err).Should(BeNil()) @@ -129,14 +129,14 @@ var _ = Describe("Test workflow usecase functions", func() { }, Default: &defaultW, } - base, err = workflowUsecase.CreateOrUpdateWorkflow(context.TODO(), &model.Application{ + base, err = workflowService.CreateOrUpdateWorkflow(context.TODO(), &model.Application{ Name: appName, }, req) Expect(err).Should(BeNil()) Expect(cmp.Diff(base.Name, req.Name)).Should(BeEmpty()) By("Test GetApplicationDefaultWorkflow function") - workflow, err := workflowUsecase.GetApplicationDefaultWorkflow(context.TODO(), &model.Application{ + workflow, err := workflowService.GetApplicationDefaultWorkflow(context.TODO(), &model.Application{ Name: appName, }) Expect(err).Should(BeNil()) @@ -150,20 +150,20 @@ var _ = Describe("Test workflow usecase functions", func() { err = json.Unmarshal(raw, app) Expect(err).Should(BeNil()) app.Annotations[oam.AnnotationWorkflowName] = "test-workflow-2" - workflow, err = workflowUsecase.GetWorkflow(context.TODO(), &model.Application{ + workflow, err = workflowService.GetWorkflow(context.TODO(), &model.Application{ Name: appName, }, "test-workflow-2") Expect(err).Should(BeNil()) for i := 0; i < 3; i++ { app.Annotations[oam.AnnotationPublishVersion] = fmt.Sprintf("list-workflow-name-%d", i) app.Status.Workflow.AppRevision = fmt.Sprintf("list-workflow-name-%d", i) - err = workflowUsecase.CreateWorkflowRecord(context.TODO(), &model.Application{ + err = workflowService.CreateWorkflowRecord(context.TODO(), &model.Application{ Name: appName, }, app, workflow) Expect(err).Should(BeNil()) } - resp, err := workflowUsecase.ListWorkflowRecords(context.TODO(), workflow, 0, 10) + resp, err := workflowService.ListWorkflowRecords(context.TODO(), workflow, 0, 10) Expect(err).Should(BeNil()) Expect(resp.Total).Should(Equal(int64(3))) @@ -176,11 +176,11 @@ var _ = Describe("Test workflow usecase functions", func() { app.Annotations[oam.AnnotationPublishVersion] = "test-workflow-2-123" app.Status.Workflow.AppRevision = "test-workflow-2-123" app.Annotations[oam.AnnotationDeployVersion] = "1234" - workflow, err = workflowUsecase.GetWorkflow(context.TODO(), &model.Application{ + workflow, err = workflowService.GetWorkflow(context.TODO(), &model.Application{ Name: appName, }, "test-workflow-2") Expect(err).Should(BeNil()) - err = workflowUsecase.CreateWorkflowRecord(context.TODO(), &model.Application{ + err = workflowService.CreateWorkflowRecord(context.TODO(), &model.Application{ Name: appName, }, app, workflow) Expect(err).Should(BeNil()) @@ -195,10 +195,10 @@ var _ = Describe("Test workflow usecase functions", func() { WorkflowName: "test-workflow-2", } - err = workflowUsecase.createTestApplicationRevision(context.TODO(), revision) + err = workflowService.createTestApplicationRevision(context.TODO(), revision) Expect(err).Should(BeNil()) - detail, err := workflowUsecase.DetailWorkflowRecord(context.TODO(), workflow, "test-workflow-2-123") + detail, err := workflowService.DetailWorkflowRecord(context.TODO(), workflow, "test-workflow-2-123") Expect(err).Should(BeNil()) Expect(detail.WorkflowRecord.Name).Should(Equal("test-workflow-2-123")) Expect(detail.DeployUser).Should(Equal("test-user")) @@ -214,11 +214,11 @@ var _ = Describe("Test workflow usecase functions", func() { app.Annotations[oam.AnnotationPublishVersion] = "test-workflow-2-233" app.Status.Workflow.AppRevision = "test-workflow-2-233" app.Annotations[oam.AnnotationDeployVersion] = "4321" - workflow, err = workflowUsecase.GetWorkflow(context.TODO(), &model.Application{ + workflow, err = workflowService.GetWorkflow(context.TODO(), &model.Application{ Name: appName, }, "test-workflow-2") Expect(err).Should(BeNil()) - err = workflowUsecase.CreateWorkflowRecord(context.TODO(), &model.Application{ + err = workflowService.CreateWorkflowRecord(context.TODO(), &model.Application{ Name: appName, }, app, workflow) Expect(err).Should(BeNil()) @@ -231,25 +231,25 @@ var _ = Describe("Test workflow usecase functions", func() { DeployUser: "test-user", WorkflowName: "test-workflow-2", } - err = workflowUsecase.createTestApplicationRevision(context.TODO(), revision) + err = workflowService.createTestApplicationRevision(context.TODO(), revision) Expect(err).Should(BeNil()) By("create the application to sync") ctx := context.Background() app.Status.Workflow.Finished = true - err = workflowUsecase.kubeClient.Create(ctx, app.DeepCopy()) + err = workflowService.KubeClient.Create(ctx, app.DeepCopy()) Expect(err).Should(BeNil()) - err = workflowUsecase.kubeClient.Status().Patch(ctx, app, client.Merge) + err = workflowService.KubeClient.Status().Patch(ctx, app, client.Merge) Expect(err).Should(BeNil()) - err = workflowUsecase.SyncWorkflowRecord(ctx) + err = workflowService.SyncWorkflowRecord(ctx) Expect(err).Should(BeNil()) - workflow, err = workflowUsecase.GetWorkflow(context.TODO(), &model.Application{ + workflow, err = workflowService.GetWorkflow(context.TODO(), &model.Application{ Name: appName, }, "test-workflow-2") Expect(err).Should(BeNil()) By("check the record") - record, err := workflowUsecase.DetailWorkflowRecord(context.TODO(), workflow, "test-workflow-2-233") + record, err := workflowService.DetailWorkflowRecord(context.TODO(), workflow, "test-workflow-2-233") Expect(err).Should(BeNil()) Expect(record.Status).Should(Equal(model.RevisionStatusComplete)) Expect(record.Steps[0].Alias).Should(Equal("step-alias-1")) @@ -258,7 +258,7 @@ var _ = Describe("Test workflow usecase functions", func() { Expect(record.Steps[1].Phase).Should(Equal(common.WorkflowStepPhaseSucceeded)) By("check the application revision") - err = workflowUsecase.ds.Get(ctx, revision) + err = workflowService.Store.Get(ctx, revision) Expect(err).Should(BeNil()) Expect(revision.Status).Should(Equal(model.RevisionStatusComplete)) @@ -267,7 +267,7 @@ var _ = Describe("Test workflow usecase functions", func() { app.Annotations[oam.AnnotationPublishVersion] = "test-workflow-2-111" app.Status.Workflow.AppRevision = "test-workflow-2-111" app.Annotations[oam.AnnotationDeployVersion] = "1111" - err = workflowUsecase.CreateWorkflowRecord(context.TODO(), &model.Application{ + err = workflowService.CreateWorkflowRecord(context.TODO(), &model.Application{ Name: appName, }, app, workflow) Expect(err).Should(BeNil()) @@ -280,7 +280,7 @@ var _ = Describe("Test workflow usecase functions", func() { DeployUser: "test-user", WorkflowName: "test-workflow-2", } - err = workflowUsecase.createTestApplicationRevision(context.TODO(), anotherRevision) + err = workflowService.createTestApplicationRevision(context.TODO(), anotherRevision) Expect(err).Should(BeNil()) By("create one controller revision to test sync workflow record") @@ -293,19 +293,19 @@ var _ = Describe("Test workflow usecase functions", func() { }, Data: runtime.RawExtension{Raw: raw}, } - err = workflowUsecase.kubeClient.Create(ctx, cr) + err = workflowService.KubeClient.Create(ctx, cr) Expect(err).Should(BeNil()) - err = workflowUsecase.SyncWorkflowRecord(ctx) + err = workflowService.SyncWorkflowRecord(ctx) Expect(err).Should(BeNil()) By("check the record") - anotherRecord, err := workflowUsecase.DetailWorkflowRecord(context.TODO(), workflow, "test-workflow-2-111") + anotherRecord, err := workflowService.DetailWorkflowRecord(context.TODO(), workflow, "test-workflow-2-111") Expect(err).Should(BeNil()) Expect(anotherRecord.Status).Should(Equal(model.RevisionStatusComplete)) By("check the application revision") - err = workflowUsecase.ds.Get(ctx, anotherRevision) + err = workflowService.Store.Get(ctx, anotherRevision) Expect(err).Should(BeNil()) Expect(anotherRevision.Status).Should(Equal(model.RevisionStatusComplete)) }) @@ -313,7 +313,7 @@ var _ = Describe("Test workflow usecase functions", func() { It("Test CreateRecord function", func() { ctx := context.TODO() for i := 0; i < 3; i++ { - workflowUsecase.ds.Add(ctx, &model.WorkflowRecord{ + workflowService.Store.Add(ctx, &model.WorkflowRecord{ AppPrimaryKey: "record-app", Name: fmt.Sprintf("test-record-%d", i), WorkflowName: "test-workflow", @@ -321,10 +321,10 @@ var _ = Describe("Test workflow usecase functions", func() { }) } - app, err := createTestSuspendApp(ctx, "record-app", "default", "revision-123", "test-workflow", "test-record-3", workflowUsecase.kubeClient) + app, err := createTestSuspendApp(ctx, "record-app", "default", "revision-123", "test-workflow", "test-record-3", workflowService.KubeClient) Expect(err).Should(BeNil()) - err = workflowUsecase.CreateWorkflowRecord(ctx, &model.Application{ + err = workflowService.CreateWorkflowRecord(ctx, &model.Application{ Name: "record-app", }, app, &model.Workflow{Name: "test-workflow"}) Expect(err).Should(BeNil()) @@ -334,7 +334,7 @@ var _ = Describe("Test workflow usecase functions", func() { AppPrimaryKey: "record-app", WorkflowName: "test-workflow", } - err = workflowUsecase.ds.Get(ctx, record) + err = workflowService.Store.Get(ctx, record) Expect(err).Should(BeNil()) Expect(record.Status).Should(Equal(model.RevisionStatusRunning)) }) @@ -342,7 +342,7 @@ var _ = Describe("Test workflow usecase functions", func() { It("Test ResumeRecord function", func() { ctx := context.TODO() - _, err := envUsecase.CreateEnv(context.TODO(), apisv1.CreateEnvRequest{Name: "resume"}) + _, err := envService.CreateEnv(context.TODO(), apisv1.CreateEnvRequest{Name: "resume"}) Expect(err).Should(BeNil()) ResumeWorkflow := "resume-workflow" req := apisv1.CreateWorkflowRequest{ @@ -351,33 +351,33 @@ var _ = Describe("Test workflow usecase functions", func() { EnvName: "resume", } - base, err := workflowUsecase.CreateOrUpdateWorkflow(context.TODO(), &model.Application{ + base, err := workflowService.CreateOrUpdateWorkflow(context.TODO(), &model.Application{ Name: appName, }, req) Expect(err).Should(BeNil()) Expect(cmp.Diff(base.Name, req.Name)).Should(BeEmpty()) - app, err := createTestSuspendApp(ctx, appName, "resume", "revision-resume1", ResumeWorkflow, "workflow-resume-1", workflowUsecase.kubeClient) + app, err := createTestSuspendApp(ctx, appName, "resume", "revision-resume1", ResumeWorkflow, "workflow-resume-1", workflowService.KubeClient) Expect(err).Should(BeNil()) - err = workflowUsecase.CreateWorkflowRecord(context.TODO(), &model.Application{ + err = workflowService.CreateWorkflowRecord(context.TODO(), &model.Application{ Name: appName, }, app, &model.Workflow{Name: ResumeWorkflow}) Expect(err).Should(BeNil()) - err = workflowUsecase.createTestApplicationRevision(ctx, &model.ApplicationRevision{ + err = workflowService.createTestApplicationRevision(ctx, &model.ApplicationRevision{ AppPrimaryKey: appName, Version: "revision-resume1", Status: model.RevisionStatusRunning, }) Expect(err).Should(BeNil()) - err = workflowUsecase.ResumeRecord(ctx, &model.Application{ + err = workflowService.ResumeRecord(ctx, &model.Application{ Name: appName, }, &model.Workflow{Name: ResumeWorkflow, EnvName: "resume"}, "workflow-resume-1") Expect(err).Should(BeNil()) - record, err := workflowUsecase.DetailWorkflowRecord(ctx, &model.Workflow{Name: ResumeWorkflow, AppPrimaryKey: appName}, "workflow-resume-1") + record, err := workflowService.DetailWorkflowRecord(ctx, &model.Workflow{Name: ResumeWorkflow, AppPrimaryKey: appName}, "workflow-resume-1") Expect(err).Should(BeNil()) Expect(record.Status).Should(Equal(model.RevisionStatusRunning)) }) @@ -385,7 +385,7 @@ var _ = Describe("Test workflow usecase functions", func() { It("Test TerminateRecord function", func() { ctx := context.TODO() - _, err := envUsecase.CreateEnv(context.TODO(), apisv1.CreateEnvRequest{Name: "terminate"}) + _, err := envService.CreateEnv(context.TODO(), apisv1.CreateEnvRequest{Name: "terminate"}) Expect(err).Should(BeNil()) workflowName := "terminate-workflow" req := apisv1.CreateWorkflowRequest{ @@ -394,40 +394,40 @@ var _ = Describe("Test workflow usecase functions", func() { EnvName: "terminate", } workflow := &model.Workflow{Name: workflowName, EnvName: "terminate"} - base, err := workflowUsecase.CreateOrUpdateWorkflow(context.TODO(), &model.Application{ + base, err := workflowService.CreateOrUpdateWorkflow(context.TODO(), &model.Application{ Name: appName, }, req) Expect(err).Should(BeNil()) Expect(cmp.Diff(base.Name, req.Name)).Should(BeEmpty()) - app, err := createTestSuspendApp(ctx, appName, "terminate", "revision-terminate1", workflow.Name, "test-workflow-2-1", workflowUsecase.kubeClient) + app, err := createTestSuspendApp(ctx, appName, "terminate", "revision-terminate1", workflow.Name, "test-workflow-2-1", workflowService.KubeClient) Expect(err).Should(BeNil()) - err = workflowUsecase.CreateWorkflowRecord(context.TODO(), &model.Application{ + err = workflowService.CreateWorkflowRecord(context.TODO(), &model.Application{ Name: appName, }, app, workflow) Expect(err).Should(BeNil()) - err = workflowUsecase.createTestApplicationRevision(ctx, &model.ApplicationRevision{ + err = workflowService.createTestApplicationRevision(ctx, &model.ApplicationRevision{ AppPrimaryKey: appName, Version: "revision-terminate1", Status: model.RevisionStatusRunning, }) Expect(err).Should(BeNil()) - err = workflowUsecase.TerminateRecord(ctx, &model.Application{ + err = workflowService.TerminateRecord(ctx, &model.Application{ Name: appName, }, workflow, "test-workflow-2-1") Expect(err).Should(BeNil()) - record, err := workflowUsecase.DetailWorkflowRecord(ctx, workflow, "test-workflow-2-1") + record, err := workflowService.DetailWorkflowRecord(ctx, workflow, "test-workflow-2-1") Expect(err).Should(BeNil()) Expect(record.Status).Should(Equal(model.RevisionStatusTerminated)) }) It("Test RollbackRecord function", func() { ctx := context.TODO() - _, err := envUsecase.CreateEnv(context.TODO(), apisv1.CreateEnvRequest{Name: "rollback"}) + _, err := envService.CreateEnv(context.TODO(), apisv1.CreateEnvRequest{Name: "rollback"}) Expect(err).Should(BeNil()) workflowName := "rollback-workflow" req := apisv1.CreateWorkflowRequest{ @@ -436,21 +436,21 @@ var _ = Describe("Test workflow usecase functions", func() { EnvName: "rollback", } workflow := &model.Workflow{Name: workflowName, EnvName: "rollback"} - base, err := workflowUsecase.CreateOrUpdateWorkflow(context.TODO(), &model.Application{ + base, err := workflowService.CreateOrUpdateWorkflow(context.TODO(), &model.Application{ Name: appName, }, req) Expect(err).Should(BeNil()) Expect(cmp.Diff(base.Name, req.Name)).Should(BeEmpty()) - app, err := createTestSuspendApp(ctx, appName, "rollback", "revision-rollback1", workflow.Name, "test-workflow-2-2", workflowUsecase.kubeClient) + app, err := createTestSuspendApp(ctx, appName, "rollback", "revision-rollback1", workflow.Name, "test-workflow-2-2", workflowService.KubeClient) Expect(err).Should(BeNil()) - err = workflowUsecase.CreateWorkflowRecord(context.TODO(), &model.Application{ + err = workflowService.CreateWorkflowRecord(context.TODO(), &model.Application{ Name: appName, }, app, workflow) Expect(err).Should(BeNil()) - err = workflowUsecase.createTestApplicationRevision(ctx, &model.ApplicationRevision{ + err = workflowService.createTestApplicationRevision(ctx, &model.ApplicationRevision{ AppPrimaryKey: appName, Version: "revision-rollback1", Status: model.RevisionStatusRunning, @@ -458,7 +458,7 @@ var _ = Describe("Test workflow usecase functions", func() { EnvName: "rollback", }) Expect(err).Should(BeNil()) - err = workflowUsecase.createTestApplicationRevision(ctx, &model.ApplicationRevision{ + err = workflowService.createTestApplicationRevision(ctx, &model.ApplicationRevision{ AppPrimaryKey: appName, Version: "revision-rollback0", ApplyAppConfig: `{"apiVersion":"core.oam.dev/v1beta1","kind":"Application","metadata":{"annotations":{"app.oam.dev/workflowName":"test-workflow-2-2","app.oam.dev/deployVersion":"revision-rollback1","vela.io/publish-version":"workflow-rollback1"},"name":"first-vela-app","namespace":"default"},"spec":{"components":[{"name":"express-server","properties":{"image":"crccheck/hello-world","port":8000},"traits":[{"properties":{"domain":"testsvc.example.com","http":{"/":8000}},"type":"ingress-1-20"}],"type":"webservice"}]}}`, @@ -468,12 +468,12 @@ var _ = Describe("Test workflow usecase functions", func() { }) Expect(err).Should(BeNil()) - err = workflowUsecase.RollbackRecord(ctx, &model.Application{ + err = workflowService.RollbackRecord(ctx, &model.Application{ Name: appName, }, workflow, "test-workflow-2-2", "revision-rollback0") Expect(err).Should(BeNil()) - recordsNum, err := workflowUsecase.ds.Count(ctx, &model.WorkflowRecord{ + recordsNum, err := workflowService.Store.Count(ctx, &model.WorkflowRecord{ AppPrimaryKey: appName, WorkflowName: workflow.Name, RevisionPrimaryKey: "revision-rollback0", @@ -484,17 +484,17 @@ var _ = Describe("Test workflow usecase functions", func() { By("rollback application without revision version") app.Annotations[oam.AnnotationPublishVersion] = "workflow-rollback-2" app.Status.Workflow.AppRevision = "workflow-rollback-2" - err = workflowUsecase.CreateWorkflowRecord(context.TODO(), &model.Application{ + err = workflowService.CreateWorkflowRecord(context.TODO(), &model.Application{ Name: appName, }, app, workflow) Expect(err).Should(BeNil()) - err = workflowUsecase.RollbackRecord(ctx, &model.Application{ + err = workflowService.RollbackRecord(ctx, &model.Application{ Name: appName, }, workflow, "workflow-rollback-2", "") Expect(err).Should(BeNil()) - recordsNum, err = workflowUsecase.ds.Count(ctx, &model.WorkflowRecord{ + recordsNum, err = workflowService.Store.Count(ctx, &model.WorkflowRecord{ AppPrimaryKey: appName, WorkflowName: workflow.Name, RevisionPrimaryKey: "revision-rollback0", @@ -506,7 +506,7 @@ var _ = Describe("Test workflow usecase functions", func() { AppPrimaryKey: appName, Version: "revision-rollback1", } - err = workflowUsecase.ds.Get(ctx, originalRevision) + err = workflowService.Store.Get(ctx, originalRevision) Expect(err).Should(BeNil()) Expect(originalRevision.Status).Should(Equal(model.RevisionStatusRollback)) Expect(originalRevision.RollbackVersion).Should(Equal("revision-rollback0")) @@ -515,7 +515,7 @@ var _ = Describe("Test workflow usecase functions", func() { It("Test resetRevisionsAndRecords function", func() { ctx := context.TODO() - err := workflowUsecase.ds.Add(ctx, &model.WorkflowRecord{ + err := workflowService.Store.Add(ctx, &model.WorkflowRecord{ AppPrimaryKey: "reset-app", WorkflowName: "reset-workflow", Name: "reset-record", @@ -531,7 +531,7 @@ var _ = Describe("Test workflow usecase functions", func() { }) Expect(err).Should(BeNil()) - err = resetRevisionsAndRecords(ctx, workflowUsecase.ds, "reset-app", "reset-workflow", "", "") + err = resetRevisionsAndRecords(ctx, workflowService.Store, "reset-app", "reset-workflow", "", "") Expect(err).Should(BeNil()) record := &model.WorkflowRecord{ @@ -539,7 +539,7 @@ var _ = Describe("Test workflow usecase functions", func() { WorkflowName: "reset-workflow", Name: "reset-record", } - err = workflowUsecase.ds.Get(ctx, record) + err = workflowService.Store.Get(ctx, record) Expect(err).Should(BeNil()) Expect(record.Status).Should(Equal(model.RevisionStatusTerminated)) Expect(record.Finished).Should(Equal("true")) @@ -590,8 +590,8 @@ status: finished: true appRevision: "test-workflow-name-111"` -func (w *workflowUsecaseImpl) createTestApplicationRevision(ctx context.Context, revision *model.ApplicationRevision) error { - if err := w.ds.Add(ctx, revision); err != nil { +func (w *workflowServiceImpl) createTestApplicationRevision(ctx context.Context, revision *model.ApplicationRevision) error { + if err := w.Store.Add(ctx, revision); err != nil { return err } return nil diff --git a/pkg/apiserver/collect/suit_test.go b/pkg/apiserver/event/collect/suit_test.go similarity index 87% rename from pkg/apiserver/collect/suit_test.go rename to pkg/apiserver/event/collect/suit_test.go index 0e1c42c2f..a891908c9 100644 --- a/pkg/apiserver/collect/suit_test.go +++ b/pkg/apiserver/event/collect/suit_test.go @@ -31,10 +31,10 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/envtest" - "github.com/oam-dev/kubevela/pkg/apiserver/clients" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore/kubeapi" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore/mongodb" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/clients" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore/kubeapi" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore/mongodb" "github.com/oam-dev/kubevela/pkg/utils/common" ) @@ -55,7 +55,7 @@ var _ = BeforeSuite(func(done Done) { ControlPlaneStartTimeout: time.Minute * 3, ControlPlaneStopTimeout: time.Minute, UseExistingCluster: pointer.BoolPtr(false), - CRDDirectoryPaths: []string{"../../../charts/vela-core/crds"}, + CRDDirectoryPaths: []string{"../../../../charts/vela-core/crds"}, } By("start kube test env") diff --git a/pkg/apiserver/collect/system_info_collect.go b/pkg/apiserver/event/collect/system_info_collect.go similarity index 88% rename from pkg/apiserver/collect/system_info_collect.go rename to pkg/apiserver/event/collect/system_info_collect.go index 2a2dea6bb..4859c083a 100644 --- a/pkg/apiserver/collect/system_info_collect.go +++ b/pkg/apiserver/event/collect/system_info_collect.go @@ -28,12 +28,12 @@ import ( "github.com/oam-dev/kubevela/apis/types" "github.com/oam-dev/kubevela/pkg/oam" - "github.com/oam-dev/kubevela/pkg/apiserver/clients" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/clients" "github.com/oam-dev/kubevela/pkg/multicluster" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/log" - "github.com/oam-dev/kubevela/pkg/apiserver/model" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/log" "github.com/robfig/cron/v3" "k8s.io/apimachinery/pkg/util/wait" @@ -56,25 +56,22 @@ var waitBackOff = wait.Backoff{ // InfoCalculateCronJob is the cronJob to calculate the system info store in db type InfoCalculateCronJob struct { - ds datastore.DataStore + Store datastore.DataStore `inject:"datastore"` + cron *cron.Cron } -// StartCalculatingInfoCronJob will start the system info calculating job. -func StartCalculatingInfoCronJob(ds datastore.DataStore) { - i := InfoCalculateCronJob{ - ds: ds, - } - - // run calculate job in 0:00 of every day +// Start start the worker +func (i *InfoCalculateCronJob) Start(ctx context.Context, errChan chan error) { i.start(CrontabSpec) + defer i.cron.Stop() + <-ctx.Done() } -func (i InfoCalculateCronJob) start(cronSpec string) { +func (i *InfoCalculateCronJob) start(cronSpec string) { c := cron.New(cron.WithChain( // don't let job panic crash whole api-server process cron.Recover(cron.DefaultLogger), )) - // ignore the entityId and error, the cron spec is defined by hard code, mustn't generate error _, _ = c.AddFunc(cronSpec, func() { @@ -94,14 +91,14 @@ func (i InfoCalculateCronJob) start(cronSpec string) { log.Logger.Errorf("After 5 tries the calculating cronJob failed: %v", err) } }) - + i.cron = c c.Start() } func (i InfoCalculateCronJob) run() error { ctx := context.Background() systemInfo := model.SystemInfo{} - e, err := i.ds.List(ctx, &systemInfo, &datastore.ListOptions{}) + e, err := i.Store.List(ctx, &systemInfo, &datastore.ListOptions{}) if err != nil { return err } @@ -153,7 +150,7 @@ func (i InfoCalculateCronJob) calculateAndUpdate(ctx context.Context, systemInfo } systemInfo.StatisticInfo = statisticInfo - if err := i.ds.Put(ctx, &systemInfo); err != nil { + if err := i.Store.Put(ctx, &systemInfo); err != nil { return err } return nil @@ -168,7 +165,7 @@ func (i InfoCalculateCronJob) calculateAppInfo(ctx context.Context) (int, []stri policyDef := map[string]int{} var app = model.Application{} - entities, err := i.ds.List(ctx, &app, &datastore.ListOptions{}) + entities, err := i.Store.List(ctx, &app, &datastore.ListOptions{}) if err != nil { return 0, nil, nil, nil, nil, err } @@ -181,7 +178,7 @@ func (i InfoCalculateCronJob) calculateAppInfo(ctx context.Context) (int, []stri comp := model.ApplicationComponent{ AppPrimaryKey: appModel.Name, } - comps, err := i.ds.List(ctx, &comp, &datastore.ListOptions{}) + comps, err := i.Store.List(ctx, &comp, &datastore.ListOptions{}) if err != nil { return 0, nil, nil, nil, nil, err } @@ -199,7 +196,7 @@ func (i InfoCalculateCronJob) calculateAppInfo(ctx context.Context) (int, []stri workflow := model.Workflow{ AppPrimaryKey: app.PrimaryKey(), } - workflows, err := i.ds.List(ctx, &workflow, &datastore.ListOptions{}) + workflows, err := i.Store.List(ctx, &workflow, &datastore.ListOptions{}) if err != nil { return 0, nil, nil, nil, nil, err } @@ -216,7 +213,7 @@ func (i InfoCalculateCronJob) calculateAppInfo(ctx context.Context) (int, []stri policy := model.ApplicationPolicy{ AppPrimaryKey: app.PrimaryKey(), } - policies, err := i.ds.List(ctx, &policy, &datastore.ListOptions{}) + policies, err := i.Store.List(ctx, &policy, &datastore.ListOptions{}) if err != nil { return 0, nil, nil, nil, nil, err } diff --git a/pkg/apiserver/collect/system_info_collect_test.go b/pkg/apiserver/event/collect/system_info_collect_test.go similarity index 98% rename from pkg/apiserver/collect/system_info_collect_test.go rename to pkg/apiserver/event/collect/system_info_collect_test.go index 161c65432..bdc584089 100644 --- a/pkg/apiserver/collect/system_info_collect_test.go +++ b/pkg/apiserver/event/collect/system_info_collect_test.go @@ -34,8 +34,8 @@ import ( "github.com/oam-dev/kubevela/apis/core.oam.dev/common" "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/model" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" "github.com/oam-dev/kubevela/pkg/oam" "github.com/oam-dev/kubevela/pkg/oam/util" ) @@ -83,7 +83,7 @@ var _ = Describe("Test calculate cronJob", func() { testProject = "test-cronjob-project" mockDataInDs() i = InfoCalculateCronJob{ - ds: ds, + Store: ds, } systemInfo := model.SystemInfo{InstallID: "test-id", EnableCollection: true} Expect(ds.Add(ctx, &systemInfo)).Should(SatisfyAny(BeNil(), DataExistMatcher{})) diff --git a/pkg/apiserver/event/event.go b/pkg/apiserver/event/event.go new file mode 100644 index 000000000..33173804e --- /dev/null +++ b/pkg/apiserver/event/event.go @@ -0,0 +1,50 @@ +/* +Copyright 2022 The KubeVela Authors. + +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 event + +import ( + "context" + + "github.com/oam-dev/kubevela/pkg/apiserver/config" + "github.com/oam-dev/kubevela/pkg/apiserver/event/collect" + "github.com/oam-dev/kubevela/pkg/apiserver/event/sync" +) + +var workers []Worker + +// Worker handle events through rotation training, listener and crontab. +type Worker interface { + Start(ctx context.Context, errChan chan error) +} + +// InitEvent init all event worker +func InitEvent(cfg config.Config) []interface{} { + workflow := &sync.WorkflowRecordSync{ + Duration: cfg.LeaderConfig.Duration, + } + application := &sync.ApplicationSync{} + collect := &collect.InfoCalculateCronJob{} + workers = append(workers, workflow, application, collect) + return []interface{}{workflow, application, collect} +} + +// StartEventWorker start all event worker +func StartEventWorker(ctx context.Context, errChan chan error) { + for i := range workers { + go workers[i].Start(ctx, errChan) + } +} diff --git a/pkg/apiserver/event/event_test.go b/pkg/apiserver/event/event_test.go new file mode 100644 index 000000000..ec2b2536d --- /dev/null +++ b/pkg/apiserver/event/event_test.go @@ -0,0 +1,30 @@ +/* +Copyright 2022 The KubeVela Authors. + +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 event + +import ( + "testing" + + "gotest.tools/assert" + + "github.com/oam-dev/kubevela/pkg/apiserver/config" +) + +func TestInitEvent(t *testing.T) { + InitEvent(config.Config{}) + assert.Equal(t, len(workers), 3) +} diff --git a/pkg/apiserver/sync/cache.go b/pkg/apiserver/event/sync/cache.go similarity index 96% rename from pkg/apiserver/sync/cache.go rename to pkg/apiserver/event/sync/cache.go index c6bd7e4f5..e6638252e 100644 --- a/pkg/apiserver/sync/cache.go +++ b/pkg/apiserver/event/sync/cache.go @@ -25,8 +25,8 @@ import ( "github.com/sirupsen/logrus" "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/model" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" ) type cached struct { diff --git a/pkg/apiserver/sync/cache_test.go b/pkg/apiserver/event/sync/cache_test.go similarity index 96% rename from pkg/apiserver/sync/cache_test.go rename to pkg/apiserver/event/sync/cache_test.go index 9e1148b07..2e9573632 100644 --- a/pkg/apiserver/sync/cache_test.go +++ b/pkg/apiserver/event/sync/cache_test.go @@ -27,8 +27,8 @@ import ( "github.com/oam-dev/kubevela/apis/core.oam.dev/common" "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/model" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" "github.com/oam-dev/kubevela/pkg/oam/util" ) diff --git a/pkg/apiserver/sync/convert.go b/pkg/apiserver/event/sync/convert.go similarity index 96% rename from pkg/apiserver/sync/convert.go rename to pkg/apiserver/event/sync/convert.go index fd20caad6..cb3de2434 100644 --- a/pkg/apiserver/sync/convert.go +++ b/pkg/apiserver/event/sync/convert.go @@ -23,8 +23,8 @@ import ( "time" "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" - "github.com/oam-dev/kubevela/pkg/apiserver/model" - "github.com/oam-dev/kubevela/pkg/apiserver/sync/convert" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/event/sync/convert" "github.com/oam-dev/kubevela/pkg/oam" "github.com/oam-dev/kubevela/pkg/workflow/step" ) diff --git a/pkg/apiserver/sync/convert/convert.go b/pkg/apiserver/event/sync/convert/convert.go similarity index 98% rename from pkg/apiserver/sync/convert/convert.go rename to pkg/apiserver/event/sync/convert/convert.go index 29e877b63..ad6438211 100644 --- a/pkg/apiserver/sync/convert/convert.go +++ b/pkg/apiserver/event/sync/convert/convert.go @@ -26,7 +26,7 @@ import ( "github.com/oam-dev/kubevela/apis/core.oam.dev/common" "github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha1" "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" - "github.com/oam-dev/kubevela/pkg/apiserver/model" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" "github.com/oam-dev/kubevela/pkg/multicluster" "github.com/oam-dev/kubevela/pkg/workflow/step" ) diff --git a/pkg/apiserver/sync/cr2ux.go b/pkg/apiserver/event/sync/cr2ux.go similarity index 91% rename from pkg/apiserver/sync/cr2ux.go rename to pkg/apiserver/event/sync/cr2ux.go index 14f962a93..fae279c89 100644 --- a/pkg/apiserver/sync/cr2ux.go +++ b/pkg/apiserver/event/sync/cr2ux.go @@ -23,10 +23,10 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/log" - "github.com/oam-dev/kubevela/pkg/apiserver/model" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/usecase" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/service" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/log" "github.com/oam-dev/kubevela/pkg/oam" ) @@ -80,10 +80,10 @@ func (c *CR2UX) getApp(ctx context.Context, name, namespace string) (*model.Appl // CR2UX provides the Add/Update/Delete method type CR2UX struct { - ds datastore.DataStore - cli client.Client - cache sync.Map - usecases map[string]interface{} + ds datastore.DataStore + cli client.Client + cache sync.Map + projectService service.ProjectService } func formatAppComposedName(name, namespace string) string { @@ -108,11 +108,7 @@ func (c *CR2UX) AddOrUpdate(ctx context.Context, targetApp *v1beta1.Application) log.Logger.Errorf("Convert App to data store err %v", err) return err } - pu, ok := c.usecases["project"].(usecase.ProjectUsecase) - if !ok { - log.Logger.Warnf("not provide project usecase instance") - } - if err = StoreProject(ctx, dsApp.AppMeta.Project, ds, pu); err != nil { + if err = StoreProject(ctx, dsApp.AppMeta.Project, ds, c.projectService); err != nil { log.Logger.Errorf("get or create project for sync process err %v", err) return err } diff --git a/pkg/apiserver/sync/cr2ux_test.go b/pkg/apiserver/event/sync/cr2ux_test.go similarity index 98% rename from pkg/apiserver/sync/cr2ux_test.go rename to pkg/apiserver/event/sync/cr2ux_test.go index e2e3fd489..62a27d2b2 100644 --- a/pkg/apiserver/sync/cr2ux_test.go +++ b/pkg/apiserver/event/sync/cr2ux_test.go @@ -27,8 +27,8 @@ import ( corev1 "k8s.io/api/core/v1" "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/model" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" "github.com/oam-dev/kubevela/pkg/oam/util" common2 "github.com/oam-dev/kubevela/pkg/utils/common" ) diff --git a/pkg/apiserver/sync/store.go b/pkg/apiserver/event/sync/store.go similarity index 94% rename from pkg/apiserver/sync/store.go rename to pkg/apiserver/event/sync/store.go index 0f740ed28..fccbe531b 100644 --- a/pkg/apiserver/sync/store.go +++ b/pkg/apiserver/event/sync/store.go @@ -21,16 +21,16 @@ import ( "errors" "strings" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/log" - "github.com/oam-dev/kubevela/pkg/apiserver/model" - v1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/usecase" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/service" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" + v1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/log" "github.com/oam-dev/kubevela/pkg/utils" ) // StoreProject will create project for synced application -func StoreProject(ctx context.Context, name string, ds datastore.DataStore, projectUsecase usecase.ProjectUsecase) error { +func StoreProject(ctx context.Context, name string, ds datastore.DataStore, projectService service.ProjectService) error { err := ds.Get(ctx, &model.Project{Name: name}) if err == nil { // it means the record already exists, don't need to add anything @@ -40,8 +40,8 @@ func StoreProject(ctx context.Context, name string, ds datastore.DataStore, proj // other database error, return it return err } - if projectUsecase != nil { - _, err := projectUsecase.CreateProject(ctx, v1.CreateProjectRequest{ + if projectService != nil { + _, err := projectService.CreateProject(ctx, v1.CreateProjectRequest{ Name: name, Alias: strings.Title(name), Owner: model.DefaultAdminUserName, diff --git a/pkg/apiserver/sync/suit_test.go b/pkg/apiserver/event/sync/suit_test.go similarity index 87% rename from pkg/apiserver/sync/suit_test.go rename to pkg/apiserver/event/sync/suit_test.go index 34f59eaa9..a5af9195e 100644 --- a/pkg/apiserver/sync/suit_test.go +++ b/pkg/apiserver/event/sync/suit_test.go @@ -30,10 +30,10 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/envtest" - "github.com/oam-dev/kubevela/pkg/apiserver/clients" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore/kubeapi" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore/mongodb" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/clients" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore/kubeapi" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore/mongodb" "github.com/oam-dev/kubevela/pkg/utils/common" ) @@ -54,7 +54,7 @@ var _ = BeforeSuite(func(done Done) { ControlPlaneStartTimeout: time.Minute * 3, ControlPlaneStopTimeout: time.Minute, UseExistingCluster: pointer.BoolPtr(false), - CRDDirectoryPaths: []string{"../../../charts/vela-core/crds"}, + CRDDirectoryPaths: []string{"../../../../charts/vela-core/crds"}, } By("start kube test env") diff --git a/pkg/apiserver/sync/testdata/test-app1.yaml b/pkg/apiserver/event/sync/testdata/test-app1.yaml similarity index 100% rename from pkg/apiserver/sync/testdata/test-app1.yaml rename to pkg/apiserver/event/sync/testdata/test-app1.yaml diff --git a/pkg/apiserver/sync/testdata/test-app2.yaml b/pkg/apiserver/event/sync/testdata/test-app2.yaml similarity index 100% rename from pkg/apiserver/sync/testdata/test-app2.yaml rename to pkg/apiserver/event/sync/testdata/test-app2.yaml diff --git a/pkg/apiserver/sync/testdata/test-app3.yaml b/pkg/apiserver/event/sync/testdata/test-app3.yaml similarity index 100% rename from pkg/apiserver/sync/testdata/test-app3.yaml rename to pkg/apiserver/event/sync/testdata/test-app3.yaml diff --git a/pkg/apiserver/sync/worker.go b/pkg/apiserver/event/sync/worker.go similarity index 57% rename from pkg/apiserver/sync/worker.go rename to pkg/apiserver/event/sync/worker.go index 295e9aefc..b439dc44f 100644 --- a/pkg/apiserver/sync/worker.go +++ b/pkg/apiserver/event/sync/worker.go @@ -22,57 +22,55 @@ import ( "sync" "github.com/fatih/color" - "github.com/sirupsen/logrus" v1 "k8s.io/api/core/v1" "k8s.io/client-go/dynamic" - "k8s.io/client-go/dynamic/dynamicinformer" + dynamicInformer "k8s.io/client-go/dynamic/dynamicinformer" "k8s.io/client-go/rest" "k8s.io/client-go/tools/cache" "k8s.io/klog/v2" "sigs.k8s.io/controller-runtime/pkg/client" "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" - "github.com/oam-dev/kubevela/pkg/apiserver/clients" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/service" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/log" ) -// Start prepares watchers and run their controllers, then waits for process termination signals -func Start(ctx context.Context, ds datastore.DataStore, cfg *rest.Config, usecases map[string]interface{}) { - k8sClient, err := clients.GetKubeClient() - if err != nil { - logrus.Fatal(err) - } - - dynamicClient, err := dynamic.NewForConfig(cfg) - if err != nil { - logrus.Fatal(err) - } - - f := dynamicinformer.NewFilteredDynamicSharedInformerFactory(dynamicClient, 0, v1.NamespaceAll, nil) - - startAppSyncing(ctx, f, ds, k8sClient, usecases) +// ApplicationSync sync application from cluster to database +type ApplicationSync struct { + KubeClient client.Client `inject:"kubeClient"` + KubeConfig *rest.Config `inject:"kubeConfig"` + Store datastore.DataStore `inject:"datastore"` + ProjectService service.ProjectService `inject:""` } -func startAppSyncing(ctx context.Context, factory dynamicinformer.DynamicSharedInformerFactory, ds datastore.DataStore, cli client.Client, usecases map[string]interface{}) { - var err error +// Start prepares watchers and run their controllers, then waits for process termination signals +func (a *ApplicationSync) Start(ctx context.Context, errorChan chan error) { + dynamicClient, err := dynamic.NewForConfig(a.KubeConfig) + if err != nil { + errorChan <- err + } + + factory := dynamicInformer.NewFilteredDynamicSharedInformerFactory(dynamicClient, 0, v1.NamespaceAll, nil) informer := factory.ForResource(v1beta1.SchemeGroupVersion.WithResource("applications")).Informer() getApp := func(obj interface{}) *v1beta1.Application { app := &v1beta1.Application{} - bs, _ := json.Marshal(obj) + bs, err := json.Marshal(obj) + if err != nil { + log.Logger.Errorf("decode the application failure %s", err.Error()) + return app + } _ = json.Unmarshal(bs, app) return app } - if usecases == nil { - usecases = make(map[string]interface{}) - } cu := &CR2UX{ - ds: ds, - cli: cli, - cache: sync.Map{}, - usecases: usecases, + ds: a.Store, + cli: a.KubeClient, + cache: sync.Map{}, + projectService: a.ProjectService, } if err = cu.initCache(ctx); err != nil { - klog.Fatal("sync app init err", err) + errorChan <- err } handlers := cache.ResourceEventHandlerFuncs{ @@ -81,7 +79,7 @@ func startAppSyncing(ctx context.Context, factory dynamicinformer.DynamicSharedI klog.Infof("watched add app event, namespace: %s, name: %s", app.Namespace, app.Name) err = cu.AddOrUpdate(ctx, app) if err != nil { - logrus.Errorf("Application %-30s Create Sync to db err %v", color.WhiteString(app.Namespace+"/"+app.Name), err) + log.Logger.Errorf("Application %-30s Create Sync to db err %v", color.WhiteString(app.Namespace+"/"+app.Name), err) } }, UpdateFunc: func(oldObj, obj interface{}) { @@ -89,7 +87,7 @@ func startAppSyncing(ctx context.Context, factory dynamicinformer.DynamicSharedI klog.Infof("watched update app event, namespace: %s, name: %s", app.Namespace, app.Name) err = cu.AddOrUpdate(ctx, app) if err != nil { - klog.Errorf("Application %-30s Update Sync to db err %v", color.WhiteString(app.Namespace+"/"+app.Name), err) + log.Logger.Errorf("Application %-30s Update Sync to db err %v", color.WhiteString(app.Namespace+"/"+app.Name), err) } }, DeleteFunc: func(obj interface{}) { @@ -97,11 +95,11 @@ func startAppSyncing(ctx context.Context, factory dynamicinformer.DynamicSharedI klog.Infof("watched delete app event, namespace: %s, name: %s", app.Namespace, app.Name) err = cu.DeleteApp(ctx, app) if err != nil { - klog.Errorf("Application %-30s Deleted Sync to db err %v", color.WhiteString(app.Namespace+"/"+app.Name), err) + log.Logger.Errorf("Application %-30s Deleted Sync to db err %v", color.WhiteString(app.Namespace+"/"+app.Name), err) } }, } informer.AddEventHandler(handlers) - klog.Info("app syncing started") + log.Logger.Info("app syncing started") informer.Run(ctx.Done()) } diff --git a/pkg/apiserver/sync/worker_test.go b/pkg/apiserver/event/sync/worker_test.go similarity index 94% rename from pkg/apiserver/sync/worker_test.go rename to pkg/apiserver/event/sync/worker_test.go index 8be05cf79..f5df64f59 100644 --- a/pkg/apiserver/sync/worker_test.go +++ b/pkg/apiserver/event/sync/worker_test.go @@ -26,8 +26,8 @@ import ( corev1 "k8s.io/api/core/v1" "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/model" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" "github.com/oam-dev/kubevela/pkg/oam/util" common2 "github.com/oam-dev/kubevela/pkg/utils/common" ) @@ -61,7 +61,13 @@ var _ = Describe("Test Worker CR sync to datastore", func() { By("Start syncing") ctx, cancel := context.WithCancel(context.Background()) defer cancel() - go Start(ctx, ds, cfg, nil) + + appSync := &ApplicationSync{ + KubeClient: k8sClient, + KubeConfig: cfg, + Store: ds, + } + go appSync.Start(ctx, make(chan error)) By("create test app1 and check the syncing results") app1 := &v1beta1.Application{} diff --git a/pkg/apiserver/event/sync/workflow_record.go b/pkg/apiserver/event/sync/workflow_record.go new file mode 100644 index 000000000..b50a4f5b2 --- /dev/null +++ b/pkg/apiserver/event/sync/workflow_record.go @@ -0,0 +1,49 @@ +/* +Copyright 2022 The KubeVela Authors. + +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 sync + +import ( + "context" + "time" + + "github.com/oam-dev/kubevela/pkg/apiserver/domain/service" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/log" +) + +// WorkflowRecordSync sync workflow record from cluster to database +type WorkflowRecordSync struct { + Duration time.Duration + WorkflowService service.WorkflowService `inject:""` +} + +// Start sync workflow record data +func (w *WorkflowRecordSync) Start(ctx context.Context, errorChan chan error) { + log.Logger.Infof("workflow record syncing worker started") + defer log.Logger.Infof("workflow record syncing worker closed") + t := time.NewTicker(w.Duration) + defer t.Stop() + for { + select { + case <-t.C: + if err := w.WorkflowService.SyncWorkflowRecord(ctx); err != nil { + log.Logger.Errorf("syncWorkflowRecordError: %s", err.Error()) + } + case <-ctx.Done(): + return + } + } +} diff --git a/pkg/apiserver/clients/kubeclient.go b/pkg/apiserver/infrastructure/clients/kubeclient.go similarity index 100% rename from pkg/apiserver/clients/kubeclient.go rename to pkg/apiserver/infrastructure/clients/kubeclient.go diff --git a/pkg/apiserver/datastore/datastore.go b/pkg/apiserver/infrastructure/datastore/datastore.go similarity index 100% rename from pkg/apiserver/datastore/datastore.go rename to pkg/apiserver/infrastructure/datastore/datastore.go diff --git a/pkg/apiserver/datastore/datastore_suite_test.go b/pkg/apiserver/infrastructure/datastore/datastore_suite_test.go similarity index 100% rename from pkg/apiserver/datastore/datastore_suite_test.go rename to pkg/apiserver/infrastructure/datastore/datastore_suite_test.go diff --git a/pkg/apiserver/datastore/datastore_test.go b/pkg/apiserver/infrastructure/datastore/datastore_test.go similarity index 96% rename from pkg/apiserver/datastore/datastore_test.go rename to pkg/apiserver/infrastructure/datastore/datastore_test.go index 26feeff76..09fd65a55 100644 --- a/pkg/apiserver/datastore/datastore_test.go +++ b/pkg/apiserver/infrastructure/datastore/datastore_test.go @@ -24,7 +24,7 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/oam-dev/kubevela/pkg/apiserver/model" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" ) var _ = Describe("Test new entity function", func() { diff --git a/pkg/apiserver/datastore/kubeapi/kubeapi.go b/pkg/apiserver/infrastructure/datastore/kubeapi/kubeapi.go similarity index 94% rename from pkg/apiserver/datastore/kubeapi/kubeapi.go rename to pkg/apiserver/infrastructure/datastore/kubeapi/kubeapi.go index 4bea92d30..60f0260f7 100644 --- a/pkg/apiserver/datastore/kubeapi/kubeapi.go +++ b/pkg/apiserver/infrastructure/datastore/kubeapi/kubeapi.go @@ -34,13 +34,13 @@ import ( "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" - "github.com/oam-dev/kubevela/pkg/apiserver/clients" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/log" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/clients" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/log" ) type kubeapi struct { - kubeclient client.Client + kubeClient client.Client namespace string } @@ -66,7 +66,7 @@ func New(ctx context.Context, cfg datastore.Config) (datastore.DataStore, error) } migrate(cfg.Database) return &kubeapi{ - kubeclient: kubeClient, + kubeClient: kubeClient, namespace: cfg.Database, }, nil } @@ -110,7 +110,7 @@ func (m *kubeapi) Add(ctx context.Context, entity datastore.Entity) error { entity.SetCreateTime(time.Now()) entity.SetUpdateTime(time.Now()) configMap := m.generateConfigMap(entity) - if err := m.kubeclient.Create(ctx, configMap); err != nil { + if err := m.kubeClient.Create(ctx, configMap); err != nil { if apierrors.IsAlreadyExists(err) { return datastore.ErrRecordExist } @@ -151,7 +151,7 @@ func (m *kubeapi) Get(ctx context.Context, entity datastore.Entity) error { return datastore.ErrTableNameEmpty } var configMap corev1.ConfigMap - if err := m.kubeclient.Get(ctx, types.NamespacedName{Namespace: m.namespace, Name: generateName(entity)}, &configMap); err != nil { + if err := m.kubeClient.Get(ctx, types.NamespacedName{Namespace: m.namespace, Name: generateName(entity)}, &configMap); err != nil { if apierrors.IsNotFound(err) { return datastore.ErrRecordNotExist } @@ -180,7 +180,7 @@ func (m *kubeapi) Put(ctx context.Context, entity datastore.Entity) error { labels["primaryKey"] = entity.PrimaryKey() entity.SetUpdateTime(time.Now()) var configMap corev1.ConfigMap - if err := m.kubeclient.Get(ctx, types.NamespacedName{Namespace: m.namespace, Name: generateName(entity)}, &configMap); err != nil { + if err := m.kubeClient.Get(ctx, types.NamespacedName{Namespace: m.namespace, Name: generateName(entity)}, &configMap); err != nil { if apierrors.IsNotFound(err) { return datastore.ErrRecordNotExist } @@ -192,7 +192,7 @@ func (m *kubeapi) Put(ctx context.Context, entity datastore.Entity) error { } configMap.BinaryData["data"] = data configMap.Labels = labels - if err := m.kubeclient.Update(ctx, &configMap); err != nil { + if err := m.kubeClient.Update(ctx, &configMap); err != nil { return datastore.NewDBError(err) } return nil @@ -207,7 +207,7 @@ func (m *kubeapi) IsExist(ctx context.Context, entity datastore.Entity) (bool, e return false, datastore.ErrTableNameEmpty } var configMap corev1.ConfigMap - if err := m.kubeclient.Get(ctx, types.NamespacedName{Namespace: m.namespace, Name: generateName(entity)}, &configMap); err != nil { + if err := m.kubeClient.Get(ctx, types.NamespacedName{Namespace: m.namespace, Name: generateName(entity)}, &configMap); err != nil { if apierrors.IsNotFound(err) { return false, nil } @@ -224,7 +224,7 @@ func (m *kubeapi) Delete(ctx context.Context, entity datastore.Entity) error { if entity.TableName() == "" { return datastore.ErrTableNameEmpty } - if err := m.kubeclient.Delete(ctx, m.generateConfigMap(entity)); err != nil { + if err := m.kubeClient.Delete(ctx, m.generateConfigMap(entity)); err != nil { if apierrors.IsNotFound(err) { return datastore.ErrRecordNotExist } @@ -382,7 +382,7 @@ func (m *kubeapi) List(ctx context.Context, entity datastore.Entity, op *datasto } } var configMaps corev1.ConfigMapList - if err := m.kubeclient.List(ctx, &configMaps, options); err != nil { + if err := m.kubeClient.List(ctx, &configMaps, options); err != nil { if apierrors.IsNotFound(err) { return nil, nil } @@ -461,7 +461,7 @@ func (m *kubeapi) Count(ctx context.Context, entity datastore.Entity, filterOpti } var configMaps corev1.ConfigMapList - if err := m.kubeclient.List(ctx, &configMaps, options); err != nil { + if err := m.kubeClient.List(ctx, &configMaps, options); err != nil { if apierrors.IsNotFound(err) { return 0, nil } diff --git a/pkg/apiserver/infrastructure/datastore/kubeapi/kubeapi_suite_test.go b/pkg/apiserver/infrastructure/datastore/kubeapi/kubeapi_suite_test.go new file mode 100644 index 000000000..32407fe70 --- /dev/null +++ b/pkg/apiserver/infrastructure/datastore/kubeapi/kubeapi_suite_test.go @@ -0,0 +1,86 @@ +/* +Copyright 2021 The KubeVela Authors. + +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 kubeapi + +import ( + "context" + "math/rand" + "testing" + "time" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "k8s.io/utils/pointer" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" + + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/clients" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" +) + +func TestKubeapi(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Kubeapi Suite") +} + +var cfg *rest.Config +var k8sClient client.Client +var testEnv *envtest.Environment +var testScheme = runtime.NewScheme() +var kubeStore datastore.DataStore + +var _ = BeforeSuite(func(done Done) { + rand.Seed(time.Now().UnixNano()) + By("bootstrapping test environment") + + testEnv = &envtest.Environment{ + ControlPlaneStartTimeout: time.Minute * 3, + ControlPlaneStopTimeout: time.Minute, + UseExistingCluster: pointer.BoolPtr(false), + } + + By("start kube test env") + var err error + cfg, err = testEnv.Start() + Expect(err).ShouldNot(HaveOccurred()) + Expect(cfg).ToNot(BeNil()) + + err = scheme.AddToScheme(testScheme) + Expect(err).NotTo(HaveOccurred()) + + By("new kube client") + cfg.Timeout = time.Minute * 2 + k8sClient, err = client.New(cfg, client.Options{Scheme: testScheme}) + Expect(err).Should(BeNil()) + Expect(k8sClient).ToNot(BeNil()) + By("new kube client success") + + clients.SetKubeClient(k8sClient) + kubeStore, err = New(context.TODO(), datastore.Config{Database: "test"}) + Expect(err).Should(BeNil()) + Expect(kubeStore).ToNot(BeNil()) + close(done) +}, 240) + +var _ = AfterSuite(func() { + By("tearing down the test environment") + err := testEnv.Stop() + Expect(err).ToNot(HaveOccurred()) +}) diff --git a/pkg/apiserver/datastore/kubeapi/kubeapi_test.go b/pkg/apiserver/infrastructure/datastore/kubeapi/kubeapi_test.go similarity index 82% rename from pkg/apiserver/datastore/kubeapi/kubeapi_test.go rename to pkg/apiserver/infrastructure/datastore/kubeapi/kubeapi_test.go index 07c82ee27..29df5e6dd 100644 --- a/pkg/apiserver/datastore/kubeapi/kubeapi_test.go +++ b/pkg/apiserver/infrastructure/datastore/kubeapi/kubeapi_test.go @@ -19,7 +19,6 @@ package kubeapi import ( "context" "fmt" - "math/rand" "strings" "time" @@ -28,84 +27,33 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/selection" - "k8s.io/client-go/kubernetes/scheme" - "k8s.io/client-go/rest" - "k8s.io/utils/pointer" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/envtest" - "github.com/oam-dev/kubevela/pkg/apiserver/clients" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/model" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" ) -var cfg *rest.Config -var k8sClient client.Client -var testEnv *envtest.Environment -var testScheme = runtime.NewScheme() - -var _ = BeforeSuite(func(done Done) { - rand.Seed(time.Now().UnixNano()) - By("bootstrapping test environment") - - testEnv = &envtest.Environment{ - ControlPlaneStartTimeout: time.Minute * 3, - ControlPlaneStopTimeout: time.Minute, - UseExistingCluster: pointer.BoolPtr(false), - } - - By("start kube test env") - var err error - cfg, err = testEnv.Start() - Expect(err).ShouldNot(HaveOccurred()) - Expect(cfg).ToNot(BeNil()) - - err = scheme.AddToScheme(testScheme) - Expect(err).NotTo(HaveOccurred()) - - By("new kube client") - cfg.Timeout = time.Minute * 2 - k8sClient, err = client.New(cfg, client.Options{Scheme: testScheme}) - Expect(err).Should(BeNil()) - Expect(k8sClient).ToNot(BeNil()) - By("new kube client success") - close(done) -}, 240) - -var _ = AfterSuite(func() { - By("tearing down the test environment") - err := testEnv.Stop() - Expect(err).ToNot(HaveOccurred()) -}) - var _ = Describe("Test kubeapi datastore driver", func() { - clients.SetKubeClient(k8sClient) - kubeStore, err := New(context.TODO(), datastore.Config{Database: "test"}) - Expect(err).Should(BeNil()) - Expect(kubeStore).ToNot(BeNil()) - It("Test add function", func() { err := kubeStore.Add(context.TODO(), &model.Application{Name: "kubevela-app", Description: "default"}) Expect(err).ToNot(HaveOccurred()) }) It("Test batch add function", func() { - var datas = []datastore.Entity{ + var entities = []datastore.Entity{ &model.Application{Name: "kubevela-app-2", Description: "this is demo 2"}, &model.Application{Name: "kubevela-app-3", Description: "this is demo 3"}, &model.Application{Name: "kubevela-app-4", Project: "testProject", Description: "this is demo 4"}, } - err := kubeStore.BatchAdd(context.TODO(), datas) + err := kubeStore.BatchAdd(context.TODO(), entities) Expect(err).ToNot(HaveOccurred()) - var datas2 = []datastore.Entity{ + var entities2 = []datastore.Entity{ &model.Application{Name: "can-delete", Description: "this is demo can-delete"}, &model.Application{Name: "kubevela-app-2", Description: "this is demo 2"}, } - err = kubeStore.BatchAdd(context.TODO(), datas2) + err = kubeStore.BatchAdd(context.TODO(), entities2) equal := cmp.Diff(strings.Contains(err.Error(), "save entities occur error"), true) Expect(equal).To(BeEmpty()) }) @@ -269,9 +217,9 @@ var _ = Describe("Test kubeapi datastore driver", func() { Expect(diff).Should(BeEmpty()) app.Name = "kubevela-app-5" - notexist, err := kubeStore.IsExist(context.TODO(), &app) + notExist, err := kubeStore.IsExist(context.TODO(), &app) Expect(err).ShouldNot(HaveOccurred()) - diff = cmp.Diff(notexist, false) + diff = cmp.Diff(notExist, false) Expect(diff).Should(BeEmpty()) }) diff --git a/pkg/apiserver/datastore/kubeapi/migrate.go b/pkg/apiserver/infrastructure/datastore/kubeapi/migrate.go similarity index 95% rename from pkg/apiserver/datastore/kubeapi/migrate.go rename to pkg/apiserver/infrastructure/datastore/kubeapi/migrate.go index 9243c26b4..f058ef3f1 100644 --- a/pkg/apiserver/datastore/kubeapi/migrate.go +++ b/pkg/apiserver/infrastructure/datastore/kubeapi/migrate.go @@ -26,8 +26,8 @@ import ( "k8s.io/klog" "sigs.k8s.io/controller-runtime/pkg/client" - "github.com/oam-dev/kubevela/pkg/apiserver/clients" - "github.com/oam-dev/kubevela/pkg/apiserver/model" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/clients" ) // MigrateKey marks the label key of the migrated data diff --git a/pkg/apiserver/datastore/kubeapi/migrate_test.go b/pkg/apiserver/infrastructure/datastore/kubeapi/migrate_test.go similarity index 86% rename from pkg/apiserver/datastore/kubeapi/migrate_test.go rename to pkg/apiserver/infrastructure/datastore/kubeapi/migrate_test.go index 628b474a7..302af0322 100644 --- a/pkg/apiserver/datastore/kubeapi/migrate_test.go +++ b/pkg/apiserver/infrastructure/datastore/kubeapi/migrate_test.go @@ -26,8 +26,8 @@ import ( v1 "k8s.io/api/core/v1" "sigs.k8s.io/controller-runtime/pkg/client" - "github.com/oam-dev/kubevela/pkg/apiserver/clients" - "github.com/oam-dev/kubevela/pkg/apiserver/model" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/clients" ) var _ = Describe("Test Migrate", func() { @@ -36,7 +36,7 @@ var _ = Describe("Test Migrate", func() { clients.SetKubeClient(k8sClient) nsName := "test-migrate" - ds := &kubeapi{kubeclient: k8sClient, namespace: nsName} + ds := &kubeapi{kubeClient: k8sClient, namespace: nsName} ns := &v1.Namespace{} ns.Name = nsName Expect(k8sClient.Create(context.Background(), ns)).Should(BeNil()) @@ -45,7 +45,7 @@ var _ = Describe("Test Migrate", func() { name := fmt.Sprintf("veladatabase-%s-%s", entity.TableName(), entity.PrimaryKey()) cm.Name = strings.ReplaceAll(name, "_", "-") cm.Namespace = nsName - Expect(ds.kubeclient.Create(context.Background(), cm)).Should(BeNil()) + Expect(ds.kubeClient.Create(context.Background(), cm)).Should(BeNil()) migrate(nsName) cmList := v1.ConfigMapList{} diff --git a/pkg/apiserver/datastore/mongodb/iterator.go b/pkg/apiserver/infrastructure/datastore/mongodb/iterator.go similarity index 100% rename from pkg/apiserver/datastore/mongodb/iterator.go rename to pkg/apiserver/infrastructure/datastore/mongodb/iterator.go diff --git a/pkg/apiserver/datastore/mongodb/mongodb.go b/pkg/apiserver/infrastructure/datastore/mongodb/mongodb.go similarity index 98% rename from pkg/apiserver/datastore/mongodb/mongodb.go rename to pkg/apiserver/infrastructure/datastore/mongodb/mongodb.go index 28f5d2e59..705e1dedd 100644 --- a/pkg/apiserver/datastore/mongodb/mongodb.go +++ b/pkg/apiserver/infrastructure/datastore/mongodb/mongodb.go @@ -28,8 +28,8 @@ import ( "go.mongodb.org/mongo-driver/mongo/options" "go.mongodb.org/mongo-driver/x/bsonx" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/log" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/log" ) type mongodb struct { diff --git a/pkg/apiserver/datastore/mongodb/mongodb_suite_test.go b/pkg/apiserver/infrastructure/datastore/mongodb/mongodb_suite_test.go similarity index 92% rename from pkg/apiserver/datastore/mongodb/mongodb_suite_test.go rename to pkg/apiserver/infrastructure/datastore/mongodb/mongodb_suite_test.go index afa23b62e..9d77fd2ce 100644 --- a/pkg/apiserver/datastore/mongodb/mongodb_suite_test.go +++ b/pkg/apiserver/infrastructure/datastore/mongodb/mongodb_suite_test.go @@ -23,7 +23,7 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" ) func TestMongodb(t *testing.T) { diff --git a/pkg/apiserver/datastore/mongodb/mongodb_test.go b/pkg/apiserver/infrastructure/datastore/mongodb/mongodb_test.go similarity index 98% rename from pkg/apiserver/datastore/mongodb/mongodb_test.go rename to pkg/apiserver/infrastructure/datastore/mongodb/mongodb_test.go index 630287076..93b97933d 100644 --- a/pkg/apiserver/datastore/mongodb/mongodb_test.go +++ b/pkg/apiserver/infrastructure/datastore/mongodb/mongodb_test.go @@ -29,8 +29,8 @@ import ( "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/model" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" ) var mongodbDriver datastore.DataStore diff --git a/pkg/apiserver/rest/webservice/addon.go b/pkg/apiserver/interfaces/api/addon.go similarity index 73% rename from pkg/apiserver/rest/webservice/addon.go rename to pkg/apiserver/interfaces/api/addon.go index 806d57c8e..524dc77c5 100644 --- a/pkg/apiserver/rest/webservice/addon.go +++ b/pkg/apiserver/interfaces/api/addon.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package webservice +package api import ( "strconv" @@ -23,35 +23,28 @@ import ( "github.com/emicklei/go-restful/v3" "github.com/oam-dev/kubevela/apis/types" - apis "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/usecase" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/service" + apis "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" ) -// NewAddonWebService returns addon web service -func NewAddonWebService(u usecase.AddonHandler, rbacUsecase usecase.RBACUsecase, cluster usecase.ClusterUsecase) WebService { - return &addonWebService{ - handler: u, - rbacUsecase: rbacUsecase, - clusterHandler: cluster, - } +// NewAddonAPIInterface returns addon web service +func NewAddonAPIInterface() Interface { + return &addonAPIInterface{} } -// NewEnabledAddonWebService returns enabled addon web service -func NewEnabledAddonWebService(u usecase.AddonHandler, rbacUsecase usecase.RBACUsecase) WebService { - return &enabledAddonWebService{ - addonUsecase: u, - rbacUsecase: rbacUsecase, - } +// NewEnabledAddonAPIInterface returns enabled addon web service +func NewEnabledAddonAPIInterface() Interface { + return &enabledAddonAPIInterface{} } -type addonWebService struct { - rbacUsecase usecase.RBACUsecase - handler usecase.AddonHandler - clusterHandler usecase.ClusterUsecase +type addonAPIInterface struct { + RbacService service.RBACService `inject:""` + AddonService service.AddonService `inject:""` + ClusterService service.ClusterService `inject:""` } -func (s *addonWebService) GetWebService() *restful.WebService { +func (s *addonAPIInterface) GetWebServiceRoute() *restful.WebService { ws := new(restful.WebService) ws.Path(versionPrefix+"/addons"). Consumes(restful.MIME_XML, restful.MIME_JSON). @@ -64,7 +57,7 @@ func (s *addonWebService) GetWebService() *restful.WebService { ws.Route(ws.GET("/").To(s.listAddons). Doc("list all addons"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(s.rbacUsecase.CheckPerm("addon", "list")). + Filter(s.RbacService.CheckPerm("addon", "list")). Param(ws.QueryParameter("registry", "filter addons from given registry").DataType("string")). Param(ws.QueryParameter("query", "Fuzzy search based on name and description.").DataType("string")). Returns(200, "OK", apis.ListAddonResponse{}). @@ -76,7 +69,7 @@ func (s *addonWebService) GetWebService() *restful.WebService { Doc("show details of an addon"). Metadata(restfulspec.KeyOpenAPITags, tags). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(s.rbacUsecase.CheckPerm("addon", "detail")). + Filter(s.RbacService.CheckPerm("addon", "detail")). Returns(200, "OK", apis.DetailAddonResponse{}). Returns(400, "Bad Request", bcode.Bcode{}). Param(ws.PathParameter("name", "addon name to query detail").DataType("string").Required(true)). @@ -89,7 +82,7 @@ func (s *addonWebService) GetWebService() *restful.WebService { ws.Route(ws.GET("/{addonName}/status").To(s.statusAddon). Doc("show status of an addon"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(s.rbacUsecase.CheckPerm("addon", "detail")). + Filter(s.RbacService.CheckPerm("addon", "detail")). Returns(200, "OK", apis.AddonStatusResponse{}). Returns(400, "Bad Request", bcode.Bcode{}). Param(ws.PathParameter("addonName", "addon name to query status").DataType("string").Required(true)). @@ -100,7 +93,7 @@ func (s *addonWebService) GetWebService() *restful.WebService { Doc("enable an addon"). Metadata(restfulspec.KeyOpenAPITags, tags). Reads(apis.EnableAddonRequest{}). - Filter(s.rbacUsecase.CheckPerm("addon", "enable")). + Filter(s.RbacService.CheckPerm("addon", "enable")). Returns(200, "OK", apis.AddonStatusResponse{}). Returns(400, "Bad Request", bcode.Bcode{}). Param(ws.PathParameter("addonName", "addon name to enable").DataType("string").Required(true)). @@ -111,7 +104,7 @@ func (s *addonWebService) GetWebService() *restful.WebService { Doc("disable an addon"). Metadata(restfulspec.KeyOpenAPITags, tags). Returns(200, "OK", apis.AddonStatusResponse{}). - Filter(s.rbacUsecase.CheckPerm("addon", "disable")). + Filter(s.RbacService.CheckPerm("addon", "disable")). Returns(400, "Bad Request", bcode.Bcode{}). Param(ws.PathParameter("addonName", "addon name to enable").DataType("string").Required(true)). Param(ws.QueryParameter("force", "force disable an addon").DataType("boolean").Required(false)). @@ -123,7 +116,7 @@ func (s *addonWebService) GetWebService() *restful.WebService { Metadata(restfulspec.KeyOpenAPITags, tags). Reads(apis.EnableAddonRequest{}). Returns(200, "OK", apis.AddonStatusResponse{}). - Filter(s.rbacUsecase.CheckPerm("addon", "update")). + Filter(s.RbacService.CheckPerm("addon", "update")). Returns(400, "Bad Request", bcode.Bcode{}). Param(ws.PathParameter("addonName", "addon name to update").DataType("string").Required(true)). Writes(apis.AddonStatusResponse{})) @@ -132,8 +125,8 @@ func (s *addonWebService) GetWebService() *restful.WebService { return ws } -func (s *addonWebService) listAddons(req *restful.Request, res *restful.Response) { - detailAddons, err := s.handler.ListAddons(req.Request.Context(), req.QueryParameter("registry"), req.QueryParameter("query")) +func (s *addonAPIInterface) listAddons(req *restful.Request, res *restful.Response) { + detailAddons, err := s.AddonService.ListAddons(req.Request.Context(), req.QueryParameter("registry"), req.QueryParameter("query")) if len(detailAddons) == 0 && err != nil { bcode.ReturnError(req, res, err) return @@ -156,9 +149,9 @@ func (s *addonWebService) listAddons(req *restful.Request, res *restful.Response } } -func (s *addonWebService) detailAddon(req *restful.Request, res *restful.Response) { +func (s *addonAPIInterface) detailAddon(req *restful.Request, res *restful.Response) { name := req.PathParameter("addonName") - addon, err := s.handler.GetAddon(req.Request.Context(), name, req.QueryParameter("registry"), req.QueryParameter("version")) + addon, err := s.AddonService.GetAddon(req.Request.Context(), name, req.QueryParameter("registry"), req.QueryParameter("version")) if err != nil { bcode.ReturnError(req, res, err) return @@ -172,7 +165,7 @@ func (s *addonWebService) detailAddon(req *restful.Request, res *restful.Respons } -func (s *addonWebService) enableAddon(req *restful.Request, res *restful.Response) { +func (s *addonAPIInterface) enableAddon(req *restful.Request, res *restful.Response) { var createReq apis.EnableAddonRequest var args []byte _, err := req.Request.Body.Read(args) @@ -195,7 +188,7 @@ func (s *addonWebService) enableAddon(req *restful.Request, res *restful.Respons } name := req.PathParameter("addonName") - err = s.handler.EnableAddon(req.Request.Context(), name, createReq) + err = s.AddonService.EnableAddon(req.Request.Context(), name, createReq) if err != nil { bcode.ReturnError(req, res, err) return @@ -204,11 +197,11 @@ func (s *addonWebService) enableAddon(req *restful.Request, res *restful.Respons s.statusAddon(req, res) } -func (s *addonWebService) disableAddon(req *restful.Request, res *restful.Response) { +func (s *addonAPIInterface) disableAddon(req *restful.Request, res *restful.Response) { name := req.PathParameter("addonName") forceParam := req.QueryParameter("force") force, _ := strconv.ParseBool(forceParam) - err := s.handler.DisableAddon(req.Request.Context(), name, force) + err := s.AddonService.DisableAddon(req.Request.Context(), name, force) if err != nil { bcode.ReturnError(req, res, err) return @@ -216,14 +209,14 @@ func (s *addonWebService) disableAddon(req *restful.Request, res *restful.Respon s.statusAddon(req, res) } -func (s *addonWebService) statusAddon(req *restful.Request, res *restful.Response) { +func (s *addonAPIInterface) statusAddon(req *restful.Request, res *restful.Response) { name := req.PathParameter("addonName") - status, err := s.handler.StatusAddon(req.Request.Context(), name) + status, err := s.AddonService.StatusAddon(req.Request.Context(), name) if err != nil { bcode.ReturnError(req, res, err) return } - clusters, err := s.clusterHandler.ListKubeClusters(req.Request.Context(), "", 0, 0) + clusters, err := s.ClusterService.ListKubeClusters(req.Request.Context(), "", 0, 0) if err == nil { // align the alias here for _, c := range clusters.Clusters { @@ -243,7 +236,7 @@ func (s *addonWebService) statusAddon(req *restful.Request, res *restful.Respons } } -func (s *addonWebService) updateAddon(req *restful.Request, res *restful.Response) { +func (s *addonAPIInterface) updateAddon(req *restful.Request, res *restful.Response) { var createReq apis.EnableAddonRequest var args []byte _, err := req.Request.Body.Read(args) @@ -266,7 +259,7 @@ func (s *addonWebService) updateAddon(req *restful.Request, res *restful.Respons } name := req.PathParameter("addonName") - err = s.handler.UpdateAddon(req.Request.Context(), name, createReq) + err = s.AddonService.UpdateAddon(req.Request.Context(), name, createReq) if err != nil { bcode.ReturnError(req, res, err) return @@ -275,12 +268,12 @@ func (s *addonWebService) updateAddon(req *restful.Request, res *restful.Respons s.statusAddon(req, res) } -type enabledAddonWebService struct { - addonUsecase usecase.AddonHandler - rbacUsecase usecase.RBACUsecase +type enabledAddonAPIInterface struct { + AddonService service.AddonService `inject:""` + RbacService service.RBACService `inject:""` } -func (s *enabledAddonWebService) GetWebService() *restful.WebService { +func (s *enabledAddonAPIInterface) GetWebServiceRoute() *restful.WebService { ws := new(restful.WebService) ws.Path(versionPrefix+"/enabled_addon"). Consumes(restful.MIME_XML, restful.MIME_JSON). @@ -293,7 +286,7 @@ func (s *enabledAddonWebService) GetWebService() *restful.WebService { ws.Route(ws.GET("/").To(s.list). Doc("list all enabled addons"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(s.rbacUsecase.CheckPerm("addon", "list")). + Filter(s.RbacService.CheckPerm("addon", "list")). Param(ws.QueryParameter("registry", "filter addons from given registry").DataType("string")). Param(ws.QueryParameter("query", "Fuzzy search based on name and description.").DataType("string")). Returns(200, "OK", apis.ListEnabledAddonResponse{}). @@ -304,8 +297,8 @@ func (s *enabledAddonWebService) GetWebService() *restful.WebService { return ws } -func (s *enabledAddonWebService) list(req *restful.Request, res *restful.Response) { - enabledAddons, err := s.addonUsecase.ListEnabledAddon(req.Request.Context()) +func (s *enabledAddonAPIInterface) list(req *restful.Request, res *restful.Response) { + enabledAddons, err := s.AddonService.ListEnabledAddon(req.Request.Context()) if err != nil { bcode.ReturnError(req, res, err) return diff --git a/pkg/apiserver/rest/webservice/addon_registry.go b/pkg/apiserver/interfaces/api/addon_registry.go similarity index 70% rename from pkg/apiserver/rest/webservice/addon_registry.go rename to pkg/apiserver/interfaces/api/addon_registry.go index 379b276b4..96de657bb 100644 --- a/pkg/apiserver/rest/webservice/addon_registry.go +++ b/pkg/apiserver/interfaces/api/addon_registry.go @@ -14,31 +14,28 @@ See the License for the specific language governing permissions and limitations under the License. */ -package webservice +package api import ( restfulspec "github.com/emicklei/go-restful-openapi/v2" "github.com/emicklei/go-restful/v3" - apis "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/usecase" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/service" + apis "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" ) -// NewAddonRegistryWebService returns addon registry web service -func NewAddonRegistryWebService(u usecase.AddonHandler, rbacUsecase usecase.RBACUsecase) WebService { - return &addonRegistryWebService{ - addonUsecase: u, - rbacUsecase: rbacUsecase, - } +// NewAddonRegistryAPIInterface returns addon registry web service +func NewAddonRegistryAPIInterface() Interface { + return &addonRegistryAPIInterface{} } -type addonRegistryWebService struct { - addonUsecase usecase.AddonHandler - rbacUsecase usecase.RBACUsecase +type addonRegistryAPIInterface struct { + AddonService service.AddonService `inject:""` + RbacService service.RBACService `inject:""` } -func (s *addonRegistryWebService) GetWebService() *restful.WebService { +func (s *addonRegistryAPIInterface) GetWebServiceRoute() *restful.WebService { ws := new(restful.WebService) ws.Path(versionPrefix+"/addon_registries"). Consumes(restful.MIME_XML, restful.MIME_JSON). @@ -52,7 +49,7 @@ func (s *addonRegistryWebService) GetWebService() *restful.WebService { Doc("create an addon registry"). Metadata(restfulspec.KeyOpenAPITags, tags). Reads(apis.CreateAddonRegistryRequest{}). - Filter(s.rbacUsecase.CheckPerm("addonRegistry", "create")). + Filter(s.RbacService.CheckPerm("addonRegistry", "create")). Returns(200, "OK", apis.AddonRegistry{}). Returns(400, "Bad Request", bcode.Bcode{}). Writes(apis.AddonRegistry{})) @@ -60,7 +57,7 @@ func (s *addonRegistryWebService) GetWebService() *restful.WebService { ws.Route(ws.GET("/").To(s.listAddonRegistry). Doc("list all addon registry"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(s.rbacUsecase.CheckPerm("addonRegistry", "list")). + Filter(s.RbacService.CheckPerm("addonRegistry", "list")). Returns(200, "OK", apis.ListAddonRegistryResponse{}). Returns(400, "Bad Request", bcode.Bcode{}). Writes(apis.ListAddonRegistryResponse{})) @@ -71,7 +68,7 @@ func (s *addonRegistryWebService) GetWebService() *restful.WebService { Metadata(restfulspec.KeyOpenAPITags, tags). Param(ws.PathParameter("addonRegName", "identifier of the addon registry").DataType("string")). Returns(200, "OK", apis.AddonRegistry{}). - Filter(s.rbacUsecase.CheckPerm("addonRegistry", "delete")). + Filter(s.RbacService.CheckPerm("addonRegistry", "delete")). Returns(400, "Bad Request", bcode.Bcode{}). Writes(apis.AddonRegistry{})) @@ -79,7 +76,7 @@ func (s *addonRegistryWebService) GetWebService() *restful.WebService { Doc("update an addon registry"). Metadata(restfulspec.KeyOpenAPITags, tags). Reads(apis.UpdateAddonRegistryRequest{}). - Filter(s.rbacUsecase.CheckPerm("addonRegistry", "update")). + Filter(s.RbacService.CheckPerm("addonRegistry", "update")). Param(ws.PathParameter("addonRegName", "identifier of the addon registry").DataType("string")). Returns(200, "OK", apis.AddonRegistry{}). Returns(400, "Bad Request", bcode.Bcode{}). @@ -89,7 +86,7 @@ func (s *addonRegistryWebService) GetWebService() *restful.WebService { return ws } -func (s *addonRegistryWebService) createAddonRegistry(req *restful.Request, res *restful.Response) { +func (s *addonRegistryAPIInterface) createAddonRegistry(req *restful.Request, res *restful.Response) { // Verify the validity of parameters var createReq apis.CreateAddonRegistryRequest if err := req.ReadEntity(&createReq); err != nil { @@ -101,8 +98,8 @@ func (s *addonRegistryWebService) createAddonRegistry(req *restful.Request, res return } - // Call the usecase layer code - meta, err := s.addonUsecase.CreateAddonRegistry(req.Request.Context(), createReq) + // Call the domain layer code + meta, err := s.AddonService.CreateAddonRegistry(req.Request.Context(), createReq) if err != nil { bcode.ReturnError(req, res, err) return @@ -115,13 +112,13 @@ func (s *addonRegistryWebService) createAddonRegistry(req *restful.Request, res } } -func (s *addonRegistryWebService) deleteAddonRegistry(req *restful.Request, res *restful.Response) { - r, err := s.addonUsecase.GetAddonRegistry(req.Request.Context(), req.PathParameter("addonRegName")) +func (s *addonRegistryAPIInterface) deleteAddonRegistry(req *restful.Request, res *restful.Response) { + r, err := s.AddonService.GetAddonRegistry(req.Request.Context(), req.PathParameter("addonRegName")) if err != nil { bcode.ReturnError(req, res, err) return } - err = s.addonUsecase.DeleteAddonRegistry(req.Request.Context(), r.Name) + err = s.AddonService.DeleteAddonRegistry(req.Request.Context(), r.Name) if err != nil { bcode.ReturnError(req, res, err) return @@ -133,8 +130,8 @@ func (s *addonRegistryWebService) deleteAddonRegistry(req *restful.Request, res } } -func (s *addonRegistryWebService) listAddonRegistry(req *restful.Request, res *restful.Response) { - registries, err := s.addonUsecase.ListAddonRegistries(req.Request.Context()) +func (s *addonRegistryAPIInterface) listAddonRegistry(req *restful.Request, res *restful.Response) { + registries, err := s.AddonService.ListAddonRegistries(req.Request.Context()) if err != nil { bcode.ReturnError(req, res, err) return @@ -145,7 +142,7 @@ func (s *addonRegistryWebService) listAddonRegistry(req *restful.Request, res *r } } -func (s *addonRegistryWebService) updateAddonRegistry(req *restful.Request, res *restful.Response) { +func (s *addonRegistryAPIInterface) updateAddonRegistry(req *restful.Request, res *restful.Response) { // Verify the validity of parameters var updateReq apis.UpdateAddonRegistryRequest if err := req.ReadEntity(&updateReq); err != nil { @@ -156,8 +153,8 @@ func (s *addonRegistryWebService) updateAddonRegistry(req *restful.Request, res bcode.ReturnError(req, res, err) return } - // Call the usecase layer code - meta, err := s.addonUsecase.UpdateAddonRegistry(req.Request.Context(), req.PathParameter("addonRegName"), updateReq) + // Call the domain layer code + meta, err := s.AddonService.UpdateAddonRegistry(req.Request.Context(), req.PathParameter("addonRegName"), updateReq) if err != nil { bcode.ReturnError(req, res, err) return diff --git a/pkg/apiserver/rest/webservice/webservice_suite_test.go b/pkg/apiserver/interfaces/api/api_suite_test.go similarity index 86% rename from pkg/apiserver/rest/webservice/webservice_suite_test.go rename to pkg/apiserver/interfaces/api/api_suite_test.go index 9857d5565..d86719d40 100644 --- a/pkg/apiserver/rest/webservice/webservice_suite_test.go +++ b/pkg/apiserver/interfaces/api/api_suite_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package webservice +package api import ( "context" @@ -31,10 +31,10 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/envtest" - "github.com/oam-dev/kubevela/pkg/apiserver/clients" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore/kubeapi" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore/mongodb" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/clients" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore/kubeapi" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore/mongodb" "github.com/oam-dev/kubevela/pkg/utils/common" ) @@ -42,9 +42,9 @@ var cfg *rest.Config var k8sClient client.Client var testEnv *envtest.Environment -func TestWebservice(t *testing.T) { +func TestAPIInterface(t *testing.T) { RegisterFailHandler(Fail) - RunSpecs(t, "Webservice Suite") + RunSpecs(t, "APIInterface Suite") } var _ = BeforeSuite(func(done Done) { diff --git a/pkg/apiserver/rest/webservice/application.go b/pkg/apiserver/interfaces/api/application.go similarity index 79% rename from pkg/apiserver/rest/webservice/application.go rename to pkg/apiserver/interfaces/api/application.go index bfd993f00..907b8aa02 100644 --- a/pkg/apiserver/rest/webservice/application.go +++ b/pkg/apiserver/interfaces/api/application.go @@ -14,44 +14,36 @@ See the License for the specific language governing permissions and limitations under the License. */ -package webservice +package api import ( "context" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils" + "github.com/oam-dev/kubevela/pkg/apiserver/utils" restfulspec "github.com/emicklei/go-restful-openapi/v2" "github.com/emicklei/go-restful/v3" - "github.com/oam-dev/kubevela/pkg/apiserver/log" - "github.com/oam-dev/kubevela/pkg/apiserver/model" - apis "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/usecase" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/service" + apis "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/log" ) -type applicationWebService struct { - workflowWebService - rbacUsecase usecase.RBACUsecase - applicationUsecase usecase.ApplicationUsecase - envBindingUsecase usecase.EnvBindingUsecase +type applicationAPIInterface struct { + WorkflowAPI WorkflowAPIInterface `inject:"inline"` + RbacService service.RBACService `inject:""` + ApplicationService service.ApplicationService `inject:""` + EnvBindingService service.EnvBindingService `inject:""` } -// NewApplicationWebService new application manage webservice -func NewApplicationWebService(applicationUsecase usecase.ApplicationUsecase, envBindingUsecase usecase.EnvBindingUsecase, workflowUsecase usecase.WorkflowUsecase, rbacUsecase usecase.RBACUsecase) WebService { - return &applicationWebService{ - workflowWebService: workflowWebService{ - workflowUsecase: workflowUsecase, - applicationUsecase: applicationUsecase, - }, - rbacUsecase: rbacUsecase, - applicationUsecase: applicationUsecase, - envBindingUsecase: envBindingUsecase, - } +// NewApplicationAPIInterface new application manage APIInterface +func NewApplicationAPIInterface() Interface { + return &applicationAPIInterface{} } -func (c *applicationWebService) GetWebService() *restful.WebService { +func (c *applicationAPIInterface) GetWebServiceRoute() *restful.WebService { ws := new(restful.WebService) ws.Path(versionPrefix+"/applications"). Consumes(restful.MIME_XML, restful.MIME_JSON). @@ -68,7 +60,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService { Param(ws.QueryParameter("env", "search base on env name").DataType("string")). Param(ws.QueryParameter("targetName", "Name of the application delivery target").DataType("string")). // This api will filter the app by user's permissions - // Filter(c.rbacUsecase.CheckPerm("application", "list")). + // Filter(c.RbacService.CheckPerm("application", "list")). Returns(200, "OK", apis.ListApplicationResponse{}). Returns(400, "Bad Request", bcode.Bcode{}). Writes(apis.ListApplicationResponse{})) @@ -77,7 +69,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService { Doc("create one application "). Metadata(restfulspec.KeyOpenAPITags, tags). Reads(apis.CreateApplicationRequest{}). - Filter(c.rbacUsecase.CheckPerm("application", "create")). + Filter(c.RbacService.CheckPerm("application", "create")). Returns(200, "OK", apis.ApplicationBase{}). Returns(400, "Bad Request", bcode.Bcode{}). Writes(apis.ApplicationBase{})) @@ -85,7 +77,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService { ws.Route(ws.DELETE("/{appName}").To(c.deleteApplication). Doc("delete one application"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(c.rbacUsecase.CheckPerm("application", "delete")). + Filter(c.RbacService.CheckPerm("application", "delete")). Filter(c.appCheckFilter). Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")). Returns(200, "OK", apis.EmptyResponse{}). @@ -95,7 +87,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService { ws.Route(ws.GET("/{appName}").To(c.detailApplication). Doc("detail one application "). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(c.rbacUsecase.CheckPerm("application", "detail")). + Filter(c.RbacService.CheckPerm("application", "detail")). Filter(c.appCheckFilter). Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")). Returns(200, "OK", apis.DetailApplicationResponse{}). @@ -105,7 +97,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService { ws.Route(ws.PUT("/{appName}").To(c.updateApplication). Doc("update one application "). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(c.rbacUsecase.CheckPerm("application", "update")). + Filter(c.RbacService.CheckPerm("application", "update")). Filter(c.appCheckFilter). Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")). Reads(apis.UpdateApplicationRequest{}). @@ -116,7 +108,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService { ws.Route(ws.GET("/{appName}/statistics").To(c.applicationStatistics). Doc("detail one application "). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(c.rbacUsecase.CheckPerm("application", "detail")). + Filter(c.RbacService.CheckPerm("application", "detail")). Filter(c.appCheckFilter). Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")). Returns(200, "OK", apis.ApplicationStatisticsResponse{}). @@ -126,7 +118,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService { ws.Route(ws.POST("/{appName}/triggers").To(c.createApplicationTrigger). Doc("create one application trigger"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(c.rbacUsecase.CheckPerm("trigger", "create")). + Filter(c.RbacService.CheckPerm("trigger", "create")). Filter(c.appCheckFilter). Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")). Reads(apis.CreateApplicationTriggerRequest{}). @@ -137,7 +129,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService { ws.Route(ws.DELETE("/{appName}/triggers/{token}").To(c.deleteApplicationTrigger). Doc("delete one application trigger"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(c.rbacUsecase.CheckPerm("trigger", "delete")). + Filter(c.RbacService.CheckPerm("trigger", "delete")). Filter(c.appCheckFilter). Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")). Param(ws.PathParameter("token", "identifier of the trigger").DataType("string")). @@ -148,7 +140,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService { ws.Route(ws.GET("/{appName}/triggers").To(c.listApplicationTriggers). Doc("list application triggers"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(c.rbacUsecase.CheckPerm("trigger", "list")). + Filter(c.RbacService.CheckPerm("trigger", "list")). Filter(c.appCheckFilter). Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")). Returns(200, "OK", apis.ListApplicationTriggerResponse{}). @@ -158,7 +150,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService { ws.Route(ws.POST("/{appName}/template").To(c.publishApplicationTemplate). Doc("create one application template"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(c.rbacUsecase.CheckPerm("applicationTemplate", "create")). + Filter(c.RbacService.CheckPerm("applicationTemplate", "create")). Filter(c.appCheckFilter). Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")). Reads(apis.CreateApplicationTemplateRequest{}). @@ -169,7 +161,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService { ws.Route(ws.POST("/{appName}/deploy").To(c.deployApplication). Doc("deploy or upgrade the application"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(c.rbacUsecase.CheckPerm("application", "deploy")). + Filter(c.RbacService.CheckPerm("application", "deploy")). Filter(c.appCheckFilter). Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")). Reads(apis.ApplicationDeployRequest{}). @@ -179,7 +171,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService { ws.Route(ws.GET("/{appName}/components").To(c.listApplicationComponents). Doc("gets the list of application components"). - Filter(c.rbacUsecase.CheckPerm("component", "list")). + Filter(c.RbacService.CheckPerm("component", "list")). Filter(c.appCheckFilter). Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")). Param(ws.QueryParameter("envName", "list components that deployed in define env").DataType("string")). @@ -190,7 +182,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService { ws.Route(ws.POST("/{appName}/components").To(c.createComponent). Doc("create component for application "). - Filter(c.rbacUsecase.CheckPerm("component", "create")). + Filter(c.RbacService.CheckPerm("component", "create")). Filter(c.appCheckFilter). Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")). Metadata(restfulspec.KeyOpenAPITags, tags). @@ -201,7 +193,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService { ws.Route(ws.GET("/{appName}/components/{compName}").To(c.detailComponent). Doc("detail component for application "). - Filter(c.rbacUsecase.CheckPerm("component", "detail")). + Filter(c.RbacService.CheckPerm("component", "detail")). Filter(c.appCheckFilter). Filter(c.componentCheckFilter). Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")). @@ -213,7 +205,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService { ws.Route(ws.PUT("/{appName}/components/{compName}").To(c.updateComponent). Doc("update component config"). - Filter(c.rbacUsecase.CheckPerm("component", "update")). + Filter(c.RbacService.CheckPerm("component", "update")). Filter(c.appCheckFilter). Filter(c.componentCheckFilter). Param(ws.PathParameter("appName", "identifier of the application").DataType("string")). @@ -226,7 +218,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService { ws.Route(ws.DELETE("/{appName}/components/{compName}").To(c.deleteComponent). Doc("delete a component"). - Filter(c.rbacUsecase.CheckPerm("component", "delete")). + Filter(c.RbacService.CheckPerm("component", "delete")). Filter(c.appCheckFilter). Filter(c.componentCheckFilter). Param(ws.PathParameter("appName", "identifier of the application").DataType("string")). @@ -239,7 +231,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService { ws.Route(ws.GET("/{appName}/policies").To(c.listApplicationPolicies). Doc("list policy for application"). - Filter(c.rbacUsecase.CheckPerm("policy", "list")). + Filter(c.RbacService.CheckPerm("policy", "list")). Filter(c.appCheckFilter). Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")). Metadata(restfulspec.KeyOpenAPITags, tags). @@ -249,7 +241,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService { ws.Route(ws.POST("/{appName}/policies").To(c.createApplicationPolicy). Doc("create policy for application"). - Filter(c.rbacUsecase.CheckPerm("policy", "create")). + Filter(c.RbacService.CheckPerm("policy", "create")). Filter(c.appCheckFilter). Param(ws.PathParameter("appName", "identifier of the application").DataType("string")). Metadata(restfulspec.KeyOpenAPITags, tags). @@ -260,7 +252,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService { ws.Route(ws.GET("/{appName}/policies/{policyName}").To(c.detailApplicationPolicy). Doc("detail policy for application"). - Filter(c.rbacUsecase.CheckPerm("policy", "detail")). + Filter(c.RbacService.CheckPerm("policy", "detail")). Filter(c.appCheckFilter). Param(ws.PathParameter("appName", "identifier of the application").DataType("string")). Param(ws.PathParameter("policyName", "identifier of the application policy").DataType("string")). @@ -271,7 +263,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService { ws.Route(ws.DELETE("/{appName}/policies/{policyName}").To(c.deleteApplicationPolicy). Doc("detail policy for application"). - Filter(c.rbacUsecase.CheckPerm("policy", "delete")). + Filter(c.RbacService.CheckPerm("policy", "delete")). Filter(c.appCheckFilter). Param(ws.PathParameter("appName", "identifier of the application").DataType("string")). Param(ws.PathParameter("policyName", "identifier of the application policy").DataType("string")). @@ -282,7 +274,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService { ws.Route(ws.PUT("/{appName}/policies/{policyName}").To(c.updateApplicationPolicy). Doc("update policy for application"). - Filter(c.rbacUsecase.CheckPerm("policy", "update")). + Filter(c.RbacService.CheckPerm("policy", "update")). Filter(c.appCheckFilter). Param(ws.PathParameter("appName", "identifier of the application").DataType("string")). Param(ws.PathParameter("policyName", "identifier of the application policy").DataType("string")). @@ -294,7 +286,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService { ws.Route(ws.POST("/{appName}/components/{compName}/traits").To(c.addApplicationTrait). Doc("add trait for a component"). - Filter(c.rbacUsecase.CheckPerm("trait", "create")). + Filter(c.RbacService.CheckPerm("trait", "create")). Filter(c.appCheckFilter). Filter(c.componentCheckFilter). Param(ws.PathParameter("appName", "identifier of the application").DataType("string")). @@ -307,7 +299,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService { ws.Route(ws.PUT("/{appName}/components/{compName}/traits/{traitType}").To(c.updateApplicationTrait). Doc("update trait from a component"). - Filter(c.rbacUsecase.CheckPerm("trait", "update")). + Filter(c.RbacService.CheckPerm("trait", "update")). Filter(c.appCheckFilter). Filter(c.componentCheckFilter). Param(ws.PathParameter("appName", "identifier of the application").DataType("string")). @@ -321,7 +313,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService { ws.Route(ws.DELETE("/{appName}/components/{compName}/traits/{traitType}").To(c.deleteApplicationTrait). Doc("delete trait from a component"). - Filter(c.rbacUsecase.CheckPerm("trait", "delete")). + Filter(c.RbacService.CheckPerm("trait", "delete")). Filter(c.appCheckFilter). Filter(c.componentCheckFilter). Param(ws.PathParameter("appName", "identifier of the application").DataType("string")). @@ -334,7 +326,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService { ws.Route(ws.GET("/{appName}/revisions").To(c.listApplicationRevisions). Doc("list revisions for application"). - Filter(c.rbacUsecase.CheckPerm("revision", "list")). + Filter(c.RbacService.CheckPerm("revision", "list")). Filter(c.appCheckFilter). Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")). Param(ws.QueryParameter("envName", "query identifier of the env").DataType("string")). @@ -348,7 +340,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService { ws.Route(ws.GET("/{appName}/revisions/{revision}").To(c.detailApplicationRevision). Doc("detail revision for application"). - Filter(c.rbacUsecase.CheckPerm("revision", "detail")). + Filter(c.RbacService.CheckPerm("revision", "detail")). Filter(c.appCheckFilter). Param(ws.PathParameter("appName", "identifier of the application").DataType("string")). Param(ws.PathParameter("revision", "identifier of the application revision").DataType("string")). @@ -359,7 +351,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService { ws.Route(ws.GET("/{appName}/envs").To(c.listApplicationEnvs). Doc("list policy for application"). - Filter(c.rbacUsecase.CheckPerm("envBinding", "list")). + Filter(c.RbacService.CheckPerm("envBinding", "list")). Filter(c.appCheckFilter). Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")). Metadata(restfulspec.KeyOpenAPITags, tags). @@ -369,7 +361,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService { ws.Route(ws.POST("/{appName}/envs").To(c.createApplicationEnv). Doc("creating an application environment "). - Filter(c.rbacUsecase.CheckPerm("envBinding", "create")). + Filter(c.RbacService.CheckPerm("envBinding", "create")). Metadata(restfulspec.KeyOpenAPITags, tags). Filter(c.appCheckFilter). Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")). @@ -381,7 +373,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService { ws.Route(ws.PUT("/{appName}/envs/{envName}").To(c.updateApplicationEnv). Doc("set application differences in the specified environment"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(c.rbacUsecase.CheckPerm("envBinding", "update")). + Filter(c.RbacService.CheckPerm("envBinding", "update")). Filter(c.appCheckFilter). Filter(c.envCheckFilter). Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")). @@ -394,7 +386,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService { ws.Route(ws.DELETE("/{appName}/envs/{envName}").To(c.deleteApplicationEnv). Doc("delete an application environment "). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(c.rbacUsecase.CheckPerm("envBinding", "delete")). + Filter(c.RbacService.CheckPerm("envBinding", "delete")). Filter(c.appCheckFilter). Filter(c.envCheckFilter). Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")). @@ -406,7 +398,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService { ws.Route(ws.GET("/{appName}/envs/{envName}/status").To(c.getApplicationStatus). Doc("get application status"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(c.rbacUsecase.CheckPerm("envBinding", "detail")). + Filter(c.RbacService.CheckPerm("envBinding", "detail")). Filter(c.appCheckFilter). Filter(c.envCheckFilter). Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")). @@ -418,7 +410,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService { ws.Route(ws.POST("/{appName}/envs/{envName}/recycle").To(c.recycleApplicationEnv). Doc("get application status"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(c.rbacUsecase.CheckPerm("envBinding", "recycle")). + Filter(c.RbacService.CheckPerm("envBinding", "recycle")). Filter(c.appCheckFilter). Filter(c.envCheckFilter). Param(ws.PathParameter("appName", "identifier of the application ").DataType("string").Required(true)). @@ -427,18 +419,18 @@ func (c *applicationWebService) GetWebService() *restful.WebService { Returns(400, "Bad Request", bcode.Bcode{}). Writes(apis.EmptyResponse{})) - ws.Route(ws.GET("/{appName}/workflows").To(c.listApplicationWorkflows). + ws.Route(ws.GET("/{appName}/workflows").To(c.WorkflowAPI.listApplicationWorkflows). Doc("list application workflow"). - Filter(c.rbacUsecase.CheckPerm("application/workflow", "list")). + Filter(c.RbacService.CheckPerm("application/workflow", "list")). Filter(c.appCheckFilter). Param(ws.PathParameter("appName", "identifier of the application.").DataType("string").Required(true)). Metadata(restfulspec.KeyOpenAPITags, tags). Returns(200, "OK", apis.ListWorkflowResponse{}). Writes(apis.ListWorkflowResponse{}).Do(returns200, returns500)) - ws.Route(ws.POST("/{appName}/workflows").To(c.createOrUpdateApplicationWorkflow). + ws.Route(ws.POST("/{appName}/workflows").To(c.WorkflowAPI.createOrUpdateApplicationWorkflow). Doc("create application workflow"). - Filter(c.rbacUsecase.CheckPerm("application/workflow", "create")). + Filter(c.RbacService.CheckPerm("application/workflow", "create")). Metadata(restfulspec.KeyOpenAPITags, tags). Reads(apis.CreateWorkflowRequest{}). Filter(c.appCheckFilter). @@ -447,109 +439,109 @@ func (c *applicationWebService) GetWebService() *restful.WebService { Returns(400, "create failure", bcode.Bcode{}). Writes(apis.DetailWorkflowResponse{}).Do(returns200, returns500)) - ws.Route(ws.GET("/{appName}/workflows/{workflowName}").To(c.detailWorkflow). + ws.Route(ws.GET("/{appName}/workflows/{workflowName}").To(c.WorkflowAPI.detailWorkflow). Doc("detail application workflow"). - Filter(c.rbacUsecase.CheckPerm("application/workflow", "detail")). + Filter(c.RbacService.CheckPerm("application/workflow", "detail")). Filter(c.appCheckFilter). - Filter(c.workflowCheckFilter). + Filter(c.WorkflowAPI.workflowCheckFilter). Param(ws.PathParameter("appName", "identifier of the application.").DataType("string").Required(true)). Param(ws.PathParameter("workflowName", "identifier of the workfloc.").DataType("string")). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(c.workflowCheckFilter). + Filter(c.WorkflowAPI.workflowCheckFilter). Returns(200, "create success", apis.DetailWorkflowResponse{}). Writes(apis.DetailWorkflowResponse{}).Do(returns200, returns500)) - ws.Route(ws.PUT("/{appName}/workflows/{workflowName}").To(c.updateWorkflow). + ws.Route(ws.PUT("/{appName}/workflows/{workflowName}").To(c.WorkflowAPI.updateWorkflow). Doc("update application workflow config"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(c.rbacUsecase.CheckPerm("application/workflow", "update")). + Filter(c.RbacService.CheckPerm("application/workflow", "update")). Filter(c.appCheckFilter). - Filter(c.workflowCheckFilter). + Filter(c.WorkflowAPI.workflowCheckFilter). Param(ws.PathParameter("appName", "identifier of the application.").DataType("string").Required(true)). Param(ws.PathParameter("workflowName", "identifier of the workflow").DataType("string")). Reads(apis.UpdateWorkflowRequest{}). Returns(200, "OK", apis.DetailWorkflowResponse{}). Writes(apis.DetailWorkflowResponse{}).Do(returns200, returns500)) - ws.Route(ws.DELETE("/{appName}/workflows/{workflowName}").To(c.deleteWorkflow). + ws.Route(ws.DELETE("/{appName}/workflows/{workflowName}").To(c.WorkflowAPI.deleteWorkflow). Doc("deletet workflow"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(c.rbacUsecase.CheckPerm("application/workflow", "delete")). + Filter(c.RbacService.CheckPerm("application/workflow", "delete")). Filter(c.appCheckFilter). - Filter(c.workflowCheckFilter). + Filter(c.WorkflowAPI.workflowCheckFilter). Param(ws.PathParameter("appName", "identifier of the application.").DataType("string").Required(true)). Param(ws.PathParameter("workflowName", "identifier of the workflow").DataType("string")). Returns(200, "OK", apis.EmptyResponse{}). Writes(apis.EmptyResponse{}).Do(returns200, returns500)) - ws.Route(ws.GET("/{appName}/workflows/{workflowName}/records").To(c.listWorkflowRecords). + ws.Route(ws.GET("/{appName}/workflows/{workflowName}/records").To(c.WorkflowAPI.listWorkflowRecords). Doc("query application workflow execution record"). Param(ws.PathParameter("appName", "identifier of the application.").DataType("string").Required(true)). Param(ws.PathParameter("workflowName", "identifier of the workflow").DataType("string")). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(c.rbacUsecase.CheckPerm("application/workflow/record", "list")). + Filter(c.RbacService.CheckPerm("application/workflow/record", "list")). Filter(c.appCheckFilter). - Filter(c.workflowCheckFilter). + Filter(c.WorkflowAPI.workflowCheckFilter). Param(ws.QueryParameter("page", "query the page number").DataType("integer")). Param(ws.QueryParameter("pageSize", "query the page size number").DataType("integer")). Returns(200, "OK", apis.ListWorkflowRecordsResponse{}). Writes(apis.ListWorkflowRecordsResponse{}).Do(returns200, returns500)) - ws.Route(ws.GET("/{appName}/workflows/{workflowName}/records/{record}").To(c.detailWorkflowRecord). + ws.Route(ws.GET("/{appName}/workflows/{workflowName}/records/{record}").To(c.WorkflowAPI.detailWorkflowRecord). Doc("query application workflow execution record detail"). - Filter(c.rbacUsecase.CheckPerm("application/workflow/record", "detail")). + Filter(c.RbacService.CheckPerm("application/workflow/record", "detail")). Param(ws.PathParameter("appName", "identifier of the application.").DataType("string").Required(true)). Param(ws.PathParameter("workflowName", "identifier of the workflow").DataType("string")). Param(ws.PathParameter("record", "identifier of the workflow record").DataType("string")). Metadata(restfulspec.KeyOpenAPITags, tags). Filter(c.appCheckFilter). - Filter(c.workflowCheckFilter). + Filter(c.WorkflowAPI.workflowCheckFilter). Returns(200, "OK", apis.DetailWorkflowRecordResponse{}). Writes(apis.DetailWorkflowRecordResponse{}).Do(returns200, returns500)) - ws.Route(ws.GET("/{appName}/workflows/{workflowName}/records/{record}/resume").To(c.resumeWorkflowRecord). + ws.Route(ws.GET("/{appName}/workflows/{workflowName}/records/{record}/resume").To(c.WorkflowAPI.resumeWorkflowRecord). Doc("resume suspend workflow record"). - Filter(c.rbacUsecase.CheckPerm("application/workflow/record", "resume")). + Filter(c.RbacService.CheckPerm("application/workflow/record", "resume")). Param(ws.PathParameter("appName", "identifier of the application.").DataType("string").Required(true)). Param(ws.PathParameter("workflowName", "identifier of the workflow").DataType("string")). Param(ws.PathParameter("record", "identifier of the workflow record").DataType("string")). Metadata(restfulspec.KeyOpenAPITags, tags). Filter(c.appCheckFilter). - Filter(c.workflowCheckFilter). + Filter(c.WorkflowAPI.workflowCheckFilter). Returns(200, "OK", nil). Returns(400, "Bad Request", bcode.Bcode{}). Writes(apis.DetailWorkflowRecordResponse{})) - ws.Route(ws.GET("/{appName}/workflows/{workflowName}/records/{record}/terminate").To(c.terminateWorkflowRecord). + ws.Route(ws.GET("/{appName}/workflows/{workflowName}/records/{record}/terminate").To(c.WorkflowAPI.terminateWorkflowRecord). Doc("terminate suspend workflow record"). - Filter(c.rbacUsecase.CheckPerm("application/workflow/record", "terminate")). + Filter(c.RbacService.CheckPerm("application/workflow/record", "terminate")). Param(ws.PathParameter("appName", "identifier of the application.").DataType("string").Required(true)). Param(ws.PathParameter("workflowName", "identifier of the workflow").DataType("string")). Param(ws.PathParameter("record", "identifier of the workflow record").DataType("string")). Metadata(restfulspec.KeyOpenAPITags, tags). Filter(c.appCheckFilter). - Filter(c.workflowCheckFilter). + Filter(c.WorkflowAPI.workflowCheckFilter). Returns(200, "OK", nil). Returns(400, "Bad Request", bcode.Bcode{}). Writes(apis.DetailWorkflowRecordResponse{})) - ws.Route(ws.GET("/{appName}/workflows/{workflowName}/records/{record}/rollback").To(c.rollbackWorkflowRecord). + ws.Route(ws.GET("/{appName}/workflows/{workflowName}/records/{record}/rollback").To(c.WorkflowAPI.rollbackWorkflowRecord). Doc("rollback suspend application record"). - Filter(c.rbacUsecase.CheckPerm("application/workflow/record", "rollback")). + Filter(c.RbacService.CheckPerm("application/workflow/record", "rollback")). Param(ws.PathParameter("appName", "identifier of the application.").DataType("string").Required(true)). Param(ws.PathParameter("workflowName", "identifier of the workflow").DataType("string")). Param(ws.PathParameter("record", "identifier of the workflow record").DataType("string")). Param(ws.QueryParameter("rollbackVersion", "identifier of the rollback revision").DataType("string")). Metadata(restfulspec.KeyOpenAPITags, tags). Filter(c.appCheckFilter). - Filter(c.workflowCheckFilter). + Filter(c.WorkflowAPI.workflowCheckFilter). Returns(200, "OK", nil). Returns(400, "Bad Request", bcode.Bcode{}). Writes(apis.DetailWorkflowRecordResponse{})) ws.Route(ws.GET("/{appName}/records").To(c.listApplicationRecords). Doc("list application records"). - Filter(c.rbacUsecase.CheckPerm("application/workflow/record", "list")). + Filter(c.RbacService.CheckPerm("application/workflow/record", "list")). Param(ws.PathParameter("appName", "identifier of the application.").DataType("string").Required(true)). Metadata(restfulspec.KeyOpenAPITags, tags). Filter(c.appCheckFilter). @@ -560,7 +552,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService { ws.Route(ws.POST("/{appName}/compare").To(c.compareAppWithLatestRevision). Doc("compare application with env latest revision"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(c.rbacUsecase.CheckPerm("application", "compare")). + Filter(c.RbacService.CheckPerm("application", "compare")). Filter(c.appCheckFilter). Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")). Returns(200, "OK", apis.ApplicationBase{}). @@ -570,7 +562,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService { ws.Route(ws.POST("/{appName}/reset").To(c.resetAppToLatestRevision). Doc("reset application to latest revision"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(c.rbacUsecase.CheckPerm("application", "reset")). + Filter(c.RbacService.CheckPerm("application", "reset")). Filter(c.appCheckFilter). Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")). Returns(200, "OK", apis.AppResetResponse{}). @@ -580,7 +572,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService { ws.Route(ws.POST("/{appName}/dry-run").To(c.dryRunAppOrRevision). Doc("dry-run application to latest revision"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(c.rbacUsecase.CheckPerm("application", "detail")). + Filter(c.RbacService.CheckPerm("application", "detail")). Filter(c.appCheckFilter). Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")). Returns(200, "OK", apis.AppDryRunResponse{}). @@ -591,7 +583,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService { return ws } -func (c *applicationWebService) createApplication(req *restful.Request, res *restful.Response) { +func (c *applicationAPIInterface) createApplication(req *restful.Request, res *restful.Response) { // Verify the validity of parameters var createReq apis.CreateApplicationRequest if err := req.ReadEntity(&createReq); err != nil { @@ -602,8 +594,8 @@ func (c *applicationWebService) createApplication(req *restful.Request, res *res bcode.ReturnError(req, res, err) return } - // Call the usecase layer code - appBase, err := c.applicationUsecase.CreateApplication(req.Request.Context(), createReq) + // Call the domain layer code + appBase, err := c.ApplicationService.CreateApplication(req.Request.Context(), createReq) if err != nil { log.Logger.Errorf("create application failure %s", err.Error()) bcode.ReturnError(req, res, err) @@ -617,12 +609,12 @@ func (c *applicationWebService) createApplication(req *restful.Request, res *res } } -func (c *applicationWebService) listApplications(req *restful.Request, res *restful.Response) { +func (c *applicationAPIInterface) listApplications(req *restful.Request, res *restful.Response) { var projetNames []string if req.QueryParameter("project") != "" { projetNames = append(projetNames, req.QueryParameter("project")) } - apps, err := c.applicationUsecase.ListApplications(req.Request.Context(), apis.ListApplicationOptions{ + apps, err := c.ApplicationService.ListApplications(req.Request.Context(), apis.ListApplicationOptions{ Projects: projetNames, Env: req.QueryParameter("env"), TargetName: req.QueryParameter("targetName"), @@ -638,9 +630,9 @@ func (c *applicationWebService) listApplications(req *restful.Request, res *rest } } -func (c *applicationWebService) detailApplication(req *restful.Request, res *restful.Response) { +func (c *applicationAPIInterface) detailApplication(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) - detail, err := c.applicationUsecase.DetailApplication(req.Request.Context(), app) + detail, err := c.ApplicationService.DetailApplication(req.Request.Context(), app) if err != nil { bcode.ReturnError(req, res, err) return @@ -651,14 +643,14 @@ func (c *applicationWebService) detailApplication(req *restful.Request, res *res } } -func (c *applicationWebService) createApplicationTrigger(req *restful.Request, res *restful.Response) { +func (c *applicationAPIInterface) createApplicationTrigger(req *restful.Request, res *restful.Response) { var createReq apis.CreateApplicationTriggerRequest if err := req.ReadEntity(&createReq); err != nil { bcode.ReturnError(req, res, err) return } app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) - base, err := c.applicationUsecase.CreateApplicationTrigger(req.Request.Context(), app, createReq) + base, err := c.ApplicationService.CreateApplicationTrigger(req.Request.Context(), app, createReq) if err != nil { bcode.ReturnError(req, res, err) return @@ -669,9 +661,9 @@ func (c *applicationWebService) createApplicationTrigger(req *restful.Request, r } } -func (c *applicationWebService) listApplicationTriggers(req *restful.Request, res *restful.Response) { +func (c *applicationAPIInterface) listApplicationTriggers(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) - triggers, err := c.applicationUsecase.ListApplicationTriggers(req.Request.Context(), app) + triggers, err := c.ApplicationService.ListApplicationTriggers(req.Request.Context(), app) if err != nil { bcode.ReturnError(req, res, err) return @@ -682,9 +674,9 @@ func (c *applicationWebService) listApplicationTriggers(req *restful.Request, re } } -func (c *applicationWebService) deleteApplicationTrigger(req *restful.Request, res *restful.Response) { +func (c *applicationAPIInterface) deleteApplicationTrigger(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) - if err := c.applicationUsecase.DeleteApplicationTrigger(req.Request.Context(), app, req.PathParameter("token")); err != nil { + if err := c.ApplicationService.DeleteApplicationTrigger(req.Request.Context(), app, req.PathParameter("token")); err != nil { bcode.ReturnError(req, res, err) return } @@ -694,9 +686,9 @@ func (c *applicationWebService) deleteApplicationTrigger(req *restful.Request, r } } -func (c *applicationWebService) publishApplicationTemplate(req *restful.Request, res *restful.Response) { +func (c *applicationAPIInterface) publishApplicationTemplate(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) - base, err := c.applicationUsecase.PublishApplicationTemplate(req.Request.Context(), app) + base, err := c.ApplicationService.PublishApplicationTemplate(req.Request.Context(), app) if err != nil { bcode.ReturnError(req, res, err) return @@ -708,7 +700,7 @@ func (c *applicationWebService) publishApplicationTemplate(req *restful.Request, } // deployApplication TODO: return event model -func (c *applicationWebService) deployApplication(req *restful.Request, res *restful.Response) { +func (c *applicationAPIInterface) deployApplication(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) // Verify the validity of parameters var createReq apis.ApplicationDeployRequest @@ -720,7 +712,7 @@ func (c *applicationWebService) deployApplication(req *restful.Request, res *res bcode.ReturnError(req, res, err) return } - deployRes, err := c.applicationUsecase.Deploy(req.Request.Context(), app, createReq) + deployRes, err := c.ApplicationService.Deploy(req.Request.Context(), app, createReq) if err != nil { bcode.ReturnError(req, res, err) return @@ -731,9 +723,9 @@ func (c *applicationWebService) deployApplication(req *restful.Request, res *res } } -func (c *applicationWebService) deleteApplication(req *restful.Request, res *restful.Response) { +func (c *applicationAPIInterface) deleteApplication(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) - err := c.applicationUsecase.DeleteApplication(req.Request.Context(), app) + err := c.ApplicationService.DeleteApplication(req.Request.Context(), app) if err != nil { bcode.ReturnError(req, res, err) return @@ -744,9 +736,9 @@ func (c *applicationWebService) deleteApplication(req *restful.Request, res *res } } -func (c *applicationWebService) listApplicationComponents(req *restful.Request, res *restful.Response) { +func (c *applicationAPIInterface) listApplicationComponents(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) - components, err := c.applicationUsecase.ListComponents(req.Request.Context(), app, apis.ListApplicationComponentOptions{ + components, err := c.ApplicationService.ListComponents(req.Request.Context(), app, apis.ListApplicationComponentOptions{ EnvName: req.QueryParameter("envName"), }) if err != nil { @@ -759,7 +751,7 @@ func (c *applicationWebService) listApplicationComponents(req *restful.Request, } } -func (c *applicationWebService) createComponent(req *restful.Request, res *restful.Response) { +func (c *applicationAPIInterface) createComponent(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) // Verify the validity of parameters var createReq apis.CreateComponentRequest @@ -771,7 +763,7 @@ func (c *applicationWebService) createComponent(req *restful.Request, res *restf bcode.ReturnError(req, res, err) return } - base, err := c.applicationUsecase.CreateComponent(req.Request.Context(), app, createReq) + base, err := c.ApplicationService.CreateComponent(req.Request.Context(), app, createReq) if err != nil { bcode.ReturnError(req, res, err) return @@ -782,9 +774,9 @@ func (c *applicationWebService) createComponent(req *restful.Request, res *restf } } -func (c *applicationWebService) detailComponent(req *restful.Request, res *restful.Response) { +func (c *applicationAPIInterface) detailComponent(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) - detail, err := c.applicationUsecase.DetailComponent(req.Request.Context(), app, req.PathParameter("compName")) + detail, err := c.ApplicationService.DetailComponent(req.Request.Context(), app, req.PathParameter("compName")) if err != nil { bcode.ReturnError(req, res, err) return @@ -795,7 +787,7 @@ func (c *applicationWebService) detailComponent(req *restful.Request, res *restf } } -func (c *applicationWebService) updateComponent(req *restful.Request, res *restful.Response) { +func (c *applicationAPIInterface) updateComponent(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) component := req.Request.Context().Value(&apis.CtxKeyApplicationComponent).(*model.ApplicationComponent) // Verify the validity of parameters @@ -808,7 +800,7 @@ func (c *applicationWebService) updateComponent(req *restful.Request, res *restf bcode.ReturnError(req, res, err) return } - base, err := c.applicationUsecase.UpdateComponent(req.Request.Context(), app, component, updateReq) + base, err := c.ApplicationService.UpdateComponent(req.Request.Context(), app, component, updateReq) if err != nil { bcode.ReturnError(req, res, err) return @@ -819,10 +811,10 @@ func (c *applicationWebService) updateComponent(req *restful.Request, res *restf } } -func (c *applicationWebService) deleteComponent(req *restful.Request, res *restful.Response) { +func (c *applicationAPIInterface) deleteComponent(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) component := req.Request.Context().Value(&apis.CtxKeyApplicationComponent).(*model.ApplicationComponent) - err := c.applicationUsecase.DeleteComponent(req.Request.Context(), app, component) + err := c.ApplicationService.DeleteComponent(req.Request.Context(), app, component) if err != nil { bcode.ReturnError(req, res, err) return @@ -833,7 +825,7 @@ func (c *applicationWebService) deleteComponent(req *restful.Request, res *restf } } -func (c *applicationWebService) createApplicationPolicy(req *restful.Request, res *restful.Response) { +func (c *applicationAPIInterface) createApplicationPolicy(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) // Verify the validity of parameters var createReq apis.CreatePolicyRequest @@ -845,7 +837,7 @@ func (c *applicationWebService) createApplicationPolicy(req *restful.Request, re bcode.ReturnError(req, res, err) return } - base, err := c.applicationUsecase.CreatePolicy(req.Request.Context(), app, createReq) + base, err := c.ApplicationService.CreatePolicy(req.Request.Context(), app, createReq) if err != nil { bcode.ReturnError(req, res, err) return @@ -856,9 +848,9 @@ func (c *applicationWebService) createApplicationPolicy(req *restful.Request, re } } -func (c *applicationWebService) listApplicationPolicies(req *restful.Request, res *restful.Response) { +func (c *applicationAPIInterface) listApplicationPolicies(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) - policies, err := c.applicationUsecase.ListPolicies(req.Request.Context(), app) + policies, err := c.ApplicationService.ListPolicies(req.Request.Context(), app) if err != nil { bcode.ReturnError(req, res, err) return @@ -869,9 +861,9 @@ func (c *applicationWebService) listApplicationPolicies(req *restful.Request, re } } -func (c *applicationWebService) detailApplicationPolicy(req *restful.Request, res *restful.Response) { +func (c *applicationAPIInterface) detailApplicationPolicy(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) - detail, err := c.applicationUsecase.DetailPolicy(req.Request.Context(), app, req.PathParameter("policyName")) + detail, err := c.ApplicationService.DetailPolicy(req.Request.Context(), app, req.PathParameter("policyName")) if err != nil { bcode.ReturnError(req, res, err) return @@ -882,9 +874,9 @@ func (c *applicationWebService) detailApplicationPolicy(req *restful.Request, re } } -func (c *applicationWebService) deleteApplicationPolicy(req *restful.Request, res *restful.Response) { +func (c *applicationAPIInterface) deleteApplicationPolicy(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) - err := c.applicationUsecase.DeletePolicy(req.Request.Context(), app, req.PathParameter("policyName")) + err := c.ApplicationService.DeletePolicy(req.Request.Context(), app, req.PathParameter("policyName")) if err != nil { bcode.ReturnError(req, res, err) return @@ -895,7 +887,7 @@ func (c *applicationWebService) deleteApplicationPolicy(req *restful.Request, re } } -func (c *applicationWebService) updateApplicationPolicy(req *restful.Request, res *restful.Response) { +func (c *applicationAPIInterface) updateApplicationPolicy(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) // Verify the validity of parameters var updateReq apis.UpdatePolicyRequest @@ -907,7 +899,7 @@ func (c *applicationWebService) updateApplicationPolicy(req *restful.Request, re bcode.ReturnError(req, res, err) return } - response, err := c.applicationUsecase.UpdatePolicy(req.Request.Context(), app, req.PathParameter("policyName"), updateReq) + response, err := c.ApplicationService.UpdatePolicy(req.Request.Context(), app, req.PathParameter("policyName"), updateReq) if err != nil { bcode.ReturnError(req, res, err) return @@ -918,7 +910,7 @@ func (c *applicationWebService) updateApplicationPolicy(req *restful.Request, re } } -func (c *applicationWebService) updateApplication(req *restful.Request, res *restful.Response) { +func (c *applicationAPIInterface) updateApplication(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) // Verify the validity of parameters var updateReq apis.UpdateApplicationRequest @@ -930,7 +922,7 @@ func (c *applicationWebService) updateApplication(req *restful.Request, res *res bcode.ReturnError(req, res, err) return } - base, err := c.applicationUsecase.UpdateApplication(req.Request.Context(), app, updateReq) + base, err := c.ApplicationService.UpdateApplication(req.Request.Context(), app, updateReq) if err != nil { bcode.ReturnError(req, res, err) return @@ -941,7 +933,7 @@ func (c *applicationWebService) updateApplication(req *restful.Request, res *res } } -func (c *applicationWebService) addApplicationTrait(req *restful.Request, res *restful.Response) { +func (c *applicationAPIInterface) addApplicationTrait(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) var createReq apis.CreateApplicationTraitRequest if err := req.ReadEntity(&createReq); err != nil { @@ -952,7 +944,7 @@ func (c *applicationWebService) addApplicationTrait(req *restful.Request, res *r bcode.ReturnError(req, res, err) return } - trait, err := c.applicationUsecase.CreateApplicationTrait(req.Request.Context(), app, + trait, err := c.ApplicationService.CreateApplicationTrait(req.Request.Context(), app, &model.ApplicationComponent{Name: req.PathParameter("compName")}, createReq) if err != nil { bcode.ReturnError(req, res, err) @@ -964,7 +956,7 @@ func (c *applicationWebService) addApplicationTrait(req *restful.Request, res *r } } -func (c *applicationWebService) updateApplicationTrait(req *restful.Request, res *restful.Response) { +func (c *applicationAPIInterface) updateApplicationTrait(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) var updateReq apis.UpdateApplicationTraitRequest if err := req.ReadEntity(&updateReq); err != nil { @@ -975,7 +967,7 @@ func (c *applicationWebService) updateApplicationTrait(req *restful.Request, res bcode.ReturnError(req, res, err) return } - trait, err := c.applicationUsecase.UpdateApplicationTrait(req.Request.Context(), app, + trait, err := c.ApplicationService.UpdateApplicationTrait(req.Request.Context(), app, &model.ApplicationComponent{Name: req.PathParameter("compName")}, req.PathParameter("traitType"), updateReq) if err != nil { bcode.ReturnError(req, res, err) @@ -987,9 +979,9 @@ func (c *applicationWebService) updateApplicationTrait(req *restful.Request, res } } -func (c *applicationWebService) deleteApplicationTrait(req *restful.Request, res *restful.Response) { +func (c *applicationAPIInterface) deleteApplicationTrait(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) - err := c.applicationUsecase.DeleteApplicationTrait(req.Request.Context(), app, + err := c.ApplicationService.DeleteApplicationTrait(req.Request.Context(), app, &model.ApplicationComponent{Name: req.PathParameter("compName")}, req.PathParameter("traitType")) if err != nil { bcode.ReturnError(req, res, err) @@ -1001,9 +993,9 @@ func (c *applicationWebService) deleteApplicationTrait(req *restful.Request, res } } -func (c *applicationWebService) getApplicationStatus(req *restful.Request, res *restful.Response) { +func (c *applicationAPIInterface) getApplicationStatus(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) - status, err := c.applicationUsecase.GetApplicationStatus(req.Request.Context(), app, req.PathParameter("envName")) + status, err := c.ApplicationService.GetApplicationStatus(req.Request.Context(), app, req.PathParameter("envName")) if err != nil { bcode.ReturnError(req, res, err) return @@ -1015,14 +1007,14 @@ func (c *applicationWebService) getApplicationStatus(req *restful.Request, res * } } -func (c *applicationWebService) listApplicationRevisions(req *restful.Request, res *restful.Response) { +func (c *applicationAPIInterface) listApplicationRevisions(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) page, pageSize, err := utils.ExtractPagingParams(req, minPageSize, maxPageSize) if err != nil { bcode.ReturnError(req, res, err) return } - revisions, err := c.applicationUsecase.ListRevisions(req.Request.Context(), app.Name, req.QueryParameter("envName"), req.QueryParameter("status"), page, pageSize) + revisions, err := c.ApplicationService.ListRevisions(req.Request.Context(), app.Name, req.QueryParameter("envName"), req.QueryParameter("status"), page, pageSize) if err != nil { bcode.ReturnError(req, res, err) return @@ -1033,9 +1025,9 @@ func (c *applicationWebService) listApplicationRevisions(req *restful.Request, r } } -func (c *applicationWebService) detailApplicationRevision(req *restful.Request, res *restful.Response) { +func (c *applicationAPIInterface) detailApplicationRevision(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) - detail, err := c.applicationUsecase.DetailRevision(req.Request.Context(), app.Name, req.PathParameter("revision")) + detail, err := c.ApplicationService.DetailRevision(req.Request.Context(), app.Name, req.PathParameter("revision")) if err != nil { bcode.ReturnError(req, res, err) return @@ -1046,7 +1038,7 @@ func (c *applicationWebService) detailApplicationRevision(req *restful.Request, } } -func (c *applicationWebService) updateApplicationEnv(req *restful.Request, res *restful.Response) { +func (c *applicationAPIInterface) updateApplicationEnv(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) // Verify the validity of parameters var updateReq apis.PutApplicationEnvBindingRequest @@ -1058,7 +1050,7 @@ func (c *applicationWebService) updateApplicationEnv(req *restful.Request, res * bcode.ReturnError(req, res, err) return } - diff, err := c.envBindingUsecase.UpdateEnvBinding(req.Request.Context(), app, req.PathParameter("envName"), updateReq) + diff, err := c.EnvBindingService.UpdateEnvBinding(req.Request.Context(), app, req.PathParameter("envName"), updateReq) if err != nil { bcode.ReturnError(req, res, err) return @@ -1069,9 +1061,9 @@ func (c *applicationWebService) updateApplicationEnv(req *restful.Request, res * } } -func (c *applicationWebService) listApplicationEnvs(req *restful.Request, res *restful.Response) { +func (c *applicationAPIInterface) listApplicationEnvs(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) - envBindings, err := c.envBindingUsecase.GetEnvBindings(req.Request.Context(), app) + envBindings, err := c.EnvBindingService.GetEnvBindings(req.Request.Context(), app) if err != nil { bcode.ReturnError(req, res, err) return @@ -1082,7 +1074,7 @@ func (c *applicationWebService) listApplicationEnvs(req *restful.Request, res *r } } -func (c *applicationWebService) createApplicationEnv(req *restful.Request, res *restful.Response) { +func (c *applicationAPIInterface) createApplicationEnv(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) // Verify the validity of parameters var createReq apis.CreateApplicationEnvbindingRequest @@ -1094,7 +1086,7 @@ func (c *applicationWebService) createApplicationEnv(req *restful.Request, res * bcode.ReturnError(req, res, err) return } - base, err := c.envBindingUsecase.CreateEnvBinding(req.Request.Context(), app, createReq) + base, err := c.EnvBindingService.CreateEnvBinding(req.Request.Context(), app, createReq) if err != nil { bcode.ReturnError(req, res, err) return @@ -1105,9 +1097,9 @@ func (c *applicationWebService) createApplicationEnv(req *restful.Request, res * } } -func (c *applicationWebService) deleteApplicationEnv(req *restful.Request, res *restful.Response) { +func (c *applicationAPIInterface) deleteApplicationEnv(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) - err := c.envBindingUsecase.DeleteEnvBinding(req.Request.Context(), app, req.PathParameter("envName")) + err := c.EnvBindingService.DeleteEnvBinding(req.Request.Context(), app, req.PathParameter("envName")) if err != nil { bcode.ReturnError(req, res, err) return @@ -1118,8 +1110,8 @@ func (c *applicationWebService) deleteApplicationEnv(req *restful.Request, res * } } -func (c *applicationWebService) appCheckFilter(req *restful.Request, res *restful.Response, chain *restful.FilterChain) { - app, err := c.applicationUsecase.GetApplication(req.Request.Context(), req.PathParameter("appName")) +func (c *applicationAPIInterface) appCheckFilter(req *restful.Request, res *restful.Response, chain *restful.FilterChain) { + app, err := c.ApplicationService.GetApplication(req.Request.Context(), req.PathParameter("appName")) if err != nil { bcode.ReturnError(req, res, err) return @@ -1128,9 +1120,9 @@ func (c *applicationWebService) appCheckFilter(req *restful.Request, res *restfu chain.ProcessFilter(req, res) } -func (c *applicationWebService) componentCheckFilter(req *restful.Request, res *restful.Response, chain *restful.FilterChain) { +func (c *applicationAPIInterface) componentCheckFilter(req *restful.Request, res *restful.Response, chain *restful.FilterChain) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) - component, err := c.applicationUsecase.GetApplicationComponent(req.Request.Context(), app, req.PathParameter("compName")) + component, err := c.ApplicationService.GetApplicationComponent(req.Request.Context(), app, req.PathParameter("compName")) if err != nil { bcode.ReturnError(req, res, err) return @@ -1139,9 +1131,9 @@ func (c *applicationWebService) componentCheckFilter(req *restful.Request, res * chain.ProcessFilter(req, res) } -func (c *applicationWebService) envCheckFilter(req *restful.Request, res *restful.Response, chain *restful.FilterChain) { +func (c *applicationAPIInterface) envCheckFilter(req *restful.Request, res *restful.Response, chain *restful.FilterChain) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) - envBinding, err := c.envBindingUsecase.GetEnvBinding(req.Request.Context(), app, req.PathParameter("envName")) + envBinding, err := c.EnvBindingService.GetEnvBinding(req.Request.Context(), app, req.PathParameter("envName")) if err != nil { bcode.ReturnError(req, res, err) return @@ -1150,9 +1142,9 @@ func (c *applicationWebService) envCheckFilter(req *restful.Request, res *restfu chain.ProcessFilter(req, res) } -func (c *applicationWebService) applicationStatistics(req *restful.Request, res *restful.Response) { +func (c *applicationAPIInterface) applicationStatistics(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) - detail, err := c.applicationUsecase.Statistics(req.Request.Context(), app) + detail, err := c.ApplicationService.Statistics(req.Request.Context(), app) if err != nil { bcode.ReturnError(req, res, err) return @@ -1163,10 +1155,10 @@ func (c *applicationWebService) applicationStatistics(req *restful.Request, res } } -func (c *applicationWebService) recycleApplicationEnv(req *restful.Request, res *restful.Response) { +func (c *applicationAPIInterface) recycleApplicationEnv(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) env := req.Request.Context().Value(&apis.CtxKeyApplicationEnvBinding).(*model.EnvBinding) - err := c.envBindingUsecase.ApplicationEnvRecycle(req.Request.Context(), app, env) + err := c.EnvBindingService.ApplicationEnvRecycle(req.Request.Context(), app, env) if err != nil { bcode.ReturnError(req, res, err) return @@ -1177,9 +1169,9 @@ func (c *applicationWebService) recycleApplicationEnv(req *restful.Request, res } } -func (c *applicationWebService) listApplicationRecords(req *restful.Request, res *restful.Response) { +func (c *applicationAPIInterface) listApplicationRecords(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) - records, err := c.applicationUsecase.ListRecords(req.Request.Context(), app.Name) + records, err := c.ApplicationService.ListRecords(req.Request.Context(), app.Name) if err != nil { bcode.ReturnError(req, res, err) return @@ -1190,7 +1182,7 @@ func (c *applicationWebService) listApplicationRecords(req *restful.Request, res } } -func (c *applicationWebService) compareAppWithLatestRevision(req *restful.Request, res *restful.Response) { +func (c *applicationAPIInterface) compareAppWithLatestRevision(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) // Verify the validity of parameters var compareReq apis.AppCompareReq @@ -1203,7 +1195,7 @@ func (c *applicationWebService) compareAppWithLatestRevision(req *restful.Reques return } - base, err := c.applicationUsecase.CompareAppWithLatestRevision(req.Request.Context(), app, compareReq) + base, err := c.ApplicationService.CompareAppWithLatestRevision(req.Request.Context(), app, compareReq) if err != nil { bcode.ReturnError(req, res, err) return @@ -1214,10 +1206,10 @@ func (c *applicationWebService) compareAppWithLatestRevision(req *restful.Reques } } -func (c *applicationWebService) resetAppToLatestRevision(req *restful.Request, res *restful.Response) { +func (c *applicationAPIInterface) resetAppToLatestRevision(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) - base, err := c.applicationUsecase.ResetAppToLatestRevision(req.Request.Context(), app.Name) + base, err := c.ApplicationService.ResetAppToLatestRevision(req.Request.Context(), app.Name) if err != nil { bcode.ReturnError(req, res, err) return @@ -1228,7 +1220,7 @@ func (c *applicationWebService) resetAppToLatestRevision(req *restful.Request, r } } -func (c *applicationWebService) dryRunAppOrRevision(req *restful.Request, res *restful.Response) { +func (c *applicationAPIInterface) dryRunAppOrRevision(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) // Verify the validity of parameters var dryRunReq apis.AppDryRunReq @@ -1244,7 +1236,7 @@ func (c *applicationWebService) dryRunAppOrRevision(req *restful.Request, res *r dryRunReq.AppName = app.Name } - base, err := c.applicationUsecase.DryRunAppOrRevision(req.Request.Context(), app, dryRunReq) + base, err := c.ApplicationService.DryRunAppOrRevision(req.Request.Context(), app, dryRunReq) if err != nil { bcode.ReturnError(req, res, err) return diff --git a/pkg/apiserver/interfaces/api/assembler/v1/do2dto.go b/pkg/apiserver/interfaces/api/assembler/v1/do2dto.go new file mode 100644 index 000000000..3093d136e --- /dev/null +++ b/pkg/apiserver/interfaces/api/assembler/v1/do2dto.go @@ -0,0 +1,209 @@ +/* +Copyright 2022 The KubeVela Authors. + +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 v1 + +import ( + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" +) + +// ConvertEnvBindingModelToBase assemble the DTO from EnvBinding model +func ConvertEnvBindingModelToBase(envBinding *model.EnvBinding, env *model.Env, targets []*model.Target) *apisv1.EnvBindingBase { + var dtMap = make(map[string]*model.Target, len(targets)) + for _, dte := range targets { + dtMap[dte.Name] = dte + } + var envBindingTargets []apisv1.EnvBindingTarget + for _, targetName := range env.Targets { + dt := dtMap[targetName] + if dt != nil { + ebt := apisv1.EnvBindingTarget{ + NameAlias: apisv1.NameAlias{Name: dt.Name, Alias: dt.Alias}, + } + if dt.Cluster != nil { + ebt.Cluster = &apisv1.ClusterTarget{ + ClusterName: dt.Cluster.ClusterName, + Namespace: dt.Cluster.Namespace, + } + } + envBindingTargets = append(envBindingTargets, ebt) + } + } + ebb := &apisv1.EnvBindingBase{ + Name: envBinding.Name, + Alias: env.Alias, + Description: env.Description, + TargetNames: env.Targets, + Targets: envBindingTargets, + CreateTime: envBinding.CreateTime, + UpdateTime: envBinding.UpdateTime, + AppDeployName: envBinding.AppDeployName, + AppDeployNamespace: env.Namespace, + } + return ebb +} + +// ConvertAppModelToBase assemble the Application model to DTO +func ConvertAppModelToBase(app *model.Application, projects []*apisv1.ProjectBase) *apisv1.ApplicationBase { + appBase := &apisv1.ApplicationBase{ + Name: app.Name, + Alias: app.Alias, + CreateTime: app.CreateTime, + UpdateTime: app.UpdateTime, + Description: app.Description, + Icon: app.Icon, + Labels: app.Labels, + Project: &apisv1.ProjectBase{Name: app.Project}, + } + if app.IsSynced() { + appBase.ReadOnly = true + } + for _, project := range projects { + if project.Name == app.Project { + appBase.Project = project + } + } + return appBase +} + +// ConvertComponentModelToBase assemble the ApplicationComponent model to DTO +func ConvertComponentModelToBase(componentModel *model.ApplicationComponent) *apisv1.ComponentBase { + if componentModel == nil { + return nil + } + return &apisv1.ComponentBase{ + Name: componentModel.Name, + Alias: componentModel.Alias, + Description: componentModel.Description, + Labels: componentModel.Labels, + ComponentType: componentModel.Type, + Icon: componentModel.Icon, + DependsOn: componentModel.DependsOn, + Inputs: componentModel.Inputs, + Outputs: componentModel.Outputs, + Creator: componentModel.Creator, + Main: componentModel.Main, + CreateTime: componentModel.CreateTime, + UpdateTime: componentModel.UpdateTime, + Traits: func() (traits []*apisv1.ApplicationTrait) { + for _, trait := range componentModel.Traits { + traits = append(traits, &apisv1.ApplicationTrait{ + Type: trait.Type, + Properties: trait.Properties, + Alias: trait.Alias, + Description: trait.Description, + CreateTime: trait.CreateTime, + UpdateTime: trait.UpdateTime, + }) + } + return + }(), + WorkloadType: componentModel.WorkloadType, + } +} + +// ConvertRevisionModelToBase assemble the ApplicationRevision model to DTO +func ConvertRevisionModelToBase(revision *model.ApplicationRevision, user *model.User) apisv1.ApplicationRevisionBase { + base := apisv1.ApplicationRevisionBase{ + Version: revision.Version, + Status: revision.Status, + Reason: revision.Reason, + Note: revision.Note, + TriggerType: revision.TriggerType, + CreateTime: revision.CreateTime, + EnvName: revision.EnvName, + CodeInfo: revision.CodeInfo, + ImageInfo: revision.ImageInfo, + DeployUser: &apisv1.NameAlias{Name: revision.DeployUser}, + } + if user != nil { + base.DeployUser.Alias = user.Alias + } + return base +} + +// ConvertFromRecordModel assemble the WorkflowRecord model to DTO +func ConvertFromRecordModel(record *model.WorkflowRecord) *apisv1.WorkflowRecord { + return &apisv1.WorkflowRecord{ + Name: record.Name, + Namespace: record.Namespace, + WorkflowName: record.WorkflowName, + WorkflowAlias: record.WorkflowAlias, + ApplicationRevision: record.RevisionPrimaryKey, + StartTime: record.StartTime, + Status: record.Status, + Steps: record.Steps, + } +} + +// ConvertFromWorkflowStepModel assemble the WorkflowStep model to DTO +func ConvertFromWorkflowStepModel(step model.WorkflowStep) apisv1.WorkflowStep { + apiStep := apisv1.WorkflowStep{ + Name: step.Name, + Type: step.Type, + Alias: step.Alias, + Description: step.Description, + Inputs: step.Inputs, + Outputs: step.Outputs, + Properties: step.Properties.JSON(), + DependsOn: step.DependsOn, + } + if step.Properties != nil { + apiStep.Properties = step.Properties.JSON() + } + return apiStep +} + +// ConvertWorkflowBase assemble the Workflow model to DTO +func ConvertWorkflowBase(workflow *model.Workflow) apisv1.WorkflowBase { + var steps []apisv1.WorkflowStep + for _, step := range workflow.Steps { + steps = append(steps, ConvertFromWorkflowStepModel(step)) + } + return apisv1.WorkflowBase{ + Name: workflow.Name, + Alias: workflow.Alias, + Description: workflow.Description, + Default: convertBool(workflow.Default), + EnvName: workflow.EnvName, + CreateTime: workflow.CreateTime, + UpdateTime: workflow.UpdateTime, + Steps: steps, + } +} + +// ConvertPolicyModelToBase assemble the ApplicationPolicy model to DTO +func ConvertPolicyModelToBase(policy *model.ApplicationPolicy) *apisv1.PolicyBase { + pb := &apisv1.PolicyBase{ + Name: policy.Name, + Type: policy.Type, + Properties: policy.Properties, + Description: policy.Description, + Creator: policy.Creator, + CreateTime: policy.CreateTime, + UpdateTime: policy.UpdateTime, + EnvName: policy.EnvName, + } + return pb +} + +func convertBool(b *bool) bool { + if b == nil { + return false + } + return *b +} diff --git a/pkg/apiserver/interfaces/api/assembler/v1/dto2do.go b/pkg/apiserver/interfaces/api/assembler/v1/dto2do.go new file mode 100644 index 000000000..56e4baf67 --- /dev/null +++ b/pkg/apiserver/interfaces/api/assembler/v1/dto2do.go @@ -0,0 +1,67 @@ +/* +Copyright 2022 The KubeVela Authors. + +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 v1 + +import ( + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/log" +) + +// CreateEnvBindingModel assemble the EnvBinding model from DTO +func CreateEnvBindingModel(app *model.Application, req apisv1.CreateApplicationEnvbindingRequest) model.EnvBinding { + envBinding := model.EnvBinding{ + AppPrimaryKey: app.Name, + Name: req.Name, + AppDeployName: app.GetAppNameForSynced(), + } + return envBinding +} + +// ConvertToEnvBindingModel assemble the EnvBinding model from DTO +func ConvertToEnvBindingModel(app *model.Application, envBind apisv1.EnvBinding) *model.EnvBinding { + re := model.EnvBinding{ + AppPrimaryKey: app.Name, + Name: envBind.Name, + AppDeployName: app.GetAppNameForSynced(), + } + return &re +} + +// CreateWorkflowStepModel assemble the WorkflowStep model from DTO +func CreateWorkflowStepModel(apiSteps []apisv1.WorkflowStep) ([]model.WorkflowStep, error) { + var steps []model.WorkflowStep + for _, step := range apiSteps { + properties, err := model.NewJSONStructByString(step.Properties) + if err != nil { + log.Logger.Errorf("parse trait properties failure %w", err) + return nil, bcode.ErrInvalidProperties + } + steps = append(steps, model.WorkflowStep{ + Name: step.Name, + Alias: step.Alias, + Description: step.Description, + DependsOn: step.DependsOn, + Type: step.Type, + Inputs: step.Inputs, + Outputs: step.Outputs, + Properties: properties, + }) + } + return steps, nil +} diff --git a/pkg/apiserver/rest/webservice/authentication.go b/pkg/apiserver/interfaces/api/authentication.go similarity index 70% rename from pkg/apiserver/rest/webservice/authentication.go rename to pkg/apiserver/interfaces/api/authentication.go index ace5354cc..bc080f15d 100644 --- a/pkg/apiserver/rest/webservice/authentication.go +++ b/pkg/apiserver/interfaces/api/authentication.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package webservice +package api import ( "context" @@ -23,25 +23,22 @@ import ( restfulspec "github.com/emicklei/go-restful-openapi/v2" "github.com/emicklei/go-restful/v3" - apis "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/usecase" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/service" + apis "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" ) -type authenticationWebService struct { - authenticationUsecase usecase.AuthenticationUsecase - userUsecase usecase.UserUsecase +type authenticationAPIInterface struct { + AuthenticationService service.AuthenticationService `inject:""` + UserService service.UserService `inject:""` } -// NewAuthenticationWebService is the webservice of authentication -func NewAuthenticationWebService(authenticationUsecase usecase.AuthenticationUsecase, userUsecase usecase.UserUsecase) WebService { - return &authenticationWebService{ - authenticationUsecase: authenticationUsecase, - userUsecase: userUsecase, - } +// NewAuthenticationAPIInterface is the APIInterface of authentication +func NewAuthenticationAPIInterface() Interface { + return &authenticationAPIInterface{} } -func (c *authenticationWebService) GetWebService() *restful.WebService { +func (c *authenticationAPIInterface) GetWebServiceRoute() *restful.WebService { ws := new(restful.WebService) ws.Path(versionPrefix+"/auth"). Consumes(restful.MIME_XML, restful.MIME_JSON). @@ -101,12 +98,12 @@ func authCheckFilter(req *restful.Request, res *restful.Response, chain *restful return } - token, err := usecase.ParseToken(splitted[1]) + token, err := service.ParseToken(splitted[1]) if err != nil { bcode.ReturnError(req, res, err) return } - if token.GrantType != usecase.GrantTypeAccess { + if token.GrantType != service.GrantTypeAccess { bcode.ReturnError(req, res, bcode.ErrNotAccessToken) return } @@ -115,13 +112,13 @@ func authCheckFilter(req *restful.Request, res *restful.Response, chain *restful chain.ProcessFilter(req, res) } -func (c *authenticationWebService) login(req *restful.Request, res *restful.Response) { +func (c *authenticationAPIInterface) login(req *restful.Request, res *restful.Response) { var loginReq apis.LoginRequest if err := req.ReadEntity(&loginReq); err != nil { bcode.ReturnError(req, res, err) return } - base, err := c.authenticationUsecase.Login(req.Request.Context(), loginReq) + base, err := c.AuthenticationService.Login(req.Request.Context(), loginReq) if err != nil { bcode.ReturnError(req, res, err) return @@ -132,8 +129,8 @@ func (c *authenticationWebService) login(req *restful.Request, res *restful.Resp } } -func (c *authenticationWebService) getDexConfig(req *restful.Request, res *restful.Response) { - base, err := c.authenticationUsecase.GetDexConfig(req.Request.Context()) +func (c *authenticationAPIInterface) getDexConfig(req *restful.Request, res *restful.Response) { + base, err := c.AuthenticationService.GetDexConfig(req.Request.Context()) if err != nil { bcode.ReturnError(req, res, err) return @@ -144,8 +141,8 @@ func (c *authenticationWebService) getDexConfig(req *restful.Request, res *restf } } -func (c *authenticationWebService) refreshToken(req *restful.Request, res *restful.Response) { - base, err := c.authenticationUsecase.RefreshToken(req.Request.Context(), req.HeaderParameter("RefreshToken")) +func (c *authenticationAPIInterface) refreshToken(req *restful.Request, res *restful.Response) { + base, err := c.AuthenticationService.RefreshToken(req.Request.Context(), req.HeaderParameter("RefreshToken")) if err != nil { bcode.ReturnError(req, res, err) return @@ -156,8 +153,8 @@ func (c *authenticationWebService) refreshToken(req *restful.Request, res *restf } } -func (c *authenticationWebService) getLoginType(req *restful.Request, res *restful.Response) { - base, err := c.authenticationUsecase.GetLoginType(req.Request.Context()) +func (c *authenticationAPIInterface) getLoginType(req *restful.Request, res *restful.Response) { + base, err := c.AuthenticationService.GetLoginType(req.Request.Context()) if err != nil { bcode.ReturnError(req, res, err) return @@ -168,8 +165,8 @@ func (c *authenticationWebService) getLoginType(req *restful.Request, res *restf } } -func (c *authenticationWebService) getLoginUserInfo(req *restful.Request, res *restful.Response) { - info, err := c.userUsecase.DetailLoginUserInfo(req.Request.Context()) +func (c *authenticationAPIInterface) getLoginUserInfo(req *restful.Request, res *restful.Response) { + info, err := c.UserService.DetailLoginUserInfo(req.Request.Context()) if err != nil { bcode.ReturnError(req, res, err) return diff --git a/pkg/apiserver/rest/webservice/cluster.go b/pkg/apiserver/interfaces/api/cluster.go similarity index 77% rename from pkg/apiserver/rest/webservice/cluster.go rename to pkg/apiserver/interfaces/api/cluster.go index 1fc59b6b3..1374948fd 100644 --- a/pkg/apiserver/rest/webservice/cluster.go +++ b/pkg/apiserver/interfaces/api/cluster.go @@ -14,31 +14,31 @@ See the License for the specific language governing permissions and limitations under the License. */ -package webservice +package api import ( restfulspec "github.com/emicklei/go-restful-openapi/v2" restful "github.com/emicklei/go-restful/v3" - apis "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/usecase" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/service" + apis "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" ) -// ClusterWebService cluster manage webservice -type ClusterWebService struct { - clusterUsecase usecase.ClusterUsecase - rbacUsecase usecase.RBACUsecase +// ClusterAPIInterface cluster manage APIInterface +type ClusterAPIInterface struct { + ClusterService service.ClusterService `inject:""` + RbacService service.RBACService `inject:""` } -// NewClusterWebService new cluster webservice -func NewClusterWebService(clusterUsecase usecase.ClusterUsecase, rbacUsecase usecase.RBACUsecase) *ClusterWebService { - return &ClusterWebService{clusterUsecase: clusterUsecase, rbacUsecase: rbacUsecase} +// NewClusterAPIInterface new cluster APIInterface +func NewClusterAPIInterface() *ClusterAPIInterface { + return &ClusterAPIInterface{} } -// GetWebService - -func (c *ClusterWebService) GetWebService() *restful.WebService { +// GetWebServiceRoute - +func (c *ClusterAPIInterface) GetWebServiceRoute() *restful.WebService { ws := new(restful.WebService) ws.Path(versionPrefix+"/clusters"). Consumes(restful.MIME_XML, restful.MIME_JSON). @@ -50,7 +50,7 @@ func (c *ClusterWebService) GetWebService() *restful.WebService { ws.Route(ws.GET("/").To(c.listKubeClusters). Doc("list all clusters"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(c.rbacUsecase.CheckPerm("cluster", "list")). + Filter(c.RbacService.CheckPerm("cluster", "list")). Param(ws.QueryParameter("query", "Fuzzy search based on name or description").DataType("string")). Param(ws.QueryParameter("page", "Page for paging").DataType("integer").DefaultValue("0")). Param(ws.QueryParameter("pageSize", "PageSize for paging").DataType("integer").DefaultValue("20")). @@ -62,7 +62,7 @@ func (c *ClusterWebService) GetWebService() *restful.WebService { Doc("create cluster"). Metadata(restfulspec.KeyOpenAPITags, tags). Reads(apis.CreateClusterRequest{}). - Filter(c.rbacUsecase.CheckPerm("cluster", "create")). + Filter(c.RbacService.CheckPerm("cluster", "create")). Returns(200, "OK", apis.ClusterBase{}). Returns(400, "Bad Request", bcode.Bcode{}). Writes(apis.ClusterBase{})) @@ -70,7 +70,7 @@ func (c *ClusterWebService) GetWebService() *restful.WebService { ws.Route(ws.GET("/{clusterName}").To(c.getKubeCluster). Doc("detail cluster info"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(c.rbacUsecase.CheckPerm("cluster", "detail")). + Filter(c.RbacService.CheckPerm("cluster", "detail")). Param(ws.PathParameter("clusterName", "identifier of the cluster").DataType("string")). Returns(200, "OK", apis.DetailClusterResponse{}). Returns(400, "Bad Request", bcode.Bcode{}). @@ -79,7 +79,7 @@ func (c *ClusterWebService) GetWebService() *restful.WebService { ws.Route(ws.PUT("/{clusterName}").To(c.modifyKubeCluster). Doc("modify cluster"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(c.rbacUsecase.CheckPerm("cluster", "update")). + Filter(c.RbacService.CheckPerm("cluster", "update")). Param(ws.PathParameter("clusterName", "identifier of the cluster").DataType("string")). Reads(apis.CreateClusterRequest{}). Returns(200, "OK", apis.ClusterBase{}). @@ -89,7 +89,7 @@ func (c *ClusterWebService) GetWebService() *restful.WebService { ws.Route(ws.DELETE("/{clusterName}").To(c.deleteKubeCluster). Doc("delete cluster"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(c.rbacUsecase.CheckPerm("cluster", "delete")). + Filter(c.RbacService.CheckPerm("cluster", "delete")). Param(ws.PathParameter("clusterName", "identifier of the cluster").DataType("string")). Returns(200, "OK", apis.ClusterBase{}). Returns(400, "Bad Request", bcode.Bcode{}). @@ -100,7 +100,7 @@ func (c *ClusterWebService) GetWebService() *restful.WebService { Metadata(restfulspec.KeyOpenAPITags, tags). Param(ws.PathParameter("clusterName", "name of the target cluster").DataType("string")). Reads(apis.CreateClusterNamespaceRequest{}). - Filter(c.rbacUsecase.CheckPerm("cluster/namespace", "create")). + Filter(c.RbacService.CheckPerm("cluster/namespace", "create")). Returns(200, "OK", apis.CreateClusterNamespaceResponse{}). Returns(400, "Bad Request", bcode.Bcode{}). Writes(apis.CreateClusterNamespaceResponse{})) @@ -108,7 +108,7 @@ func (c *ClusterWebService) GetWebService() *restful.WebService { ws.Route(ws.POST("/cloud_clusters/{provider}").To(c.listCloudClusters). Doc("list cloud clusters"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(c.rbacUsecase.CheckPerm("cluster", "create")). + Filter(c.RbacService.CheckPerm("cluster", "create")). Param(ws.PathParameter("provider", "identifier of the cloud provider").DataType("string")). Param(ws.QueryParameter("page", "Page for paging").DataType("integer").DefaultValue("0")). Param(ws.QueryParameter("pageSize", "PageSize for paging").DataType("integer").DefaultValue("20")). @@ -120,7 +120,7 @@ func (c *ClusterWebService) GetWebService() *restful.WebService { ws.Route(ws.POST("/cloud_clusters/{provider}/connect").To(c.connectCloudCluster). Doc("create cluster from cloud cluster"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(c.rbacUsecase.CheckPerm("cluster", "create")). + Filter(c.RbacService.CheckPerm("cluster", "create")). Param(ws.PathParameter("provider", "identifier of the cloud provider").DataType("string")). Reads(apis.ConnectCloudClusterRequest{}). Returns(200, "OK", apis.ClusterBase{}). @@ -130,7 +130,7 @@ func (c *ClusterWebService) GetWebService() *restful.WebService { ws.Route(ws.POST("/cloud_clusters/{provider}/create").To(c.createCloudCluster). Doc("create cloud cluster"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(c.rbacUsecase.CheckPerm("cluster", "create")). + Filter(c.RbacService.CheckPerm("cluster", "create")). Param(ws.PathParameter("provider", "identifier of the cloud provider").DataType("string").Required(true)). Reads(apis.CreateCloudClusterRequest{}). Returns(200, "OK", apis.CreateCloudClusterResponse{}). @@ -140,7 +140,7 @@ func (c *ClusterWebService) GetWebService() *restful.WebService { ws.Route(ws.GET("/cloud_clusters/{provider}/creation/{cloudClusterName}").To(c.getCloudClusterCreationStatus). Doc("check cloud cluster create status"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(c.rbacUsecase.CheckPerm("cluster", "create")). + Filter(c.RbacService.CheckPerm("cluster", "create")). Param(ws.PathParameter("provider", "identifier of the cloud provider").DataType("string")). Param(ws.PathParameter("cloudClusterName", "identifier for cloud cluster which is creating").DataType("string")). Returns(200, "OK", apis.CreateCloudClusterResponse{}). @@ -149,7 +149,7 @@ func (c *ClusterWebService) GetWebService() *restful.WebService { ws.Route(ws.GET("/cloud_clusters/{provider}/creation").To(c.listCloudClusterCreation). Doc("list cloud cluster creation"). - Filter(c.rbacUsecase.CheckPerm("cluster", "create")). + Filter(c.RbacService.CheckPerm("cluster", "create")). Metadata(restfulspec.KeyOpenAPITags, tags). Param(ws.PathParameter("provider", "identifier of the cloud provider").DataType("string")). Returns(200, "OK", apis.ListCloudClusterCreationResponse{}). @@ -159,7 +159,7 @@ func (c *ClusterWebService) GetWebService() *restful.WebService { ws.Route(ws.DELETE("/cloud_clusters/{provider}/creation/{cloudClusterName}").To(c.deleteCloudClusterCreation). Doc("delete cloud cluster creation"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(c.rbacUsecase.CheckPerm("cluster", "create")). + Filter(c.RbacService.CheckPerm("cluster", "create")). Param(ws.PathParameter("provider", "identifier of the cloud provider").DataType("string")). Param(ws.PathParameter("cloudClusterName", "identifier for cloud cluster which is creating").DataType("string")). Returns(200, "OK", apis.CreateCloudClusterResponse{}). @@ -170,7 +170,7 @@ func (c *ClusterWebService) GetWebService() *restful.WebService { return ws } -func (c *ClusterWebService) listKubeClusters(req *restful.Request, res *restful.Response) { +func (c *ClusterAPIInterface) listKubeClusters(req *restful.Request, res *restful.Response) { query := req.QueryParameter("query") page, pageSize, err := utils.ExtractPagingParams(req, minPageSize, maxPageSize) if err != nil { @@ -178,8 +178,8 @@ func (c *ClusterWebService) listKubeClusters(req *restful.Request, res *restful. return } - // Call the usecase layer code - clusters, err := c.clusterUsecase.ListKubeClusters(req.Request.Context(), query, page, pageSize) + // Call the domain layer code + clusters, err := c.ClusterService.ListKubeClusters(req.Request.Context(), query, page, pageSize) if err != nil { bcode.ReturnError(req, res, err) return @@ -192,7 +192,7 @@ func (c *ClusterWebService) listKubeClusters(req *restful.Request, res *restful. } } -func (c *ClusterWebService) createKubeCluster(req *restful.Request, res *restful.Response) { +func (c *ClusterAPIInterface) createKubeCluster(req *restful.Request, res *restful.Response) { // Verify the validity of parameters var createReq apis.CreateClusterRequest if err := req.ReadEntity(&createReq); err != nil { @@ -203,8 +203,8 @@ func (c *ClusterWebService) createKubeCluster(req *restful.Request, res *restful bcode.ReturnError(req, res, err) return } - // Call the usecase layer code - clusterBase, err := c.clusterUsecase.CreateKubeCluster(req.Request.Context(), createReq) + // Call the domain layer code + clusterBase, err := c.ClusterService.CreateKubeCluster(req.Request.Context(), createReq) if err != nil { bcode.ReturnError(req, res, err) return @@ -217,11 +217,11 @@ func (c *ClusterWebService) createKubeCluster(req *restful.Request, res *restful } } -func (c *ClusterWebService) getKubeCluster(req *restful.Request, res *restful.Response) { +func (c *ClusterAPIInterface) getKubeCluster(req *restful.Request, res *restful.Response) { clusterName := req.PathParameter("clusterName") - // Call the usecase layer code - clusterDetail, err := c.clusterUsecase.GetKubeCluster(req.Request.Context(), clusterName) + // Call the domain layer code + clusterDetail, err := c.ClusterService.GetKubeCluster(req.Request.Context(), clusterName) if err != nil { bcode.ReturnError(req, res, err) return @@ -234,7 +234,7 @@ func (c *ClusterWebService) getKubeCluster(req *restful.Request, res *restful.Re } } -func (c *ClusterWebService) modifyKubeCluster(req *restful.Request, res *restful.Response) { +func (c *ClusterAPIInterface) modifyKubeCluster(req *restful.Request, res *restful.Response) { // Verify the validity of parameters var createReq apis.CreateClusterRequest if err := req.ReadEntity(&createReq); err != nil { @@ -247,8 +247,8 @@ func (c *ClusterWebService) modifyKubeCluster(req *restful.Request, res *restful } clusterName := req.PathParameter("clusterName") - // Call the usecase layer code - clusterBase, err := c.clusterUsecase.ModifyKubeCluster(req.Request.Context(), createReq, clusterName) + // Call the domain layer code + clusterBase, err := c.ClusterService.ModifyKubeCluster(req.Request.Context(), createReq, clusterName) if err != nil { bcode.ReturnError(req, res, err) return @@ -261,11 +261,11 @@ func (c *ClusterWebService) modifyKubeCluster(req *restful.Request, res *restful } } -func (c *ClusterWebService) deleteKubeCluster(req *restful.Request, res *restful.Response) { +func (c *ClusterAPIInterface) deleteKubeCluster(req *restful.Request, res *restful.Response) { clusterName := req.PathParameter("clusterName") - // Call the usecase layer code - clusterBase, err := c.clusterUsecase.DeleteKubeCluster(req.Request.Context(), clusterName) + // Call the domain layer code + clusterBase, err := c.ClusterService.DeleteKubeCluster(req.Request.Context(), clusterName) if err != nil { bcode.ReturnError(req, res, err) return @@ -278,7 +278,7 @@ func (c *ClusterWebService) deleteKubeCluster(req *restful.Request, res *restful } } -func (c *ClusterWebService) createNamespace(req *restful.Request, res *restful.Response) { +func (c *ClusterAPIInterface) createNamespace(req *restful.Request, res *restful.Response) { clusterName := req.PathParameter("clusterName") // Verify the validity of parameters @@ -292,8 +292,8 @@ func (c *ClusterWebService) createNamespace(req *restful.Request, res *restful.R return } - // Call the usecase layer code - resp, err := c.clusterUsecase.CreateClusterNamespace(req.Request.Context(), clusterName, createReq) + // Call the domain layer code + resp, err := c.ClusterService.CreateClusterNamespace(req.Request.Context(), clusterName, createReq) if err != nil { bcode.ReturnError(req, res, err) return @@ -306,7 +306,7 @@ func (c *ClusterWebService) createNamespace(req *restful.Request, res *restful.R } } -func (c *ClusterWebService) listCloudClusters(req *restful.Request, res *restful.Response) { +func (c *ClusterAPIInterface) listCloudClusters(req *restful.Request, res *restful.Response) { provider := req.PathParameter("provider") page, pageSize, err := utils.ExtractPagingParams(req, minPageSize, maxPageSize) if err != nil { @@ -325,8 +325,8 @@ func (c *ClusterWebService) listCloudClusters(req *restful.Request, res *restful return } - // Call the usecase layer code - clustersResp, err := c.clusterUsecase.ListCloudClusters(req.Request.Context(), provider, accessKeyRequest, page, pageSize) + // Call the domain layer code + clustersResp, err := c.ClusterService.ListCloudClusters(req.Request.Context(), provider, accessKeyRequest, page, pageSize) if err != nil { bcode.ReturnError(req, res, err) return @@ -339,7 +339,7 @@ func (c *ClusterWebService) listCloudClusters(req *restful.Request, res *restful } } -func (c *ClusterWebService) connectCloudCluster(req *restful.Request, res *restful.Response) { +func (c *ClusterAPIInterface) connectCloudCluster(req *restful.Request, res *restful.Response) { provider := req.PathParameter("provider") // Verify the validity of parameters @@ -353,8 +353,8 @@ func (c *ClusterWebService) connectCloudCluster(req *restful.Request, res *restf return } - // Call the usecase layer code - cluster, err := c.clusterUsecase.ConnectCloudCluster(req.Request.Context(), provider, connectReq) + // Call the domain layer code + cluster, err := c.ClusterService.ConnectCloudCluster(req.Request.Context(), provider, connectReq) if err != nil { bcode.ReturnError(req, res, err) return @@ -367,7 +367,7 @@ func (c *ClusterWebService) connectCloudCluster(req *restful.Request, res *restf } } -func (c *ClusterWebService) createCloudCluster(req *restful.Request, res *restful.Response) { +func (c *ClusterAPIInterface) createCloudCluster(req *restful.Request, res *restful.Response) { provider := req.PathParameter("provider") // Verify the validity of parameters @@ -381,8 +381,8 @@ func (c *ClusterWebService) createCloudCluster(req *restful.Request, res *restfu return } - // Call the usecase layer code - resp, err := c.clusterUsecase.CreateCloudCluster(req.Request.Context(), provider, createReq) + // Call the domain layer code + resp, err := c.ClusterService.CreateCloudCluster(req.Request.Context(), provider, createReq) if err != nil { bcode.ReturnError(req, res, err) return @@ -395,12 +395,12 @@ func (c *ClusterWebService) createCloudCluster(req *restful.Request, res *restfu } } -func (c *ClusterWebService) getCloudClusterCreationStatus(req *restful.Request, res *restful.Response) { +func (c *ClusterAPIInterface) getCloudClusterCreationStatus(req *restful.Request, res *restful.Response) { provider := req.PathParameter("provider") cloudClusterName := req.PathParameter("cloudClusterName") - // Call the usecase layer code - resp, err := c.clusterUsecase.GetCloudClusterCreationStatus(req.Request.Context(), provider, cloudClusterName) + // Call the domain layer code + resp, err := c.ClusterService.GetCloudClusterCreationStatus(req.Request.Context(), provider, cloudClusterName) if err != nil { bcode.ReturnError(req, res, err) return @@ -413,11 +413,11 @@ func (c *ClusterWebService) getCloudClusterCreationStatus(req *restful.Request, } } -func (c *ClusterWebService) listCloudClusterCreation(req *restful.Request, res *restful.Response) { +func (c *ClusterAPIInterface) listCloudClusterCreation(req *restful.Request, res *restful.Response) { provider := req.PathParameter("provider") - // Call the usecase layer code - resp, err := c.clusterUsecase.ListCloudClusterCreation(req.Request.Context(), provider) + // Call the domain layer code + resp, err := c.ClusterService.ListCloudClusterCreation(req.Request.Context(), provider) if err != nil { bcode.ReturnError(req, res, err) return @@ -430,12 +430,12 @@ func (c *ClusterWebService) listCloudClusterCreation(req *restful.Request, res * } } -func (c *ClusterWebService) deleteCloudClusterCreation(req *restful.Request, res *restful.Response) { +func (c *ClusterAPIInterface) deleteCloudClusterCreation(req *restful.Request, res *restful.Response) { provider := req.PathParameter("provider") cloudClusterName := req.PathParameter("cloudClusterName") - // Call the usecase layer code - resp, err := c.clusterUsecase.DeleteCloudClusterCreation(req.Request.Context(), provider, cloudClusterName) + // Call the domain layer code + resp, err := c.ClusterService.DeleteCloudClusterCreation(req.Request.Context(), provider, cloudClusterName) if err != nil { bcode.ReturnError(req, res, err) return diff --git a/pkg/apiserver/rest/webservice/config.go b/pkg/apiserver/interfaces/api/config.go similarity index 70% rename from pkg/apiserver/rest/webservice/config.go rename to pkg/apiserver/interfaces/api/config.go index 2deb48738..b9db648e5 100644 --- a/pkg/apiserver/rest/webservice/config.go +++ b/pkg/apiserver/interfaces/api/config.go @@ -14,31 +14,28 @@ See the License for the specific language governing permissions and limitations under the License. */ -package webservice +package api import ( restfulspec "github.com/emicklei/go-restful-openapi/v2" "github.com/emicklei/go-restful/v3" - apis "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/usecase" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/service" + apis "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" ) -// ConfigWebService returns config web service -func ConfigWebService(u usecase.ConfigHandler, rbacUseCase usecase.RBACUsecase) WebService { - return &configWebService{ - handler: u, - rbacUseCase: rbacUseCase, - } +// ConfigAPIInterface returns config web service +func ConfigAPIInterface() Interface { + return &configAPIInterface{} } -type configWebService struct { - handler usecase.ConfigHandler - rbacUseCase usecase.RBACUsecase +type configAPIInterface struct { + ConfigService service.ConfigService `inject:""` + RbacService service.RBACService `inject:""` } -func (s *configWebService) GetWebService() *restful.WebService { +func (s *configAPIInterface) GetWebServiceRoute() *restful.WebService { ws := new(restful.WebService) ws.Path(versionPrefix+"/config_types"). Consumes(restful.MIME_XML, restful.MIME_JSON). @@ -50,7 +47,7 @@ func (s *configWebService) GetWebService() *restful.WebService { ws.Route(ws.GET("/").To(s.listConfigTypes). Doc("list all config types"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(s.rbacUseCase.CheckPerm("configType", "list")). + Filter(s.RbacService.CheckPerm("configType", "list")). Param(ws.QueryParameter("query", "Fuzzy search based on name and description.").DataType("string")). Returns(200, "OK", []apis.ConfigType{}). Returns(400, "Bad Request", bcode.Bcode{}). @@ -59,7 +56,7 @@ func (s *configWebService) GetWebService() *restful.WebService { ws.Route(ws.GET("/{configType}").To(s.getConfigType). Doc("get a config type"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(s.rbacUseCase.CheckPerm("configType", "get")). + Filter(s.RbacService.CheckPerm("configType", "get")). Param(ws.PathParameter("configType", "identifier of the config type").DataType("string")). Returns(200, "OK", apis.ConfigType{}). Returns(400, "Bad Request", bcode.Bcode{}). @@ -68,7 +65,7 @@ func (s *configWebService) GetWebService() *restful.WebService { ws.Route(ws.POST("/{configType}").To(s.createConfig). Doc("create or update a config"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(s.rbacUseCase.CheckPerm("configType", "create")). + Filter(s.RbacService.CheckPerm("configType", "create")). Param(ws.PathParameter("configType", "identifier of the config type").DataType("string")). Reads(apis.CreateConfigRequest{}). Returns(200, "OK", apis.EmptyResponse{}). @@ -79,7 +76,7 @@ func (s *configWebService) GetWebService() *restful.WebService { ws.Route(ws.GET("/{configType}/configs").To(s.getConfigs). Doc("get configs from a config type"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(s.rbacUseCase.CheckPerm("config", "list")). + Filter(s.RbacService.CheckPerm("config", "list")). Param(ws.PathParameter("configType", "identifier of the config").DataType("string")). Returns(200, "OK", []*apis.Config{}). Returns(400, "Bad Request", bcode.Bcode{}). @@ -88,7 +85,7 @@ func (s *configWebService) GetWebService() *restful.WebService { ws.Route(ws.GET("/{configType}/configs/{name}").To(s.getConfig). Doc("get a config from a config type"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(s.rbacUseCase.CheckPerm("config", "get")). + Filter(s.RbacService.CheckPerm("config", "get")). Param(ws.PathParameter("configType", "identifier of the config type").DataType("string")). Param(ws.PathParameter("name", "identifier of the config").DataType("string")). Returns(200, "OK", []*apis.Config{}). @@ -98,7 +95,7 @@ func (s *configWebService) GetWebService() *restful.WebService { ws.Route(ws.DELETE("/{configType}/configs/{name}").To(s.deleteConfig). Doc("delete a config"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(s.rbacUseCase.CheckPerm("config", "delete")). + Filter(s.RbacService.CheckPerm("config", "delete")). Param(ws.PathParameter("configType", "identifier of the config type").DataType("string")). Param(ws.PathParameter("name", "identifier of the config").DataType("string")). Returns(200, "OK", apis.EmptyResponse{}). @@ -110,8 +107,8 @@ func (s *configWebService) GetWebService() *restful.WebService { return ws } -func (s *configWebService) listConfigTypes(req *restful.Request, res *restful.Response) { - types, err := s.handler.ListConfigTypes(req.Request.Context(), req.QueryParameter("query")) +func (s *configAPIInterface) listConfigTypes(req *restful.Request, res *restful.Response) { + types, err := s.ConfigService.ListConfigTypes(req.Request.Context(), req.QueryParameter("query")) if len(types) == 0 && err != nil { bcode.ReturnError(req, res, err) return @@ -123,8 +120,8 @@ func (s *configWebService) listConfigTypes(req *restful.Request, res *restful.Re } } -func (s *configWebService) getConfigType(req *restful.Request, res *restful.Response) { - t, err := s.handler.GetConfigType(req.Request.Context(), req.PathParameter("configType")) +func (s *configAPIInterface) getConfigType(req *restful.Request, res *restful.Response) { + t, err := s.ConfigService.GetConfigType(req.Request.Context(), req.PathParameter("configType")) if err != nil { bcode.ReturnError(req, res, err) return @@ -136,7 +133,7 @@ func (s *configWebService) getConfigType(req *restful.Request, res *restful.Resp } } -func (s *configWebService) createConfig(req *restful.Request, res *restful.Response) { +func (s *configAPIInterface) createConfig(req *restful.Request, res *restful.Response) { // Verify the validity of parameters var createReq apis.CreateConfigRequest if err := req.ReadEntity(&createReq); err != nil { @@ -148,7 +145,7 @@ func (s *configWebService) createConfig(req *restful.Request, res *restful.Respo return } - err := s.handler.CreateConfig(req.Request.Context(), createReq) + err := s.ConfigService.CreateConfig(req.Request.Context(), createReq) if err != nil { bcode.ReturnError(req, res, err) return @@ -159,8 +156,8 @@ func (s *configWebService) createConfig(req *restful.Request, res *restful.Respo } } -func (s *configWebService) getConfigs(req *restful.Request, res *restful.Response) { - configs, err := s.handler.GetConfigs(req.Request.Context(), req.PathParameter("configType")) +func (s *configAPIInterface) getConfigs(req *restful.Request, res *restful.Response) { + configs, err := s.ConfigService.GetConfigs(req.Request.Context(), req.PathParameter("configType")) if err != nil { bcode.ReturnError(req, res, err) return @@ -172,8 +169,8 @@ func (s *configWebService) getConfigs(req *restful.Request, res *restful.Respons } } -func (s *configWebService) getConfig(req *restful.Request, res *restful.Response) { - t, err := s.handler.GetConfig(req.Request.Context(), req.PathParameter("configType"), req.PathParameter("name")) +func (s *configAPIInterface) getConfig(req *restful.Request, res *restful.Response) { + t, err := s.ConfigService.GetConfig(req.Request.Context(), req.PathParameter("configType"), req.PathParameter("name")) if err != nil { bcode.ReturnError(req, res, err) return @@ -185,8 +182,8 @@ func (s *configWebService) getConfig(req *restful.Request, res *restful.Response } } -func (s *configWebService) deleteConfig(req *restful.Request, res *restful.Response) { - err := s.handler.DeleteConfig(req.Request.Context(), req.PathParameter("configType"), req.PathParameter("name")) +func (s *configAPIInterface) deleteConfig(req *restful.Request, res *restful.Response) { + err := s.ConfigService.DeleteConfig(req.Request.Context(), req.PathParameter("configType"), req.PathParameter("name")) if err != nil { bcode.ReturnError(req, res, err) return diff --git a/pkg/apiserver/rest/webservice/definition.go b/pkg/apiserver/interfaces/api/definition.go similarity index 75% rename from pkg/apiserver/rest/webservice/definition.go rename to pkg/apiserver/interfaces/api/definition.go index fc07bb55c..a95181fef 100644 --- a/pkg/apiserver/rest/webservice/definition.go +++ b/pkg/apiserver/interfaces/api/definition.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package webservice +package api import ( "strconv" @@ -22,18 +22,18 @@ import ( restfulspec "github.com/emicklei/go-restful-openapi/v2" restful "github.com/emicklei/go-restful/v3" - apis "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/usecase" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/service" + apis "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" ) -type definitionWebservice struct { - definitionUsecase usecase.DefinitionUsecase - rbacUsecase usecase.RBACUsecase +type definitionAPIInterface struct { + DefinitionService service.DefinitionService `inject:""` + RbacService service.RBACService `inject:""` } -func (d *definitionWebservice) GetWebService() *restful.WebService { +func (d *definitionAPIInterface) GetWebServiceRoute() *restful.WebService { ws := new(restful.WebService) ws.Path(versionPrefix+"/definitions"). Consumes(restful.MIME_XML, restful.MIME_JSON). @@ -46,7 +46,7 @@ func (d *definitionWebservice) GetWebService() *restful.WebService { Doc("list all definitions"). Metadata(restfulspec.KeyOpenAPITags, tags). // TODO: provide project scope api for query definition list - // Filter(d.rbacUsecase.CheckPerm("definition", "list")). + // Filter(d.RbacService.CheckPerm("definition", "list")). Param(ws.QueryParameter("type", "query the definition type").DataType("string").Required(true).AllowableValues(map[string]string{"component": "", "trait": "", "workflowstep": ""})). Param(ws.QueryParameter("queryAll", "query all definitions include hidden in UI").DataType("boolean").DefaultValue("false")). Param(ws.QueryParameter("appliedWorkload", "if specified, query the trait definition applied to the workload").DataType("string")). @@ -55,7 +55,7 @@ func (d *definitionWebservice) GetWebService() *restful.WebService { ws.Route(ws.GET("/{definitionName}").To(d.detailDefinition). Doc("Detail a definition"). - // Filter(d.rbacUsecase.CheckPerm("definition", "detail")). + // Filter(d.RbacService.CheckPerm("definition", "detail")). Param(ws.PathParameter("definitionName", "identifier of the definition").DataType("string")). Param(ws.QueryParameter("type", "query the definition type").DataType("string")). Metadata(restfulspec.KeyOpenAPITags, tags). @@ -64,7 +64,7 @@ func (d *definitionWebservice) GetWebService() *restful.WebService { ws.Route(ws.PUT("/{definitionName}/uischema").To(d.updateUISchema). Doc("Update the UI schema for a definition"). - Filter(d.rbacUsecase.CheckPerm("definition", "update")). + Filter(d.RbacService.CheckPerm("definition", "update")). Metadata(restfulspec.KeyOpenAPITags, tags). Reads(apis.UpdateUISchemaRequest{}). Returns(200, "update successfully", utils.UISchema{}). @@ -72,7 +72,7 @@ func (d *definitionWebservice) GetWebService() *restful.WebService { ws.Route(ws.PUT("/{definitionName}/status").To(d.updateDefinitionStatus). Doc("Update the status for a definition"). - Filter(d.rbacUsecase.CheckPerm("definition", "update")). + Filter(d.RbacService.CheckPerm("definition", "update")). Metadata(restfulspec.KeyOpenAPITags, tags). Reads(apis.UpdateDefinitionStatusRequest{}). Returns(200, "update successfully", utils.UISchema{}). @@ -82,20 +82,17 @@ func (d *definitionWebservice) GetWebService() *restful.WebService { return ws } -// NewDefinitionWebservice new definition webservice -func NewDefinitionWebservice(du usecase.DefinitionUsecase, rbacUsecase usecase.RBACUsecase) WebService { - return &definitionWebservice{ - definitionUsecase: du, - rbacUsecase: rbacUsecase, - } +// NewDefinitionAPIInterface new definition APIInterface +func NewDefinitionAPIInterface() Interface { + return &definitionAPIInterface{} } -func (d *definitionWebservice) listDefinitions(req *restful.Request, res *restful.Response) { +func (d *definitionAPIInterface) listDefinitions(req *restful.Request, res *restful.Response) { queryAll, err := strconv.ParseBool(req.QueryParameter("queryAll")) if err != nil { queryAll = false } - definitions, err := d.definitionUsecase.ListDefinitions(req.Request.Context(), usecase.DefinitionQueryOption{ + definitions, err := d.DefinitionService.ListDefinitions(req.Request.Context(), service.DefinitionQueryOption{ Type: req.QueryParameter("type"), AppliedWorkloads: req.QueryParameter("appliedWorkload"), QueryAll: queryAll, @@ -110,8 +107,8 @@ func (d *definitionWebservice) listDefinitions(req *restful.Request, res *restfu } } -func (d *definitionWebservice) detailDefinition(req *restful.Request, res *restful.Response) { - definition, err := d.definitionUsecase.DetailDefinition(req.Request.Context(), req.PathParameter("definitionName"), req.QueryParameter("type")) +func (d *definitionAPIInterface) detailDefinition(req *restful.Request, res *restful.Response) { + definition, err := d.DefinitionService.DetailDefinition(req.Request.Context(), req.PathParameter("definitionName"), req.QueryParameter("type")) if err != nil { bcode.ReturnError(req, res, err) return @@ -122,7 +119,7 @@ func (d *definitionWebservice) detailDefinition(req *restful.Request, res *restf } } -func (d *definitionWebservice) updateUISchema(req *restful.Request, res *restful.Response) { +func (d *definitionAPIInterface) updateUISchema(req *restful.Request, res *restful.Response) { // Verify the validity of parameters var updateReq apis.UpdateUISchemaRequest if err := req.ReadEntity(&updateReq); err != nil { @@ -137,7 +134,7 @@ func (d *definitionWebservice) updateUISchema(req *restful.Request, res *restful bcode.ReturnError(req, res, bcode.ErrInvalidDefinitionUISchema.SetMessage(err.Error())) return } - schema, err := d.definitionUsecase.AddDefinitionUISchema(req.Request.Context(), req.PathParameter("definitionName"), updateReq.DefinitionType, updateReq.UISchema) + schema, err := d.DefinitionService.AddDefinitionUISchema(req.Request.Context(), req.PathParameter("definitionName"), updateReq.DefinitionType, updateReq.UISchema) if err != nil { bcode.ReturnError(req, res, err) return @@ -148,7 +145,7 @@ func (d *definitionWebservice) updateUISchema(req *restful.Request, res *restful } } -func (d *definitionWebservice) updateDefinitionStatus(req *restful.Request, res *restful.Response) { +func (d *definitionAPIInterface) updateDefinitionStatus(req *restful.Request, res *restful.Response) { // Verify the validity of parameters var updateReq apis.UpdateDefinitionStatusRequest if err := req.ReadEntity(&updateReq); err != nil { @@ -159,7 +156,7 @@ func (d *definitionWebservice) updateDefinitionStatus(req *restful.Request, res bcode.ReturnError(req, res, err) return } - schema, err := d.definitionUsecase.UpdateDefinitionStatus(req.Request.Context(), req.PathParameter("definitionName"), updateReq) + schema, err := d.DefinitionService.UpdateDefinitionStatus(req.Request.Context(), req.PathParameter("definitionName"), updateReq) if err != nil { bcode.ReturnError(req, res, err) return diff --git a/pkg/apiserver/rest/apis/v1/oam_application.go b/pkg/apiserver/interfaces/api/dto/v1/oam_application.go similarity index 100% rename from pkg/apiserver/rest/apis/v1/oam_application.go rename to pkg/apiserver/interfaces/api/dto/v1/oam_application.go diff --git a/pkg/apiserver/rest/apis/v1/types.go b/pkg/apiserver/interfaces/api/dto/v1/types.go similarity index 99% rename from pkg/apiserver/rest/apis/v1/types.go rename to pkg/apiserver/interfaces/api/dto/v1/types.go index a6f4c7c85..b0a2924b1 100644 --- a/pkg/apiserver/rest/apis/v1/types.go +++ b/pkg/apiserver/interfaces/api/dto/v1/types.go @@ -27,8 +27,8 @@ import ( "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" "github.com/oam-dev/kubevela/apis/types" "github.com/oam-dev/kubevela/pkg/addon" - "github.com/oam-dev/kubevela/pkg/apiserver/model" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/utils" "github.com/oam-dev/kubevela/pkg/cloudprovider" ) diff --git a/pkg/apiserver/rest/webservice/env.go b/pkg/apiserver/interfaces/api/env.go similarity index 71% rename from pkg/apiserver/rest/webservice/env.go rename to pkg/apiserver/interfaces/api/env.go index 10285acbe..7b24b9619 100644 --- a/pkg/apiserver/rest/webservice/env.go +++ b/pkg/apiserver/interfaces/api/env.go @@ -14,31 +14,31 @@ See the License for the specific language governing permissions and limitations under the License. */ -package webservice +package api import ( restfulspec "github.com/emicklei/go-restful-openapi/v2" "github.com/emicklei/go-restful/v3" - "github.com/oam-dev/kubevela/pkg/apiserver/log" - apis "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/usecase" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/service" + apis "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/log" ) -type envWebService struct { - envUsecase usecase.EnvUsecase - appUsecase usecase.ApplicationUsecase - rbacUsecase usecase.RBACUsecase +type envAPIInterface struct { + EnvService service.EnvService `inject:""` + ApplicationService service.ApplicationService `inject:""` + RBACService service.RBACService `inject:""` } -// NewEnvWebService new env webservice -func NewEnvWebService(envUsecase usecase.EnvUsecase, appUseCase usecase.ApplicationUsecase, rbacUsecase usecase.RBACUsecase) WebService { - return &envWebService{envUsecase: envUsecase, appUsecase: appUseCase, rbacUsecase: rbacUsecase} +// NewEnvAPIInterface new env APIInterface +func NewEnvAPIInterface() Interface { + return &envAPIInterface{} } -func (n *envWebService) GetWebService() *restful.WebService { +func (n *envAPIInterface) GetWebServiceRoute() *restful.WebService { ws := new(restful.WebService) ws.Path(versionPrefix+"/envs"). Consumes(restful.MIME_XML, restful.MIME_JSON). @@ -51,7 +51,7 @@ func (n *envWebService) GetWebService() *restful.WebService { Operation("envlist"). Doc("list all envs"). // This api will filter the environments by user's permissions - // Filter(n.rbacUsecase.CheckPerm("environment", "list")). + // Filter(n.RbacService.CheckPerm("environment", "list")). Metadata(restfulspec.KeyOpenAPITags, tags). Returns(200, "OK", apis.ListEnvResponse{}). Writes(apis.ListEnvResponse{})) @@ -59,7 +59,7 @@ func (n *envWebService) GetWebService() *restful.WebService { ws.Route(ws.POST("/").To(n.create). Operation("envcreate"). Doc("create an env"). - Filter(n.rbacUsecase.CheckPerm("environment", "create")). + Filter(n.RBACService.CheckPerm("environment", "create")). Metadata(restfulspec.KeyOpenAPITags, tags). Reads(apis.CreateEnvRequest{}). Returns(200, "OK", apis.Env{}). @@ -69,7 +69,7 @@ func (n *envWebService) GetWebService() *restful.WebService { Operation("envupdate"). Doc("update an env"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(n.rbacUsecase.CheckPerm("environment", "update")). + Filter(n.RBACService.CheckPerm("environment", "update")). Param(ws.PathParameter("envName", "identifier of the application ").DataType("string")). Reads(apis.CreateEnvRequest{}). Returns(200, "OK", apis.Env{}). @@ -79,7 +79,7 @@ func (n *envWebService) GetWebService() *restful.WebService { Operation("envdelete"). Doc("delete one env"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(n.rbacUsecase.CheckPerm("environment", "delete")). + Filter(n.RBACService.CheckPerm("environment", "delete")). Param(ws.PathParameter("envName", "identifier of the application ").DataType("string")). Returns(200, "OK", apis.EmptyResponse{}). Returns(400, "Bad Request", bcode.Bcode{}). @@ -89,14 +89,14 @@ func (n *envWebService) GetWebService() *restful.WebService { return ws } -func (n *envWebService) list(req *restful.Request, res *restful.Response) { +func (n *envAPIInterface) list(req *restful.Request, res *restful.Response) { page, pageSize, err := utils.ExtractPagingParams(req, minPageSize, maxPageSize) if err != nil { bcode.ReturnError(req, res, err) return } project := req.QueryParameter("project") - envs, err := n.envUsecase.ListEnvs(req.Request.Context(), page, pageSize, apis.ListEnvOptions{Project: project}) + envs, err := n.EnvService.ListEnvs(req.Request.Context(), page, pageSize, apis.ListEnvOptions{Project: project}) if err != nil { bcode.ReturnError(req, res, err) return @@ -108,11 +108,11 @@ func (n *envWebService) list(req *restful.Request, res *restful.Response) { } // it will prevent the deletion if there's still application in it. -func (n *envWebService) delete(req *restful.Request, res *restful.Response) { +func (n *envAPIInterface) delete(req *restful.Request, res *restful.Response) { envname := req.PathParameter("envName") ctx := req.Request.Context() - lists, err := n.appUsecase.ListApplications(ctx, apis.ListApplicationOptions{Env: envname}) + lists, err := n.ApplicationService.ListApplications(ctx, apis.ListApplicationOptions{Env: envname}) if err != nil { bcode.ReturnError(req, res, err) return @@ -123,7 +123,7 @@ func (n *envWebService) delete(req *restful.Request, res *restful.Response) { return } - err = n.envUsecase.DeleteEnv(ctx, envname) + err = n.EnvService.DeleteEnv(ctx, envname) if err != nil { bcode.ReturnError(req, res, err) return @@ -134,7 +134,7 @@ func (n *envWebService) delete(req *restful.Request, res *restful.Response) { } } -func (n *envWebService) create(req *restful.Request, res *restful.Response) { +func (n *envAPIInterface) create(req *restful.Request, res *restful.Response) { // Verify the validity of parameters var createReq apis.CreateEnvRequest if err := req.ReadEntity(&createReq); err != nil { @@ -145,8 +145,8 @@ func (n *envWebService) create(req *restful.Request, res *restful.Response) { bcode.ReturnError(req, res, err) return } - // Call the usecase layer code - env, err := n.envUsecase.CreateEnv(req.Request.Context(), createReq) + // Call the Domain layer code + env, err := n.EnvService.CreateEnv(req.Request.Context(), createReq) if err != nil { log.Logger.Errorf("create application failure %s", err.Error()) bcode.ReturnError(req, res, err) @@ -160,7 +160,7 @@ func (n *envWebService) create(req *restful.Request, res *restful.Response) { } } -func (n *envWebService) update(req *restful.Request, res *restful.Response) { +func (n *envAPIInterface) update(req *restful.Request, res *restful.Response) { // Verify the validity of parameters var updateReq apis.UpdateEnvRequest if err := req.ReadEntity(&updateReq); err != nil { @@ -172,7 +172,7 @@ func (n *envWebService) update(req *restful.Request, res *restful.Response) { return } - env, err := n.envUsecase.UpdateEnv(req.Request.Context(), req.PathParameter("envName"), updateReq) + env, err := n.EnvService.UpdateEnv(req.Request.Context(), req.PathParameter("envName"), updateReq) if err != nil { bcode.ReturnError(req, res, err) return diff --git a/pkg/apiserver/rest/webservice/helm.go b/pkg/apiserver/interfaces/api/helm.go similarity index 76% rename from pkg/apiserver/rest/webservice/helm.go rename to pkg/apiserver/interfaces/api/helm.go index 49d3c1efe..f2d7844d3 100644 --- a/pkg/apiserver/rest/webservice/helm.go +++ b/pkg/apiserver/interfaces/api/helm.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package webservice +package api import ( "context" @@ -23,21 +23,22 @@ import ( restfulspec "github.com/emicklei/go-restful-openapi/v2" "github.com/emicklei/go-restful/v3" - v1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/usecase" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/service" + v1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" + "github.com/oam-dev/kubevela/pkg/utils" ) -type helmWebService struct { - usecase usecase.HelmHandler +type helmAPIInterface struct { + HelmService service.HelmService `inject:""` } -// NewHelmWebService will return helm webService -func NewHelmWebService(u usecase.HelmHandler) WebService { - return helmWebService{usecase: u} +// NewHelmAPIInterface will return helm APIInterface +func NewHelmAPIInterface() Interface { + return &helmAPIInterface{} } -func (h helmWebService) GetWebService() *restful.WebService { +func (h helmAPIInterface) GetWebServiceRoute() *restful.WebService { ws := new(restful.WebService) ws.Path(versionPrefix+"/repository"). Consumes(restful.MIME_XML, restful.MIME_JSON). @@ -89,15 +90,15 @@ func (h helmWebService) GetWebService() *restful.WebService { return ws } -func (h helmWebService) listCharts(req *restful.Request, res *restful.Response) { - url := req.QueryParameter("repoUrl") - secName := req.QueryParameter("secretName") +func (h helmAPIInterface) listCharts(req *restful.Request, res *restful.Response) { + url := utils.Sanitize(req.QueryParameter("repoUrl")) + secName := utils.Sanitize(req.QueryParameter("secretName")) skipCache, err := isSkipCache(req) if err != nil { bcode.ReturnError(req, res, bcode.ErrSkipCacheParameter) return } - charts, err := h.usecase.ListChartNames(context.Background(), url, secName, skipCache) + charts, err := h.HelmService.ListChartNames(context.Background(), url, secName, skipCache) if err != nil { bcode.ReturnError(req, res, err) return @@ -109,7 +110,7 @@ func (h helmWebService) listCharts(req *restful.Request, res *restful.Response) } } -func (h helmWebService) listVersions(req *restful.Request, res *restful.Response) { +func (h helmAPIInterface) listVersions(req *restful.Request, res *restful.Response) { url := req.QueryParameter("repoUrl") chartName := req.PathParameter("chart") secName := req.QueryParameter("secretName") @@ -119,7 +120,7 @@ func (h helmWebService) listVersions(req *restful.Request, res *restful.Response return } - versions, err := h.usecase.ListChartVersions(context.Background(), url, chartName, secName, skipCache) + versions, err := h.HelmService.ListChartVersions(context.Background(), url, chartName, secName, skipCache) if err != nil { bcode.ReturnError(req, res, err) return @@ -131,7 +132,7 @@ func (h helmWebService) listVersions(req *restful.Request, res *restful.Response } } -func (h helmWebService) chartValues(req *restful.Request, res *restful.Response) { +func (h helmAPIInterface) chartValues(req *restful.Request, res *restful.Response) { url := req.QueryParameter("repoUrl") secName := req.QueryParameter("secretName") chartName := req.PathParameter("chart") @@ -142,7 +143,7 @@ func (h helmWebService) chartValues(req *restful.Request, res *restful.Response) return } - versions, err := h.usecase.GetChartValues(context.Background(), url, chartName, version, secName, skipCache) + versions, err := h.HelmService.GetChartValues(context.Background(), url, chartName, version, secName, skipCache) if err != nil { bcode.ReturnError(req, res, err) return @@ -154,9 +155,9 @@ func (h helmWebService) chartValues(req *restful.Request, res *restful.Response) } } -func (h helmWebService) listRepo(req *restful.Request, res *restful.Response) { +func (h helmAPIInterface) listRepo(req *restful.Request, res *restful.Response) { project := req.QueryParameter("project") - repos, err := h.usecase.ListChartRepo(context.Background(), project) + repos, err := h.HelmService.ListChartRepo(context.Background(), project) if err != nil { bcode.ReturnError(req, res, err) return diff --git a/pkg/apiserver/interfaces/api/interfaces.go b/pkg/apiserver/interfaces/api/interfaces.go new file mode 100644 index 000000000..ef1d2de33 --- /dev/null +++ b/pkg/apiserver/interfaces/api/interfaces.go @@ -0,0 +1,94 @@ +/* +Copyright 2021 The KubeVela Authors. + +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 api + +import ( + "net/http" + + apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + + "github.com/emicklei/go-restful/v3" +) + +// versionPrefix API version prefix. +var versionPrefix = "/api/v1" + +// Interface the API should define the http route +type Interface interface { + GetWebServiceRoute() *restful.WebService +} + +var registeredAPIInterface []Interface + +// RegisterAPIInterface register APIInterface +func RegisterAPIInterface(ws Interface) { + registeredAPIInterface = append(registeredAPIInterface, ws) +} + +// GetRegisteredAPIInterface return registeredAPIInterface +func GetRegisteredAPIInterface() []Interface { + return registeredAPIInterface +} + +func returns200(b *restful.RouteBuilder) { + b.Returns(http.StatusOK, "OK", apisv1.SimpleResponse{Status: "ok"}) +} + +func returns500(b *restful.RouteBuilder) { + b.Returns(http.StatusInternalServerError, "Bummer, something went wrong", nil) +} + +// InitAPIBean inits all APIInterface, pass in the required parameter object. +// It can be implemented using the idea of dependency injection. +func InitAPIBean() []interface{} { + // Application + RegisterAPIInterface(NewApplicationAPIInterface()) + RegisterAPIInterface(NewProjectAPIInterface()) + RegisterAPIInterface(NewEnvAPIInterface()) + + // Extension + RegisterAPIInterface(NewDefinitionAPIInterface()) + RegisterAPIInterface(NewAddonAPIInterface()) + RegisterAPIInterface(NewEnabledAddonAPIInterface()) + RegisterAPIInterface(NewAddonRegistryAPIInterface()) + + // Config management + RegisterAPIInterface(ConfigAPIInterface()) + + // Resources + RegisterAPIInterface(NewClusterAPIInterface()) + RegisterAPIInterface(NewOAMApplication()) + RegisterAPIInterface(NewPayloadTypesAPIInterface()) + RegisterAPIInterface(NewTargetAPIInterface()) + RegisterAPIInterface(NewVelaQLAPIInterface()) + RegisterAPIInterface(NewWebhookAPIInterface()) + RegisterAPIInterface(NewHelmAPIInterface()) + + // Authentication + RegisterAPIInterface(NewAuthenticationAPIInterface()) + RegisterAPIInterface(NewUserAPIInterface()) + RegisterAPIInterface(NewSystemInfoAPIInterface()) + + // RBAC + RegisterAPIInterface(NewRBACAPIInterface()) + var beans []interface{} + for i := range registeredAPIInterface { + beans = append(beans, registeredAPIInterface[i]) + } + beans = append(beans, NewWorkflowAPIInterface()) + return beans +} diff --git a/pkg/apiserver/datastore/kubeapi/kubeapi_suite_test.go b/pkg/apiserver/interfaces/api/interfaces_test.go similarity index 73% rename from pkg/apiserver/datastore/kubeapi/kubeapi_suite_test.go rename to pkg/apiserver/interfaces/api/interfaces_test.go index 0f2956c96..92e9c7d0c 100644 --- a/pkg/apiserver/datastore/kubeapi/kubeapi_suite_test.go +++ b/pkg/apiserver/interfaces/api/interfaces_test.go @@ -1,5 +1,5 @@ /* -Copyright 2021 The KubeVela Authors. +Copyright 2022 The KubeVela Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,16 +14,14 @@ See the License for the specific language governing permissions and limitations under the License. */ -package kubeapi +package api import ( "testing" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" + "gotest.tools/assert" ) -func TestKubeapi(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Kubeapi Suite") +func TestInitAPIBean(t *testing.T) { + assert.Equal(t, len(InitAPIBean()), 20) } diff --git a/pkg/apiserver/rest/webservice/oam_application.go b/pkg/apiserver/interfaces/api/oam_application.go similarity index 72% rename from pkg/apiserver/rest/webservice/oam_application.go rename to pkg/apiserver/interfaces/api/oam_application.go index 5b57ea6f5..9bc672a01 100644 --- a/pkg/apiserver/rest/webservice/oam_application.go +++ b/pkg/apiserver/interfaces/api/oam_application.go @@ -14,32 +14,29 @@ See the License for the specific language governing permissions and limitations under the License. */ -package webservice +package api import ( restfulspec "github.com/emicklei/go-restful-openapi/v2" "github.com/emicklei/go-restful/v3" - "github.com/oam-dev/kubevela/pkg/apiserver/log" - apis "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/usecase" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/service" + apis "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/log" ) -type oamApplicationWebService struct { - oamApplicationUsecase usecase.OAMApplicationUsecase - rbacUsecase usecase.RBACUsecase +type oamApplicationAPIInterface struct { + OamApplicationService service.OAMApplicationService `inject:""` + RbacService service.RBACService `inject:""` } // NewOAMApplication new oam application -func NewOAMApplication(oamApplicationUsecase usecase.OAMApplicationUsecase, rbacUsecase usecase.RBACUsecase) WebService { - return &oamApplicationWebService{ - oamApplicationUsecase: oamApplicationUsecase, - rbacUsecase: rbacUsecase, - } +func NewOAMApplication() Interface { + return &oamApplicationAPIInterface{} } -func (c *oamApplicationWebService) GetWebService() *restful.WebService { +func (c *oamApplicationAPIInterface) GetWebServiceRoute() *restful.WebService { ws := new(restful.WebService) ws.Path("/v1"). Consumes(restful.MIME_XML, restful.MIME_JSON). @@ -51,7 +48,7 @@ func (c *oamApplicationWebService) GetWebService() *restful.WebService { ws.Route(ws.GET("/namespaces/{namespace}/applications/{appname}").To(c.getApplication). Doc("get the specified oam application in the specified namespace"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(c.rbacUsecase.CheckPerm("application", "detail")). + Filter(c.RbacService.CheckPerm("application", "detail")). Param(ws.PathParameter("namespace", "identifier of the namespace").DataType("string")). Param(ws.PathParameter("appname", "identifier of the oam application").DataType("string")). Returns(200, "OK", apis.ApplicationResponse{}). @@ -60,7 +57,7 @@ func (c *oamApplicationWebService) GetWebService() *restful.WebService { ws.Route(ws.POST("/namespaces/{namespace}/applications/{appname}").To(c.createOrUpdateApplication). Doc("create or update oam application in the specified namespace"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(c.rbacUsecase.CheckPerm("application", "deploy")). + Filter(c.RbacService.CheckPerm("application", "deploy")). Param(ws.PathParameter("namespace", "identifier of the namespace").DataType("string")). Param(ws.PathParameter("appname", "identifier of the oam application").DataType("string")). Reads(apis.ApplicationRequest{})) @@ -69,7 +66,7 @@ func (c *oamApplicationWebService) GetWebService() *restful.WebService { Operation("deleteOAMApplication"). Doc("create or update oam application in the specified namespace"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(c.rbacUsecase.CheckPerm("application", "delete")). + Filter(c.RbacService.CheckPerm("application", "delete")). Param(ws.PathParameter("namespace", "identifier of the namespace").DataType("string")). Param(ws.PathParameter("appname", "identifier of the oam application").DataType("string"))) @@ -77,10 +74,10 @@ func (c *oamApplicationWebService) GetWebService() *restful.WebService { return ws } -func (c *oamApplicationWebService) getApplication(req *restful.Request, res *restful.Response) { +func (c *oamApplicationAPIInterface) getApplication(req *restful.Request, res *restful.Response) { namespace := req.PathParameter("namespace") appName := req.PathParameter("appname") - appRes, err := c.oamApplicationUsecase.GetOAMApplication(req.Request.Context(), appName, namespace) + appRes, err := c.OamApplicationService.GetOAMApplication(req.Request.Context(), appName, namespace) if err != nil { log.Logger.Errorf("get application failure %s", err.Error()) bcode.ReturnError(req, res, err) @@ -94,7 +91,7 @@ func (c *oamApplicationWebService) getApplication(req *restful.Request, res *res } } -func (c *oamApplicationWebService) createOrUpdateApplication(req *restful.Request, res *restful.Response) { +func (c *oamApplicationAPIInterface) createOrUpdateApplication(req *restful.Request, res *restful.Response) { namespace := req.PathParameter("namespace") appName := req.PathParameter("appname") @@ -104,7 +101,7 @@ func (c *oamApplicationWebService) createOrUpdateApplication(req *restful.Reques return } - err := c.oamApplicationUsecase.CreateOrUpdateOAMApplication(req.Request.Context(), createReq, appName, namespace) + err := c.OamApplicationService.CreateOrUpdateOAMApplication(req.Request.Context(), createReq, appName, namespace) if err != nil { log.Logger.Errorf("create application failure %s", err.Error()) bcode.ReturnError(req, res, err) @@ -117,11 +114,11 @@ func (c *oamApplicationWebService) createOrUpdateApplication(req *restful.Reques } } -func (c *oamApplicationWebService) deleteApplication(req *restful.Request, res *restful.Response) { +func (c *oamApplicationAPIInterface) deleteApplication(req *restful.Request, res *restful.Response) { namespace := req.PathParameter("namespace") appName := req.PathParameter("appname") - err := c.oamApplicationUsecase.DeleteOAMApplication(req.Request.Context(), appName, namespace) + err := c.OamApplicationService.DeleteOAMApplication(req.Request.Context(), appName, namespace) if err != nil { log.Logger.Errorf("delete application failure %s", err.Error()) bcode.ReturnError(req, res, err) diff --git a/pkg/apiserver/rest/webservice/payload_types.go b/pkg/apiserver/interfaces/api/payload_types.go similarity index 70% rename from pkg/apiserver/rest/webservice/payload_types.go rename to pkg/apiserver/interfaces/api/payload_types.go index 9d5142ead..290e82d53 100644 --- a/pkg/apiserver/rest/webservice/payload_types.go +++ b/pkg/apiserver/interfaces/api/payload_types.go @@ -14,20 +14,25 @@ See the License for the specific language governing permissions and limitations under the License. */ -package webservice +package api import ( restfulspec "github.com/emicklei/go-restful-openapi/v2" restful "github.com/emicklei/go-restful/v3" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/usecase" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/service" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" ) -type payloadTypesWebservice struct { +type payloadTypesAPIInterface struct { } -func (c *payloadTypesWebservice) GetWebService() *restful.WebService { +// NewPayloadTypesAPIInterface new - +func NewPayloadTypesAPIInterface() Interface { + return &payloadTypesAPIInterface{} +} + +func (c *payloadTypesAPIInterface) GetWebServiceRoute() *restful.WebService { ws := new(restful.WebService) ws.Path(versionPrefix+"/payload_types"). Consumes(restful.MIME_XML, restful.MIME_JSON). @@ -47,8 +52,8 @@ func (c *payloadTypesWebservice) GetWebService() *restful.WebService { return ws } -func (c *payloadTypesWebservice) ListPayloadTypes(req *restful.Request, res *restful.Response) { - if err := res.WriteEntity(usecase.WebhookHandlers); err != nil { +func (c *payloadTypesAPIInterface) ListPayloadTypes(req *restful.Request, res *restful.Response) { + if err := res.WriteEntity(service.WebhookHandlers); err != nil { bcode.ReturnError(req, res, err) return } diff --git a/pkg/apiserver/rest/webservice/project.go b/pkg/apiserver/interfaces/api/project.go similarity index 76% rename from pkg/apiserver/rest/webservice/project.go rename to pkg/apiserver/interfaces/api/project.go index fdb254f0a..e15c032f8 100644 --- a/pkg/apiserver/rest/webservice/project.go +++ b/pkg/apiserver/interfaces/api/project.go @@ -14,31 +14,31 @@ See the License for the specific language governing permissions and limitations under the License. */ -package webservice +package api import ( restfulspec "github.com/emicklei/go-restful-openapi/v2" "github.com/emicklei/go-restful/v3" - "github.com/oam-dev/kubevela/pkg/apiserver/log" - apis "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/usecase" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/service" + apis "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/log" ) -type projectWebService struct { - rbacUsecase usecase.RBACUsecase - projectUsecase usecase.ProjectUsecase - targetUsecase usecase.TargetUsecase +type projectAPIInterface struct { + RbacService service.RBACService `inject:""` + ProjectService service.ProjectService `inject:""` + TargetService service.TargetService `inject:""` } -// NewProjectWebService new project webservice -func NewProjectWebService(projectUsecase usecase.ProjectUsecase, rbacUsecase usecase.RBACUsecase, targetUsecase usecase.TargetUsecase) WebService { - return &projectWebService{projectUsecase: projectUsecase, rbacUsecase: rbacUsecase, targetUsecase: targetUsecase} +// NewProjectAPIInterface new project APIInterface +func NewProjectAPIInterface() Interface { + return &projectAPIInterface{} } -func (n *projectWebService) GetWebService() *restful.WebService { +func (n *projectAPIInterface) GetWebServiceRoute() *restful.WebService { ws := new(restful.WebService) ws.Path(versionPrefix+"/projects"). Consumes(restful.MIME_XML, restful.MIME_JSON). @@ -50,14 +50,14 @@ func (n *projectWebService) GetWebService() *restful.WebService { ws.Route(ws.GET("/").To(n.listprojects). Doc("list all projects"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(n.rbacUsecase.CheckPerm("project", "list")). + Filter(n.RbacService.CheckPerm("project", "list")). Returns(200, "OK", apis.ListProjectResponse{}). Writes(apis.ListProjectResponse{})) ws.Route(ws.POST("/").To(n.createproject). Doc("create a project"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(n.rbacUsecase.CheckPerm("project", "create")). + Filter(n.RbacService.CheckPerm("project", "create")). Reads(apis.CreateProjectRequest{}). Returns(200, "OK", apis.ProjectBase{}). Writes(apis.ProjectBase{})) @@ -66,7 +66,7 @@ func (n *projectWebService) GetWebService() *restful.WebService { Doc("detail a project"). Metadata(restfulspec.KeyOpenAPITags, tags). Param(ws.PathParameter("projectName", "identifier of the project").DataType("string")). - Filter(n.rbacUsecase.CheckPerm("project", "detail")). + Filter(n.RbacService.CheckPerm("project", "detail")). Returns(200, "OK", apis.ProjectBase{}). Writes(apis.ProjectBase{})) @@ -74,7 +74,7 @@ func (n *projectWebService) GetWebService() *restful.WebService { Doc("update a project"). Metadata(restfulspec.KeyOpenAPITags, tags). Param(ws.PathParameter("projectName", "identifier of the project").DataType("string")). - Filter(n.rbacUsecase.CheckPerm("project", "update")). + Filter(n.RbacService.CheckPerm("project", "update")). Reads(apis.UpdateProjectRequest{}). Returns(200, "OK", apis.ProjectBase{}). Writes(apis.ProjectBase{})) @@ -83,7 +83,7 @@ func (n *projectWebService) GetWebService() *restful.WebService { Doc("delete a project"). Metadata(restfulspec.KeyOpenAPITags, tags). Param(ws.PathParameter("projectName", "identifier of the project").DataType("string")). - Filter(n.rbacUsecase.CheckPerm("project", "delete")). + Filter(n.RbacService.CheckPerm("project", "delete")). Returns(200, "OK", apis.EmptyResponse{}). Writes(apis.EmptyResponse{})) @@ -91,7 +91,7 @@ func (n *projectWebService) GetWebService() *restful.WebService { Doc("get targets list belong to a project"). Metadata(restfulspec.KeyOpenAPITags, tags). Param(ws.PathParameter("projectName", "identifier of the project").DataType("string")). - Filter(n.rbacUsecase.CheckPerm("project", "detail")). + Filter(n.RbacService.CheckPerm("project", "detail")). Returns(200, "OK", apis.EmptyResponse{}). Writes(apis.EmptyResponse{})) @@ -99,7 +99,7 @@ func (n *projectWebService) GetWebService() *restful.WebService { Doc("add a user to a project"). Metadata(restfulspec.KeyOpenAPITags, tags). Param(ws.PathParameter("projectName", "identifier of the project").DataType("string")). - Filter(n.rbacUsecase.CheckPerm("project/projectUser", "create")). + Filter(n.RbacService.CheckPerm("project/projectUser", "create")). Reads(apis.AddProjectUserRequest{}). Returns(200, "OK", apis.ProjectUserBase{}). Writes(apis.ProjectUserBase{})) @@ -108,7 +108,7 @@ func (n *projectWebService) GetWebService() *restful.WebService { Doc("list all users belong to a project"). Metadata(restfulspec.KeyOpenAPITags, tags). Param(ws.PathParameter("projectName", "identifier of the project").DataType("string")). - Filter(n.rbacUsecase.CheckPerm("project/projectUser", "list")). + Filter(n.RbacService.CheckPerm("project/projectUser", "list")). Returns(200, "OK", apis.ListProjectUsersResponse{}). Writes(apis.ListProjectUsersResponse{})) @@ -118,7 +118,7 @@ func (n *projectWebService) GetWebService() *restful.WebService { Reads(apis.UpdateProjectUserRequest{}). Param(ws.PathParameter("projectName", "identifier of the project").DataType("string")). Param(ws.PathParameter("userName", "identifier of the project user").DataType("string")). - Filter(n.rbacUsecase.CheckPerm("project/projectUser", "create")). + Filter(n.RbacService.CheckPerm("project/projectUser", "create")). Returns(200, "OK", apis.ProjectUserBase{}). Writes(apis.ProjectUserBase{})) @@ -128,7 +128,7 @@ func (n *projectWebService) GetWebService() *restful.WebService { Reads(apis.UpdateProjectUserRequest{}). Param(ws.PathParameter("projectName", "identifier of the project").DataType("string")). Param(ws.PathParameter("userName", "identifier of the project user").DataType("string")). - Filter(n.rbacUsecase.CheckPerm("project/projectUser", "delete")). + Filter(n.RbacService.CheckPerm("project/projectUser", "delete")). Returns(200, "OK", apis.EmptyResponse{}). Writes(apis.EmptyResponse{})) @@ -136,7 +136,7 @@ func (n *projectWebService) GetWebService() *restful.WebService { Doc("list all project level roles"). Metadata(restfulspec.KeyOpenAPITags, tags). Param(ws.PathParameter("projectName", "identifier of the project").DataType("string")). - Filter(n.rbacUsecase.CheckPerm("project/role", "list")). + Filter(n.RbacService.CheckPerm("project/role", "list")). Returns(200, "OK", apis.ListRolesResponse{}). Writes(apis.ListRolesResponse{})) @@ -144,7 +144,7 @@ func (n *projectWebService) GetWebService() *restful.WebService { Doc("create project level role"). Metadata(restfulspec.KeyOpenAPITags, tags). Param(ws.PathParameter("projectName", "identifier of the project").DataType("string")). - Filter(n.rbacUsecase.CheckPerm("project/role", "create")). + Filter(n.RbacService.CheckPerm("project/role", "create")). Returns(200, "OK", apis.RoleBase{}). Reads(apis.CreateRoleRequest{}). Writes(apis.RoleBase{})) @@ -154,7 +154,7 @@ func (n *projectWebService) GetWebService() *restful.WebService { Metadata(restfulspec.KeyOpenAPITags, tags). Param(ws.PathParameter("projectName", "identifier of the project").DataType("string")). Param(ws.PathParameter("roleName", "identifier of the project role").DataType("string")). - Filter(n.rbacUsecase.CheckPerm("project/role", "update")). + Filter(n.RbacService.CheckPerm("project/role", "update")). Reads(apis.UpdateRoleRequest{}). Returns(200, "OK", apis.RoleBase{}). Writes(apis.RoleBase{})) @@ -164,7 +164,7 @@ func (n *projectWebService) GetWebService() *restful.WebService { Metadata(restfulspec.KeyOpenAPITags, tags). Param(ws.PathParameter("projectName", "identifier of the project").DataType("string")). Param(ws.PathParameter("roleName", "identifier of the project role").DataType("string")). - Filter(n.rbacUsecase.CheckPerm("project/role", "delete")). + Filter(n.RbacService.CheckPerm("project/role", "delete")). Returns(200, "OK", apis.EmptyResponse{}). Writes(apis.EmptyResponse{})) @@ -172,14 +172,14 @@ func (n *projectWebService) GetWebService() *restful.WebService { Doc("list all project level perm policies"). Metadata(restfulspec.KeyOpenAPITags, tags). Param(ws.PathParameter("projectName", "identifier of the project").DataType("string")). - Filter(n.rbacUsecase.CheckPerm("project/permission", "list")). + Filter(n.RbacService.CheckPerm("project/permission", "list")). Returns(200, "OK", []apis.PermissionBase{}). Writes([]apis.PermissionBase{})) ws.Route(ws.GET("/{projectName}/configs").To(n.getConfigs). Doc("get configs which are in a project"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(n.rbacUsecase.CheckPerm("project/configs", "list")). + Filter(n.RbacService.CheckPerm("project/configs", "list")). Param(ws.QueryParameter("configType", "config type").DataType("string")). Param(ws.PathParameter("projectName", "identifier of the project").DataType("string")). Returns(200, "OK", []*apis.Config{}). @@ -189,7 +189,7 @@ func (n *projectWebService) GetWebService() *restful.WebService { ws.Route(ws.GET("/{projectName}/validate_image").To(n.validateImage). Doc("validate an image in a project"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(n.rbacUsecase.CheckPerm("project/image", "get")). + Filter(n.RbacService.CheckPerm("project/image", "get")). Param(ws.QueryParameter("image", "image name").DataType("string")). Param(ws.PathParameter("projectName", "identifier of the project").DataType("string")). Returns(200, "OK", []*apis.ImageResponse{}). @@ -200,13 +200,13 @@ func (n *projectWebService) GetWebService() *restful.WebService { return ws } -func (n *projectWebService) listprojects(req *restful.Request, res *restful.Response) { +func (n *projectAPIInterface) listprojects(req *restful.Request, res *restful.Response) { page, pageSize, err := utils.ExtractPagingParams(req, minPageSize, maxPageSize) if err != nil { bcode.ReturnError(req, res, err) return } - projects, err := n.projectUsecase.ListProjects(req.Request.Context(), page, pageSize) + projects, err := n.ProjectService.ListProjects(req.Request.Context(), page, pageSize) if err != nil { bcode.ReturnError(req, res, err) return @@ -217,7 +217,7 @@ func (n *projectWebService) listprojects(req *restful.Request, res *restful.Resp } } -func (n *projectWebService) createproject(req *restful.Request, res *restful.Response) { +func (n *projectAPIInterface) createproject(req *restful.Request, res *restful.Response) { // Verify the validity of parameters var createReq apis.CreateProjectRequest if err := req.ReadEntity(&createReq); err != nil { @@ -228,8 +228,8 @@ func (n *projectWebService) createproject(req *restful.Request, res *restful.Res bcode.ReturnError(req, res, err) return } - // Call the usecase layer code - projectBase, err := n.projectUsecase.CreateProject(req.Request.Context(), createReq) + // Call the domain layer code + projectBase, err := n.ProjectService.CreateProject(req.Request.Context(), createReq) if err != nil { log.Logger.Errorf("create project failure %s", err.Error()) bcode.ReturnError(req, res, err) @@ -243,7 +243,7 @@ func (n *projectWebService) createproject(req *restful.Request, res *restful.Res } } -func (n *projectWebService) updateProject(req *restful.Request, res *restful.Response) { +func (n *projectAPIInterface) updateProject(req *restful.Request, res *restful.Response) { // Verify the validity of parameters var updateReq apis.UpdateProjectRequest if err := req.ReadEntity(&updateReq); err != nil { @@ -254,8 +254,8 @@ func (n *projectWebService) updateProject(req *restful.Request, res *restful.Res bcode.ReturnError(req, res, err) return } - // Call the usecase layer code - projectBase, err := n.projectUsecase.UpdateProject(req.Request.Context(), req.PathParameter("projectName"), updateReq) + // Call the domain layer code + projectBase, err := n.ProjectService.UpdateProject(req.Request.Context(), req.PathParameter("projectName"), updateReq) if err != nil { log.Logger.Errorf("update project failure %s", err.Error()) bcode.ReturnError(req, res, err) @@ -269,8 +269,8 @@ func (n *projectWebService) updateProject(req *restful.Request, res *restful.Res } } -func (n *projectWebService) detailProject(req *restful.Request, res *restful.Response) { - project, err := n.projectUsecase.DetailProject(req.Request.Context(), req.PathParameter("projectName")) +func (n *projectAPIInterface) detailProject(req *restful.Request, res *restful.Response) { + project, err := n.ProjectService.DetailProject(req.Request.Context(), req.PathParameter("projectName")) if err != nil { bcode.ReturnError(req, res, err) return @@ -282,8 +282,8 @@ func (n *projectWebService) detailProject(req *restful.Request, res *restful.Res } } -func (n *projectWebService) deleteProject(req *restful.Request, res *restful.Response) { - err := n.projectUsecase.DeleteProject(req.Request.Context(), req.PathParameter("projectName")) +func (n *projectAPIInterface) deleteProject(req *restful.Request, res *restful.Response) { + err := n.ProjectService.DeleteProject(req.Request.Context(), req.PathParameter("projectName")) if err != nil { bcode.ReturnError(req, res, err) return @@ -295,13 +295,13 @@ func (n *projectWebService) deleteProject(req *restful.Request, res *restful.Res } } -func (n *projectWebService) listProjectTargets(req *restful.Request, res *restful.Response) { - project, err := n.projectUsecase.GetProject(req.Request.Context(), req.PathParameter("projectName")) +func (n *projectAPIInterface) listProjectTargets(req *restful.Request, res *restful.Response) { + project, err := n.ProjectService.GetProject(req.Request.Context(), req.PathParameter("projectName")) if err != nil { bcode.ReturnError(req, res, err) return } - projects, err := n.targetUsecase.ListTargets(req.Request.Context(), 0, 0, project.Name) + projects, err := n.TargetService.ListTargets(req.Request.Context(), 0, 0, project.Name) if err != nil { bcode.ReturnError(req, res, err) return @@ -313,7 +313,7 @@ func (n *projectWebService) listProjectTargets(req *restful.Request, res *restfu } } -func (n *projectWebService) createProjectUser(req *restful.Request, res *restful.Response) { +func (n *projectAPIInterface) createProjectUser(req *restful.Request, res *restful.Response) { // Verify the validity of parameters var createReq apis.AddProjectUserRequest if err := req.ReadEntity(&createReq); err != nil { @@ -328,8 +328,8 @@ func (n *projectWebService) createProjectUser(req *restful.Request, res *restful bcode.ReturnError(req, res, bcode.ErrProjectRoleCheckFailure) return } - // Call the usecase layer code - userBase, err := n.projectUsecase.AddProjectUser(req.Request.Context(), req.PathParameter("projectName"), createReq) + // Call the domain layer code + userBase, err := n.ProjectService.AddProjectUser(req.Request.Context(), req.PathParameter("projectName"), createReq) if err != nil { log.Logger.Errorf("create project user failure %s", err.Error()) bcode.ReturnError(req, res, err) @@ -343,14 +343,14 @@ func (n *projectWebService) createProjectUser(req *restful.Request, res *restful } } -func (n *projectWebService) listProjectUser(req *restful.Request, res *restful.Response) { +func (n *projectAPIInterface) listProjectUser(req *restful.Request, res *restful.Response) { page, pageSize, err := utils.ExtractPagingParams(req, minPageSize, maxPageSize) if err != nil { bcode.ReturnError(req, res, err) return } - // Call the usecase layer code - users, err := n.projectUsecase.ListProjectUser(req.Request.Context(), req.PathParameter("projectName"), page, pageSize) + // Call the domain layer code + users, err := n.ProjectService.ListProjectUser(req.Request.Context(), req.PathParameter("projectName"), page, pageSize) if err != nil { log.Logger.Errorf("list project users failure %s", err.Error()) bcode.ReturnError(req, res, err) @@ -364,7 +364,7 @@ func (n *projectWebService) listProjectUser(req *restful.Request, res *restful.R } } -func (n *projectWebService) updateProjectUser(req *restful.Request, res *restful.Response) { +func (n *projectAPIInterface) updateProjectUser(req *restful.Request, res *restful.Response) { // Verify the validity of parameters var updateReq apis.UpdateProjectUserRequest if err := req.ReadEntity(&updateReq); err != nil { @@ -379,8 +379,8 @@ func (n *projectWebService) updateProjectUser(req *restful.Request, res *restful bcode.ReturnError(req, res, bcode.ErrProjectRoleCheckFailure) return } - // Call the usecase layer code - userBase, err := n.projectUsecase.UpdateProjectUser(req.Request.Context(), req.PathParameter("projectName"), req.PathParameter("userName"), updateReq) + // Call the domain layer code + userBase, err := n.ProjectService.UpdateProjectUser(req.Request.Context(), req.PathParameter("projectName"), req.PathParameter("userName"), updateReq) if err != nil { log.Logger.Errorf("update project user failure %s", err.Error()) bcode.ReturnError(req, res, err) @@ -394,9 +394,9 @@ func (n *projectWebService) updateProjectUser(req *restful.Request, res *restful } } -func (n *projectWebService) deleteProjectUser(req *restful.Request, res *restful.Response) { - // Call the usecase layer code - err := n.projectUsecase.DeleteProjectUser(req.Request.Context(), req.PathParameter("projectName"), req.PathParameter("userName")) +func (n *projectAPIInterface) deleteProjectUser(req *restful.Request, res *restful.Response) { + // Call the domain layer code + err := n.ProjectService.DeleteProjectUser(req.Request.Context(), req.PathParameter("projectName"), req.PathParameter("userName")) if err != nil { log.Logger.Errorf("delete project user failure %s", err.Error()) bcode.ReturnError(req, res, err) @@ -410,7 +410,7 @@ func (n *projectWebService) deleteProjectUser(req *restful.Request, res *restful } } -func (n *projectWebService) listProjectRoles(req *restful.Request, res *restful.Response) { +func (n *projectAPIInterface) listProjectRoles(req *restful.Request, res *restful.Response) { if req.PathParameter("projectName") == "" { bcode.ReturnError(req, res, bcode.ErrProjectIsNotExist) return @@ -420,7 +420,7 @@ func (n *projectWebService) listProjectRoles(req *restful.Request, res *restful. bcode.ReturnError(req, res, err) return } - roles, err := n.rbacUsecase.ListRole(req.Request.Context(), req.PathParameter("projectName"), page, pageSize) + roles, err := n.RbacService.ListRole(req.Request.Context(), req.PathParameter("projectName"), page, pageSize) if err != nil { bcode.ReturnError(req, res, err) return @@ -431,7 +431,7 @@ func (n *projectWebService) listProjectRoles(req *restful.Request, res *restful. } } -func (n *projectWebService) createProjectRole(req *restful.Request, res *restful.Response) { +func (n *projectAPIInterface) createProjectRole(req *restful.Request, res *restful.Response) { if req.PathParameter("projectName") == "" { bcode.ReturnError(req, res, bcode.ErrProjectIsNotExist) return @@ -446,8 +446,8 @@ func (n *projectWebService) createProjectRole(req *restful.Request, res *restful bcode.ReturnError(req, res, err) return } - // Call the usecase layer code - projectBase, err := n.rbacUsecase.CreateRole(req.Request.Context(), req.PathParameter("projectName"), createReq) + // Call the domain layer code + projectBase, err := n.RbacService.CreateRole(req.Request.Context(), req.PathParameter("projectName"), createReq) if err != nil { log.Logger.Errorf("create role failure %s", err.Error()) bcode.ReturnError(req, res, err) @@ -461,7 +461,7 @@ func (n *projectWebService) createProjectRole(req *restful.Request, res *restful } } -func (n *projectWebService) updateProjectRole(req *restful.Request, res *restful.Response) { +func (n *projectAPIInterface) updateProjectRole(req *restful.Request, res *restful.Response) { if req.PathParameter("projectName") == "" { bcode.ReturnError(req, res, bcode.ErrProjectIsNotExist) return @@ -476,8 +476,8 @@ func (n *projectWebService) updateProjectRole(req *restful.Request, res *restful bcode.ReturnError(req, res, err) return } - // Call the usecase layer code - roleBase, err := n.rbacUsecase.UpdateRole(req.Request.Context(), req.PathParameter("projectName"), req.PathParameter("roleName"), updateReq) + // Call the domain layer code + roleBase, err := n.RbacService.UpdateRole(req.Request.Context(), req.PathParameter("projectName"), req.PathParameter("roleName"), updateReq) if err != nil { log.Logger.Errorf("update role failure %s", err.Error()) bcode.ReturnError(req, res, err) @@ -491,12 +491,12 @@ func (n *projectWebService) updateProjectRole(req *restful.Request, res *restful } } -func (n *projectWebService) deleteProjectRole(req *restful.Request, res *restful.Response) { +func (n *projectAPIInterface) deleteProjectRole(req *restful.Request, res *restful.Response) { if req.PathParameter("projectName") == "" { bcode.ReturnError(req, res, bcode.ErrProjectIsNotExist) return } - err := n.rbacUsecase.DeleteRole(req.Request.Context(), req.PathParameter("projectName"), req.PathParameter("roleName")) + err := n.RbacService.DeleteRole(req.Request.Context(), req.PathParameter("projectName"), req.PathParameter("roleName")) if err != nil { bcode.ReturnError(req, res, err) return @@ -508,12 +508,12 @@ func (n *projectWebService) deleteProjectRole(req *restful.Request, res *restful } } -func (n *projectWebService) listProjectPermissions(req *restful.Request, res *restful.Response) { +func (n *projectAPIInterface) listProjectPermissions(req *restful.Request, res *restful.Response) { if req.PathParameter("projectName") == "" { bcode.ReturnError(req, res, bcode.ErrProjectIsNotExist) return } - policies, err := n.rbacUsecase.ListPermissions(req.Request.Context(), req.PathParameter("projectName")) + policies, err := n.RbacService.ListPermissions(req.Request.Context(), req.PathParameter("projectName")) if err != nil { bcode.ReturnError(req, res, err) return @@ -524,8 +524,8 @@ func (n *projectWebService) listProjectPermissions(req *restful.Request, res *re } } -func (n *projectWebService) getConfigs(req *restful.Request, res *restful.Response) { - configs, err := n.projectUsecase.GetConfigs(req.Request.Context(), req.PathParameter("projectName"), req.QueryParameter("configType")) +func (n *projectAPIInterface) getConfigs(req *restful.Request, res *restful.Response) { + configs, err := n.ProjectService.GetConfigs(req.Request.Context(), req.PathParameter("projectName"), req.QueryParameter("configType")) if err != nil { bcode.ReturnError(req, res, err) return @@ -544,8 +544,8 @@ func (n *projectWebService) getConfigs(req *restful.Request, res *restful.Respon } } -func (n *projectWebService) validateImage(req *restful.Request, res *restful.Response) { - resp, err := n.projectUsecase.ValidateImage(req.Request.Context(), req.PathParameter("projectName"), req.QueryParameter("image")) +func (n *projectAPIInterface) validateImage(req *restful.Request, res *restful.Response) { + resp, err := n.ProjectService.ValidateImage(req.Request.Context(), req.PathParameter("projectName"), req.QueryParameter("image")) if err != nil { bcode.ReturnError(req, res, err) return diff --git a/pkg/apiserver/rest/webservice/rbac.go b/pkg/apiserver/interfaces/api/rbac.go similarity index 69% rename from pkg/apiserver/rest/webservice/rbac.go rename to pkg/apiserver/interfaces/api/rbac.go index a89b517ac..fa7961b1f 100644 --- a/pkg/apiserver/rest/webservice/rbac.go +++ b/pkg/apiserver/interfaces/api/rbac.go @@ -14,29 +14,29 @@ See the License for the specific language governing permissions and limitations under the License. */ -package webservice +package api import ( restfulspec "github.com/emicklei/go-restful-openapi/v2" "github.com/emicklei/go-restful/v3" - "github.com/oam-dev/kubevela/pkg/apiserver/log" - apis "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/usecase" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/service" + apis "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/log" ) -type rbacWebService struct { - rbacUsecase usecase.RBACUsecase +type rbacAPIInterface struct { + RbacService service.RBACService `inject:""` } -// NewRBACWebService new rbac webservice -func NewRBACWebService(rbacUsecase usecase.RBACUsecase) WebService { - return &rbacWebService{rbacUsecase: rbacUsecase} +// NewRBACAPIInterface new rbac APIInterface +func NewRBACAPIInterface() Interface { + return &rbacAPIInterface{} } -func (r *rbacWebService) GetWebService() *restful.WebService { +func (r *rbacAPIInterface) GetWebServiceRoute() *restful.WebService { ws := new(restful.WebService) ws.Path(versionPrefix). Consumes(restful.MIME_XML, restful.MIME_JSON). @@ -48,14 +48,14 @@ func (r *rbacWebService) GetWebService() *restful.WebService { ws.Route(ws.GET("/roles").To(r.listPlatformRoles). Doc("list all platform level roles"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(r.rbacUsecase.CheckPerm("role", "list")). + Filter(r.RbacService.CheckPerm("role", "list")). Returns(200, "OK", apis.ListRolesResponse{}). Writes(apis.ListRolesResponse{})) ws.Route(ws.POST("/roles").To(r.createPlatformRole). Doc("create platform level role"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(r.rbacUsecase.CheckPerm("role", "create")). + Filter(r.RbacService.CheckPerm("role", "create")). Returns(200, "OK", apis.RoleBase{}). Reads(apis.CreateRoleRequest{}). Writes(apis.RoleBase{})) @@ -63,7 +63,7 @@ func (r *rbacWebService) GetWebService() *restful.WebService { ws.Route(ws.PUT("/roles/{roleName}").To(r.updatePlatformRole). Doc("update platform level role"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(r.rbacUsecase.CheckPerm("role", "update")). + Filter(r.RbacService.CheckPerm("role", "update")). Reads(apis.UpdateRoleRequest{}). Returns(200, "OK", apis.RoleBase{}). Writes(apis.RoleBase{})) @@ -71,14 +71,14 @@ func (r *rbacWebService) GetWebService() *restful.WebService { ws.Route(ws.DELETE("/roles/{roleName}").To(r.deletePlatformRole). Doc("update platform level role"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(r.rbacUsecase.CheckPerm("role", "delete")). + Filter(r.RbacService.CheckPerm("role", "delete")). Returns(200, "OK", apis.EmptyResponse{}). Writes(apis.EmptyResponse{})) ws.Route(ws.GET("/permissions").To(r.listPlatformPermissions). Doc("list all project level perm policies"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(r.rbacUsecase.CheckPerm("permission", "list")). + Filter(r.RbacService.CheckPerm("permission", "list")). Returns(200, "OK", []apis.PermissionBase{}). Writes([]apis.PermissionBase{})) @@ -86,13 +86,13 @@ func (r *rbacWebService) GetWebService() *restful.WebService { return ws } -func (r *rbacWebService) listPlatformRoles(req *restful.Request, res *restful.Response) { +func (r *rbacAPIInterface) listPlatformRoles(req *restful.Request, res *restful.Response) { page, pageSize, err := utils.ExtractPagingParams(req, minPageSize, maxPageSize) if err != nil { bcode.ReturnError(req, res, err) return } - roles, err := r.rbacUsecase.ListRole(req.Request.Context(), "", page, pageSize) + roles, err := r.RbacService.ListRole(req.Request.Context(), "", page, pageSize) if err != nil { bcode.ReturnError(req, res, err) return @@ -103,7 +103,7 @@ func (r *rbacWebService) listPlatformRoles(req *restful.Request, res *restful.Re } } -func (r *rbacWebService) createPlatformRole(req *restful.Request, res *restful.Response) { +func (r *rbacAPIInterface) createPlatformRole(req *restful.Request, res *restful.Response) { // Verify the validity of parameters var createReq apis.CreateRoleRequest if err := req.ReadEntity(&createReq); err != nil { @@ -114,8 +114,8 @@ func (r *rbacWebService) createPlatformRole(req *restful.Request, res *restful.R bcode.ReturnError(req, res, err) return } - // Call the usecase layer code - projectBase, err := r.rbacUsecase.CreateRole(req.Request.Context(), "", createReq) + // Call the domain layer code + projectBase, err := r.RbacService.CreateRole(req.Request.Context(), "", createReq) if err != nil { log.Logger.Errorf("create role failure %s", err.Error()) bcode.ReturnError(req, res, err) @@ -129,7 +129,7 @@ func (r *rbacWebService) createPlatformRole(req *restful.Request, res *restful.R } } -func (r *rbacWebService) updatePlatformRole(req *restful.Request, res *restful.Response) { +func (r *rbacAPIInterface) updatePlatformRole(req *restful.Request, res *restful.Response) { // Verify the validity of parameters var updateReq apis.UpdateRoleRequest if err := req.ReadEntity(&updateReq); err != nil { @@ -140,8 +140,8 @@ func (r *rbacWebService) updatePlatformRole(req *restful.Request, res *restful.R bcode.ReturnError(req, res, err) return } - // Call the usecase layer code - roleBase, err := r.rbacUsecase.UpdateRole(req.Request.Context(), "", req.PathParameter("roleName"), updateReq) + // Call the domain layer code + roleBase, err := r.RbacService.UpdateRole(req.Request.Context(), "", req.PathParameter("roleName"), updateReq) if err != nil { log.Logger.Errorf("update role failure %s", err.Error()) bcode.ReturnError(req, res, err) @@ -155,8 +155,8 @@ func (r *rbacWebService) updatePlatformRole(req *restful.Request, res *restful.R } } -func (r *rbacWebService) deletePlatformRole(req *restful.Request, res *restful.Response) { - err := r.rbacUsecase.DeleteRole(req.Request.Context(), "", req.PathParameter("roleName")) +func (r *rbacAPIInterface) deletePlatformRole(req *restful.Request, res *restful.Response) { + err := r.RbacService.DeleteRole(req.Request.Context(), "", req.PathParameter("roleName")) if err != nil { bcode.ReturnError(req, res, err) return @@ -168,8 +168,8 @@ func (r *rbacWebService) deletePlatformRole(req *restful.Request, res *restful.R } } -func (r *rbacWebService) listPlatformPermissions(req *restful.Request, res *restful.Response) { - policies, err := r.rbacUsecase.ListPermissions(req.Request.Context(), "") +func (r *rbacAPIInterface) listPlatformPermissions(req *restful.Request, res *restful.Response) { + policies, err := r.RbacService.ListPermissions(req.Request.Context(), "") if err != nil { bcode.ReturnError(req, res, err) return diff --git a/pkg/apiserver/rest/webservice/system_info.go b/pkg/apiserver/interfaces/api/system_info.go similarity index 66% rename from pkg/apiserver/rest/webservice/system_info.go rename to pkg/apiserver/interfaces/api/system_info.go index 958ffeba5..92dce9d4a 100644 --- a/pkg/apiserver/rest/webservice/system_info.go +++ b/pkg/apiserver/interfaces/api/system_info.go @@ -14,29 +14,29 @@ See the License for the specific language governing permissions and limitations under the License. */ -package webservice +package api import ( restfulspec "github.com/emicklei/go-restful-openapi/v2" "github.com/emicklei/go-restful/v3" - apis "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/usecase" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/service" + apis "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" ) -type systemInfoWebService struct { - useCase usecase.SystemInfoUsecase - rbacUsecase usecase.RBACUsecase +type systemInfoAPIInterface struct { + SystemInfoService service.SystemInfoService `inject:""` + RbacService service.RBACService `inject:""` } -// NewSystemInfoWebService return systemInfo webservice -func NewSystemInfoWebService(systemInfoUseCase usecase.SystemInfoUsecase, rbacUsecase usecase.RBACUsecase) WebService { - return &systemInfoWebService{useCase: systemInfoUseCase, rbacUsecase: rbacUsecase} +// NewSystemInfoAPIInterface return systemInfo APIInterface +func NewSystemInfoAPIInterface() Interface { + return &systemInfoAPIInterface{} } -// GetWebService return systemInfo webservice -func (u systemInfoWebService) GetWebService() *restful.WebService { +// GetAPIInterface return systemInfo APIInterface +func (u systemInfoAPIInterface) GetWebServiceRoute() *restful.WebService { ws := new(restful.WebService) ws.Path(versionPrefix+"/system_info").Consumes(restful.MIME_XML, restful.MIME_JSON). Produces(restful.MIME_JSON, restful.MIME_XML). @@ -55,7 +55,7 @@ func (u systemInfoWebService) GetWebService() *restful.WebService { ws.Route(ws.PUT("/").To(u.updateSystemInfo). Metadata(restfulspec.KeyOpenAPITags, tags). Reads(apis.SystemInfoRequest{}). - Filter(u.rbacUsecase.CheckPerm("systemSetting", "update")). + Filter(u.RbacService.CheckPerm("systemSetting", "update")). Returns(200, "OK", apis.SystemInfoResponse{}). Returns(400, "Bad Request", bcode.Bcode{}). Writes(apis.SystemInfoResponse{})) @@ -64,8 +64,8 @@ func (u systemInfoWebService) GetWebService() *restful.WebService { return ws } -func (u systemInfoWebService) getSystemInfo(req *restful.Request, res *restful.Response) { - info, err := u.useCase.GetSystemInfo(req.Request.Context()) +func (u systemInfoAPIInterface) getSystemInfo(req *restful.Request, res *restful.Response) { + info, err := u.SystemInfoService.GetSystemInfo(req.Request.Context()) if err != nil { bcode.ReturnError(req, res, err) return @@ -76,7 +76,7 @@ func (u systemInfoWebService) getSystemInfo(req *restful.Request, res *restful.R } } -func (u systemInfoWebService) updateSystemInfo(req *restful.Request, res *restful.Response) { +func (u systemInfoAPIInterface) updateSystemInfo(req *restful.Request, res *restful.Response) { var systemInfoReq apis.SystemInfoRequest var args []byte _, err := req.Request.Body.Read(args) @@ -92,7 +92,7 @@ func (u systemInfoWebService) updateSystemInfo(req *restful.Request, res *restfu } } - info, err := u.useCase.UpdateSystemInfo(req.Request.Context(), systemInfoReq) + info, err := u.SystemInfoService.UpdateSystemInfo(req.Request.Context(), systemInfoReq) if err != nil { bcode.ReturnError(req, res, err) return diff --git a/pkg/apiserver/rest/webservice/target.go b/pkg/apiserver/interfaces/api/target.go similarity index 69% rename from pkg/apiserver/rest/webservice/target.go rename to pkg/apiserver/interfaces/api/target.go index bd9c2a816..86e267cce 100644 --- a/pkg/apiserver/rest/webservice/target.go +++ b/pkg/apiserver/interfaces/api/target.go @@ -14,44 +14,40 @@ See the License for the specific language governing permissions and limitations under the License. */ -package webservice +package api import ( "context" "github.com/pkg/errors" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" restfulspec "github.com/emicklei/go-restful-openapi/v2" "github.com/emicklei/go-restful/v3" - "github.com/oam-dev/kubevela/pkg/apiserver/log" - "github.com/oam-dev/kubevela/pkg/apiserver/model" - apis "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/usecase" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/service" + apis "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/log" ) -// NewTargetWebService new Target webservice -func NewTargetWebService(targetUsecase usecase.TargetUsecase, applicationUsecase usecase.ApplicationUsecase, rbacUsecase usecase.RBACUsecase) WebService { - return &TargetWebService{ - TargetUsecase: targetUsecase, - applicationUsecase: applicationUsecase, - rbacUsecase: rbacUsecase, - } +// NewTargetAPIInterface new Target Interface +func NewTargetAPIInterface() Interface { + return &TargetAPIInterface{} } -// TargetWebService target web service -type TargetWebService struct { - TargetUsecase usecase.TargetUsecase - applicationUsecase usecase.ApplicationUsecase - rbacUsecase usecase.RBACUsecase +// TargetAPIInterface target web service +type TargetAPIInterface struct { + TargetService service.TargetService `inject:""` + ApplicationService service.ApplicationService `inject:""` + RbacService service.RBACService `inject:""` } -// GetWebService get web service -func (dt *TargetWebService) GetWebService() *restful.WebService { +// GetWebServiceRoute get web service +func (dt *TargetAPIInterface) GetWebServiceRoute() *restful.WebService { ws := new(restful.WebService) ws.Path(versionPrefix+"/targets"). Consumes(restful.MIME_XML, restful.MIME_JSON). @@ -63,7 +59,7 @@ func (dt *TargetWebService) GetWebService() *restful.WebService { ws.Route(ws.GET("/").To(dt.listTargets). Doc("list Target"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(dt.rbacUsecase.CheckPerm("target", "list")). + Filter(dt.RbacService.CheckPerm("target", "list")). Param(ws.QueryParameter("page", "Page for paging").DataType("integer")). Param(ws.QueryParameter("pageSize", "PageSize for paging").DataType("integer")). Param(ws.QueryParameter("project", "list targets by project name").DataType("string")). @@ -74,7 +70,7 @@ func (dt *TargetWebService) GetWebService() *restful.WebService { Doc("create Target"). Metadata(restfulspec.KeyOpenAPITags, tags). Reads(apis.CreateTargetRequest{}). - Filter(dt.rbacUsecase.CheckPerm("target", "create")). + Filter(dt.RbacService.CheckPerm("target", "create")). Returns(200, "create success", apis.DetailTargetResponse{}). Returns(400, "create failure", bcode.Bcode{}). Writes(apis.DetailTargetResponse{}).Do(returns200, returns500)) @@ -84,7 +80,7 @@ func (dt *TargetWebService) GetWebService() *restful.WebService { Param(ws.PathParameter("targetName", "identifier of the Target.").DataType("string")). Metadata(restfulspec.KeyOpenAPITags, tags). Filter(dt.targetCheckFilter). - Filter(dt.rbacUsecase.CheckPerm("target", "detail")). + Filter(dt.RbacService.CheckPerm("target", "detail")). Returns(200, "create success", apis.DetailTargetResponse{}). Writes(apis.DetailTargetResponse{}).Do(returns200, returns500)) @@ -94,7 +90,7 @@ func (dt *TargetWebService) GetWebService() *restful.WebService { Filter(dt.targetCheckFilter). Param(ws.PathParameter("targetName", "identifier of the Target").DataType("string")). Reads(apis.UpdateTargetRequest{}). - Filter(dt.rbacUsecase.CheckPerm("target", "update")). + Filter(dt.RbacService.CheckPerm("target", "update")). Returns(200, "OK", apis.DetailTargetResponse{}). Writes(apis.DetailTargetResponse{}).Do(returns200, returns500)) @@ -102,7 +98,7 @@ func (dt *TargetWebService) GetWebService() *restful.WebService { Doc("deletet Target"). Metadata(restfulspec.KeyOpenAPITags, tags). Filter(dt.targetCheckFilter). - Filter(dt.rbacUsecase.CheckPerm("target", "delete")). + Filter(dt.RbacService.CheckPerm("target", "delete")). Param(ws.PathParameter("targetName", "identifier of the Target").DataType("string")). Returns(200, "OK", apis.EmptyResponse{}). Writes(apis.EmptyResponse{}).Do(returns200, returns500)) @@ -111,7 +107,7 @@ func (dt *TargetWebService) GetWebService() *restful.WebService { return ws } -func (dt *TargetWebService) createTarget(req *restful.Request, res *restful.Response) { +func (dt *TargetAPIInterface) createTarget(req *restful.Request, res *restful.Response) { // Verify the validity of parameters var createReq apis.CreateTargetRequest if err := req.ReadEntity(&createReq); err != nil { @@ -122,8 +118,8 @@ func (dt *TargetWebService) createTarget(req *restful.Request, res *restful.Resp bcode.ReturnError(req, res, err) return } - // Call the usecase layer code - TargetDetail, err := dt.TargetUsecase.CreateTarget(req.Request.Context(), createReq) + // Call the domain layer code + TargetDetail, err := dt.TargetService.CreateTarget(req.Request.Context(), createReq) if err != nil { log.Logger.Errorf("create -target failure %s", err.Error()) bcode.ReturnError(req, res, err) @@ -136,8 +132,8 @@ func (dt *TargetWebService) createTarget(req *restful.Request, res *restful.Resp } } -func (dt *TargetWebService) targetCheckFilter(req *restful.Request, res *restful.Response, chain *restful.FilterChain) { - Target, err := dt.TargetUsecase.GetTarget(req.Request.Context(), req.PathParameter("targetName")) +func (dt *TargetAPIInterface) targetCheckFilter(req *restful.Request, res *restful.Response, chain *restful.FilterChain) { + Target, err := dt.TargetService.GetTarget(req.Request.Context(), req.PathParameter("targetName")) if err != nil { bcode.ReturnError(req, res, err) return @@ -146,9 +142,9 @@ func (dt *TargetWebService) targetCheckFilter(req *restful.Request, res *restful chain.ProcessFilter(req, res) } -func (dt *TargetWebService) detailTarget(req *restful.Request, res *restful.Response) { +func (dt *TargetAPIInterface) detailTarget(req *restful.Request, res *restful.Response) { Target := req.Request.Context().Value(&apis.CtxKeyTarget).(*model.Target) - detail, err := dt.TargetUsecase.DetailTarget(req.Request.Context(), Target) + detail, err := dt.TargetService.DetailTarget(req.Request.Context(), Target) if err != nil { bcode.ReturnError(req, res, err) return @@ -159,7 +155,7 @@ func (dt *TargetWebService) detailTarget(req *restful.Request, res *restful.Resp } } -func (dt *TargetWebService) updateTarget(req *restful.Request, res *restful.Response) { +func (dt *TargetAPIInterface) updateTarget(req *restful.Request, res *restful.Response) { Target := req.Request.Context().Value(&apis.CtxKeyTarget).(*model.Target) // Verify the validity of parameters var updateReq apis.UpdateTargetRequest @@ -171,7 +167,7 @@ func (dt *TargetWebService) updateTarget(req *restful.Request, res *restful.Resp bcode.ReturnError(req, res, err) return } - detail, err := dt.TargetUsecase.UpdateTarget(req.Request.Context(), Target, updateReq) + detail, err := dt.TargetService.UpdateTarget(req.Request.Context(), Target, updateReq) if err != nil { bcode.ReturnError(req, res, err) return @@ -182,10 +178,10 @@ func (dt *TargetWebService) updateTarget(req *restful.Request, res *restful.Resp } } -func (dt *TargetWebService) deleteTarget(req *restful.Request, res *restful.Response) { +func (dt *TargetAPIInterface) deleteTarget(req *restful.Request, res *restful.Response) { TargetName := req.PathParameter("targetName") // Target in use, can't be deleted - applications, err := dt.applicationUsecase.ListApplications(req.Request.Context(), apis.ListApplicationOptions{TargetName: TargetName}) + applications, err := dt.ApplicationService.ListApplications(req.Request.Context(), apis.ListApplicationOptions{TargetName: TargetName}) if err != nil { if !errors.Is(err, datastore.ErrRecordNotExist) { bcode.ReturnError(req, res, err) @@ -196,7 +192,7 @@ func (dt *TargetWebService) deleteTarget(req *restful.Request, res *restful.Resp bcode.ReturnError(req, res, bcode.ErrTargetInUseCantDeleted) return } - if err := dt.TargetUsecase.DeleteTarget(req.Request.Context(), TargetName); err != nil { + if err := dt.TargetService.DeleteTarget(req.Request.Context(), TargetName); err != nil { bcode.ReturnError(req, res, err) return } @@ -206,13 +202,13 @@ func (dt *TargetWebService) deleteTarget(req *restful.Request, res *restful.Resp } } -func (dt *TargetWebService) listTargets(req *restful.Request, res *restful.Response) { +func (dt *TargetAPIInterface) listTargets(req *restful.Request, res *restful.Response) { page, pageSize, err := utils.ExtractPagingParams(req, minPageSize, maxPageSize) if err != nil { bcode.ReturnError(req, res, err) return } - Targets, err := dt.TargetUsecase.ListTargets(req.Request.Context(), page, pageSize, req.QueryParameter("project")) + Targets, err := dt.TargetService.ListTargets(req.Request.Context(), page, pageSize, req.QueryParameter("project")) if err != nil { bcode.ReturnError(req, res, err) return diff --git a/pkg/apiserver/rest/webservice/user.go b/pkg/apiserver/interfaces/api/user.go similarity index 71% rename from pkg/apiserver/rest/webservice/user.go rename to pkg/apiserver/interfaces/api/user.go index 0687f6586..eb830ad3d 100644 --- a/pkg/apiserver/rest/webservice/user.go +++ b/pkg/apiserver/interfaces/api/user.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package webservice +package api import ( "context" @@ -22,27 +22,24 @@ import ( restfulspec "github.com/emicklei/go-restful-openapi/v2" "github.com/emicklei/go-restful/v3" - "github.com/oam-dev/kubevela/pkg/apiserver/model" - apis "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/usecase" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/service" + apis "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" ) -type userWebService struct { - userUsecase usecase.UserUsecase - rbacUsecase usecase.RBACUsecase +type userAPIInterface struct { + UserService service.UserService `inject:""` + RbacService service.RBACService `inject:""` } -// NewUserWebService is the webservice of user -func NewUserWebService(userUsecase usecase.UserUsecase, rbacUsecase usecase.RBACUsecase) WebService { - return &userWebService{ - userUsecase: userUsecase, - rbacUsecase: rbacUsecase, - } +// NewUserAPIInterface is the APIInterface of user +func NewUserAPIInterface() Interface { + return &userAPIInterface{} } -func (c *userWebService) GetWebService() *restful.WebService { +func (c *userAPIInterface) GetWebServiceRoute() *restful.WebService { ws := new(restful.WebService) ws.Path(versionPrefix+"/users"). Consumes(restful.MIME_XML, restful.MIME_JSON). @@ -54,7 +51,7 @@ func (c *userWebService) GetWebService() *restful.WebService { ws.Route(ws.GET("/").To(c.listUser). Doc("list users"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(c.rbacUsecase.CheckPerm("user", "list")). + Filter(c.RbacService.CheckPerm("user", "list")). Param(ws.QueryParameter("page", "query the page number").DataType("integer")). Param(ws.QueryParameter("pageSize", "query the page size number").DataType("integer")). Param(ws.QueryParameter("name", "fuzzy search based on name").DataType("string")). @@ -66,7 +63,7 @@ func (c *userWebService) GetWebService() *restful.WebService { ws.Route(ws.POST("/").To(c.createUser). Doc("create a user"). - Filter(c.rbacUsecase.CheckPerm("user", "create")). + Filter(c.RbacService.CheckPerm("user", "create")). Metadata(restfulspec.KeyOpenAPITags, tags). Reads(apis.CreateUserRequest{}). Returns(200, "OK", apis.UserBase{}). @@ -76,7 +73,7 @@ func (c *userWebService) GetWebService() *restful.WebService { ws.Route(ws.GET("/{username}").To(c.detailUser). Doc("get user detail"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(c.rbacUsecase.CheckPerm("user", "detail")). + Filter(c.RbacService.CheckPerm("user", "detail")). Filter(c.userCheckFilter). Returns(200, "OK", apis.DetailUserResponse{}). Returns(400, "Bad Request", bcode.Bcode{}). @@ -85,7 +82,7 @@ func (c *userWebService) GetWebService() *restful.WebService { ws.Route(ws.PUT("/{username}").To(c.updateUser). Doc("update a user's alias or password"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(c.rbacUsecase.CheckPerm("user", "update")). + Filter(c.RbacService.CheckPerm("user", "update")). Filter(c.userCheckFilter). Returns(200, "OK", apis.UserBase{}). Returns(400, "Bad Request", bcode.Bcode{}). @@ -94,7 +91,7 @@ func (c *userWebService) GetWebService() *restful.WebService { ws.Route(ws.DELETE("/{username}").To(c.deleteUser). Doc("delete a user"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(c.rbacUsecase.CheckPerm("user", "delete")). + Filter(c.RbacService.CheckPerm("user", "delete")). Returns(200, "OK", apis.EmptyResponse{}). Returns(400, "Bad Request", bcode.Bcode{}). Writes(apis.EmptyResponse{})) @@ -102,7 +99,7 @@ func (c *userWebService) GetWebService() *restful.WebService { ws.Route(ws.GET("/{username}/disable").To(c.disableUser). Doc("disable a user"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(c.rbacUsecase.CheckPerm("user", "disable")). + Filter(c.RbacService.CheckPerm("user", "disable")). Filter(c.userCheckFilter). Returns(200, "OK", apis.EmptyResponse{}). Returns(400, "Bad Request", bcode.Bcode{}). @@ -111,7 +108,7 @@ func (c *userWebService) GetWebService() *restful.WebService { ws.Route(ws.GET("/{username}/enable").To(c.enableUser). Doc("enable a user"). Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(c.rbacUsecase.CheckPerm("user", "enable")). + Filter(c.RbacService.CheckPerm("user", "enable")). Filter(c.userCheckFilter). Returns(200, "OK", apis.EmptyResponse{}). Returns(400, "Bad Request", bcode.Bcode{}). @@ -121,8 +118,8 @@ func (c *userWebService) GetWebService() *restful.WebService { return ws } -func (c *userWebService) userCheckFilter(req *restful.Request, res *restful.Response, chain *restful.FilterChain) { - user, err := c.userUsecase.GetUser(req.Request.Context(), req.PathParameter("username")) +func (c *userAPIInterface) userCheckFilter(req *restful.Request, res *restful.Response, chain *restful.FilterChain) { + user, err := c.UserService.GetUser(req.Request.Context(), req.PathParameter("username")) if err != nil { bcode.ReturnError(req, res, err) return @@ -131,7 +128,7 @@ func (c *userWebService) userCheckFilter(req *restful.Request, res *restful.Resp chain.ProcessFilter(req, res) } -func (c *userWebService) createUser(req *restful.Request, res *restful.Response) { +func (c *userAPIInterface) createUser(req *restful.Request, res *restful.Response) { var createReq apis.CreateUserRequest if err := req.ReadEntity(&createReq); err != nil { bcode.ReturnError(req, res, err) @@ -141,7 +138,7 @@ func (c *userWebService) createUser(req *restful.Request, res *restful.Response) bcode.ReturnError(req, res, err) return } - resp, err := c.userUsecase.CreateUser(req.Request.Context(), createReq) + resp, err := c.UserService.CreateUser(req.Request.Context(), createReq) if err != nil { bcode.ReturnError(req, res, err) return @@ -152,9 +149,9 @@ func (c *userWebService) createUser(req *restful.Request, res *restful.Response) } } -func (c *userWebService) detailUser(req *restful.Request, res *restful.Response) { +func (c *userAPIInterface) detailUser(req *restful.Request, res *restful.Response) { user := req.Request.Context().Value(&apis.CtxKeyUser).(*model.User) - resp, err := c.userUsecase.DetailUser(req.Request.Context(), user) + resp, err := c.UserService.DetailUser(req.Request.Context(), user) if err != nil { bcode.ReturnError(req, res, err) return @@ -165,8 +162,8 @@ func (c *userWebService) detailUser(req *restful.Request, res *restful.Response) } } -func (c *userWebService) deleteUser(req *restful.Request, res *restful.Response) { - err := c.userUsecase.DeleteUser(req.Request.Context(), req.PathParameter("username")) +func (c *userAPIInterface) deleteUser(req *restful.Request, res *restful.Response) { + err := c.UserService.DeleteUser(req.Request.Context(), req.PathParameter("username")) if err != nil { bcode.ReturnError(req, res, err) return @@ -177,13 +174,13 @@ func (c *userWebService) deleteUser(req *restful.Request, res *restful.Response) } } -func (c *userWebService) listUser(req *restful.Request, res *restful.Response) { +func (c *userAPIInterface) listUser(req *restful.Request, res *restful.Response) { page, pageSize, err := utils.ExtractPagingParams(req, minPageSize, maxPageSize) if err != nil { bcode.ReturnError(req, res, err) return } - resp, err := c.userUsecase.ListUsers(req.Request.Context(), page, pageSize, apis.ListUserOptions{ + resp, err := c.UserService.ListUsers(req.Request.Context(), page, pageSize, apis.ListUserOptions{ Name: req.QueryParameter("name"), Alias: req.QueryParameter("alias"), Email: req.QueryParameter("email"), @@ -198,7 +195,7 @@ func (c *userWebService) listUser(req *restful.Request, res *restful.Response) { } } -func (c *userWebService) updateUser(req *restful.Request, res *restful.Response) { +func (c *userAPIInterface) updateUser(req *restful.Request, res *restful.Response) { user := req.Request.Context().Value(&apis.CtxKeyUser).(*model.User) var updateReq apis.UpdateUserRequest if err := req.ReadEntity(&updateReq); err != nil { @@ -209,7 +206,7 @@ func (c *userWebService) updateUser(req *restful.Request, res *restful.Response) bcode.ReturnError(req, res, err) return } - resp, err := c.userUsecase.UpdateUser(req.Request.Context(), user, updateReq) + resp, err := c.UserService.UpdateUser(req.Request.Context(), user, updateReq) if err != nil { bcode.ReturnError(req, res, err) return @@ -220,9 +217,9 @@ func (c *userWebService) updateUser(req *restful.Request, res *restful.Response) } } -func (c *userWebService) disableUser(req *restful.Request, res *restful.Response) { +func (c *userAPIInterface) disableUser(req *restful.Request, res *restful.Response) { user := req.Request.Context().Value(&apis.CtxKeyUser).(*model.User) - err := c.userUsecase.DisableUser(req.Request.Context(), user) + err := c.UserService.DisableUser(req.Request.Context(), user) if err != nil { bcode.ReturnError(req, res, err) return @@ -233,9 +230,9 @@ func (c *userWebService) disableUser(req *restful.Request, res *restful.Response } } -func (c *userWebService) enableUser(req *restful.Request, res *restful.Response) { +func (c *userAPIInterface) enableUser(req *restful.Request, res *restful.Response) { user := req.Request.Context().Value(&apis.CtxKeyUser).(*model.User) - err := c.userUsecase.EnableUser(req.Request.Context(), user) + err := c.UserService.EnableUser(req.Request.Context(), user) if err != nil { bcode.ReturnError(req, res, err) return diff --git a/pkg/apiserver/rest/webservice/validate.go b/pkg/apiserver/interfaces/api/validate.go similarity index 95% rename from pkg/apiserver/rest/webservice/validate.go rename to pkg/apiserver/interfaces/api/validate.go index ec7012474..4c38f9624 100644 --- a/pkg/apiserver/rest/webservice/validate.go +++ b/pkg/apiserver/interfaces/api/validate.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package webservice +package api import ( "regexp" @@ -22,7 +22,7 @@ import ( "github.com/go-playground/validator/v10" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/usecase" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/service" ) var validate = validator.New() @@ -58,7 +58,7 @@ func init() { // ValidatePayloadType check PayloadType func ValidatePayloadType(fl validator.FieldLevel) bool { value := fl.Field().String() - for _, v := range usecase.WebhookHandlers { + for _, v := range service.WebhookHandlers { if v == value { return true } diff --git a/pkg/apiserver/rest/webservice/validate_test.go b/pkg/apiserver/interfaces/api/validate_test.go similarity index 97% rename from pkg/apiserver/rest/webservice/validate_test.go rename to pkg/apiserver/interfaces/api/validate_test.go index 05566e915..a95074b9b 100644 --- a/pkg/apiserver/rest/webservice/validate_test.go +++ b/pkg/apiserver/interfaces/api/validate_test.go @@ -14,14 +14,14 @@ See the License for the specific language governing permissions and limitations under the License. */ -package webservice +package api import ( "github.com/google/go-cmp/cmp" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" + apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" ) var _ = Describe("Test validate function", func() { diff --git a/pkg/apiserver/rest/webservice/velaql.go b/pkg/apiserver/interfaces/api/velaql.go similarity index 66% rename from pkg/apiserver/rest/webservice/velaql.go rename to pkg/apiserver/interfaces/api/velaql.go index 77d01a1a4..56905a569 100644 --- a/pkg/apiserver/rest/webservice/velaql.go +++ b/pkg/apiserver/interfaces/api/velaql.go @@ -14,31 +14,28 @@ limitations under the License. */ -package webservice +package api import ( restfulspec "github.com/emicklei/go-restful-openapi/v2" "github.com/emicklei/go-restful/v3" - apis "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/usecase" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/service" + apis "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" ) -type velaQLWebService struct { - velaQLUsecase usecase.VelaQLUsecase - rbacUsecase usecase.RBACUsecase +type velaQLAPIInterface struct { + VelaQLService service.VelaQLService `inject:""` + RbacService service.RBACService `inject:""` } -// NewVelaQLWebService new velaQL webservice -func NewVelaQLWebService(velaQLUsecase usecase.VelaQLUsecase, rbacUsecase usecase.RBACUsecase) WebService { - return &velaQLWebService{ - velaQLUsecase: velaQLUsecase, - rbacUsecase: rbacUsecase, - } +// NewVelaQLAPIInterface new velaQL APIInterface +func NewVelaQLAPIInterface() Interface { + return &velaQLAPIInterface{} } -func (v *velaQLWebService) GetWebService() *restful.WebService { +func (v *velaQLAPIInterface) GetWebServiceRoute() *restful.WebService { ws := new(restful.WebService) ws.Path(versionPrefix+"/query"). Consumes(restful.MIME_XML, restful.MIME_JSON). @@ -51,7 +48,7 @@ func (v *velaQLWebService) GetWebService() *restful.WebService { Doc("use velaQL to query resource status"). Metadata(restfulspec.KeyOpenAPITags, tags). // TODO: VelaQL is an open data query API that is currently not compatible with RBAC. - // Filter(v.rbacUsecase.CheckPerm("application", "detail")). + // Filter(v.RbacService.CheckPerm("application", "detail")). Param(ws.QueryParameter("velaql", "velaql query statement").DataType("string")). Returns(200, "OK", apis.VelaQLViewResponse{}). Returns(400, "Bad Request", bcode.Bcode{}). @@ -61,10 +58,10 @@ func (v *velaQLWebService) GetWebService() *restful.WebService { return ws } -func (v *velaQLWebService) queryView(req *restful.Request, res *restful.Response) { +func (v *velaQLAPIInterface) queryView(req *restful.Request, res *restful.Response) { velaQL := req.QueryParameter("velaql") - qlResp, err := v.velaQLUsecase.QueryView(req.Request.Context(), velaQL) + qlResp, err := v.VelaQLService.QueryView(req.Request.Context(), velaQL) if err != nil { bcode.ReturnError(req, res, err) return diff --git a/pkg/apiserver/rest/webservice/webhook.go b/pkg/apiserver/interfaces/api/webhook.go similarity index 64% rename from pkg/apiserver/rest/webservice/webhook.go rename to pkg/apiserver/interfaces/api/webhook.go index e7b96ef93..8e83c2f49 100644 --- a/pkg/apiserver/rest/webservice/webhook.go +++ b/pkg/apiserver/interfaces/api/webhook.go @@ -14,31 +14,28 @@ See the License for the specific language governing permissions and limitations under the License. */ -package webservice +package api import ( restfulspec "github.com/emicklei/go-restful-openapi/v2" "github.com/emicklei/go-restful/v3" - apis "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/usecase" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/service" + apis "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" ) -type webhookWebService struct { - webhookUsecase usecase.WebhookUsecase - applicationUsecase usecase.ApplicationUsecase +type webhookAPIInterface struct { + WebhookService service.WebhookService `inject:""` + ApplicationService service.ApplicationService `inject:""` } -// NewWebhookWebService new application manage webservice -func NewWebhookWebService(webhookUsecase usecase.WebhookUsecase, applicationUsecase usecase.ApplicationUsecase) WebService { - return &webhookWebService{ - webhookUsecase: webhookUsecase, - applicationUsecase: applicationUsecase, - } +// NewWebhookAPIInterface new application manage APIInterface +func NewWebhookAPIInterface() Interface { + return &webhookAPIInterface{} } -func (c *webhookWebService) GetWebService() *restful.WebService { +func (c *webhookAPIInterface) GetWebServiceRoute() *restful.WebService { ws := new(restful.WebService) ws.Path(versionPrefix+"/webhook"). Consumes(restful.MIME_XML, restful.MIME_JSON). @@ -58,8 +55,8 @@ func (c *webhookWebService) GetWebService() *restful.WebService { return ws } -func (c *webhookWebService) handleApplicationWebhook(req *restful.Request, res *restful.Response) { - base, err := c.webhookUsecase.HandleApplicationWebhook(req.Request.Context(), req.PathParameter("token"), req) +func (c *webhookAPIInterface) handleApplicationWebhook(req *restful.Request, res *restful.Response) { + base, err := c.WebhookService.HandleApplicationWebhook(req.Request.Context(), req.PathParameter("token"), req) if err != nil { bcode.ReturnError(req, res, err) return diff --git a/pkg/apiserver/rest/webservice/workflow.go b/pkg/apiserver/interfaces/api/workflow.go similarity index 67% rename from pkg/apiserver/rest/webservice/workflow.go rename to pkg/apiserver/interfaces/api/workflow.go index 60f48b56a..84124d0dd 100644 --- a/pkg/apiserver/rest/webservice/workflow.go +++ b/pkg/apiserver/interfaces/api/workflow.go @@ -14,29 +14,35 @@ See the License for the specific language governing permissions and limitations under the License. */ -package webservice +package api import ( "context" restful "github.com/emicklei/go-restful/v3" - "github.com/oam-dev/kubevela/pkg/apiserver/log" - "github.com/oam-dev/kubevela/pkg/apiserver/model" - apis "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/usecase" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/service" + apis "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/log" ) -type workflowWebService struct { - workflowUsecase usecase.WorkflowUsecase - applicationUsecase usecase.ApplicationUsecase +// WorkflowAPIInterface workflow api +type WorkflowAPIInterface struct { + WorkflowService service.WorkflowService `inject:""` + ApplicationService service.ApplicationService `inject:""` } -func (w *workflowWebService) workflowCheckFilter(req *restful.Request, res *restful.Response, chain *restful.FilterChain) { +// NewWorkflowAPIInterface new workflow api interface +func NewWorkflowAPIInterface() interface{} { + return &WorkflowAPIInterface{} +} + +func (w *WorkflowAPIInterface) workflowCheckFilter(req *restful.Request, res *restful.Response, chain *restful.FilterChain) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) - workflow, err := w.workflowUsecase.GetWorkflow(req.Request.Context(), app, req.PathParameter("workflowName")) + workflow, err := w.WorkflowService.GetWorkflow(req.Request.Context(), app, req.PathParameter("workflowName")) if err != nil { bcode.ReturnError(req, res, err) return @@ -45,9 +51,9 @@ func (w *workflowWebService) workflowCheckFilter(req *restful.Request, res *rest chain.ProcessFilter(req, res) } -func (w *workflowWebService) listApplicationWorkflows(req *restful.Request, res *restful.Response) { +func (w *WorkflowAPIInterface) listApplicationWorkflows(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) - workflows, err := w.workflowUsecase.ListApplicationWorkflow(req.Request.Context(), app) + workflows, err := w.WorkflowService.ListApplicationWorkflow(req.Request.Context(), app) if err != nil { bcode.ReturnError(req, res, err) return @@ -58,7 +64,7 @@ func (w *workflowWebService) listApplicationWorkflows(req *restful.Request, res } } -func (w *workflowWebService) createOrUpdateApplicationWorkflow(req *restful.Request, res *restful.Response) { +func (w *WorkflowAPIInterface) createOrUpdateApplicationWorkflow(req *restful.Request, res *restful.Response) { // Verify the validity of parameters var createReq apis.CreateWorkflowRequest if err := req.ReadEntity(&createReq); err != nil { @@ -70,8 +76,8 @@ func (w *workflowWebService) createOrUpdateApplicationWorkflow(req *restful.Requ return } app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) - // Call the usecase layer code - workflowDetail, err := w.workflowUsecase.CreateOrUpdateWorkflow(req.Request.Context(), app, createReq) + // Call the domain service layer code + workflowDetail, err := w.WorkflowService.CreateOrUpdateWorkflow(req.Request.Context(), app, createReq) if err != nil { log.Logger.Errorf("create application failure %s", err.Error()) bcode.ReturnError(req, res, err) @@ -85,9 +91,9 @@ func (w *workflowWebService) createOrUpdateApplicationWorkflow(req *restful.Requ } } -func (w *workflowWebService) detailWorkflow(req *restful.Request, res *restful.Response) { +func (w *WorkflowAPIInterface) detailWorkflow(req *restful.Request, res *restful.Response) { workflow := req.Request.Context().Value(&apis.CtxKeyWorkflow).(*model.Workflow) - detail, err := w.workflowUsecase.DetailWorkflow(req.Request.Context(), workflow) + detail, err := w.WorkflowService.DetailWorkflow(req.Request.Context(), workflow) if err != nil { bcode.ReturnError(req, res, err) return @@ -98,7 +104,7 @@ func (w *workflowWebService) detailWorkflow(req *restful.Request, res *restful.R } } -func (w *workflowWebService) updateWorkflow(req *restful.Request, res *restful.Response) { +func (w *WorkflowAPIInterface) updateWorkflow(req *restful.Request, res *restful.Response) { workflow := req.Request.Context().Value(&apis.CtxKeyWorkflow).(*model.Workflow) // Verify the validity of parameters var updateReq apis.UpdateWorkflowRequest @@ -110,7 +116,7 @@ func (w *workflowWebService) updateWorkflow(req *restful.Request, res *restful.R bcode.ReturnError(req, res, err) return } - detail, err := w.workflowUsecase.UpdateWorkflow(req.Request.Context(), workflow, updateReq) + detail, err := w.WorkflowService.UpdateWorkflow(req.Request.Context(), workflow, updateReq) if err != nil { bcode.ReturnError(req, res, err) return @@ -121,9 +127,9 @@ func (w *workflowWebService) updateWorkflow(req *restful.Request, res *restful.R } } -func (w *workflowWebService) deleteWorkflow(req *restful.Request, res *restful.Response) { +func (w *WorkflowAPIInterface) deleteWorkflow(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) - if err := w.workflowUsecase.DeleteWorkflow(req.Request.Context(), app, req.PathParameter("workflowName")); err != nil { + if err := w.WorkflowService.DeleteWorkflow(req.Request.Context(), app, req.PathParameter("workflowName")); err != nil { bcode.ReturnError(req, res, err) return } @@ -133,14 +139,14 @@ func (w *workflowWebService) deleteWorkflow(req *restful.Request, res *restful.R } } -func (w *workflowWebService) listWorkflowRecords(req *restful.Request, res *restful.Response) { +func (w *WorkflowAPIInterface) listWorkflowRecords(req *restful.Request, res *restful.Response) { page, pageSize, err := utils.ExtractPagingParams(req, minPageSize, maxPageSize) if err != nil { bcode.ReturnError(req, res, err) return } workflow := req.Request.Context().Value(&apis.CtxKeyWorkflow).(*model.Workflow) - records, err := w.workflowUsecase.ListWorkflowRecords(req.Request.Context(), workflow, page, pageSize) + records, err := w.WorkflowService.ListWorkflowRecords(req.Request.Context(), workflow, page, pageSize) if err != nil { bcode.ReturnError(req, res, err) return @@ -152,9 +158,9 @@ func (w *workflowWebService) listWorkflowRecords(req *restful.Request, res *rest } } -func (w *workflowWebService) detailWorkflowRecord(req *restful.Request, res *restful.Response) { +func (w *WorkflowAPIInterface) detailWorkflowRecord(req *restful.Request, res *restful.Response) { workflow := req.Request.Context().Value(&apis.CtxKeyWorkflow).(*model.Workflow) - record, err := w.workflowUsecase.DetailWorkflowRecord(req.Request.Context(), workflow, req.PathParameter("record")) + record, err := w.WorkflowService.DetailWorkflowRecord(req.Request.Context(), workflow, req.PathParameter("record")) if err != nil { bcode.ReturnError(req, res, err) return @@ -166,10 +172,10 @@ func (w *workflowWebService) detailWorkflowRecord(req *restful.Request, res *res } } -func (w *workflowWebService) resumeWorkflowRecord(req *restful.Request, res *restful.Response) { +func (w *WorkflowAPIInterface) resumeWorkflowRecord(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) workflow := req.Request.Context().Value(&apis.CtxKeyWorkflow).(*model.Workflow) - err := w.workflowUsecase.ResumeRecord(req.Request.Context(), app, workflow, req.PathParameter("record")) + err := w.WorkflowService.ResumeRecord(req.Request.Context(), app, workflow, req.PathParameter("record")) if err != nil { bcode.ReturnError(req, res, err) return @@ -180,10 +186,10 @@ func (w *workflowWebService) resumeWorkflowRecord(req *restful.Request, res *res } } -func (w *workflowWebService) terminateWorkflowRecord(req *restful.Request, res *restful.Response) { +func (w *WorkflowAPIInterface) terminateWorkflowRecord(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) workflow := req.Request.Context().Value(&apis.CtxKeyWorkflow).(*model.Workflow) - err := w.workflowUsecase.TerminateRecord(req.Request.Context(), app, workflow, req.PathParameter("record")) + err := w.WorkflowService.TerminateRecord(req.Request.Context(), app, workflow, req.PathParameter("record")) if err != nil { bcode.ReturnError(req, res, err) return @@ -194,10 +200,10 @@ func (w *workflowWebService) terminateWorkflowRecord(req *restful.Request, res * } } -func (w *workflowWebService) rollbackWorkflowRecord(req *restful.Request, res *restful.Response) { +func (w *WorkflowAPIInterface) rollbackWorkflowRecord(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) workflow := req.Request.Context().Value(&apis.CtxKeyWorkflow).(*model.Workflow) - err := w.workflowUsecase.RollbackRecord(req.Request.Context(), app, workflow, req.PathParameter("record"), req.QueryParameter("rollbackVersion")) + err := w.WorkflowService.RollbackRecord(req.Request.Context(), app, workflow, req.PathParameter("record"), req.QueryParameter("rollbackVersion")) if err != nil { bcode.ReturnError(req, res, err) return diff --git a/pkg/apiserver/rest/usecase/converter.go b/pkg/apiserver/rest/usecase/converter.go deleted file mode 100644 index 72c29fcd7..000000000 --- a/pkg/apiserver/rest/usecase/converter.go +++ /dev/null @@ -1,78 +0,0 @@ -/* - Copyright 2021 The KubeVela Authors. - - 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 usecase - -import ( - "github.com/oam-dev/kubevela/pkg/apiserver/log" - "github.com/oam-dev/kubevela/pkg/apiserver/model" - apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" -) - -func convertPolicyModelToBase(policy *model.ApplicationPolicy) *apisv1.PolicyBase { - pb := &apisv1.PolicyBase{ - Name: policy.Name, - Type: policy.Type, - Properties: policy.Properties, - Description: policy.Description, - Creator: policy.Creator, - CreateTime: policy.CreateTime, - UpdateTime: policy.UpdateTime, - EnvName: policy.EnvName, - } - return pb -} - -func convertWorkflowBase(workflow *model.Workflow) apisv1.WorkflowBase { - var steps []apisv1.WorkflowStep - for _, step := range workflow.Steps { - steps = append(steps, convertFromWorkflowStepModel(step)) - } - return apisv1.WorkflowBase{ - Name: workflow.Name, - Alias: workflow.Alias, - Description: workflow.Description, - Default: convertBool(workflow.Default), - EnvName: workflow.EnvName, - CreateTime: workflow.CreateTime, - UpdateTime: workflow.UpdateTime, - Steps: steps, - } -} - -// convertAPIStep2ModelStep will convert api types of workflow step to model type -func convertAPIStep2ModelStep(apiSteps []apisv1.WorkflowStep) ([]model.WorkflowStep, error) { - var steps []model.WorkflowStep - for _, step := range apiSteps { - properties, err := model.NewJSONStructByString(step.Properties) - if err != nil { - log.Logger.Errorf("parse trait properties failire %w", err) - return nil, bcode.ErrInvalidProperties - } - steps = append(steps, model.WorkflowStep{ - Name: step.Name, - Alias: step.Alias, - Description: step.Description, - DependsOn: step.DependsOn, - Type: step.Type, - Inputs: step.Inputs, - Outputs: step.Outputs, - Properties: properties, - }) - } - return steps, nil -} diff --git a/pkg/apiserver/rest/usecase/envbinding.go b/pkg/apiserver/rest/usecase/envbinding.go deleted file mode 100644 index 1120c8645..000000000 --- a/pkg/apiserver/rest/usecase/envbinding.go +++ /dev/null @@ -1,425 +0,0 @@ -/* -Copyright 2021 The KubeVela Authors. - -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 usecase - -import ( - "context" - "errors" - "fmt" - - apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - - "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" - "github.com/oam-dev/kubevela/pkg/apiserver/clients" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/log" - "github.com/oam-dev/kubevela/pkg/apiserver/model" - apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" - utils2 "github.com/oam-dev/kubevela/pkg/utils" -) - -const ( - // Deploy2Env deploy app to target cluster, suitable for common applications - Deploy2Env string = "deploy2env" - // DeployCloudResource deploy app to local and copy secret to target cluster, suitable for cloud application. - DeployCloudResource string = "deploy-cloud-resource" - // TerraformWorkloadType cloud application - TerraformWorkloadType string = "configurations.terraform.core.oam.dev" - // TerraformWorkloadKind terraform workload kind - TerraformWorkloadKind string = "Configuration" -) - -// EnvBindingUsecase envbinding usecase -type EnvBindingUsecase interface { - GetEnvBindings(ctx context.Context, app *model.Application) ([]*apisv1.EnvBindingBase, error) - GetEnvBinding(ctx context.Context, app *model.Application, envName string) (*model.EnvBinding, error) - CreateEnvBinding(ctx context.Context, app *model.Application, env apisv1.CreateApplicationEnvbindingRequest) (*apisv1.EnvBinding, error) - BatchCreateEnvBinding(ctx context.Context, app *model.Application, env apisv1.EnvBindingList) error - UpdateEnvBinding(ctx context.Context, app *model.Application, envName string, diff apisv1.PutApplicationEnvBindingRequest) (*apisv1.DetailEnvBindingResponse, error) - DeleteEnvBinding(ctx context.Context, app *model.Application, envName string) error - BatchDeleteEnvBinding(ctx context.Context, app *model.Application) error - DetailEnvBinding(ctx context.Context, app *model.Application, envBinding *model.EnvBinding) (*apisv1.DetailEnvBindingResponse, error) - ApplicationEnvRecycle(ctx context.Context, appModel *model.Application, envBinding *model.EnvBinding) error -} - -type envBindingUsecaseImpl struct { - ds datastore.DataStore - workflowUsecase WorkflowUsecase - envUsecase EnvUsecase - definitionUsecase DefinitionUsecase - kubeClient client.Client -} - -// NewEnvBindingUsecase new envBinding usecase -func NewEnvBindingUsecase(ds datastore.DataStore, workflowUsecase WorkflowUsecase, definitionUsecase DefinitionUsecase, envUsecase EnvUsecase) EnvBindingUsecase { - kubecli, err := clients.GetKubeClient() - if err != nil { - log.Logger.Fatalf("get kubeclient failure %s", err.Error()) - } - return &envBindingUsecaseImpl{ - ds: ds, - workflowUsecase: workflowUsecase, - definitionUsecase: definitionUsecase, - kubeClient: kubecli, - envUsecase: envUsecase, - } -} - -func pickEnv(envs []*model.Env, name string) (*model.Env, error) { - for _, e := range envs { - if e.Name == name { - return e, nil - } - } - return nil, bcode.ErrEnvNotExisted -} - -func listFullEnvBinding(ctx context.Context, ds datastore.DataStore, option envListOption) ([]*apisv1.EnvBindingBase, error) { - envBindings, err := listEnvBindings(ctx, ds, option) - if err != nil { - return nil, bcode.ErrEnvBindingsNotExist - } - targets, err := listTarget(ctx, ds, option.projectName, nil) - if err != nil { - return nil, err - } - var listOption *datastore.ListOptions - if option.projectName != "" { - listOption = &datastore.ListOptions{ - FilterOptions: datastore.FilterOptions{ - In: []datastore.InQueryOption{ - { - Key: "project", - Values: []string{option.projectName}, - }, - }, - }, - } - } - envs, err := listEnvs(ctx, ds, listOption) - if err != nil { - return nil, err - } - var list []*apisv1.EnvBindingBase - for _, eb := range envBindings { - env, err := pickEnv(envs, eb.Name) - if err != nil { - log.Logger.Errorf("envbinding invalid %s", err.Error()) - continue - } - list = append(list, convertEnvBindingModelToBase(eb, env, targets)) - } - return list, nil -} - -func (e *envBindingUsecaseImpl) GetEnvBindings(ctx context.Context, app *model.Application) ([]*apisv1.EnvBindingBase, error) { - full, err := listFullEnvBinding(ctx, e.ds, envListOption{appPrimaryKey: app.PrimaryKey(), projectName: app.Project}) - if err != nil { - log.Logger.Errorf("list envbinding for app %s err: %v\n", app.Name, err) - return nil, err - } - return full, nil -} - -func (e *envBindingUsecaseImpl) GetEnvBinding(ctx context.Context, app *model.Application, envName string) (*model.EnvBinding, error) { - envBinding, err := e.getBindingByEnv(ctx, app, envName) - if err != nil { - if errors.Is(err, datastore.ErrRecordNotExist) { - return nil, bcode.ErrEnvBindingsNotExist - } - return nil, err - } - return envBinding, nil -} - -// CheckAppEnvBindingsContainTarget check envbinding contain target -func CheckAppEnvBindingsContainTarget(envBindings []*apisv1.EnvBindingBase, targetName string) (bool, error) { - var filteredList []*apisv1.EnvBindingBase - for _, envBinding := range envBindings { - if utils.StringsContain(envBinding.TargetNames, targetName) { - filteredList = append(filteredList, envBinding) - } - } - return len(filteredList) > 0, nil -} - -func (e *envBindingUsecaseImpl) CreateEnvBinding(ctx context.Context, app *model.Application, envReq apisv1.CreateApplicationEnvbindingRequest) (*apisv1.EnvBinding, error) { - envBinding, err := e.getBindingByEnv(ctx, app, envReq.Name) - if err != nil { - if !errors.Is(err, datastore.ErrRecordNotExist) { - return nil, err - } - } - if envBinding != nil { - return nil, bcode.ErrEnvBindingExist - } - env, err := getEnv(ctx, e.ds, envReq.Name) - if err != nil { - return nil, err - } - envBindingModel := convertCreateReqToEnvBindingModel(app, envReq) - err = e.createEnvWorkflow(ctx, app, env, false) - if err != nil { - return nil, err - } - if err := e.ds.Add(ctx, &envBindingModel); err != nil { - return nil, err - } - - return &envReq.EnvBinding, nil -} - -func (e *envBindingUsecaseImpl) BatchCreateEnvBinding(ctx context.Context, app *model.Application, envbindings apisv1.EnvBindingList) error { - for i := range envbindings { - envBindingModel := convertToEnvBindingModel(app, *envbindings[i]) - env, err := getEnv(ctx, e.ds, envBindingModel.Name) - if err != nil { - log.Logger.Errorf("get env failure %s", err.Error()) - continue - } - if err := e.ds.Add(ctx, envBindingModel); err != nil { - log.Logger.Errorf("add envbinding %s failure %s", utils2.Sanitize(envBindingModel.Name), err.Error()) - continue - } - err = e.createEnvWorkflow(ctx, app, env, i == 0) - if err != nil { - log.Logger.Errorf("create env workflow failure %s", err.Error()) - continue - } - } - return nil -} - -func (e *envBindingUsecaseImpl) getBindingByEnv(ctx context.Context, app *model.Application, envName string) (*model.EnvBinding, error) { - var envBinding = model.EnvBinding{ - AppPrimaryKey: app.PrimaryKey(), - Name: envName, - } - err := e.ds.Get(ctx, &envBinding) - if err != nil { - return nil, err - } - return &envBinding, nil -} - -func (e *envBindingUsecaseImpl) UpdateEnvBinding(ctx context.Context, app *model.Application, envName string, _ apisv1.PutApplicationEnvBindingRequest) (*apisv1.DetailEnvBindingResponse, error) { - envBinding, err := e.getBindingByEnv(ctx, app, envName) - if err != nil { - if errors.Is(err, datastore.ErrRecordNotExist) { - return nil, bcode.ErrEnvBindingNotExist - } - return nil, err - } - env, err := getEnv(ctx, e.ds, envName) - if err != nil { - return nil, err - } - // update env - if err := e.ds.Put(ctx, envBinding); err != nil { - return nil, err - } - // update env workflow - if err := UpdateEnvWorkflow(ctx, e.kubeClient, e.ds, app, env); err != nil { - return nil, bcode.ErrEnvBindingUpdateWorkflow - } - return e.DetailEnvBinding(ctx, app, envBinding) -} - -func (e *envBindingUsecaseImpl) DeleteEnvBinding(ctx context.Context, appModel *model.Application, envName string) error { - envBinding, err := e.getBindingByEnv(ctx, appModel, envName) - if err != nil { - if errors.Is(err, datastore.ErrRecordNotExist) { - return bcode.ErrEnvBindingNotExist - } - return err - } - env, err := getEnv(ctx, e.ds, envName) - if err != nil && errors.Is(err, datastore.ErrRecordNotExist) { - return err - } - if env != nil { - var app v1beta1.Application - err = e.kubeClient.Get(ctx, types.NamespacedName{Namespace: env.Namespace, Name: appModel.Name}, &app) - if err == nil || !apierrors.IsNotFound(err) { - return bcode.ErrApplicationEnvRefusedDelete - } - if err := e.ds.Delete(ctx, &model.EnvBinding{AppPrimaryKey: appModel.PrimaryKey(), Name: envBinding.Name}); err != nil { - return err - } - } - // delete env workflow - if err := e.deleteEnvWorkflow(ctx, appModel, convertWorkflowName(envBinding.Name)); err != nil { - return fmt.Errorf("fail to clear the workflow belong to the env %w", err) - } - - // delete the topology and env-bindings policies - if err := e.ds.Delete(ctx, &model.ApplicationPolicy{AppPrimaryKey: appModel.PrimaryKey(), EnvName: envName}); err != nil && !errors.Is(err, datastore.ErrRecordNotExist) { - return fmt.Errorf("fail to clear the policies belong to the env %w", err) - } - return nil -} - -func (e *envBindingUsecaseImpl) BatchDeleteEnvBinding(ctx context.Context, app *model.Application) error { - envBindings, err := e.GetEnvBindings(ctx, app) - if err != nil { - return err - } - for _, envBinding := range envBindings { - // delete env - if err := e.ds.Delete(ctx, &model.EnvBinding{AppPrimaryKey: app.PrimaryKey(), Name: envBinding.Name}); err != nil { - return err - } - // delete env workflow - err := e.deleteEnvWorkflow(ctx, app, convertWorkflowName(envBinding.Name)) - if err != nil { - return err - } - } - return nil -} - -func (e *envBindingUsecaseImpl) createEnvWorkflow(ctx context.Context, app *model.Application, env *model.Env, isDefault bool) error { - steps, policies := GenEnvWorkflowStepsAndPolicies(ctx, e.kubeClient, e.ds, env, app) - workflow := &model.Workflow{ - Steps: steps, - Name: convertWorkflowName(env.Name), - Alias: fmt.Sprintf("%s Workflow", env.Alias), - Description: "Created automatically by envbinding.", - Default: &isDefault, - EnvName: env.Name, - AppPrimaryKey: app.PrimaryKey(), - } - log.Logger.Infof("create workflow %s for app %s", utils2.Sanitize(workflow.Name), utils2.Sanitize(app.PrimaryKey())) - if err := e.ds.Add(ctx, workflow); err != nil { - return err - } - err := e.ds.BatchAdd(ctx, policies) - if err != nil { - if err := e.workflowUsecase.DeleteWorkflow(ctx, app, convertWorkflowName(env.Name)); err != nil { - log.Logger.Errorf("fail to rollback the workflow after fail to create policies, %s", err.Error()) - } - return fmt.Errorf("fail to create policies %w", err) - } - return nil -} - -func (e *envBindingUsecaseImpl) deleteEnvWorkflow(ctx context.Context, app *model.Application, workflowName string) error { - if err := e.workflowUsecase.DeleteWorkflow(ctx, app, workflowName); err != nil { - if !errors.Is(err, bcode.ErrWorkflowNotExist) { - return err - } - } - return nil -} - -func (e *envBindingUsecaseImpl) DetailEnvBinding(ctx context.Context, app *model.Application, envBinding *model.EnvBinding) (*apisv1.DetailEnvBindingResponse, error) { - targets, err := listTarget(ctx, e.ds, "", nil) - if err != nil { - return nil, err - } - env, err := getEnv(ctx, e.ds, envBinding.Name) - if err != nil { - return nil, err - } - return &apisv1.DetailEnvBindingResponse{ - EnvBindingBase: *convertEnvBindingModelToBase(envBinding, env, targets), - }, nil -} - -func (e *envBindingUsecaseImpl) ApplicationEnvRecycle(ctx context.Context, appModel *model.Application, envBinding *model.EnvBinding) error { - env, err := getEnv(ctx, e.ds, envBinding.Name) - if err != nil { - return err - } - var app v1beta1.Application - err = e.kubeClient.Get(ctx, types.NamespacedName{Namespace: env.Namespace, Name: appModel.Name}, &app) - if err != nil { - if apierrors.IsNotFound(err) { - return nil - } - return err - } - if err := e.kubeClient.Delete(ctx, &app); err != nil { - return err - } - - if err := resetRevisionsAndRecords(ctx, e.ds, appModel.Name, "", "", ""); err != nil { - return err - } - return nil -} - -func convertCreateReqToEnvBindingModel(app *model.Application, req apisv1.CreateApplicationEnvbindingRequest) model.EnvBinding { - envBinding := model.EnvBinding{ - AppPrimaryKey: app.Name, - Name: req.Name, - AppDeployName: app.GetAppNameForSynced(), - } - return envBinding -} - -func convertEnvBindingModelToBase(envBinding *model.EnvBinding, env *model.Env, targets []*model.Target) *apisv1.EnvBindingBase { - var dtMap = make(map[string]*model.Target, len(targets)) - for _, dte := range targets { - dtMap[dte.Name] = dte - } - var envBindingTargets []apisv1.EnvBindingTarget - for _, targetName := range env.Targets { - dt := dtMap[targetName] - if dt != nil { - ebt := apisv1.EnvBindingTarget{ - NameAlias: apisv1.NameAlias{Name: dt.Name, Alias: dt.Alias}, - } - if dt.Cluster != nil { - ebt.Cluster = &apisv1.ClusterTarget{ - ClusterName: dt.Cluster.ClusterName, - Namespace: dt.Cluster.Namespace, - } - } - envBindingTargets = append(envBindingTargets, ebt) - } - } - ebb := &apisv1.EnvBindingBase{ - Name: envBinding.Name, - Alias: env.Alias, - Description: env.Description, - TargetNames: env.Targets, - Targets: envBindingTargets, - CreateTime: envBinding.CreateTime, - UpdateTime: envBinding.UpdateTime, - AppDeployName: envBinding.AppDeployName, - AppDeployNamespace: env.Namespace, - } - return ebb -} - -func convertToEnvBindingModel(app *model.Application, envBind apisv1.EnvBinding) *model.EnvBinding { - re := model.EnvBinding{ - AppPrimaryKey: app.Name, - Name: envBind.Name, - AppDeployName: app.GetAppNameForSynced(), - } - return &re -} - -func convertWorkflowName(envName string) string { - return fmt.Sprintf("workflow-%s", envName) -} diff --git a/pkg/apiserver/rest/usecase/envbinding_model.go b/pkg/apiserver/rest/usecase/envbinding_model.go deleted file mode 100644 index 6d6008871..000000000 --- a/pkg/apiserver/rest/usecase/envbinding_model.go +++ /dev/null @@ -1,53 +0,0 @@ -/* - Copyright 2021 The KubeVela Authors. - - 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 usecase - -import ( - "context" - - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/model" -) - -type envListOption struct { - appPrimaryKey string - envName string - projectName string -} - -func listEnvBindings(ctx context.Context, ds datastore.DataStore, listOption envListOption) ([]*model.EnvBinding, error) { - var envBinding = model.EnvBinding{} - if listOption.appPrimaryKey != "" { - envBinding.AppPrimaryKey = listOption.appPrimaryKey - } - if listOption.envName != "" { - envBinding.Name = listOption.envName - } - envBindings, err := ds.List(ctx, &envBinding, &datastore.ListOptions{}) - if err != nil { - return nil, err - } - var ret []*model.EnvBinding - for _, et := range envBindings { - eb, ok := et.(*model.EnvBinding) - if !ok { - continue - } - ret = append(ret, eb) - } - return ret, nil -} diff --git a/pkg/apiserver/rest/webservice/webservice.go b/pkg/apiserver/rest/webservice/webservice.go deleted file mode 100644 index 66c3270cf..000000000 --- a/pkg/apiserver/rest/webservice/webservice.go +++ /dev/null @@ -1,132 +0,0 @@ -/* -Copyright 2021 The KubeVela Authors. - -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 webservice - -import ( - "context" - "log" - "net/http" - "time" - - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/usecase" - - "github.com/emicklei/go-restful/v3" -) - -// versionPrefix API version prefix. -var versionPrefix = "/api/v1" - -// WebService webservice interface -type WebService interface { - GetWebService() *restful.WebService -} - -var registeredWebService []WebService - -// RegisterWebService register webservice -func RegisterWebService(ws WebService) { - registeredWebService = append(registeredWebService, ws) -} - -// GetRegisteredWebService return registeredWebService -func GetRegisteredWebService() []WebService { - return registeredWebService -} - -func returns200(b *restful.RouteBuilder) { - b.Returns(http.StatusOK, "OK", apisv1.SimpleResponse{Status: "ok"}) -} - -func returns500(b *restful.RouteBuilder) { - b.Returns(http.StatusInternalServerError, "Bummer, something went wrong", nil) -} - -// Init inits all webservice, pass in the required parameter object. -// It can be implemented using the idea of dependency injection. -func Init(ctx context.Context, ds datastore.DataStore, addonCacheTime time.Duration, initDatabase bool) map[string]interface{} { - clusterUsecase := usecase.NewClusterUsecase(ds) - rbacUsecase := usecase.NewRBACUsecase(ds) - projectUsecase := usecase.NewProjectUsecase(ds, rbacUsecase) - envUsecase := usecase.NewEnvUsecase(ds, projectUsecase) - targetUsecase := usecase.NewTargetUsecase(ds) - workflowUsecase := usecase.NewWorkflowUsecase(ds, envUsecase) - oamApplicationUsecase := usecase.NewOAMApplicationUsecase() - velaQLUsecase := usecase.NewVelaQLUsecase() - definitionUsecase := usecase.NewDefinitionUsecase() - addonUsecase := usecase.NewAddonUsecase(addonCacheTime) - envBindingUsecase := usecase.NewEnvBindingUsecase(ds, workflowUsecase, definitionUsecase, envUsecase) - systemInfoUsecase := usecase.NewSystemInfoUsecase(ds) - helmUsecase := usecase.NewHelmUsecase() - userUsecase := usecase.NewUserUsecase(ds, projectUsecase, systemInfoUsecase, rbacUsecase) - authenticationUsecase := usecase.NewAuthenticationUsecase(ds, systemInfoUsecase, userUsecase) - configUseCase := usecase.NewConfigUseCase(authenticationUsecase) - applicationUsecase := usecase.NewApplicationUsecase(ds, workflowUsecase, envBindingUsecase, envUsecase, targetUsecase, definitionUsecase, projectUsecase, userUsecase) - webhookUsecase := usecase.NewWebhookUsecase(ds, applicationUsecase) - // Modules that require default data initialization, Call it here in order - if initDatabase { - initData(ctx, userUsecase, rbacUsecase, projectUsecase, targetUsecase, systemInfoUsecase) - } - - // Application - RegisterWebService(NewApplicationWebService(applicationUsecase, envBindingUsecase, workflowUsecase, rbacUsecase)) - RegisterWebService(NewProjectWebService(projectUsecase, rbacUsecase, targetUsecase)) - RegisterWebService(NewEnvWebService(envUsecase, applicationUsecase, rbacUsecase)) - - // Extension - RegisterWebService(NewDefinitionWebservice(definitionUsecase, rbacUsecase)) - RegisterWebService(NewAddonWebService(addonUsecase, rbacUsecase, clusterUsecase)) - RegisterWebService(NewEnabledAddonWebService(addonUsecase, rbacUsecase)) - RegisterWebService(NewAddonRegistryWebService(addonUsecase, rbacUsecase)) - - // Config management - RegisterWebService(ConfigWebService(configUseCase, rbacUsecase)) - - // Resources - RegisterWebService(NewClusterWebService(clusterUsecase, rbacUsecase)) - RegisterWebService(NewOAMApplication(oamApplicationUsecase, rbacUsecase)) - RegisterWebService(&payloadTypesWebservice{}) - RegisterWebService(NewTargetWebService(targetUsecase, applicationUsecase, rbacUsecase)) - RegisterWebService(NewVelaQLWebService(velaQLUsecase, rbacUsecase)) - RegisterWebService(NewWebhookWebService(webhookUsecase, applicationUsecase)) - RegisterWebService(NewHelmWebService(helmUsecase)) - - // Authentication - RegisterWebService(NewAuthenticationWebService(authenticationUsecase, userUsecase)) - RegisterWebService(NewUserWebService(userUsecase, rbacUsecase)) - RegisterWebService(NewSystemInfoWebService(systemInfoUsecase, rbacUsecase)) - - // RBAC - RegisterWebService(NewRBACWebService(rbacUsecase)) - - // return some usecase instance - return map[string]interface{}{"workflow": workflowUsecase, "project": projectUsecase} -} - -// InitUsecase the usecase set that needs init data -type InitUsecase interface { - Init(ctx context.Context) error -} - -func initData(ctx context.Context, inits ...InitUsecase) { - for _, init := range inits { - if err := init.Init(ctx); err != nil { - log.Fatalf("database init failure %s", err.Error()) - } - } -} diff --git a/pkg/apiserver/rest/rest_server.go b/pkg/apiserver/server.go similarity index 53% rename from pkg/apiserver/rest/rest_server.go rename to pkg/apiserver/server.go index a5c4f36ad..8b363770e 100644 --- a/pkg/apiserver/rest/rest_server.go +++ b/pkg/apiserver/server.go @@ -14,18 +14,15 @@ See the License for the specific language governing permissions and limitations under the License. */ -package rest +package apiserver import ( "context" "fmt" "net/http" - "os" "time" - "github.com/oam-dev/kubevela/pkg/apiserver/collect" - - restfulspec "github.com/emicklei/go-restful-openapi/v2" + restfulSpec "github.com/emicklei/go-restful-openapi/v2" "github.com/emicklei/go-restful/v3" "github.com/go-openapi/spec" "k8s.io/client-go/tools/leaderelection" @@ -34,61 +31,37 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "github.com/oam-dev/kubevela/apis/types" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore/kubeapi" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore/mongodb" - "github.com/oam-dev/kubevela/pkg/apiserver/log" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/usecase" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/webservice" - velasync "github.com/oam-dev/kubevela/pkg/apiserver/sync" - utils2 "github.com/oam-dev/kubevela/pkg/utils" + "github.com/oam-dev/kubevela/pkg/apiserver/config" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/service" + "github.com/oam-dev/kubevela/pkg/apiserver/event" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/clients" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore/kubeapi" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore/mongodb" + "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api" + "github.com/oam-dev/kubevela/pkg/apiserver/utils" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/container" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/log" + pkgUtils "github.com/oam-dev/kubevela/pkg/utils" + "github.com/oam-dev/kubevela/pkg/utils/apply" ) -var _ APIServer = &restServer{} - -// Config config for server -type Config struct { - // api server bind address - BindAddr string - // monitor metric path - MetricPath string - - // Datastore config - Datastore datastore.Config - - // LeaderConfig for leader election - LeaderConfig leaderConfig - - // AddonCacheTime is how long between two cache operations - AddonCacheTime time.Duration - - // DisableStatisticCronJob close the calculate system info cronJob - DisableStatisticCronJob bool -} - -type leaderConfig struct { - ID string - LockName string - Duration time.Duration -} - // APIServer interface for call api server type APIServer interface { - Run(context.Context) error - RegisterServices(ctx context.Context, initDatabase bool) restfulspec.Config + Run(context.Context, chan error) error + BuildRestfulConfig() (*restfulSpec.Config, error) } +// restServer rest server type restServer struct { - webContainer *restful.Container - cfg Config - dataStore datastore.DataStore - // usecases, we register part of the usecase instances - usecases map[string]interface{} + webContainer *restful.Container + beanContainer *container.Container + cfg config.Config + dataStore datastore.DataStore } -// New create restserver with config data -func New(cfg Config) (a APIServer, err error) { +// New create api server with config data +func New(cfg config.Config) (a APIServer, err error) { var ds datastore.DataStore switch cfg.Datastore.Type { case "mongodb": @@ -106,17 +79,73 @@ func New(cfg Config) (a APIServer, err error) { } s := &restServer{ - webContainer: restful.NewContainer(), - cfg: cfg, - dataStore: ds, + webContainer: restful.NewContainer(), + beanContainer: container.NewContainer(), + cfg: cfg, + dataStore: ds, } return s, nil } -func (s *restServer) Run(ctx context.Context) error { - s.RegisterServices(ctx, true) +func (s *restServer) buildIoCContainer() error { + // infrastructure + if err := s.beanContainer.ProvideWithName("datastore", s.dataStore); err != nil { + return fmt.Errorf("fail to provides the datastore bean to the container: %w", err) + } + kubeConfig, err := clients.GetKubeConfig() + if err != nil { + return err + } + kubeClient, err := clients.GetKubeClient() + if err != nil { + return err + } + if err := s.beanContainer.ProvideWithName("kubeClient", kubeClient); err != nil { + return fmt.Errorf("fail to provides the kubeClient bean to the container: %w", err) + } + if err := s.beanContainer.ProvideWithName("kubeConfig", kubeConfig); err != nil { + return fmt.Errorf("fail to provides the kubeConfig bean to the container: %w", err) + } + if err := s.beanContainer.ProvideWithName("apply", apply.NewAPIApplicator(kubeClient)); err != nil { + return fmt.Errorf("fail to provides the apply bean to the container: %w", err) + } - l, err := s.setupLeaderElection() + // domain + if err := s.beanContainer.Provides(service.InitServiceBean(s.cfg)...); err != nil { + return fmt.Errorf("fail to provides the service bean to the container: %w", err) + } + + // interfaces + if err := s.beanContainer.Provides(api.InitAPIBean()...); err != nil { + return fmt.Errorf("fail to provides the api bean to the container: %w", err) + } + + // event + if err := s.beanContainer.Provides(event.InitEvent(s.cfg)...); err != nil { + return fmt.Errorf("fail to provides the event bean to the container: %w", err) + } + + if err := s.beanContainer.Populate(); err != nil { + return fmt.Errorf("fail to populate the bean container: %w", err) + } + return nil +} + +func (s *restServer) Run(ctx context.Context, errChan chan error) error { + + // build the Ioc Container + if err := s.buildIoCContainer(); err != nil { + return err + } + + // init database + if err := service.InitData(ctx); err != nil { + return fmt.Errorf("fail to init database %w", err) + } + + s.RegisterAPIRoute() + + l, err := s.setupLeaderElection(errChan) if err != nil { return err } @@ -127,7 +156,7 @@ func (s *restServer) Run(ctx context.Context) error { return s.startHTTP(ctx) } -func (s *restServer) setupLeaderElection() (*leaderelection.LeaderElectionConfig, error) { +func (s *restServer) setupLeaderElection(errChan chan error) (*leaderelection.LeaderElectionConfig, error) { restCfg := ctrl.GetConfigOrDie() rl, err := resourcelock.NewFromKubeconfig(resourcelock.LeasesResourceLock, types.DefaultKubeVelaNS, s.cfg.LeaderConfig.LockName, resourcelock.ResourceLockConfig{ @@ -145,18 +174,10 @@ func (s *restServer) setupLeaderElection() (*leaderelection.LeaderElectionConfig RetryPeriod: time.Second * 2, Callbacks: leaderelection.LeaderCallbacks{ OnStartedLeading: func(ctx context.Context) { - go velasync.Start(ctx, s.dataStore, restCfg, s.usecases) - if !s.cfg.DisableStatisticCronJob { - collect.StartCalculatingInfoCronJob(s.dataStore) - } - // this process would block the whole process, any other handler should start before this func - s.runWorkflowRecordSync(ctx, s.cfg.LeaderConfig.Duration) + go event.StartEventWorker(ctx, errChan) }, OnStoppedLeading: func() { - klog.Infof("leader lost: %s", s.cfg.LeaderConfig.ID) - // Currently, the started goroutine will all closed by the context, so there seems no need to call os.Exit here. - // But it can be safe to stop the process as leader lost. - os.Exit(0) + errChan <- fmt.Errorf("leader lost %s", s.cfg.LeaderConfig.ID) }, OnNewLeader: func(identity string) { if identity == s.cfg.LeaderConfig.ID { @@ -169,32 +190,21 @@ func (s *restServer) setupLeaderElection() (*leaderelection.LeaderElectionConfig }, nil } -func (s *restServer) runWorkflowRecordSync(ctx context.Context, duration time.Duration) { - klog.Infof("start to syncing workflow record") - w := s.usecases["workflow"].(usecase.WorkflowUsecase) - t := time.NewTicker(duration) - defer t.Stop() - - for { - select { - case <-t.C: - if err := w.SyncWorkflowRecord(ctx); err != nil { - klog.ErrorS(err, "syncWorkflowRecordError") - } - case <-ctx.Done(): - return - } +// BuildRestfulConfig build the restful config +// This function will build the smallest set of beans +func (s *restServer) BuildRestfulConfig() (*restfulSpec.Config, error) { + if err := s.buildIoCContainer(); err != nil { + return nil, err } + config := s.RegisterAPIRoute() + return &config, nil } -// RegisterServices register web service -func (s *restServer) RegisterServices(ctx context.Context, initDatabase bool) restfulspec.Config { - s.usecases = webservice.Init(ctx, s.dataStore, s.cfg.AddonCacheTime, initDatabase) - +// RegisterAPIRoute register the API route +func (s *restServer) RegisterAPIRoute() restfulSpec.Config { /* ************************************************************** */ /* ************* Open API Route Group ***************** */ /* ************************************************************** */ - // Add container filter to enable CORS cors := restful.CrossOriginResourceSharing{ ExposeHeaders: []string{}, @@ -210,16 +220,16 @@ func (s *restServer) RegisterServices(ctx context.Context, initDatabase bool) re // Add request log s.webContainer.Filter(s.requestLog) - // Register all custom webservice - for _, handler := range webservice.GetRegisteredWebService() { - s.webContainer.Add(handler.GetWebService()) + // Register all custom api + for _, handler := range api.GetRegisteredAPIInterface() { + s.webContainer.Add(handler.GetWebServiceRoute()) } - config := restfulspec.Config{ + config := restfulSpec.Config{ WebServices: s.webContainer.RegisteredWebServices(), // you control what services are visible APIPath: "/apidocs.json", PostBuildSwaggerObjectHandler: enrichSwaggerObject} - s.webContainer.Add(restfulspec.NewOpenAPIService(config)) + s.webContainer.Add(restfulSpec.NewOpenAPIService(config)) return config } @@ -230,8 +240,8 @@ func (s *restServer) requestLog(req *restful.Request, resp *restful.Response, ch chain.ProcessFilter(req, resp) takeTime := time.Since(start) log.Logger.With( - "clientIP", utils2.Sanitize(utils.ClientIP(req.Request)), - "path", utils2.Sanitize(req.Request.URL.Path), + "clientIP", pkgUtils.Sanitize(utils.ClientIP(req.Request)), + "path", pkgUtils.Sanitize(req.Request.URL.Path), "method", req.Request.Method, "status", c.StatusCode(), "time", takeTime.String(), diff --git a/pkg/apiserver/server_test.go b/pkg/apiserver/server_test.go new file mode 100644 index 000000000..ac9aa43dd --- /dev/null +++ b/pkg/apiserver/server_test.go @@ -0,0 +1,17 @@ +/* +Copyright 2022 The KubeVela Authors. + +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 apiserver diff --git a/pkg/apiserver/rest/utils/bcode/001_application.go b/pkg/apiserver/utils/bcode/001_application.go similarity index 100% rename from pkg/apiserver/rest/utils/bcode/001_application.go rename to pkg/apiserver/utils/bcode/001_application.go diff --git a/pkg/apiserver/rest/utils/bcode/002_workflow.go b/pkg/apiserver/utils/bcode/002_workflow.go similarity index 100% rename from pkg/apiserver/rest/utils/bcode/002_workflow.go rename to pkg/apiserver/utils/bcode/002_workflow.go diff --git a/pkg/apiserver/rest/utils/bcode/003_project.go b/pkg/apiserver/utils/bcode/003_project.go similarity index 100% rename from pkg/apiserver/rest/utils/bcode/003_project.go rename to pkg/apiserver/utils/bcode/003_project.go diff --git a/pkg/apiserver/rest/utils/bcode/004_cluster.go b/pkg/apiserver/utils/bcode/004_cluster.go similarity index 100% rename from pkg/apiserver/rest/utils/bcode/004_cluster.go rename to pkg/apiserver/utils/bcode/004_cluster.go diff --git a/pkg/apiserver/rest/utils/bcode/005_addon.go b/pkg/apiserver/utils/bcode/005_addon.go similarity index 100% rename from pkg/apiserver/rest/utils/bcode/005_addon.go rename to pkg/apiserver/utils/bcode/005_addon.go diff --git a/pkg/apiserver/rest/utils/bcode/006_velaql.go b/pkg/apiserver/utils/bcode/006_velaql.go similarity index 100% rename from pkg/apiserver/rest/utils/bcode/006_velaql.go rename to pkg/apiserver/utils/bcode/006_velaql.go diff --git a/pkg/apiserver/rest/utils/bcode/007_definition.go b/pkg/apiserver/utils/bcode/007_definition.go similarity index 100% rename from pkg/apiserver/rest/utils/bcode/007_definition.go rename to pkg/apiserver/utils/bcode/007_definition.go diff --git a/pkg/apiserver/rest/utils/bcode/008_target.go b/pkg/apiserver/utils/bcode/008_target.go similarity index 100% rename from pkg/apiserver/rest/utils/bcode/008_target.go rename to pkg/apiserver/utils/bcode/008_target.go diff --git a/pkg/apiserver/rest/utils/bcode/009_envbinding.go b/pkg/apiserver/utils/bcode/009_envbinding.go similarity index 100% rename from pkg/apiserver/rest/utils/bcode/009_envbinding.go rename to pkg/apiserver/utils/bcode/009_envbinding.go diff --git a/pkg/apiserver/rest/utils/bcode/011_env.go b/pkg/apiserver/utils/bcode/011_env.go similarity index 100% rename from pkg/apiserver/rest/utils/bcode/011_env.go rename to pkg/apiserver/utils/bcode/011_env.go diff --git a/pkg/apiserver/rest/utils/bcode/012_authentication.go b/pkg/apiserver/utils/bcode/012_authentication.go similarity index 100% rename from pkg/apiserver/rest/utils/bcode/012_authentication.go rename to pkg/apiserver/utils/bcode/012_authentication.go diff --git a/pkg/apiserver/rest/utils/bcode/013_repository.go b/pkg/apiserver/utils/bcode/013_repository.go similarity index 100% rename from pkg/apiserver/rest/utils/bcode/013_repository.go rename to pkg/apiserver/utils/bcode/013_repository.go diff --git a/pkg/apiserver/rest/utils/bcode/014_user.go b/pkg/apiserver/utils/bcode/014_user.go similarity index 100% rename from pkg/apiserver/rest/utils/bcode/014_user.go rename to pkg/apiserver/utils/bcode/014_user.go diff --git a/pkg/apiserver/rest/utils/bcode/015_rbac.go b/pkg/apiserver/utils/bcode/015_rbac.go similarity index 100% rename from pkg/apiserver/rest/utils/bcode/015_rbac.go rename to pkg/apiserver/utils/bcode/015_rbac.go diff --git a/pkg/apiserver/rest/utils/bcode/bcode.go b/pkg/apiserver/utils/bcode/bcode.go similarity index 96% rename from pkg/apiserver/rest/utils/bcode/bcode.go rename to pkg/apiserver/utils/bcode/bcode.go index 0b652c635..6f53f3e31 100644 --- a/pkg/apiserver/rest/utils/bcode/bcode.go +++ b/pkg/apiserver/utils/bcode/bcode.go @@ -23,8 +23,8 @@ import ( "github.com/emicklei/go-restful/v3" "github.com/go-playground/validator/v10" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - "github.com/oam-dev/kubevela/pkg/apiserver/log" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/log" "github.com/oam-dev/kubevela/pkg/utils" ) diff --git a/pkg/apiserver/rest/utils/bcode/bcode_suite_test.go b/pkg/apiserver/utils/bcode/bcode_suite_test.go similarity index 100% rename from pkg/apiserver/rest/utils/bcode/bcode_suite_test.go rename to pkg/apiserver/utils/bcode/bcode_suite_test.go diff --git a/pkg/apiserver/rest/utils/bcode/bcode_test.go b/pkg/apiserver/utils/bcode/bcode_test.go similarity index 100% rename from pkg/apiserver/rest/utils/bcode/bcode_test.go rename to pkg/apiserver/utils/bcode/bcode_test.go diff --git a/pkg/apiserver/rest/utils/cache.go b/pkg/apiserver/utils/cache.go similarity index 100% rename from pkg/apiserver/rest/utils/cache.go rename to pkg/apiserver/utils/cache.go diff --git a/pkg/apiserver/rest/utils/cache_test.go b/pkg/apiserver/utils/cache_test.go similarity index 100% rename from pkg/apiserver/rest/utils/cache_test.go rename to pkg/apiserver/utils/cache_test.go diff --git a/pkg/apiserver/utils/container/container.go b/pkg/apiserver/utils/container/container.go new file mode 100644 index 000000000..aff45f330 --- /dev/null +++ b/pkg/apiserver/utils/container/container.go @@ -0,0 +1,61 @@ +/* +Copyright 2022 The KubeVela Authors. + +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 container + +import ( + "github.com/barnettZQG/inject" + "helm.sh/helm/v3/pkg/time" + + "github.com/oam-dev/kubevela/pkg/apiserver/utils/log" +) + +// NewContainer new a IoC container +func NewContainer() *Container { + return &Container{ + graph: inject.Graph{}, + } +} + +// Container the IoC container +type Container struct { + graph inject.Graph +} + +// Provides provide some beans with default name +func (c *Container) Provides(beans ...interface{}) error { + for _, bean := range beans { + if err := c.graph.Provide(&inject.Object{Value: bean}); err != nil { + return err + } + } + return nil +} + +// ProvideWithName provide the bean with name +func (c *Container) ProvideWithName(name string, bean interface{}) error { + return c.graph.Provide(&inject.Object{Name: name, Value: bean}) +} + +// Populate populate dependency fields for all beans. +// this function must be called after providing all beans +func (c *Container) Populate() error { + start := time.Now() + defer func() { + log.Logger.Infof("populate the bean container take time %s", time.Now().Sub(start)) + }() + return c.graph.Populate() +} diff --git a/pkg/apiserver/rest/utils/dex.go b/pkg/apiserver/utils/dex.go similarity index 100% rename from pkg/apiserver/rest/utils/dex.go rename to pkg/apiserver/utils/dex.go diff --git a/pkg/apiserver/rest/utils/dex_test.go b/pkg/apiserver/utils/dex_test.go similarity index 100% rename from pkg/apiserver/rest/utils/dex_test.go rename to pkg/apiserver/utils/dex_test.go diff --git a/pkg/apiserver/rest/utils/http.go b/pkg/apiserver/utils/http.go similarity index 100% rename from pkg/apiserver/rest/utils/http.go rename to pkg/apiserver/utils/http.go diff --git a/pkg/apiserver/rest/utils/http_test.go b/pkg/apiserver/utils/http_test.go similarity index 100% rename from pkg/apiserver/rest/utils/http_test.go rename to pkg/apiserver/utils/http_test.go diff --git a/pkg/apiserver/log/logger.go b/pkg/apiserver/utils/log/logger.go similarity index 100% rename from pkg/apiserver/log/logger.go rename to pkg/apiserver/utils/log/logger.go diff --git a/pkg/apiserver/rest/utils/params.go b/pkg/apiserver/utils/params.go similarity index 100% rename from pkg/apiserver/rest/utils/params.go rename to pkg/apiserver/utils/params.go diff --git a/pkg/apiserver/rest/utils/params_test.go b/pkg/apiserver/utils/params_test.go similarity index 100% rename from pkg/apiserver/rest/utils/params_test.go rename to pkg/apiserver/utils/params_test.go diff --git a/pkg/apiserver/rest/utils/ui_schema.go b/pkg/apiserver/utils/ui_schema.go similarity index 100% rename from pkg/apiserver/rest/utils/ui_schema.go rename to pkg/apiserver/utils/ui_schema.go diff --git a/pkg/apiserver/rest/utils/ui_schema_test.go b/pkg/apiserver/utils/ui_schema_test.go similarity index 100% rename from pkg/apiserver/rest/utils/ui_schema_test.go rename to pkg/apiserver/utils/ui_schema_test.go diff --git a/pkg/apiserver/rest/utils/utils_suite_test.go b/pkg/apiserver/utils/utils_suite_test.go similarity index 100% rename from pkg/apiserver/rest/utils/utils_suite_test.go rename to pkg/apiserver/utils/utils_suite_test.go diff --git a/pkg/apiserver/rest/utils/version.go b/pkg/apiserver/utils/version.go similarity index 100% rename from pkg/apiserver/rest/utils/version.go rename to pkg/apiserver/utils/version.go diff --git a/pkg/apiserver/rest/utils/version_test.go b/pkg/apiserver/utils/version_test.go similarity index 100% rename from pkg/apiserver/rest/utils/version_test.go rename to pkg/apiserver/utils/version_test.go diff --git a/pkg/utils/config/application.go b/pkg/utils/config/application.go index 76df9ca81..535e873e6 100644 --- a/pkg/utils/config/application.go +++ b/pkg/utils/config/application.go @@ -31,7 +31,7 @@ import ( "github.com/oam-dev/kubevela/apis/core.oam.dev/common" "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" "github.com/oam-dev/kubevela/apis/types" - "github.com/oam-dev/kubevela/pkg/apiserver/model" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" ) const ( diff --git a/pkg/utils/helm/helm_helper.go b/pkg/utils/helm/helm_helper.go index a6f1f9bc4..d47899c09 100644 --- a/pkg/utils/helm/helm_helper.go +++ b/pkg/utils/helm/helm_helper.go @@ -45,7 +45,7 @@ import ( k8scmdutil "k8s.io/kubectl/pkg/cmd/util" "sigs.k8s.io/yaml" - utils2 "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils" + utils2 "github.com/oam-dev/kubevela/pkg/apiserver/utils" "github.com/oam-dev/kubevela/pkg/utils" "github.com/oam-dev/kubevela/pkg/utils/common" cmdutil "github.com/oam-dev/kubevela/pkg/utils/util" @@ -203,6 +203,7 @@ func (h *Helper) ListVersions(repoURL string, chartName string, skipCache bool, // GetIndexInfo get index.yaml form given repo url func (h *Helper) GetIndexInfo(repoURL string, skipCache bool, opts *common.HTTPOption) (*repo.IndexFile, error) { + repoURL = utils.Sanitize(repoURL) if h.cache != nil && !skipCache { if i := h.cache.Get(fmt.Sprintf(repoPatten, repoURL)); i != nil { return i.(*repo.IndexFile), nil diff --git a/pkg/velaql/suite_test.go b/pkg/velaql/suite_test.go index ac92454b4..3d7eae575 100644 --- a/pkg/velaql/suite_test.go +++ b/pkg/velaql/suite_test.go @@ -31,7 +31,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/envtest" - "github.com/oam-dev/kubevela/pkg/apiserver/clients" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/clients" "github.com/oam-dev/kubevela/pkg/cue/packages" "github.com/oam-dev/kubevela/pkg/oam/discoverymapper" "github.com/oam-dev/kubevela/pkg/utils/common" diff --git a/references/cli/uischema.go b/references/cli/uischema.go index 09f3dda43..35373fc06 100644 --- a/references/cli/uischema.go +++ b/references/cli/uischema.go @@ -36,7 +36,7 @@ import ( "sigs.k8s.io/yaml" "github.com/oam-dev/kubevela/apis/types" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils" + "github.com/oam-dev/kubevela/pkg/apiserver/utils" "github.com/oam-dev/kubevela/pkg/utils/common" "github.com/oam-dev/kubevela/pkg/utils/util" ) diff --git a/test/e2e-apiserver-test/addon_test.go b/test/e2e-apiserver-test/addon_test.go index 2146e7dd6..2ef04beff 100644 --- a/test/e2e-apiserver-test/addon_test.go +++ b/test/e2e-apiserver-test/addon_test.go @@ -24,7 +24,7 @@ import ( . "github.com/onsi/gomega" "github.com/oam-dev/kubevela/pkg/addon" - apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" + apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" ) var _ = Describe("Test addon rest api", func() { diff --git a/test/e2e-apiserver-test/application_test.go b/test/e2e-apiserver-test/application_test.go index 4fcd736df..7ab734535 100644 --- a/test/e2e-apiserver-test/application_test.go +++ b/test/e2e-apiserver-test/application_test.go @@ -27,8 +27,8 @@ import ( "k8s.io/apimachinery/pkg/types" "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" - "github.com/oam-dev/kubevela/pkg/apiserver/model" - apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/domain/model" + apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" ) var appName = "app-e2e" diff --git a/test/e2e-apiserver-test/cluster_test.go b/test/e2e-apiserver-test/cluster_test.go index 91fab73a3..63c7e1f80 100644 --- a/test/e2e-apiserver-test/cluster_test.go +++ b/test/e2e-apiserver-test/cluster_test.go @@ -25,7 +25,7 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - v1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" + v1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" "github.com/oam-dev/kubevela/pkg/multicluster" util "github.com/oam-dev/kubevela/pkg/utils" ) diff --git a/test/e2e-apiserver-test/definition_test.go b/test/e2e-apiserver-test/definition_test.go index 890ec3b3f..3077eba11 100644 --- a/test/e2e-apiserver-test/definition_test.go +++ b/test/e2e-apiserver-test/definition_test.go @@ -24,8 +24,8 @@ import ( . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" - apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils" + apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils" "github.com/oam-dev/kubevela/pkg/oam/util" "github.com/oam-dev/kubevela/pkg/utils/common" ) diff --git a/test/e2e-apiserver-test/env_test.go b/test/e2e-apiserver-test/env_test.go index 6e75242c4..6d97370c7 100644 --- a/test/e2e-apiserver-test/env_test.go +++ b/test/e2e-apiserver-test/env_test.go @@ -24,7 +24,7 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" + apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" "github.com/oam-dev/kubevela/pkg/multicluster" ) diff --git a/test/e2e-apiserver-test/oam_application_test.go b/test/e2e-apiserver-test/oam_application_test.go index 5420946a0..e08b2a002 100644 --- a/test/e2e-apiserver-test/oam_application_test.go +++ b/test/e2e-apiserver-test/oam_application_test.go @@ -28,7 +28,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" - apiv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" + apiv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" "github.com/oam-dev/kubevela/pkg/utils/common" ) diff --git a/test/e2e-apiserver-test/project_test.go b/test/e2e-apiserver-test/project_test.go index 2a2ce7da6..a8d32e6f2 100644 --- a/test/e2e-apiserver-test/project_test.go +++ b/test/e2e-apiserver-test/project_test.go @@ -24,7 +24,7 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" + apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" ) var _ = Describe("Test project rest api", func() { diff --git a/test/e2e-apiserver-test/suite_test.go b/test/e2e-apiserver-test/suite_test.go index cde4ea3a2..242719d01 100644 --- a/test/e2e-apiserver-test/suite_test.go +++ b/test/e2e-apiserver-test/suite_test.go @@ -32,11 +32,12 @@ import ( v1 "k8s.io/api/core/v1" "sigs.k8s.io/controller-runtime/pkg/client" - "github.com/oam-dev/kubevela/pkg/apiserver/clients" - "github.com/oam-dev/kubevela/pkg/apiserver/datastore" - arest "github.com/oam-dev/kubevela/pkg/apiserver/rest" - apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + "github.com/oam-dev/kubevela/pkg/apiserver" + "github.com/oam-dev/kubevela/pkg/apiserver/config" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/clients" + "github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore" + apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode" ) var k8sClient client.Client @@ -64,7 +65,7 @@ var _ = BeforeSuite(func() { ctx := context.Background() - cfg := arest.Config{ + cfg := config.Config{ BindAddr: "127.0.0.1:8000", Datastore: datastore.Config{ Type: "kubeapi", @@ -76,11 +77,11 @@ var _ = BeforeSuite(func() { cfg.LeaderConfig.LockName = "apiserver-lock" cfg.LeaderConfig.Duration = time.Second * 10 - server, err := arest.New(cfg) + server, err := apiserver.New(cfg) Expect(err).ShouldNot(HaveOccurred()) Expect(server).ShouldNot(BeNil()) go func() { - err = server.Run(ctx) + err = server.Run(ctx, make(chan error)) Expect(err).ShouldNot(HaveOccurred()) }() By("wait for api server to start") diff --git a/test/e2e-apiserver-test/system_info_test.go b/test/e2e-apiserver-test/system_info_test.go index defd69cb6..13d6773de 100644 --- a/test/e2e-apiserver-test/system_info_test.go +++ b/test/e2e-apiserver-test/system_info_test.go @@ -20,7 +20,7 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" + apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" ) var _ = Describe("Test system info rest api", func() { diff --git a/test/e2e-apiserver-test/user_test.go b/test/e2e-apiserver-test/user_test.go index cd8ef95fb..8d92340c3 100644 --- a/test/e2e-apiserver-test/user_test.go +++ b/test/e2e-apiserver-test/user_test.go @@ -22,7 +22,7 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" + apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" ) const ( diff --git a/test/e2e-apiserver-test/velaql_test.go b/test/e2e-apiserver-test/velaql_test.go index fd191afeb..d6c5ecdef 100644 --- a/test/e2e-apiserver-test/velaql_test.go +++ b/test/e2e-apiserver-test/velaql_test.go @@ -34,7 +34,7 @@ import ( common2 "github.com/oam-dev/kubevela/apis/core.oam.dev/common" "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" - apiv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" + apiv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1" "github.com/oam-dev/kubevela/pkg/oam/util" "github.com/oam-dev/kubevela/pkg/utils/common" )