Skip to content

Commit 69b5597

Browse files
FEATURE: app labels tagging (#580)
* api spec added for app labels api's * open api spec updated for app labels * spec added for create api and app listing and refactored app labels spec * spec added for create api and app listing and refactored app labels spec * modification in app listing open api spec * app label api added * open api spec for app labels review changes * labels changes to key values wip * labels changes to key values wip * db migration script added for app labels * db script change for app label * app label api fix * repo error fixed for app labels * code refactored and transaction for app labels * db migration renamed 15 to 16 * duplicate key check added, removed validation component * delete or untag labels from application * app label meta api app id remove in response * app label meta api app id remove in response, struct changes * unique key constraints removed for app labels * review changes app labels - doc updated with visuals
1 parent 77233ec commit 69b5597

23 files changed

+1259
-108
lines changed

Wire.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -691,6 +691,16 @@ func InitializeApp() (*App, error) {
691691
pipeline.NewWebhookEventDataConfigImpl,
692692
wire.Bind(new(pipeline.WebhookEventDataConfig), new(*pipeline.WebhookEventDataConfigImpl)),
693693

694+
router.NewAppLabelRouterImpl,
695+
wire.Bind(new(router.AppLabelRouter), new(*router.AppLabelRouterImpl)),
696+
restHandler.NewAppLabelRestHandlerImpl,
697+
wire.Bind(new(restHandler.AppLabelRestHandler), new(*restHandler.AppLabelRestHandlerImpl)),
698+
699+
app.NewAppLabelServiceImpl,
700+
wire.Bind(new(app.AppLabelService), new(*app.AppLabelServiceImpl)),
701+
pipelineConfig.NewAppLabelRepositoryImpl,
702+
wire.Bind(new(pipelineConfig.AppLabelRepository), new(*pipelineConfig.AppLabelRepositoryImpl)),
703+
694704
)
695705
return &App{}, nil
696706
}
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
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+
"github.com/devtron-labs/devtron/pkg/app"
23+
"github.com/devtron-labs/devtron/pkg/bean"
24+
"github.com/devtron-labs/devtron/pkg/user"
25+
"github.com/devtron-labs/devtron/util/rbac"
26+
"github.com/gorilla/mux"
27+
"go.uber.org/zap"
28+
"gopkg.in/go-playground/validator.v9"
29+
"net/http"
30+
"strconv"
31+
)
32+
33+
type AppLabelRestHandler interface {
34+
UpdateLabelsInApp(w http.ResponseWriter, r *http.Request)
35+
GetAllLabels(w http.ResponseWriter, r *http.Request)
36+
GetAppMetaInfo(w http.ResponseWriter, r *http.Request)
37+
}
38+
39+
type AppLabelRestHandlerImpl struct {
40+
logger *zap.SugaredLogger
41+
appLabelService app.AppLabelService
42+
userAuthService user.UserService
43+
validator *validator.Validate
44+
enforcerUtil rbac.EnforcerUtil
45+
enforcer rbac.Enforcer
46+
}
47+
48+
func NewAppLabelRestHandlerImpl(logger *zap.SugaredLogger, appLabelService app.AppLabelService,
49+
userAuthService user.UserService, validator *validator.Validate, enforcerUtil rbac.EnforcerUtil,
50+
enforcer rbac.Enforcer) *AppLabelRestHandlerImpl {
51+
handler := &AppLabelRestHandlerImpl{
52+
logger: logger,
53+
appLabelService: appLabelService,
54+
userAuthService: userAuthService,
55+
validator: validator,
56+
enforcerUtil: enforcerUtil,
57+
enforcer: enforcer,
58+
}
59+
return handler
60+
}
61+
62+
func (handler AppLabelRestHandlerImpl) GetAllLabels(w http.ResponseWriter, r *http.Request) {
63+
userId, err := handler.userAuthService.GetLoggedInUser(r)
64+
if userId == 0 || err != nil {
65+
writeJsonResp(w, err, "Unauthorized User", http.StatusUnauthorized)
66+
return
67+
}
68+
token := r.Header.Get("token")
69+
results := make([]*bean.AppLabelDto, 0)
70+
labels, err := handler.appLabelService.FindAll()
71+
if err != nil {
72+
handler.logger.Errorw("service err, GetAllLabels", "err", err)
73+
writeJsonResp(w, err, nil, http.StatusInternalServerError)
74+
return
75+
}
76+
objects := handler.enforcerUtil.GetRbacObjectsForAllApps()
77+
for _, label := range labels {
78+
object := objects[label.AppId]
79+
if ok := handler.enforcer.Enforce(token, rbac.ResourceApplications, rbac.ActionGet, object); ok {
80+
results = append(results, label)
81+
}
82+
}
83+
writeJsonResp(w, nil, results, http.StatusOK)
84+
}
85+
86+
func (handler AppLabelRestHandlerImpl) GetAppMetaInfo(w http.ResponseWriter, r *http.Request) {
87+
userId, err := handler.userAuthService.GetLoggedInUser(r)
88+
if userId == 0 || err != nil {
89+
writeJsonResp(w, err, "Unauthorized User", http.StatusUnauthorized)
90+
return
91+
}
92+
vars := mux.Vars(r)
93+
appId, err := strconv.Atoi(vars["appId"])
94+
if err != nil {
95+
handler.logger.Errorw("request err, GetAppMetaInfo", "err", err, "appId", appId)
96+
writeJsonResp(w, err, nil, http.StatusBadRequest)
97+
return
98+
}
99+
100+
//rback implementation starts here
101+
token := r.Header.Get("token")
102+
object := handler.enforcerUtil.GetAppRBACNameByAppId(appId)
103+
if ok := handler.enforcer.Enforce(token, rbac.ResourceApplications, rbac.ActionGet, object); !ok {
104+
writeJsonResp(w, err, "Unauthorized User", http.StatusForbidden)
105+
return
106+
}
107+
//rback implementation ends here
108+
109+
res, err := handler.appLabelService.GetAppMetaInfo(appId)
110+
if err != nil {
111+
handler.logger.Errorw("service err, GetAppMetaInfo", "err", err)
112+
writeJsonResp(w, err, nil, http.StatusInternalServerError)
113+
return
114+
}
115+
writeJsonResp(w, nil, res, http.StatusOK)
116+
}
117+
118+
func (handler AppLabelRestHandlerImpl) UpdateLabelsInApp(w http.ResponseWriter, r *http.Request) {
119+
userId, err := handler.userAuthService.GetLoggedInUser(r)
120+
if userId == 0 || err != nil {
121+
writeJsonResp(w, err, "Unauthorized User", http.StatusUnauthorized)
122+
return
123+
}
124+
decoder := json.NewDecoder(r.Body)
125+
var request bean.AppLabelsDto
126+
err = decoder.Decode(&request)
127+
request.UserId = userId
128+
if err != nil {
129+
handler.logger.Errorw("request err, UpdateLabelsInApp", "err", err, "request", request)
130+
writeJsonResp(w, err, nil, http.StatusBadRequest)
131+
return
132+
}
133+
handler.logger.Infow("request payload, UpdateLabelsInApp", "request", request)
134+
err = handler.validator.Struct(request)
135+
if err != nil {
136+
handler.logger.Errorw("validation err, UpdateLabelsInApp", "err", err, "request", request)
137+
writeJsonResp(w, err, nil, http.StatusBadRequest)
138+
return
139+
}
140+
//rback implementation starts here
141+
token := r.Header.Get("token")
142+
object := handler.enforcerUtil.GetAppRBACNameByAppId(request.AppId)
143+
if ok := handler.enforcer.Enforce(token, rbac.ResourceApplications, rbac.ActionUpdate, object); !ok {
144+
writeJsonResp(w, err, "Unauthorized User", http.StatusForbidden)
145+
return
146+
}
147+
//rback implementation ends here
148+
res, err := handler.appLabelService.UpdateLabelsInApp(&request)
149+
if err != nil {
150+
handler.logger.Errorw("service err, UpdateLabelsInApp", "err", err)
151+
writeJsonResp(w, err, nil, http.StatusInternalServerError)
152+
return
153+
}
154+
writeJsonResp(w, nil, res, http.StatusOK)
155+
}

