From d44065d4b3772aa408ee18a1d2e0f0a8e2be3ad7 Mon Sep 17 00:00:00 2001 From: Allan Guwatudde Date: Tue, 21 Feb 2023 17:36:44 +0300 Subject: [PATCH] [MM-49455] - Self-serve workspace deletion (#22200) * [MM-49455] - Self-serve workspace deletion * add subscription id to deletion request * add server telemetry * fix mocks * feedback impl --- api4/cloud.go | 31 +++++++++++++++++++++++++++-- einterfaces/cloud.go | 2 ++ einterfaces/mocks/CloudInterface.go | 14 +++++++++++++ i18n/en.json | 4 ++++ model/cloud.go | 17 ++++++++++------ 5 files changed, 60 insertions(+), 8 deletions(-) diff --git a/api4/cloud.go b/api4/cloud.go index b55e5d36d3..ccf6d0aabb 100644 --- a/api4/cloud.go +++ b/api4/cloud.go @@ -56,6 +56,8 @@ func (api *API) InitCloud() { // GET /api/v4/cloud/cws-health-check api.BaseRoutes.Cloud.Handle("/check-cws-connection", api.APIHandler(handleCheckCWSConnection)).Methods("GET") + + api.BaseRoutes.Cloud.Handle("/delete-workspace", api.APISessionRequired(selfServeDeleteWorkspace)).Methods(http.MethodDelete) } func getSubscription(c *Context, w http.ResponseWriter, r *http.Request) { @@ -142,8 +144,8 @@ func changeSubscription(c *Context, w http.ResponseWriter, r *http.Request) { return } - if subscriptionChange.DowngradeFeedback != nil { - c.App.Srv().GetTelemetryService().SendTelemetry("downgrade_feedback", subscriptionChange.DowngradeFeedback.ToMap()) + if subscriptionChange.Feedback != nil { + c.App.Srv().GetTelemetryService().SendTelemetry("downgrade_feedback", subscriptionChange.Feedback.ToMap()) } json, err := json.Marshal(changedSub) @@ -774,3 +776,28 @@ func handleCheckCWSConnection(c *Context, w http.ResponseWriter, r *http.Request ReturnStatusOK(w) } + +func selfServeDeleteWorkspace(c *Context, w http.ResponseWriter, r *http.Request) { + bodyBytes, err := io.ReadAll(r.Body) + if err != nil { + c.Err = model.NewAppError("Api4.selfServeDeleteWorkspace", "api.cloud.app_error", nil, err.Error(), http.StatusBadRequest) + return + } + defer r.Body.Close() + + var deleteRequest *model.WorkspaceDeletionRequest + if err = json.Unmarshal(bodyBytes, &deleteRequest); err != nil { + c.Err = model.NewAppError("Api4.selfServeDeleteWorkspace", "api.cloud.app_error", nil, err.Error(), http.StatusInternalServerError) + return + } + + if err := c.App.Cloud().SelfServeDeleteWorkspace(c.AppContext.Session().UserId, deleteRequest); err != nil { + c.Err = model.NewAppError("Api4.selfServeDeleteWorkspace", "api.server.cws.delete_workspace.app_error", nil, "CWS Server failed to delete workspace.", http.StatusInternalServerError) + return + } + + c.App.Srv().GetTelemetryService().SendTelemetry("delete_workspace_feedback", deleteRequest.Feedback.ToMap()) + + ReturnStatusOK(w) + +} diff --git a/einterfaces/cloud.go b/einterfaces/cloud.go index bfaabae67e..c17bc424d2 100644 --- a/einterfaces/cloud.go +++ b/einterfaces/cloud.go @@ -47,4 +47,6 @@ type CloudInterface interface { HandleLicenseChange() error CheckCWSConnection(userId string) error + + SelfServeDeleteWorkspace(userID string, deletionRequest *model.WorkspaceDeletionRequest) error } diff --git a/einterfaces/mocks/CloudInterface.go b/einterfaces/mocks/CloudInterface.go index 4b19465161..3f5d64aa0d 100644 --- a/einterfaces/mocks/CloudInterface.go +++ b/einterfaces/mocks/CloudInterface.go @@ -540,6 +540,20 @@ func (_m *CloudInterface) SelfHostedSignupAvailable() error { return r0 } +// SelfServeDeleteWorkspace provides a mock function with given fields: userID, deletionRequest +func (_m *CloudInterface) SelfServeDeleteWorkspace(userID string, deletionRequest *model.WorkspaceDeletionRequest) error { + ret := _m.Called(userID, deletionRequest) + + var r0 error + if rf, ok := ret.Get(0).(func(string, *model.WorkspaceDeletionRequest) error); ok { + r0 = rf(userID, deletionRequest) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // UpdateCloudCustomer provides a mock function with given fields: userID, customerInfo func (_m *CloudInterface) UpdateCloudCustomer(userID string, customerInfo *model.CloudCustomerInfo) (*model.CloudCustomer, error) { ret := _m.Called(userID, customerInfo) diff --git a/i18n/en.json b/i18n/en.json index ee31587cf4..e6e013be2c 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -2587,6 +2587,10 @@ "id": "api.scheme.patch_scheme.license.error", "translation": "Your license does not support update permissions schemes" }, + { + "id": "api.server.cws.delete_workspace.app_error", + "translation": "CWS Server failed to delete workspace." + }, { "id": "api.server.cws.health_check.app_error", "translation": "CWS Server is not available." diff --git a/model/cloud.go b/model/cloud.go index 1f4a53c5c4..76ce685a0a 100644 --- a/model/cloud.go +++ b/model/cloud.go @@ -264,10 +264,10 @@ type CloudWorkspaceOwner struct { } type SubscriptionChange struct { - ProductID string `json:"product_id"` - Seats int `json:"seats"` - DowngradeFeedback *DowngradeFeedback `json:"downgrade_feedback"` - ShippingAddress *Address `json:"shipping_address"` + ProductID string `json:"product_id"` + Seats int `json:"seats"` + Feedback *Feedback `json:"downgrade_feedback"` + ShippingAddress *Address `json:"shipping_address"` } // TODO remove BoardsLimits. @@ -314,11 +314,16 @@ type CreateSubscriptionRequest struct { DiscountID string `json:"discount_id"` } -type DowngradeFeedback struct { +type Feedback struct { Reason string `json:"reason"` Comments string `json:"comments"` } +type WorkspaceDeletionRequest struct { + SubscriptionID string `json:"subscription_id"` + Feedback *Feedback `json:"delete_feedback"` +} + func (p *Product) IsYearly() bool { return p.RecurringInterval == RecurringIntervalYearly } @@ -327,7 +332,7 @@ func (p *Product) IsMonthly() bool { return p.RecurringInterval == RecurringIntervalMonthly } -func (df *DowngradeFeedback) ToMap() map[string]any { +func (df *Feedback) ToMap() map[string]any { var res map[string]any feedback, err := json.Marshal(df) if err != nil {