Skip to content

Commit d76ccac

Browse files
Guided onboarding user metadata (#2191)
* initial commit * user attributes code commit * error msg wrapped * get user attribute fix * update migrator file version * code review comments incorporated * refactoring needed * mentioned mapping for swagger generation * user attributes commited in EA mode also * send telemetry event API exposed * refactoring * telemetry API for EA mode * super admin permission removed * db patch file updated
1 parent 4b428ed commit d76ccac

File tree

15 files changed

+620
-11
lines changed

15 files changed

+620
-11
lines changed

Wire.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -729,6 +729,14 @@ func InitializeApp() (*App, error) {
729729
app.NewPipelineStatusTimelineServiceImpl,
730730
wire.Bind(new(app.PipelineStatusTimelineService), new(*app.PipelineStatusTimelineServiceImpl)),
731731

732+
router.NewUserAttributesRouterImpl,
733+
wire.Bind(new(router.UserAttributesRouter), new(*router.UserAttributesRouterImpl)),
734+
restHandler.NewUserAttributesRestHandlerImpl,
735+
wire.Bind(new(restHandler.UserAttributesRestHandler), new(*restHandler.UserAttributesRestHandlerImpl)),
736+
attributes.NewUserAttributesServiceImpl,
737+
wire.Bind(new(attributes.UserAttributesService), new(*attributes.UserAttributesServiceImpl)),
738+
repository.NewUserAttributesRepositoryImpl,
739+
wire.Bind(new(repository.UserAttributesRepository), new(*repository.UserAttributesRepositoryImpl)),
732740
pipelineConfig.NewPipelineStatusTimelineRepositoryImpl,
733741
wire.Bind(new(pipelineConfig.PipelineStatusTimelineRepository), new(*pipelineConfig.PipelineStatusTimelineRepositoryImpl)),
734742
)

api/restHandler/TelemetryRestHandler.go

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,24 +18,36 @@
1818
package restHandler
1919

2020
import (
21+
"encoding/json"
22+
"errors"
2123
"github.com/devtron-labs/devtron/api/restHandler/common"
2224
"github.com/devtron-labs/devtron/client/telemetry"
25+
"github.com/devtron-labs/devtron/pkg/user"
26+
"github.com/devtron-labs/devtron/pkg/user/casbin"
2327
"go.uber.org/zap"
2428
"net/http"
2529
)
2630

2731
type TelemetryRestHandler interface {
2832
GetTelemetryMetaInfo(w http.ResponseWriter, r *http.Request)
33+
SendTelemetryData(w http.ResponseWriter, r *http.Request)
2934
}
3035

3136
type TelemetryRestHandlerImpl struct {
3237
logger *zap.SugaredLogger
3338
telemetryEventClient telemetry.TelemetryEventClient
39+
enforcer casbin.Enforcer
40+
userService user.UserService
41+
}
42+
43+
type TelemetryGenericEvent struct {
44+
eventType string
45+
eventPayload map[string]interface{}
3446
}
3547

3648
func NewTelemetryRestHandlerImpl(logger *zap.SugaredLogger,
37-
telemetryEventClient telemetry.TelemetryEventClient) *TelemetryRestHandlerImpl {
38-
handler := &TelemetryRestHandlerImpl{logger: logger, telemetryEventClient: telemetryEventClient}
49+
telemetryEventClient telemetry.TelemetryEventClient, enforcer casbin.Enforcer, userService user.UserService) *TelemetryRestHandlerImpl {
50+
handler := &TelemetryRestHandlerImpl{logger: logger, telemetryEventClient: telemetryEventClient, enforcer: enforcer, userService: userService}
3951
return handler
4052
}
4153

@@ -48,3 +60,36 @@ func (handler TelemetryRestHandlerImpl) GetTelemetryMetaInfo(w http.ResponseWrit
4860
}
4961
common.WriteJsonResp(w, nil, res, http.StatusOK)
5062
}
63+
64+
func (handler TelemetryRestHandlerImpl) SendTelemetryData(w http.ResponseWriter, r *http.Request) {
65+
userId, err := handler.userService.GetLoggedInUser(r)
66+
if userId == 0 || err != nil {
67+
common.WriteJsonResp(w, err, "Unauthorized User", http.StatusUnauthorized)
68+
return
69+
}
70+
decoder := json.NewDecoder(r.Body)
71+
var payload map[string]interface{}
72+
err = decoder.Decode(&payload)
73+
if err != nil {
74+
handler.logger.Errorw("request err, SendTelemetryData", "err", err, "payload", payload)
75+
common.WriteJsonResp(w, err, nil, http.StatusBadRequest)
76+
return
77+
}
78+
token := r.Header.Get("token")
79+
if ok := handler.enforcer.Enforce(token, casbin.ResourceGlobal, casbin.ActionGet, "*"); !ok {
80+
common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden)
81+
return
82+
}
83+
84+
eventType := payload["eventType"]
85+
eventTypeString := eventType.(string)
86+
err = handler.telemetryEventClient.SendGenericTelemetryEvent(eventTypeString, payload)
87+
88+
if err != nil {
89+
handler.logger.Errorw("service err, SendTelemetryData", "err", err)
90+
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
91+
return
92+
}
93+
common.WriteJsonResp(w, nil, "success", http.StatusOK)
94+
95+
}
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
/*
2+
* Copyright (c) 2020 Devtron Labs
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
18+
package restHandler
19+
20+
import (
21+
"encoding/json"
22+
"errors"
23+
"github.com/devtron-labs/devtron/api/restHandler/common"
24+
"github.com/devtron-labs/devtron/pkg/attributes"
25+
"github.com/devtron-labs/devtron/pkg/user"
26+
"github.com/devtron-labs/devtron/pkg/user/casbin"
27+
"github.com/gorilla/mux"
28+
"go.uber.org/zap"
29+
"net/http"
30+
)
31+
32+
type UserAttributesRestHandler interface {
33+
AddUserAttributes(w http.ResponseWriter, r *http.Request)
34+
UpdateUserAttributes(w http.ResponseWriter, r *http.Request)
35+
GetUserAttribute(w http.ResponseWriter, r *http.Request)
36+
}
37+
38+
type UserAttributesRestHandlerImpl struct {
39+
logger *zap.SugaredLogger
40+
enforcer casbin.Enforcer
41+
userService user.UserService
42+
userAttributesService attributes.UserAttributesService
43+
}
44+
45+
func NewUserAttributesRestHandlerImpl(logger *zap.SugaredLogger, enforcer casbin.Enforcer,
46+
userService user.UserService, userAttributesService attributes.UserAttributesService) *UserAttributesRestHandlerImpl {
47+
userAuthHandler := &UserAttributesRestHandlerImpl{
48+
logger: logger,
49+
enforcer: enforcer,
50+
userService: userService,
51+
userAttributesService: userAttributesService,
52+
}
53+
return userAuthHandler
54+
}
55+
56+
func (handler *UserAttributesRestHandlerImpl) AddUserAttributes(w http.ResponseWriter, r *http.Request) {
57+
userId, err := handler.userService.GetLoggedInUser(r)
58+
if userId == 0 || err != nil {
59+
common.WriteJsonResp(w, err, "Unauthorized User", http.StatusUnauthorized)
60+
return
61+
}
62+
decoder := json.NewDecoder(r.Body)
63+
var dto attributes.UserAttributesDto
64+
err = decoder.Decode(&dto)
65+
if err != nil {
66+
handler.logger.Errorw("request err, AddUserAttributes", "err", err, "payload", dto)
67+
common.WriteJsonResp(w, err, nil, http.StatusBadRequest)
68+
return
69+
}
70+
71+
dto.UserId = userId
72+
token := r.Header.Get("token")
73+
//if ok := handler.enforcer.Enforce(token, casbin.ResourceGlobal, casbin.ActionCreate, "*"); !ok {
74+
// common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden)
75+
// return
76+
//}
77+
emailId, err := handler.userService.GetEmailFromToken(token)
78+
if err != nil {
79+
handler.logger.Errorw("request err, UpdateUserAttributes", "err", err, "payload", dto)
80+
common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden)
81+
return
82+
}
83+
dto.EmailId = emailId
84+
85+
handler.logger.Infow("request payload, AddUserAttributes", "payload", dto)
86+
resp, err := handler.userAttributesService.AddUserAttributes(&dto)
87+
if err != nil {
88+
handler.logger.Errorw("service err, AddUserAttributes", "err", err, "payload", dto)
89+
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
90+
return
91+
}
92+
common.WriteJsonResp(w, nil, resp, http.StatusOK)
93+
}
94+
95+
// @Summary update user attributes
96+
// @version 1.0
97+
// @produce application/json
98+
// @Param payload body attributes.UserAttributesDto true "Input key"
99+
// @Success 200 {object} attributes.UserAttributesDto
100+
// @Router /orchestrator/attributes/user/update [POST]
101+
func (handler *UserAttributesRestHandlerImpl) UpdateUserAttributes(w http.ResponseWriter, r *http.Request) {
102+
userId, err := handler.userService.GetLoggedInUser(r)
103+
if userId == 0 || err != nil {
104+
common.WriteJsonResp(w, err, "Unauthorized User", http.StatusUnauthorized)
105+
return
106+
}
107+
108+
decoder := json.NewDecoder(r.Body)
109+
var dto attributes.UserAttributesDto
110+
err = decoder.Decode(&dto)
111+
if err != nil {
112+
handler.logger.Errorw("request err, UpdateUserAttributes", "err", err, "payload", dto)
113+
common.WriteJsonResp(w, err, nil, http.StatusBadRequest)
114+
return
115+
}
116+
117+
dto.UserId = userId
118+
token := r.Header.Get("token")
119+
//if ok := handler.enforcer.Enforce(token, casbin.ResourceGlobal, casbin.ActionUpdate, "*"); !ok {
120+
// common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden)
121+
// return
122+
//}
123+
124+
emailId, err := handler.userService.GetEmailFromToken(token)
125+
if err != nil {
126+
handler.logger.Errorw("request err, UpdateUserAttributes", "err", err, "payload", dto)
127+
common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden)
128+
return
129+
}
130+
dto.EmailId = emailId
131+
132+
handler.logger.Infow("request payload, UpdateUserAttributes", "payload", dto)
133+
resp, err := handler.userAttributesService.UpdateUserAttributes(&dto)
134+
if err != nil {
135+
handler.logger.Errorw("service err, UpdateUserAttributes", "err", err, "payload", dto)
136+
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
137+
return
138+
}
139+
common.WriteJsonResp(w, nil, resp, http.StatusOK)
140+
}
141+
142+
// @Summary get user attributes
143+
// @version 1.0
144+
// @produce application/json
145+
// @Param name query string true "Input key"
146+
// @Success 200 {object} attributes.UserAttributesDto
147+
// @Router /orchestrator/attributes/user/get [GET]
148+
func (handler *UserAttributesRestHandlerImpl) GetUserAttribute(w http.ResponseWriter, r *http.Request) {
149+
userId, err := handler.userService.GetLoggedInUser(r)
150+
if userId == 0 || err != nil {
151+
common.WriteJsonResp(w, err, "Unauthorized User", http.StatusUnauthorized)
152+
return
153+
}
154+
155+
vars := mux.Vars(r)
156+
key := vars["key"]
157+
if key == "" {
158+
handler.logger.Errorw("request err, GetUserAttribute", "err", err, "key", key)
159+
common.WriteJsonResp(w, err, nil, http.StatusBadRequest)
160+
return
161+
}
162+
163+
token := r.Header.Get("token")
164+
//if ok := handler.enforcer.Enforce(token, casbin.ResourceGlobal, casbin.ActionGet, "*"); !ok {
165+
// common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden)
166+
// return
167+
//}
168+
169+
dto := attributes.UserAttributesDto{}
170+
171+
emailId, err := handler.userService.GetEmailFromToken(token)
172+
if err != nil {
173+
handler.logger.Errorw("request err, UpdateUserAttributes", "err", err, "payload", dto)
174+
common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden)
175+
return
176+
}
177+
dto.EmailId = emailId
178+
dto.Key = key
179+
180+
res, err := handler.userAttributesService.GetUserAttribute(&dto)
181+
if err != nil {
182+
handler.logger.Errorw("service err, GetAttributesById", "err", err)
183+
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
184+
return
185+
}
186+
common.WriteJsonResp(w, nil, res, http.StatusOK)
187+
}

api/router/TelemetryRouter.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import (
2424
)
2525

2626
type TelemetryRouter interface {
27-
initTelemetryRouter(router *mux.Router)
27+
InitTelemetryRouter(router *mux.Router)
2828
}
2929

3030
type TelemetryRouterImpl struct {
@@ -39,7 +39,10 @@ func NewTelemetryRouterImpl(logger *zap.SugaredLogger, handler restHandler.Telem
3939
return router
4040
}
4141

42-
func (router TelemetryRouterImpl) initTelemetryRouter(telemetryRouter *mux.Router) {
42+
func (router TelemetryRouterImpl) InitTelemetryRouter(telemetryRouter *mux.Router) {
4343
telemetryRouter.Path("/meta").
4444
HandlerFunc(router.handler.GetTelemetryMetaInfo).Methods("GET")
45+
telemetryRouter.Path("/event").
46+
HandlerFunc(router.handler.SendTelemetryData).Methods("POST")
47+
4548
}

api/router/UserAttributesRouter.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright (c) 2020 Devtron Labs
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
18+
package router
19+
20+
import (
21+
user "github.com/devtron-labs/devtron/api/restHandler"
22+
"github.com/gorilla/mux"
23+
)
24+
25+
type UserAttributesRouter interface {
26+
InitUserAttributesRouter(helmRouter *mux.Router)
27+
}
28+
29+
type UserAttributesRouterImpl struct {
30+
userAttributesRestHandler user.UserAttributesRestHandler
31+
}
32+
33+
func NewUserAttributesRouterImpl(userAttributesRestHandler user.UserAttributesRestHandler) *UserAttributesRouterImpl {
34+
router := &UserAttributesRouterImpl{
35+
userAttributesRestHandler: userAttributesRestHandler,
36+
}
37+
return router
38+
}
39+
40+
func (router UserAttributesRouterImpl) InitUserAttributesRouter(attributesRouter *mux.Router) {
41+
attributesRouter.Path("/update").
42+
HandlerFunc(router.userAttributesRestHandler.UpdateUserAttributes).Methods("POST")
43+
attributesRouter.Path("/get").
44+
HandlerFunc(router.userAttributesRestHandler.GetUserAttribute).Queries("key", "{key}").Methods("GET")
45+
}

api/router/router.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ type MuxRouter struct {
8888
gitOpsConfigRouter GitOpsConfigRouter
8989
dashboardRouter dashboard.DashboardRouter
9090
attributesRouter AttributesRouter
91+
userAttributesRouter UserAttributesRouter
9192
commonRouter CommonRouter
9293
grafanaRouter GrafanaRouter
9394
ssoLoginRouter sso.SsoLoginRouter
@@ -129,7 +130,7 @@ func NewMuxRouter(logger *zap.SugaredLogger, HelmRouter HelmRouter, PipelineConf
129130
ChartRefRouter ChartRefRouter, ConfigMapRouter ConfigMapRouter, AppStoreRouter appStore.AppStoreRouter, chartRepositoryRouter chartRepo.ChartRepositoryRouter,
130131
ReleaseMetricsRouter ReleaseMetricsRouter, deploymentGroupRouter DeploymentGroupRouter, batchOperationRouter BatchOperationRouter,
131132
chartGroupRouter ChartGroupRouter, testSuitRouter TestSuitRouter, imageScanRouter ImageScanRouter,
132-
policyRouter PolicyRouter, gitOpsConfigRouter GitOpsConfigRouter, dashboardRouter dashboard.DashboardRouter, attributesRouter AttributesRouter,
133+
policyRouter PolicyRouter, gitOpsConfigRouter GitOpsConfigRouter, dashboardRouter dashboard.DashboardRouter, attributesRouter AttributesRouter, userAttributesRouter UserAttributesRouter,
133134
commonRouter CommonRouter, grafanaRouter GrafanaRouter, ssoLoginRouter sso.SsoLoginRouter, telemetryRouter TelemetryRouter, telemetryWatcher telemetry.TelemetryEventClient, bulkUpdateRouter BulkUpdateRouter, webhookListenerRouter WebhookListenerRouter, appLabelsRouter AppLabelRouter,
134135
coreAppRouter CoreAppRouter, helmAppRouter client.HelmAppRouter, k8sApplicationRouter k8s.K8sApplicationRouter,
135136
pProfRouter PProfRouter, deploymentConfigRouter deployment.DeploymentConfigRouter, dashboardTelemetryRouter dashboardEvent.DashboardTelemetryRouter,
@@ -175,6 +176,7 @@ func NewMuxRouter(logger *zap.SugaredLogger, HelmRouter HelmRouter, PipelineConf
175176
policyRouter: policyRouter,
176177
gitOpsConfigRouter: gitOpsConfigRouter,
177178
attributesRouter: attributesRouter,
179+
userAttributesRouter: userAttributesRouter,
178180
dashboardRouter: dashboardRouter,
179181
commonRouter: commonRouter,
180182
grafanaRouter: grafanaRouter,
@@ -322,6 +324,9 @@ func (r MuxRouter) Init() {
322324
attributeRouter := r.Router.PathPrefix("/orchestrator/attributes").Subrouter()
323325
r.attributesRouter.initAttributesRouter(attributeRouter)
324326

327+
userAttributeRouter := r.Router.PathPrefix("/orchestrator/attributes/user").Subrouter()
328+
r.userAttributesRouter.InitUserAttributesRouter(userAttributeRouter)
329+
325330
dashboardRouter := r.Router.PathPrefix("/dashboard").Subrouter()
326331
r.dashboardRouter.InitDashboardRouter(dashboardRouter)
327332

@@ -339,7 +344,7 @@ func (r MuxRouter) Init() {
339344
r.ssoLoginRouter.InitSsoLoginRouter(ssoLoginRouter)
340345

341346
telemetryRouter := r.Router.PathPrefix("/orchestrator/telemetry").Subrouter()
342-
r.telemetryRouter.initTelemetryRouter(telemetryRouter)
347+
r.telemetryRouter.InitTelemetryRouter(telemetryRouter)
343348

344349
bulkUpdateRouter := r.Router.PathPrefix("/orchestrator/batch").Subrouter()
345350
r.bulkUpdateRouter.initBulkUpdateRouter(bulkUpdateRouter)

0 commit comments

Comments
 (0)