Merge pull request #22917 from mattermost/MM-51907-route-true-up-telemetry-to-cws
MM-51907 - Route True-Up Review Telemetry to CWS
Этот коммит содержится в:
@@ -8803,3 +8803,17 @@ func (c *Client4) GetWorkTemplatesByCategory(category string) ([]*WorkTemplate,
|
|||||||
err = json.NewDecoder(r.Body).Decode(&templates)
|
err = json.NewDecoder(r.Body).Decode(&templates)
|
||||||
return templates, BuildResponse(r), err
|
return templates, BuildResponse(r), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client4) SubmitTrueUpReview(req map[string]any) (*Response, error) {
|
||||||
|
reqBytes, err := json.Marshal(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, NewAppError("SubmitTrueUpReview", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||||
|
}
|
||||||
|
r, err := c.DoAPIPostBytes(c.licenseRoute()+"/review", reqBytes)
|
||||||
|
if err != nil {
|
||||||
|
return BuildResponse(r), nil
|
||||||
|
}
|
||||||
|
defer closeBody(r)
|
||||||
|
|
||||||
|
return BuildResponse(r), nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
timePkg "time"
|
timePkg "time"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/v6/server/channels/einterfaces"
|
|
||||||
"github.com/mattermost/mattermost-server/v6/model"
|
"github.com/mattermost/mattermost-server/v6/model"
|
||||||
|
"github.com/mattermost/mattermost-server/v6/server/channels/einterfaces"
|
||||||
)
|
)
|
||||||
|
|
||||||
type apiTimerLayer struct {
|
type apiTimerLayer struct {
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
timePkg "time"
|
timePkg "time"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/v6/server/channels/einterfaces"
|
|
||||||
"github.com/mattermost/mattermost-server/v6/model"
|
"github.com/mattermost/mattermost-server/v6/model"
|
||||||
|
"github.com/mattermost/mattermost-server/v6/server/channels/einterfaces"
|
||||||
)
|
)
|
||||||
|
|
||||||
type hooksTimerLayer struct {
|
type hooksTimerLayer struct {
|
||||||
|
|||||||
@@ -351,17 +351,20 @@ func requestTrueUpReview(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not send true-up review data if the user has already requested one for the quarter.
|
// True-up is only enabled when telemetry is disabled.
|
||||||
// And only send a true-up review via as a one-time telemetry request if telemetry is disabled.
|
// When telemetry is enabled, we already have all the data necessary for true-up reviews to be completed.
|
||||||
telemetryEnabled := c.App.Config().LogSettings.EnableDiagnostics
|
telemetryEnabled := c.App.Config().LogSettings.EnableDiagnostics
|
||||||
if telemetryEnabled != nil && !*telemetryEnabled {
|
if telemetryEnabled != nil && !*telemetryEnabled {
|
||||||
// Send telemetry data
|
err = c.App.Cloud().SubmitTrueUpReview(c.AppContext.Session().UserId, profileMap)
|
||||||
c.App.Srv().GetTelemetryService().SendTelemetry(model.TrueUpReviewTelemetryName, profileMap)
|
if err != nil {
|
||||||
|
c.Err = model.NewAppError("requestTrueUpReview", "api.license.true_up_review.failed_to_submit", nil, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Update the review status to reflect the completion.
|
// Update the review status to reflect the completion.
|
||||||
status.Completed = true
|
status.Completed = true
|
||||||
c.App.Srv().Store().TrueUpReview().Update(status)
|
c.App.Srv().Store().TrueUpReview().Update(status)
|
||||||
}
|
|
||||||
|
|
||||||
// Encode to string rather than byte[] otherwise json.Marshal will encode it further.
|
// Encode to string rather than byte[] otherwise json.Marshal will encode it further.
|
||||||
encodedData := b64.StdEncoding.EncodeToString(profileMapJson)
|
encodedData := b64.StdEncoding.EncodeToString(profileMapJson)
|
||||||
@@ -370,6 +373,7 @@ func requestTrueUpReview(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
}{Content: encodedData}
|
}{Content: encodedData}
|
||||||
response, _ := json.Marshal(responseContent)
|
response, _ := json.Marshal(responseContent)
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
w.Write(response)
|
w.Write(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -478,18 +478,33 @@ func TestRequestRenewalLink(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestRequestTrueUpReview(t *testing.T) {
|
func TestRequestTrueUpReview(t *testing.T) {
|
||||||
th := Setup(t)
|
t.Run("returns status 200 when telemetry data sent", func(t *testing.T) {
|
||||||
|
th := Setup(t).InitBasic()
|
||||||
defer th.TearDown()
|
defer th.TearDown()
|
||||||
|
|
||||||
th.App.Srv().SetLicense(model.NewTestLicense())
|
th.App.Srv().SetLicense(model.NewTestLicense())
|
||||||
|
|
||||||
t.Run("returns status 200 when telemetry data sent", func(t *testing.T) {
|
th.Client.Login(th.SystemAdminUser.Email, th.SystemAdminUser.Password)
|
||||||
resp, err := th.SystemAdminClient.DoAPIPost("/license/review", "")
|
|
||||||
|
cloud := mocks.CloudInterface{}
|
||||||
|
cloud.Mock.On("SubmitTrueUpReview", mock.Anything, mock.Anything).Return(nil)
|
||||||
|
|
||||||
|
cloudImpl := th.App.Srv().Cloud
|
||||||
|
defer func() {
|
||||||
|
th.App.Srv().Cloud = cloudImpl
|
||||||
|
}()
|
||||||
|
th.App.Srv().Cloud = &cloud
|
||||||
|
|
||||||
|
var reviewProfile map[string]any
|
||||||
|
resp, err := th.Client.SubmitTrueUpReview(reviewProfile)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, http.StatusOK, resp.StatusCode)
|
require.Equal(t, http.StatusOK, resp.StatusCode)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("returns 501 when ran by cloud user", func(t *testing.T) {
|
t.Run("returns 501 when ran by cloud user", func(t *testing.T) {
|
||||||
|
th := Setup(t).InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
th.App.Srv().SetLicense(model.NewTestLicense())
|
||||||
|
|
||||||
th.App.Srv().SetLicense(model.NewTestLicense("cloud"))
|
th.App.Srv().SetLicense(model.NewTestLicense("cloud"))
|
||||||
|
|
||||||
resp, err := th.SystemAdminClient.DoAPIPost("/license/review", "")
|
resp, err := th.SystemAdminClient.DoAPIPost("/license/review", "")
|
||||||
@@ -500,12 +515,19 @@ func TestRequestTrueUpReview(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("returns 403 when user does not have permissions", func(t *testing.T) {
|
t.Run("returns 403 when user does not have permissions", func(t *testing.T) {
|
||||||
|
th := Setup(t).InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
th.App.Srv().SetLicense(model.NewTestLicense())
|
||||||
|
|
||||||
resp, err := th.Client.DoAPIPost("/license/review", "")
|
resp, err := th.Client.DoAPIPost("/license/review", "")
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
require.Equal(t, http.StatusForbidden, resp.StatusCode)
|
require.Equal(t, http.StatusForbidden, resp.StatusCode)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("returns 400 when license is nil", func(t *testing.T) {
|
t.Run("returns 400 when license is nil", func(t *testing.T) {
|
||||||
|
th := Setup(t).InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
th.App.Srv().SetLicense(nil)
|
th.App.Srv().SetLicense(nil)
|
||||||
|
|
||||||
resp, err := th.SystemAdminClient.DoAPIPost("/license/review", "")
|
resp, err := th.SystemAdminClient.DoAPIPost("/license/review", "")
|
||||||
|
|||||||
@@ -48,4 +48,7 @@ type CloudInterface interface {
|
|||||||
|
|
||||||
SelfServeDeleteWorkspace(userID string, deletionRequest *model.WorkspaceDeletionRequest) error
|
SelfServeDeleteWorkspace(userID string, deletionRequest *model.WorkspaceDeletionRequest) error
|
||||||
SubscribeToNewsletter(userID string, req *model.SubscribeNewsletterRequest) error
|
SubscribeToNewsletter(userID string, req *model.SubscribeNewsletterRequest) error
|
||||||
|
|
||||||
|
// Used only for when a customer has telemetry disabled. In this scenario, true up review telemetry will be submitted via CWS.
|
||||||
|
SubmitTrueUpReview(userID string, trueUpReviewProfile map[string]any) error
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -594,6 +594,20 @@ func (_m *CloudInterface) SelfServeDeleteWorkspace(userID string, deletionReques
|
|||||||
return r0
|
return r0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SubmitTrueUpReview provides a mock function with given fields: userID, trueUpReviewProfile
|
||||||
|
func (_m *CloudInterface) SubmitTrueUpReview(userID string, trueUpReviewProfile map[string]interface{}) error {
|
||||||
|
ret := _m.Called(userID, trueUpReviewProfile)
|
||||||
|
|
||||||
|
var r0 error
|
||||||
|
if rf, ok := ret.Get(0).(func(string, map[string]interface{}) error); ok {
|
||||||
|
r0 = rf(userID, trueUpReviewProfile)
|
||||||
|
} else {
|
||||||
|
r0 = ret.Error(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
// SubscribeToNewsletter provides a mock function with given fields: userID, req
|
// SubscribeToNewsletter provides a mock function with given fields: userID, req
|
||||||
func (_m *CloudInterface) SubscribeToNewsletter(userID string, req *model.SubscribeNewsletterRequest) error {
|
func (_m *CloudInterface) SubscribeToNewsletter(userID string, req *model.SubscribeNewsletterRequest) error {
|
||||||
ret := _m.Called(userID, req)
|
ret := _m.Called(userID, req)
|
||||||
|
|||||||
@@ -2089,6 +2089,10 @@
|
|||||||
"id": "api.license.true_up_review.create_error",
|
"id": "api.license.true_up_review.create_error",
|
||||||
"translation": "Could not create true up status record"
|
"translation": "Could not create true up status record"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "api.license.true_up_review.failed_to_submit",
|
||||||
|
"translation": "Failed to submit true up review profile to CWS."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "api.license.true_up_review.get_status_error",
|
"id": "api.license.true_up_review.get_status_error",
|
||||||
"translation": "Could not get true up status records"
|
"translation": "Could not get true up status records"
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user