api/restHandler/AppListingRestHandler.go

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ type AppListingRestHandler interface {
5050

5151
FetchOtherEnvironment(w http.ResponseWriter, r *http.Request)
5252
RedirectToLinkouts(w http.ResponseWriter, r *http.Request)
53-
GetAppMetaInfo(w http.ResponseWriter, r *http.Request)
5453
}
5554

5655
type AppListingRestHandlerImpl struct {
@@ -562,36 +561,4 @@ func (handler AppListingRestHandlerImpl) RedirectToLinkouts(w http.ResponseWrite
562561
return
563562
}
564563
http.Redirect(w, r, link, http.StatusOK)
565-
}
566-
567-
func (handler AppListingRestHandlerImpl) GetAppMetaInfo(w http.ResponseWriter, r *http.Request) {
568-
token := r.Header.Get("token")
569-
userId, err := handler.userService.GetLoggedInUser(r)
570-
if userId == 0 || err != nil {
571-
writeJsonResp(w, err, "Unauthorized User", http.StatusUnauthorized)
572-
return
573-
}
574-
vars := mux.Vars(r)
575-
appId, err := strconv.Atoi(vars["appId"])
576-
if err != nil {
577-
handler.logger.Errorw("request err, GetAppMetaInfo", "err", err, "appId", appId)
578-
writeJsonResp(w, err, nil, http.StatusBadRequest)
579-
return
580-
}
581-
582-
// RBAC enforcer applying
583-
object := handler.enforcerUtil.GetAppRBACNameByAppId(appId)
584-
if ok := handler.enforcer.Enforce(token, rbac.ResourceApplications, rbac.ActionGet, object); !ok {
585-
writeJsonResp(w, err, "unauthorized user", http.StatusForbidden)
586-
return
587-
}
588-
//RBAC enforcer Ends
589-
590-
res, err := handler.appListingService.GetAppMetaInfo(appId)
591-
if err != nil {
592-
handler.logger.Errorw("service err, GetAppMetaInfo", "err", err)
593-
writeJsonResp(w, err, nil, http.StatusInternalServerError)
594-
return
595-
}
596-
writeJsonResp(w, nil, res, http.StatusOK)
597-
}
564+
}

