MM-43529 - start freemium trial (#20163)
* MM-43529 - start freemium trial * change the method to put * Add unit testing for the request-trial-endpoint * use correct require * add the translation texts * fix texts * remove unnecessary log * change response status code to forbidden when non cloud Co-authored-by: Pablo Velez Vidal <pablo.velez@mattermost.com>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
617007a56d
Коммит
f08d65909f
@@ -41,6 +41,9 @@ func (api *API) InitCloud() {
|
||||
api.BaseRoutes.Cloud.Handle("/subscription/invoices/{invoice_id:in_[A-Za-z0-9]+}/pdf", api.APISessionRequired(getSubscriptionInvoicePDF)).Methods("GET")
|
||||
api.BaseRoutes.Cloud.Handle("/subscription", api.APISessionRequired(changeSubscription)).Methods("PUT")
|
||||
|
||||
// GET /api/v4/cloud/request-trial
|
||||
api.BaseRoutes.Cloud.Handle("/request-trial", api.APISessionRequired(requestCloudTrial)).Methods("PUT")
|
||||
|
||||
// POST /api/v4/cloud/webhook
|
||||
api.BaseRoutes.Cloud.Handle("/webhook", api.CloudAPIKeyRequired(handleCWSWebhook)).Methods("POST")
|
||||
}
|
||||
@@ -121,6 +124,43 @@ func changeSubscription(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
w.Write(json)
|
||||
}
|
||||
|
||||
func requestCloudTrial(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if c.App.Channels().License() == nil || !*c.App.Channels().License().Features.Cloud {
|
||||
c.Err = model.NewAppError("Api4.requestCloudTrial", "api.cloud.license_error", nil, "", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionSysconsoleWriteBilling) {
|
||||
c.SetPermissionError(model.PermissionSysconsoleWriteBilling)
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.Config().FeatureFlags.CloudFree {
|
||||
c.Err = model.NewAppError("Api4.requestCloudTrial", "api.cloud.cloud_free_feature_flag_off_error", nil, "", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
currentSubscription, appErr := c.App.Cloud().GetSubscription(c.AppContext.Session().UserId)
|
||||
if appErr != nil {
|
||||
c.Err = model.NewAppError("Api4.requestCloudTrial", "api.cloud.app_error", nil, appErr.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
changedSub, err := c.App.Cloud().RequestCloudTrial(c.AppContext.Session().UserId, currentSubscription.ID)
|
||||
if err != nil {
|
||||
c.Err = model.NewAppError("Api4.requestCloudTrial", "api.cloud.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
json, err := json.Marshal(changedSub)
|
||||
if err != nil {
|
||||
c.Err = model.NewAppError("Api4.requestCloudTrial", "api.cloud.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Write(json)
|
||||
}
|
||||
|
||||
func getCloudProducts(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if c.App.Channels().License() == nil || !*c.App.Channels().License().Features.Cloud {
|
||||
c.Err = model.NewAppError("Api4.getCloudProducts", "api.cloud.license_error", nil, "", http.StatusNotImplemented)
|
||||
|
||||
@@ -141,3 +141,108 @@ func Test_getCloudLimits(t *testing.T) {
|
||||
require.Equal(t, *mockLimits.Messages.History, *limits.Messages.History)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_requestTrial(t *testing.T) {
|
||||
subscription := &model.Subscription{
|
||||
ID: "MySubscriptionID",
|
||||
CustomerID: "MyCustomer",
|
||||
ProductID: "SomeProductId",
|
||||
AddOns: []string{},
|
||||
StartAt: 1000000000,
|
||||
EndAt: 2000000000,
|
||||
CreateAt: 1000000000,
|
||||
Seats: 10,
|
||||
DNS: "some.dns.server",
|
||||
IsPaidTier: "false",
|
||||
}
|
||||
|
||||
t.Run("NON Admin users are UNABLE to request the trial", func(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
os.Setenv("MM_FEATUREFLAGS_CLOUDFREE", "true")
|
||||
defer os.Unsetenv("MM_FEATUREFLAGS_CLOUDFREE")
|
||||
th.App.ReloadConfig()
|
||||
|
||||
th.Client.Login(th.BasicUser.Email, th.BasicUser.Password)
|
||||
|
||||
th.App.Srv().SetLicense(model.NewTestLicense("cloud"))
|
||||
|
||||
cloud := mocks.CloudInterface{}
|
||||
|
||||
cloud.Mock.On("GetSubscription", mock.Anything).Return(subscription, nil)
|
||||
cloud.Mock.On("RequestCloudTrial", mock.Anything, mock.Anything).Return(subscription, nil)
|
||||
|
||||
cloudImpl := th.App.Srv().Cloud
|
||||
defer func() {
|
||||
th.App.Srv().Cloud = cloudImpl
|
||||
}()
|
||||
th.App.Srv().Cloud = &cloud
|
||||
|
||||
subscriptionChanged, r, err := th.Client.RequestCloudTrial()
|
||||
t.Logf("\n\nresp %#v, \n\n r: %v\n\n, err: %v\n\n", subscriptionChanged, r, err)
|
||||
require.Error(t, err)
|
||||
require.Nil(t, subscriptionChanged)
|
||||
require.Equal(t, http.StatusForbidden, r.StatusCode, "403 Forbidden")
|
||||
})
|
||||
|
||||
t.Run("cloudFree feature flag FALSE and Admin user are UNABLE to request the trial", func(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
os.Setenv("MM_FEATUREFLAGS_CLOUDFREE", "false")
|
||||
defer os.Unsetenv("MM_FEATUREFLAGS_CLOUDFREE")
|
||||
th.App.ReloadConfig()
|
||||
|
||||
th.Client.Login(th.BasicUser.Email, th.BasicUser.Password)
|
||||
|
||||
th.App.Srv().SetLicense(model.NewTestLicense("cloud"))
|
||||
|
||||
cloud := mocks.CloudInterface{}
|
||||
|
||||
cloud.Mock.On("GetSubscription", mock.Anything).Return(subscription, nil)
|
||||
cloud.Mock.On("RequestCloudTrial", mock.Anything, mock.Anything).Return(subscription, nil)
|
||||
|
||||
cloudImpl := th.App.Srv().Cloud
|
||||
defer func() {
|
||||
th.App.Srv().Cloud = cloudImpl
|
||||
}()
|
||||
th.App.Srv().Cloud = &cloud
|
||||
|
||||
subscriptionChanged, r, err := th.SystemAdminClient.RequestCloudTrial()
|
||||
|
||||
require.Error(t, err)
|
||||
require.Nil(t, subscriptionChanged)
|
||||
require.Equal(t, http.StatusInternalServerError, r.StatusCode, "Expected 500 Internal Server Error")
|
||||
})
|
||||
|
||||
t.Run("cloudFree feature flag TRUE and ADMIN user are ABLE to request the trial", func(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
os.Setenv("MM_FEATUREFLAGS_CLOUDFREE", "true")
|
||||
defer os.Unsetenv("MM_FEATUREFLAGS_CLOUDFREE")
|
||||
th.App.ReloadConfig()
|
||||
|
||||
th.Client.Login(th.BasicUser.Email, th.BasicUser.Password)
|
||||
|
||||
th.App.Srv().SetLicense(model.NewTestLicense("cloud"))
|
||||
|
||||
cloud := mocks.CloudInterface{}
|
||||
|
||||
cloud.Mock.On("GetSubscription", mock.Anything).Return(subscription, nil)
|
||||
cloud.Mock.On("RequestCloudTrial", mock.Anything, mock.Anything).Return(subscription, nil)
|
||||
|
||||
cloudImpl := th.App.Srv().Cloud
|
||||
defer func() {
|
||||
th.App.Srv().Cloud = cloudImpl
|
||||
}()
|
||||
th.App.Srv().Cloud = &cloud
|
||||
|
||||
subscriptionChanged, r, err := th.SystemAdminClient.RequestCloudTrial()
|
||||
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, subscriptionChanged, subscription)
|
||||
require.Equal(t, http.StatusOK, r.StatusCode, "Status OK")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@ type CloudInterface interface {
|
||||
|
||||
ChangeSubscription(userID, subscriptionID string, subscriptionChange *model.SubscriptionChange) (*model.Subscription, error)
|
||||
|
||||
RequestCloudTrial(userID, subscriptionID string) (*model.Subscription, error)
|
||||
|
||||
// GetLicenseRenewalStatus checks on the portal whether it is possible to use token to renew a license
|
||||
GetLicenseRenewalStatus(userID, token string) error
|
||||
InvalidateCaches() error
|
||||
|
||||
@@ -247,6 +247,29 @@ func (_m *CloudInterface) InvalidateCaches() error {
|
||||
return r0
|
||||
}
|
||||
|
||||
// RequestCloudTrial provides a mock function with given fields: userID, subscriptionID
|
||||
func (_m *CloudInterface) RequestCloudTrial(userID string, subscriptionID string) (*model.Subscription, error) {
|
||||
ret := _m.Called(userID, subscriptionID)
|
||||
|
||||
var r0 *model.Subscription
|
||||
if rf, ok := ret.Get(0).(func(string, string) *model.Subscription); ok {
|
||||
r0 = rf(userID, subscriptionID)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*model.Subscription)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(string, string) error); ok {
|
||||
r1 = rf(userID, subscriptionID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
@@ -463,6 +463,10 @@
|
||||
"id": "api.cloud.app_error",
|
||||
"translation": "Internal error during cloud api request."
|
||||
},
|
||||
{
|
||||
"id": "api.cloud.cloud_free_feature_flag_off_error",
|
||||
"translation": "CloudFree feature flag is off."
|
||||
},
|
||||
{
|
||||
"id": "api.cloud.cws_webhook_event_missing_error",
|
||||
"translation": "Webhook event was not handled. Either it is missing or it is not valid."
|
||||
|
||||
@@ -7784,6 +7784,19 @@ func (c *Client4) ConfirmCustomerPayment(confirmRequest *ConfirmPaymentMethodReq
|
||||
return BuildResponse(r), nil
|
||||
}
|
||||
|
||||
func (c *Client4) RequestCloudTrial() (*Subscription, *Response, error) {
|
||||
r, err := c.DoAPIPut(c.cloudRoute()+"/request-trial", "")
|
||||
if err != nil {
|
||||
return nil, BuildResponse(r), err
|
||||
}
|
||||
defer closeBody(r)
|
||||
|
||||
var subscription *Subscription
|
||||
json.NewDecoder(r.Body).Decode(&subscription)
|
||||
|
||||
return subscription, BuildResponse(r), nil
|
||||
}
|
||||
|
||||
func (c *Client4) GetCloudCustomer() (*CloudCustomer, *Response, error) {
|
||||
r, err := c.DoAPIGet(c.cloudRoute()+"/customer", "")
|
||||
if err != nil {
|
||||
|
||||
Ссылка в новой задаче
Block a user