MM-47420 - Add endpoint to fetch selfhosted products (#21678)
* created selfhosted endpoint * make autogenerated mocks * add test * add user id * remove user id requirement * add tests * send user id with request Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
ed3f3fec46
Коммит
966456567d
@@ -22,6 +22,8 @@ func (api *API) InitCloud() {
|
|||||||
// GET /api/v4/cloud/limits
|
// GET /api/v4/cloud/limits
|
||||||
api.BaseRoutes.Cloud.Handle("/limits", api.APISessionRequired(getCloudLimits)).Methods("GET")
|
api.BaseRoutes.Cloud.Handle("/limits", api.APISessionRequired(getCloudLimits)).Methods("GET")
|
||||||
|
|
||||||
|
api.BaseRoutes.Cloud.Handle("/products/selfhosted", api.APISessionRequired(getSelfHostedProducts)).Methods("GET")
|
||||||
|
|
||||||
// POST /api/v4/cloud/payment
|
// POST /api/v4/cloud/payment
|
||||||
// POST /api/v4/cloud/payment/confirm
|
// POST /api/v4/cloud/payment/confirm
|
||||||
api.BaseRoutes.Cloud.Handle("/payment", api.APISessionRequired(createCustomerPayment)).Methods("POST")
|
api.BaseRoutes.Cloud.Handle("/payment", api.APISessionRequired(createCustomerPayment)).Methods("POST")
|
||||||
@@ -276,6 +278,40 @@ func validateWorkspaceBusinessEmail(c *Context, w http.ResponseWriter, r *http.R
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getSelfHostedProducts(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
|
products, err := c.App.Cloud().GetSelfHostedProducts(c.AppContext.Session().UserId)
|
||||||
|
if err != nil {
|
||||||
|
c.Err = model.NewAppError("Api4.getSelfHostedProducts", "api.cloud.request_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
byteProductsData, err := json.Marshal(products)
|
||||||
|
if err != nil {
|
||||||
|
c.Err = model.NewAppError("Api4.getSelfHostedProducts", "api.cloud.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionSysconsoleReadBilling) {
|
||||||
|
sanitizedProducts := []model.UserFacingProduct{}
|
||||||
|
err = json.Unmarshal(byteProductsData, &sanitizedProducts)
|
||||||
|
if err != nil {
|
||||||
|
c.Err = model.NewAppError("Api4.getSelfHostedProducts", "api.cloud.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
byteSanitizedProductsData, err := json.Marshal(sanitizedProducts)
|
||||||
|
if err != nil {
|
||||||
|
c.Err = model.NewAppError("Api4.getSelfHostedProducts", "api.cloud.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write(byteSanitizedProductsData)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write(byteProductsData)
|
||||||
|
}
|
||||||
|
|
||||||
func getCloudProducts(c *Context, w http.ResponseWriter, r *http.Request) {
|
func getCloudProducts(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
if !c.App.Channels().License().IsCloud() {
|
if !c.App.Channels().License().IsCloud() {
|
||||||
c.Err = model.NewAppError("Api4.getCloudProducts", "api.cloud.license_error", nil, "", http.StatusForbidden)
|
c.Err = model.NewAppError("Api4.getCloudProducts", "api.cloud.license_error", nil, "", http.StatusForbidden)
|
||||||
|
|||||||
@@ -651,3 +651,110 @@ func TestGetCloudProducts(t *testing.T) {
|
|||||||
require.Equal(t, returnedProducts[2].CrossSellsTo, "prod_test2")
|
require.Equal(t, returnedProducts[2].CrossSellsTo, "prod_test2")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetSelfHostedProducts(t *testing.T) {
|
||||||
|
products := []*model.Product{
|
||||||
|
{
|
||||||
|
ID: "prod_test",
|
||||||
|
Name: "Self-Hosted Professional",
|
||||||
|
Description: "Ideal for small companies and departments with data security requirements",
|
||||||
|
PricePerSeat: 10,
|
||||||
|
SKU: "professional",
|
||||||
|
PriceID: "price_1JPXbNI67GP2qpb4VuFdFbwQ",
|
||||||
|
Family: "on-prem",
|
||||||
|
RecurringInterval: model.RecurringIntervalYearly,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: "prod_test2",
|
||||||
|
Name: "Self-Hosted Enterprise",
|
||||||
|
Description: "Built to scale for high-trust organizations and companies in regulated industries.",
|
||||||
|
PricePerSeat: 30,
|
||||||
|
SKU: "enterprise",
|
||||||
|
PriceID: "price_1JPXaVI67GP2qpb4l40bXyRu",
|
||||||
|
Family: "on-prem",
|
||||||
|
RecurringInterval: model.RecurringIntervalYearly,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
sanitizedProducts := []*model.Product{
|
||||||
|
{
|
||||||
|
ID: "prod_test",
|
||||||
|
Name: "Self-Hosted Professional",
|
||||||
|
PricePerSeat: 10,
|
||||||
|
SKU: "professional",
|
||||||
|
RecurringInterval: model.RecurringIntervalYearly,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: "prod_test2",
|
||||||
|
Name: "Self-Hosted Enterprise",
|
||||||
|
PricePerSeat: 30,
|
||||||
|
SKU: "enterprise",
|
||||||
|
RecurringInterval: model.RecurringIntervalYearly,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("get products for admins", func(t *testing.T) {
|
||||||
|
th := Setup(t).InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
th.Client.Login(th.SystemAdminUser.Email, th.SystemAdminUser.Password)
|
||||||
|
|
||||||
|
cloud := mocks.CloudInterface{}
|
||||||
|
cloud.Mock.On("GetSelfHostedProducts", mock.Anything, mock.Anything).Return(products, nil)
|
||||||
|
cloudImpl := th.App.Srv().Cloud
|
||||||
|
defer func() {
|
||||||
|
th.App.Srv().Cloud = cloudImpl
|
||||||
|
}()
|
||||||
|
th.App.Srv().Cloud = &cloud
|
||||||
|
|
||||||
|
returnedProducts, r, err := th.Client.GetSelfHostedProducts()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, http.StatusOK, r.StatusCode, "Status OK")
|
||||||
|
require.Equal(t, returnedProducts, products)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("get products for non admins", func(t *testing.T) {
|
||||||
|
th := Setup(t).InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
th.Client.Login(th.BasicUser.Email, th.BasicUser.Password)
|
||||||
|
|
||||||
|
cloud := mocks.CloudInterface{}
|
||||||
|
|
||||||
|
cloud.Mock.On("GetSelfHostedProducts", mock.Anything, mock.Anything).Return(products, nil)
|
||||||
|
|
||||||
|
cloudImpl := th.App.Srv().Cloud
|
||||||
|
defer func() {
|
||||||
|
th.App.Srv().Cloud = cloudImpl
|
||||||
|
}()
|
||||||
|
th.App.Srv().Cloud = &cloud
|
||||||
|
|
||||||
|
returnedProducts, r, err := th.Client.GetSelfHostedProducts()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, http.StatusOK, r.StatusCode, "Status OK")
|
||||||
|
require.Equal(t, returnedProducts, sanitizedProducts)
|
||||||
|
|
||||||
|
// make a more explicit check
|
||||||
|
require.Equal(t, returnedProducts[0].ID, "prod_test")
|
||||||
|
require.Equal(t, returnedProducts[0].Name, "Self-Hosted Professional")
|
||||||
|
require.Equal(t, returnedProducts[0].SKU, "professional")
|
||||||
|
require.Equal(t, returnedProducts[0].PricePerSeat, float64(10))
|
||||||
|
require.Equal(t, returnedProducts[0].Description, "")
|
||||||
|
require.Equal(t, returnedProducts[0].PriceID, "")
|
||||||
|
require.Equal(t, returnedProducts[0].Family, model.SubscriptionFamily(""))
|
||||||
|
require.Equal(t, returnedProducts[0].RecurringInterval, model.RecurringInterval("year"))
|
||||||
|
require.Equal(t, returnedProducts[0].BillingScheme, model.BillingScheme(""))
|
||||||
|
require.Equal(t, returnedProducts[0].CrossSellsTo, "")
|
||||||
|
|
||||||
|
require.Equal(t, returnedProducts[1].ID, "prod_test2")
|
||||||
|
require.Equal(t, returnedProducts[1].Name, "Self-Hosted Enterprise")
|
||||||
|
require.Equal(t, returnedProducts[1].SKU, "enterprise")
|
||||||
|
require.Equal(t, returnedProducts[1].PricePerSeat, float64(30))
|
||||||
|
require.Equal(t, returnedProducts[1].Description, "")
|
||||||
|
require.Equal(t, returnedProducts[1].PriceID, "")
|
||||||
|
require.Equal(t, returnedProducts[1].Family, model.SubscriptionFamily(""))
|
||||||
|
require.Equal(t, returnedProducts[1].RecurringInterval, model.RecurringInterval("year"))
|
||||||
|
require.Equal(t, returnedProducts[1].BillingScheme, model.BillingScheme(""))
|
||||||
|
require.Equal(t, returnedProducts[1].CrossSellsTo, "")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
type CloudInterface interface {
|
type CloudInterface interface {
|
||||||
GetCloudProduct(userID string, productID string) (*model.Product, error)
|
GetCloudProduct(userID string, productID string) (*model.Product, error)
|
||||||
GetCloudProducts(userID string, includeLegacyProducts bool) ([]*model.Product, error)
|
GetCloudProducts(userID string, includeLegacyProducts bool) ([]*model.Product, error)
|
||||||
|
GetSelfHostedProducts(userID string) ([]*model.Product, error)
|
||||||
GetCloudLimits(userID string) (*model.ProductLimits, error)
|
GetCloudLimits(userID string) (*model.ProductLimits, error)
|
||||||
|
|
||||||
CreateCustomerPayment(userID string) (*model.StripeSetupIntent, error)
|
CreateCustomerPayment(userID string) (*model.StripeSetupIntent, error)
|
||||||
|
|||||||
@@ -279,6 +279,29 @@ func (_m *CloudInterface) GetLicenseRenewalStatus(userID string, token string) e
|
|||||||
return r0
|
return r0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetSelfHostedProducts provides a mock function with given fields: userID
|
||||||
|
func (_m *CloudInterface) GetSelfHostedProducts(userID string) ([]*model.Product, error) {
|
||||||
|
ret := _m.Called(userID)
|
||||||
|
|
||||||
|
var r0 []*model.Product
|
||||||
|
if rf, ok := ret.Get(0).(func(string) []*model.Product); ok {
|
||||||
|
r0 = rf(userID)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).([]*model.Product)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(1).(func(string) error); ok {
|
||||||
|
r1 = rf(userID)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
// GetSubscription provides a mock function with given fields: userID
|
// GetSubscription provides a mock function with given fields: userID
|
||||||
func (_m *CloudInterface) GetSubscription(userID string) (*model.Subscription, error) {
|
func (_m *CloudInterface) GetSubscription(userID string) (*model.Subscription, error) {
|
||||||
ret := _m.Called(userID)
|
ret := _m.Called(userID)
|
||||||
|
|||||||
@@ -8041,6 +8041,19 @@ func (c *Client4) GetCloudProducts() ([]*Product, *Response, error) {
|
|||||||
return cloudProducts, BuildResponse(r), nil
|
return cloudProducts, BuildResponse(r), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client4) GetSelfHostedProducts() ([]*Product, *Response, error) {
|
||||||
|
r, err := c.DoAPIGet(c.cloudRoute()+"/products/selfhosted", "")
|
||||||
|
if err != nil {
|
||||||
|
return nil, BuildResponse(r), err
|
||||||
|
}
|
||||||
|
defer closeBody(r)
|
||||||
|
|
||||||
|
var products []*Product
|
||||||
|
json.NewDecoder(r.Body).Decode(&products)
|
||||||
|
|
||||||
|
return products, BuildResponse(r), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client4) GetProductLimits() (*ProductLimits, *Response, error) {
|
func (c *Client4) GetProductLimits() (*ProductLimits, *Response, error) {
|
||||||
r, err := c.DoAPIGet(c.cloudRoute()+"/limits", "")
|
r, err := c.DoAPIGet(c.cloudRoute()+"/limits", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user