From 1f5a6e439e8329ac1c1ce853c3516a24a868c6ca Mon Sep 17 00:00:00 2001 From: Allan Guwatudde Date: Fri, 3 Mar 2023 18:33:18 +0300 Subject: [PATCH] [MM-50534] - Renew email adds portal link when not eligible for self-serve renewal (#22350) * [MM-50534] - Renew email adds portal link when not eligible for self-serve renewal * fix translations * make more checks * improve endpoint to make checks * use new checks * fix translations * fix lint * add meaningful names * rename struct * rename endpoint --- api4/cloud.go | 13 ++++----- api4/cloud_test.go | 14 +++++----- api4/license.go | 9 +++++-- app/email/email.go | 14 +++++----- app/email/mocks/ServiceInterface.go | 20 +++++++------- app/email/service.go | 4 +-- app/server.go | 41 ++++++++++++++++++++++++----- einterfaces/cloud.go | 4 +-- einterfaces/mocks/CloudInterface.go | 24 ++++------------- i18n/en.json | 4 +++ model/client4.go | 10 +++---- model/cloud.go | 3 ++- 12 files changed, 91 insertions(+), 69 deletions(-) diff --git a/api4/cloud.go b/api4/cloud.go index ccf6d0aabb..38f27838dd 100644 --- a/api4/cloud.go +++ b/api4/cloud.go @@ -41,7 +41,7 @@ func (api *API) InitCloud() { api.BaseRoutes.Cloud.Handle("/subscription", api.APISessionRequired(getSubscription)).Methods("GET") api.BaseRoutes.Cloud.Handle("/subscription/invoices", api.APISessionRequired(getInvoicesForSubscription)).Methods("GET") api.BaseRoutes.Cloud.Handle("/subscription/invoices/{invoice_id:[_A-Za-z0-9]+}/pdf", api.APISessionRequired(getSubscriptionInvoicePDF)).Methods("GET") - api.BaseRoutes.Cloud.Handle("/subscription/expand", api.APISessionRequired(GetLicenseExpandStatus)).Methods("GET") + api.BaseRoutes.Cloud.Handle("/subscription/self-serve-status", api.APISessionRequired(getLicenseSelfServeStatus)).Methods("GET") api.BaseRoutes.Cloud.Handle("/subscription", api.APISessionRequired(changeSubscription)).Methods("PUT") // GET /api/v4/cloud/request-trial @@ -430,7 +430,8 @@ func getCloudCustomer(c *Context, w http.ResponseWriter, r *http.Request) { w.Write(json) } -func GetLicenseExpandStatus(c *Context, w http.ResponseWriter, r *http.Request) { +// getLicenseSelfServeStatus makes check for the license in the CWS self-serve portal and establishes if the license is renewable, expandable etc. +func getLicenseSelfServeStatus(c *Context, w http.ResponseWriter, r *http.Request) { if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionManageLicenseInformation) { c.SetPermissionError(model.PermissionManageLicenseInformation) return @@ -443,15 +444,15 @@ func GetLicenseExpandStatus(c *Context, w http.ResponseWriter, r *http.Request) return } - res, cloudErr := c.App.Cloud().GetLicenseExpandStatus(c.AppContext.Session().UserId, token) + status, cloudErr := c.App.Cloud().GetLicenseSelfServeStatus(c.AppContext.Session().UserId, token) if cloudErr != nil { - c.Err = model.NewAppError("Api4.GetLicenseExpandStatusForSubscription", "api.cloud.request_error", nil, "", http.StatusInternalServerError).Wrap(cloudErr) + c.Err = model.NewAppError("Api4.getLicenseSelfServeStatus", "api.cloud.request_error", nil, "", http.StatusInternalServerError).Wrap(cloudErr) return } - json, jsonErr := json.Marshal(res) + json, jsonErr := json.Marshal(status) if jsonErr != nil { - c.Err = model.NewAppError("Api4.GetLicenseExpandStatusForSubscription", "api.cloud.app_error", nil, "", http.StatusInternalServerError).Wrap(jsonErr) + c.Err = model.NewAppError("Api4.getLicenseSelfServeStatus", "api.cloud.app_error", nil, "", http.StatusInternalServerError).Wrap(jsonErr) return } diff --git a/api4/cloud_test.go b/api4/cloud_test.go index 399ee67f45..36d782a9a4 100644 --- a/api4/cloud_test.go +++ b/api4/cloud_test.go @@ -650,7 +650,7 @@ func TestGetCloudProducts(t *testing.T) { } func Test_GetExpandStatsForSubscription(t *testing.T) { - isExpandable := &model.SubscriptionExpandStatus{ + status := &model.SubscriptionLicenseSelfServeStatusResponse{ IsExpandable: true, } @@ -664,7 +664,7 @@ func Test_GetExpandStatsForSubscription(t *testing.T) { cloud := mocks.CloudInterface{} - cloud.Mock.On("GetLicenseExpandStatus", mock.Anything).Return(isExpandable, nil) + cloud.Mock.On("GetLicenseSelfServeStatus", mock.Anything).Return(status, nil) cloudImpl := th.App.Srv().Cloud defer func() { @@ -672,9 +672,9 @@ func Test_GetExpandStatsForSubscription(t *testing.T) { }() th.App.Srv().Cloud = &cloud - subscriptionExpandable, r, err := th.Client.GetExpandStats(licenseId) + checksMade, r, err := th.Client.GetSubscriptionStatus(licenseId) require.Error(t, err) - require.Nil(t, subscriptionExpandable) + require.Nil(t, checksMade) require.Equal(t, http.StatusForbidden, r.StatusCode, "403 Forbidden") }) @@ -686,7 +686,7 @@ func Test_GetExpandStatsForSubscription(t *testing.T) { cloud := mocks.CloudInterface{} - cloud.Mock.On("GetLicenseExpandStatus", mock.Anything).Return(isExpandable, nil) + cloud.Mock.On("GetLicenseSelfServeStatus", mock.Anything).Return(status, nil) cloudImpl := th.App.Srv().Cloud defer func() { @@ -694,9 +694,9 @@ func Test_GetExpandStatsForSubscription(t *testing.T) { }() th.App.Srv().Cloud = &cloud - subscriptionExpandable, r, err := th.Client.GetExpandStats("") + checks, r, err := th.Client.GetSubscriptionStatus("") require.Error(t, err) - require.Nil(t, subscriptionExpandable) + require.Nil(t, checks) require.Equal(t, http.StatusBadRequest, r.StatusCode, "400 Bad Request") }) } diff --git a/api4/license.go b/api4/license.go index 69537ba9e5..c7b81441df 100644 --- a/api4/license.go +++ b/api4/license.go @@ -261,9 +261,14 @@ func requestRenewalLink(c *Context, w http.ResponseWriter, r *http.Request) { } // check if it is possible to renew license on the portal with generated token - e := c.App.Cloud().GetLicenseRenewalStatus(c.AppContext.Session().UserId, token) + status, e := c.App.Cloud().GetLicenseSelfServeStatus(c.AppContext.Session().UserId, token) if e != nil { - c.Err = model.NewAppError("requestRenewalLink", "api.license.request_renewal_link.cannot_renew_on_cws", nil, e.Error(), http.StatusBadRequest) + c.Err = model.NewAppError("requestRenewalLink", "api.license.request_renewal_link.cannot_renew_on_cws", nil, e.Error(), http.StatusInternalServerError) + return + } + + if !status.IsRenewable { + c.Err = model.NewAppError("requestRenewalLink", "api.license.request_renewal_link.cannot_renew_on_cws", nil, "License is not self-serve renewable", http.StatusBadRequest) return } diff --git a/app/email/email.go b/app/email/email.go index 5891a7895e..25e12b40cf 100644 --- a/app/email/email.go +++ b/app/email/email.go @@ -989,7 +989,7 @@ func (es *Service) SendLicenseInactivityEmail(email, name, locale, siteURL strin return nil } -func (es *Service) SendLicenseUpForRenewalEmail(email, name, locale, siteURL, renewalLink string, daysToExpiration int) error { +func (es *Service) SendLicenseUpForRenewalEmail(email, name, locale, siteURL, ctaTitle, ctaLink, ctaText string, daysToExpiration int) error { T := i18n.GetUserTranslations(locale) subject := T("api.templates.license_up_for_renewal_subject") @@ -997,10 +997,10 @@ func (es *Service) SendLicenseUpForRenewalEmail(email, name, locale, siteURL, re data.Props["SiteURL"] = siteURL data.Props["Title"] = T("api.templates.license_up_for_renewal_title") data.Props["SubTitle"] = T("api.templates.license_up_for_renewal_subtitle", map[string]any{"UserName": name, "Days": daysToExpiration}) - data.Props["SubTitleTwo"] = T("api.templates.license_up_for_renewal_subtitle_two") + data.Props["SubTitleTwo"] = ctaTitle data.Props["EmailUs"] = T("api.templates.email_us_anytime_at") - data.Props["Button"] = T("api.templates.license_up_for_renewal_renew_now") - data.Props["ButtonURL"] = renewalLink + data.Props["Button"] = ctaText + data.Props["ButtonURL"] = ctaLink data.Props["QuestionTitle"] = T("api.templates.questions_footer.title") data.Props["SupportEmail"] = "feedback@mattermost.com" data.Props["QuestionInfo"] = T("api.templates.questions_footer.info") @@ -1298,7 +1298,7 @@ func (es *Service) SendDelinquencyEmail90(email, locale, siteURL string) error { // SendRemoveExpiredLicenseEmail formats an email and uses the email service to send the email to user with link pointing to CWS // to renew the user license -func (es *Service) SendRemoveExpiredLicenseEmail(renewalLink, email string, locale, siteURL string) error { +func (es *Service) SendRemoveExpiredLicenseEmail(ctaText, ctaLink, email, locale, siteURL string) error { T := i18n.GetUserTranslations(locale) subject := T("api.templates.remove_expired_license.subject", map[string]any{"SiteName": es.config().TeamSettings.SiteName}) @@ -1306,8 +1306,8 @@ func (es *Service) SendRemoveExpiredLicenseEmail(renewalLink, email string, loca data := es.NewEmailTemplateData(locale) data.Props["SiteURL"] = siteURL data.Props["Title"] = T("api.templates.remove_expired_license.body.title") - data.Props["Link"] = renewalLink - data.Props["LinkButton"] = T("api.templates.remove_expired_license.body.renew_button") + data.Props["Link"] = ctaLink + data.Props["LinkButton"] = ctaText body, err := es.templatesContainer.RenderToString("remove_expired_license", data) if err != nil { diff --git a/app/email/mocks/ServiceInterface.go b/app/email/mocks/ServiceInterface.go index a1ebbb5220..16e6c05195 100644 --- a/app/email/mocks/ServiceInterface.go +++ b/app/email/mocks/ServiceInterface.go @@ -358,13 +358,13 @@ func (_m *ServiceInterface) SendLicenseInactivityEmail(_a0 string, name string, return r0 } -// SendLicenseUpForRenewalEmail provides a mock function with given fields: _a0, name, locale, siteURL, renewalLink, daysToExpiration -func (_m *ServiceInterface) SendLicenseUpForRenewalEmail(_a0 string, name string, locale string, siteURL string, renewalLink string, daysToExpiration int) error { - ret := _m.Called(_a0, name, locale, siteURL, renewalLink, daysToExpiration) +// SendLicenseUpForRenewalEmail provides a mock function with given fields: _a0, name, locale, siteURL, ctaTitle, ctaLink, ctaText, daysToExpiration +func (_m *ServiceInterface) SendLicenseUpForRenewalEmail(_a0 string, name string, locale string, siteURL string, ctaTitle string, ctaLink string, ctaText string, daysToExpiration int) error { + ret := _m.Called(_a0, name, locale, siteURL, ctaTitle, ctaLink, ctaText, daysToExpiration) var r0 error - if rf, ok := ret.Get(0).(func(string, string, string, string, string, int) error); ok { - r0 = rf(_a0, name, locale, siteURL, renewalLink, daysToExpiration) + if rf, ok := ret.Get(0).(func(string, string, string, string, string, string, string, int) error); ok { + r0 = rf(_a0, name, locale, siteURL, ctaTitle, ctaLink, ctaText, daysToExpiration) } else { r0 = ret.Error(0) } @@ -484,13 +484,13 @@ func (_m *ServiceInterface) SendPaymentFailedEmail(_a0 string, locale string, fa return r0, r1 } -// SendRemoveExpiredLicenseEmail provides a mock function with given fields: renewalLink, _a1, locale, siteURL -func (_m *ServiceInterface) SendRemoveExpiredLicenseEmail(renewalLink string, _a1 string, locale string, siteURL string) error { - ret := _m.Called(renewalLink, _a1, locale, siteURL) +// SendRemoveExpiredLicenseEmail provides a mock function with given fields: ctaText, ctaLink, _a2, locale, siteURL +func (_m *ServiceInterface) SendRemoveExpiredLicenseEmail(ctaText string, ctaLink string, _a2 string, locale string, siteURL string) error { + ret := _m.Called(ctaText, ctaLink, _a2, locale, siteURL) var r0 error - if rf, ok := ret.Get(0).(func(string, string, string, string) error); ok { - r0 = rf(renewalLink, _a1, locale, siteURL) + if rf, ok := ret.Get(0).(func(string, string, string, string, string) error); ok { + r0 = rf(ctaText, ctaLink, _a2, locale, siteURL) } else { r0 = ret.Error(0) } diff --git a/app/email/service.go b/app/email/service.go index 5c9708d9ce..d75e81dab0 100644 --- a/app/email/service.go +++ b/app/email/service.go @@ -141,7 +141,7 @@ type ServiceInterface interface { SendDeactivateAccountEmail(email string, locale, siteURL string) error SendNotificationMail(to, subject, htmlBody string) error SendMailWithEmbeddedFiles(to, subject, htmlBody string, embeddedFiles map[string]io.Reader, messageID string, inReplyTo string, references string, category string) error - SendLicenseUpForRenewalEmail(email, name, locale, siteURL, renewalLink string, daysToExpiration int) error + SendLicenseUpForRenewalEmail(email, name, locale, siteURL, ctaTitle, ctaLink, ctaText string, daysToExpiration int) error SendPaymentFailedEmail(email string, locale string, failedPayment *model.FailedPayment, planName, siteURL string) (bool, error) // Cloud delinquency email sequence SendDelinquencyEmail7(email, locale, siteURL, planName string) error @@ -152,7 +152,7 @@ type ServiceInterface interface { SendDelinquencyEmail75(email, locale, siteURL, planName, delinquencyDate string) error SendDelinquencyEmail90(email, locale, siteURL string) error SendNoCardPaymentFailedEmail(email string, locale string, siteURL string) error - SendRemoveExpiredLicenseEmail(renewalLink, email string, locale, siteURL string) error + SendRemoveExpiredLicenseEmail(ctaText, ctaLink, email, locale, siteURL string) error AddNotificationEmailToBatch(user *model.User, post *model.Post, team *model.Team) *model.AppError GetMessageForNotification(post *model.Post, translateFunc i18n.TranslateFunc) string InitEmailBatching() diff --git a/app/server.go b/app/server.go index 2de968b765..d74af9907d 100644 --- a/app/server.go +++ b/app/server.go @@ -1288,11 +1288,16 @@ func (s *Server) sendLicenseUpForRenewalEmail(users map[string]*model.User, lice daysToExpiration := license.DaysToExpiration() - renewalLink, _, appErr := s.GenerateLicenseRenewalLink() + ctaLink, tokenToBeUsedForRenew, appErr := s.GenerateLicenseRenewalLink() if appErr != nil { return model.NewAppError("s.sendLicenseUpForRenewalEmail", "api.server.license_up_for_renewal.error_generating_link", nil, "", http.StatusInternalServerError).Wrap(appErr) } + status, err := s.Cloud.GetLicenseSelfServeStatus("", tokenToBeUsedForRenew) + if err != nil { + return model.NewAppError("s.sendLicenseUpForRenewalEmail", "api.cloud.request_error", nil, "", http.StatusInternalServerError).Wrap(err) + } + // we want to at least have one email sent out to an admin countNotOks := 0 @@ -1301,7 +1306,16 @@ func (s *Server) sendLicenseUpForRenewalEmail(users map[string]*model.User, lice if name == "" { name = user.Username } - if err := s.EmailService.SendLicenseUpForRenewalEmail(user.Email, name, user.Locale, *s.platform.Config().ServiceSettings.SiteURL, renewalLink, daysToExpiration); err != nil { + T := i18n.GetUserTranslations(user.Locale) + ctaTitle := T("api.templates.license_up_for_renewal_subtitle_two") + ctaText := T("api.templates.license_up_for_renewal_renew_now") + if !status.IsRenewable { + ctaTitle = "" + ctaText = T("api.templates.license_up_for_renewal_contact_sales") + ctaLink = "https://mattermost.com/contact-sales/" + } + + if err := s.EmailService.SendLicenseUpForRenewalEmail(user.Email, name, user.Locale, *s.platform.Config().ServiceSettings.SiteURL, ctaTitle, ctaLink, ctaText, daysToExpiration); err != nil { mlog.Error("Error sending license up for renewal email to", mlog.String("user_email", user.Email), mlog.Err(err)) countNotOks++ } @@ -1366,9 +1380,15 @@ func (s *Server) doLicenseExpirationCheck() { return } - renewalLink, _, appErr := s.GenerateLicenseRenewalLink() + ctaLink, tokenToBeUsedForRenew, appErr := s.GenerateLicenseRenewalLink() if appErr != nil { - mlog.Error("Error while sending the license expired email.", mlog.Err(appErr)) + mlog.Debug(model.NewAppError("s.sendLicenseUpForRenewalEmail", "api.server.license_up_for_renewal.error_generating_link", nil, "", http.StatusInternalServerError).Wrap(appErr).Error()) + return + } + + status, err := s.Cloud.GetLicenseSelfServeStatus("", tokenToBeUsedForRenew) + if err != nil { + mlog.Debug(model.NewAppError("s.sendLicenseUpForRenewalEmail", "api.cloud.request_error", nil, "", http.StatusInternalServerError).Wrap(err).Error()) return } @@ -1380,9 +1400,16 @@ func (s *Server) doLicenseExpirationCheck() { continue } + T := i18n.GetUserTranslations(user.Locale) + ctaText := T("api.templates.remove_expired_license.body.renew_button") + if !status.IsRenewable { + ctaText = T("api.templates.license_up_for_renewal_contact_sales") + ctaLink = "https://mattermost.com/contact-sales/" + } + mlog.Debug("Sending license expired email.", mlog.String("user_email", user.Email)) s.Go(func() { - if err := s.SendRemoveExpiredLicenseEmail(user.Email, renewalLink, user.Locale, *s.platform.Config().ServiceSettings.SiteURL); err != nil { + if err := s.SendRemoveExpiredLicenseEmail(user.Email, ctaText, ctaLink, user.Locale, *s.platform.Config().ServiceSettings.SiteURL); err != nil { mlog.Error("Error while sending the license expired email.", mlog.String("user_email", user.Email), mlog.Err(err)) } }) @@ -1394,9 +1421,9 @@ func (s *Server) doLicenseExpirationCheck() { // SendRemoveExpiredLicenseEmail formats an email and uses the email service to send the email to user with link pointing to CWS // to renew the user license -func (s *Server) SendRemoveExpiredLicenseEmail(email string, renewalLink, locale, siteURL string) *model.AppError { +func (s *Server) SendRemoveExpiredLicenseEmail(email, ctaText, ctaLink, locale, siteURL string) *model.AppError { - if err := s.EmailService.SendRemoveExpiredLicenseEmail(renewalLink, email, locale, siteURL); err != nil { + if err := s.EmailService.SendRemoveExpiredLicenseEmail(ctaText, ctaLink, email, locale, siteURL); err != nil { return model.NewAppError("SendRemoveExpiredLicenseEmail", "api.license.remove_expired_license.failed.error", nil, "", http.StatusInternalServerError).Wrap(err) } diff --git a/einterfaces/cloud.go b/einterfaces/cloud.go index c17bc424d2..b5d6a75b68 100644 --- a/einterfaces/cloud.go +++ b/einterfaces/cloud.go @@ -17,7 +17,7 @@ type CloudInterface interface { ConfirmCustomerPayment(userID string, confirmRequest *model.ConfirmPaymentMethodRequest) error GetCloudCustomer(userID string) (*model.CloudCustomer, error) - GetLicenseExpandStatus(userID string, token string) (*model.SubscriptionExpandStatus, error) + GetLicenseSelfServeStatus(userID string, token string) (*model.SubscriptionLicenseSelfServeStatusResponse, error) UpdateCloudCustomer(userID string, customerInfo *model.CloudCustomerInfo) (*model.CloudCustomer, error) UpdateCloudCustomerAddress(userID string, address *model.Address) (*model.CloudCustomer, error) @@ -30,8 +30,6 @@ type CloudInterface interface { RequestCloudTrial(userID, subscriptionID, newValidBusinessEmail string) (*model.Subscription, error) ValidateBusinessEmail(userID, email string) error - // GetLicenseRenewalStatus checks on the portal whether it is possible to use token to renew a license - GetLicenseRenewalStatus(userID, token string) error InvalidateCaches() error // hosted customer methods diff --git a/einterfaces/mocks/CloudInterface.go b/einterfaces/mocks/CloudInterface.go index 3f5d64aa0d..db7c86acc2 100644 --- a/einterfaces/mocks/CloudInterface.go +++ b/einterfaces/mocks/CloudInterface.go @@ -339,16 +339,16 @@ func (_m *CloudInterface) GetInvoicesForSubscription(userID string) ([]*model.In return r0, r1 } -// GetLicenseExpandStatus provides a mock function with given fields: userID, token -func (_m *CloudInterface) GetLicenseExpandStatus(userID string, token string) (*model.SubscriptionExpandStatus, error) { +// GetLicenseSelfServeStatus provides a mock function with given fields: userID, token +func (_m *CloudInterface) GetLicenseSelfServeStatus(userID string, token string) (*model.SubscriptionLicenseSelfServeStatusResponse, error) { ret := _m.Called(userID, token) - var r0 *model.SubscriptionExpandStatus - if rf, ok := ret.Get(0).(func(string, string) *model.SubscriptionExpandStatus); ok { + var r0 *model.SubscriptionLicenseSelfServeStatusResponse + if rf, ok := ret.Get(0).(func(string, string) *model.SubscriptionLicenseSelfServeStatusResponse); ok { r0 = rf(userID, token) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(*model.SubscriptionExpandStatus) + r0 = ret.Get(0).(*model.SubscriptionLicenseSelfServeStatusResponse) } } @@ -362,20 +362,6 @@ func (_m *CloudInterface) GetLicenseExpandStatus(userID string, token string) (* return r0, r1 } -// GetLicenseRenewalStatus provides a mock function with given fields: userID, token -func (_m *CloudInterface) GetLicenseRenewalStatus(userID string, token string) error { - ret := _m.Called(userID, token) - - var r0 error - if rf, ok := ret.Get(0).(func(string, string) error); ok { - r0 = rf(userID, token) - } else { - r0 = ret.Error(0) - } - - return r0 -} - // GetSelfHostedInvoicePDF provides a mock function with given fields: invoiceID func (_m *CloudInterface) GetSelfHostedInvoicePDF(invoiceID string) ([]byte, string, error) { ret := _m.Called(invoiceID) diff --git a/i18n/en.json b/i18n/en.json index e6e013be2c..9abad6d78c 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -3623,6 +3623,10 @@ "id": "api.templates.invite_team_and_channels_subject", "translation": "[{{ .SiteName }}] {{ .SenderName }} invited you to join {{ .ChannelsLen }} channels on the {{ .TeamDisplayName }} Team" }, + { + "id": "api.templates.license_up_for_renewal_contact_sales", + "translation": "Contact sales" + }, { "id": "api.templates.license_up_for_renewal_renew_now", "translation": "Renew now" diff --git a/model/client4.go b/model/client4.go index 7a7742b957..853cfdd080 100644 --- a/model/client4.go +++ b/model/client4.go @@ -8235,17 +8235,17 @@ func (c *Client4) GetCloudCustomer() (*CloudCustomer, *Response, error) { return cloudCustomer, BuildResponse(r), nil } -func (c *Client4) GetExpandStats(licenseId string) (*SubscriptionExpandStatus, *Response, error) { - r, err := c.DoAPIGet(fmt.Sprintf("%s%s?licenseID=%s", c.cloudRoute(), "/subscription/expand", licenseId), "") +func (c *Client4) GetSubscriptionStatus(licenseId string) (*SubscriptionLicenseSelfServeStatusResponse, *Response, error) { + r, err := c.DoAPIGet(fmt.Sprintf("%s%s?licenseID=%s", c.cloudRoute(), "/subscription/self-serve-status", licenseId), "") if err != nil { return nil, BuildResponse(r), err } defer closeBody(r) - var subscriptionExpandable *SubscriptionExpandStatus - json.NewDecoder(r.Body).Decode(&subscriptionExpandable) + var status *SubscriptionLicenseSelfServeStatusResponse + json.NewDecoder(r.Body).Decode(&status) - return subscriptionExpandable, BuildResponse(r), nil + return status, BuildResponse(r), nil } func (c *Client4) GetSubscription() (*Subscription, *Response, error) { diff --git a/model/cloud.go b/model/cloud.go index 76ce685a0a..2e1765a8ea 100644 --- a/model/cloud.go +++ b/model/cloud.go @@ -125,8 +125,9 @@ type ValidateBusinessEmailResponse struct { IsValid bool `json:"is_valid"` } -type SubscriptionExpandStatus struct { +type SubscriptionLicenseSelfServeStatusResponse struct { IsExpandable bool `json:"is_expandable"` + IsRenewable bool `json:"is_renewable"` } // CloudCustomerInfo represents editable info of a customer.