api/restHandler/PipelineConfigRestHandler.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ func (handler PipelineConfigRestHandlerImpl) CreateApp(w http.ResponseWriter, r
275275
writeJsonResp(w, err, nil, http.StatusBadRequest)
276276
return
277277
}
278+
278279
team, err := handler.teamService.FetchOne(createRequest.TeamId)
279280
if err != nil {
280281
writeJsonResp(w, err, nil, http.StatusBadRequest)

api/router/AppLabelsRouter.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
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+
"github.com/devtron-labs/devtron/api/restHandler"
22+
"github.com/gorilla/mux"
23+
"go.uber.org/zap"
24+
)
25+
26+
type AppLabelRouter interface {
27+
initLabelRouter(router *mux.Router)
28+
}
29+
30+
type AppLabelRouterImpl struct {
31+
logger *zap.SugaredLogger
32+
handler restHandler.AppLabelRestHandler
33+
}
34+
35+
func NewAppLabelRouterImpl(logger *zap.SugaredLogger, handler restHandler.AppLabelRestHandler) *AppLabelRouterImpl {
36+
router := &AppLabelRouterImpl{
37+
logger: logger,
38+
handler: handler,
39+
}
40+
return router
41+
}
42+
43+
func (router AppLabelRouterImpl) initLabelRouter(appLabelsRouter *mux.Router) {
44+
appLabelsRouter.Path("/labels/list").
45+
HandlerFunc(router.handler.GetAllLabels).Methods("GET")
46+
appLabelsRouter.Path("/meta/info/{appId}").
47+
HandlerFunc(router.handler.GetAppMetaInfo).Methods("GET")
48+
appLabelsRouter.Path("/labels").
49+
HandlerFunc(router.handler.UpdateLabelsInApp).Methods("POST")
50+
}

api/router/AppListingRouter.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,4 @@ func (router AppListingRouterImpl) initAppListingRouter(appListingRouter *mux.Ro
6464
Queries("containerName", "{containerName}").
6565
HandlerFunc(router.appListingRestHandler.RedirectToLinkouts).
6666
Methods("GET")
67-
68-
appListingRouter.Path("/meta/info/{appId}").
69-
HandlerFunc(router.appListingRestHandler.GetAppMetaInfo).Methods("GET")
70-
71-
7267
}

api/router/router.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ type MuxRouter struct {
7878
telemetryWatcher telemetry.TelemetryEventClient
7979
bulkUpdateRouter BulkUpdateRouter
8080
WebhookListenerRouter WebhookListenerRouter
81+
appLabelsRouter AppLabelRouter
8182
}
8283

8384
func NewMuxRouter(logger *zap.SugaredLogger, HelmRouter HelmRouter, PipelineConfigRouter PipelineConfigRouter,
@@ -97,7 +98,7 @@ func NewMuxRouter(logger *zap.SugaredLogger, HelmRouter HelmRouter, PipelineConf
9798
ReleaseMetricsRouter ReleaseMetricsRouter, deploymentGroupRouter DeploymentGroupRouter, batchOperationRouter BatchOperationRouter,
9899
chartGroupRouter ChartGroupRouter, testSuitRouter TestSuitRouter, imageScanRouter ImageScanRouter,
99100
policyRouter PolicyRouter, gitOpsConfigRouter GitOpsConfigRouter, dashboardRouter DashboardRouter, attributesRouter AttributesRouter,
100-
commonRouter CommonRouter, grafanaRouter GrafanaRouter, ssoLoginRouter SsoLoginRouter, telemetryRouter TelemetryRouter, telemetryWatcher telemetry.TelemetryEventClient, bulkUpdateRouter BulkUpdateRouter, webhookListenerRouter WebhookListenerRouter) *MuxRouter {
101+
commonRouter CommonRouter, grafanaRouter GrafanaRouter, ssoLoginRouter SsoLoginRouter, telemetryRouter TelemetryRouter, telemetryWatcher telemetry.TelemetryEventClient, bulkUpdateRouter BulkUpdateRouter, webhookListenerRouter WebhookListenerRouter,appLabelsRouter AppLabelRouter) *MuxRouter {
101102
r := &MuxRouter{
102103
Router: mux.NewRouter(),
103104
HelmRouter: HelmRouter,
@@ -146,6 +147,7 @@ func NewMuxRouter(logger *zap.SugaredLogger, HelmRouter HelmRouter, PipelineConf
146147
telemetryWatcher: telemetryWatcher,
147148
bulkUpdateRouter: bulkUpdateRouter,
148149
WebhookListenerRouter: webhookListenerRouter,
150+
appLabelsRouter: appLabelsRouter,
149151
}
150152
return r
151153
}
@@ -176,6 +178,7 @@ func (r MuxRouter) Init() {
176178
r.PipelineConfigRouter.initPipelineConfigRouter(pipelineConfigRouter)
177179
r.AppListingRouter.initAppListingRouter(pipelineConfigRouter)
178180
r.HelmRouter.initHelmRouter(pipelineConfigRouter)
181+
r.appLabelsRouter.initLabelRouter(pipelineConfigRouter)
179182

180183
migrateRouter := r.Router.PathPrefix("/orchestrator/migrate").Subrouter()
181184
r.MigrateDbRouter.InitMigrateDbRouter(migrateRouter)
308 KB
Loading
372 KB
Loading

docs/user-guide/app-labels.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# App Labels Tagging
2+
3+
Overview
4+
============
5+
6+
This feature helps you to tag labels on applications, this will help you to filter out and categories your applications.
7+
8+
labels are combinations of key value pairs, each application may have multiple labels.
9+
10+
every app must have unique key.
11+
12+
### 1. Add labels on creation of application
13+
14+
Login with valid credentials and go to `Applications` and click on right top button `Add New App` and fill the required
15+
values.
16+
17+
Labels are optionals and can be entered `key:values` format. multiple labels can be added without repeating `key` name.
18+
19+
![](../.gitbook/assets/app-labels-1.png)
20+
21+
### 2. Application meta info
22+
23+
Login with valid credentials and go to `Applications` and click on any application and go to detail page, click on top
24+
left button next to application name `?`.
25+
26+
This will open show you the applications meta details like project, labels associated with it.
27+
28+
![](../.gitbook/assets/app-labels-2.png)
29+
30+
### 3. Edit Labels to existing apps
31+
32+
We also add or remove labels for app from here.
33+
34+
![](../.gitbook/assets/app-labels-2.png)
35+
36+
### 3. Label Payload
37+
38+
* `id` : integer unique label id
39+
* `appId` : integer application id
40+
* `key` : string key is tha part of label stores individually in db.
41+
* `value` : string value is tha part of label stores individually in db.

0 commit comments

Comments
 (0)