Skip to content

Commit a77f3fd

Browse files
Custom app grouping (#3295)
* added app group api's * db migration and api added for app group * app grouping apis modified and added handling for custom app group * fix * added delete api for app group and app group id support * app grouping api's handling app group id and app ids * description in app group * added ci and cd min apis for app group * changes in min cd pipeline api * fix * auth changes for custom app grouping and db migration updated * db migration seq fixed * while delete app group, renmaed and marked deleted * admin check removed for app group * remove app group if has no app authorized * duplicate and delete fix * changes after qa * added more detail in ci min api * added more detail in ci min api * fix * remove eliminated item from app group update * fix post cd not triggered * fix for ci pipeline min
1 parent 8327405 commit a77f3fd

22 files changed

+1564
-138
lines changed

Wire.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ import (
6262
"github.com/devtron-labs/devtron/client/telemetry"
6363
"github.com/devtron-labs/devtron/internal/sql/repository"
6464
app2 "github.com/devtron-labs/devtron/internal/sql/repository/app"
65+
appGroup2 "github.com/devtron-labs/devtron/internal/sql/repository/appGroup"
6566
appStatusRepo "github.com/devtron-labs/devtron/internal/sql/repository/appStatus"
6667
appWorkflow2 "github.com/devtron-labs/devtron/internal/sql/repository/appWorkflow"
6768
"github.com/devtron-labs/devtron/internal/sql/repository/bulkUpdate"
@@ -75,6 +76,7 @@ import (
7576
"github.com/devtron-labs/devtron/pkg/app"
7677
"github.com/devtron-labs/devtron/pkg/appClone"
7778
"github.com/devtron-labs/devtron/pkg/appClone/batch"
79+
"github.com/devtron-labs/devtron/pkg/appGroup"
7880
"github.com/devtron-labs/devtron/pkg/appStatus"
7981
appStoreBean "github.com/devtron-labs/devtron/pkg/appStore/bean"
8082
appStoreDeploymentFullMode "github.com/devtron-labs/devtron/pkg/appStore/deployment/fullMode"
@@ -827,6 +829,14 @@ func InitializeApp() (*App, error) {
827829

828830
router.NewAppGroupingRouterImpl,
829831
wire.Bind(new(router.AppGroupingRouter), new(*router.AppGroupingRouterImpl)),
832+
restHandler.NewAppGroupRestHandlerImpl,
833+
wire.Bind(new(restHandler.AppGroupRestHandler), new(*restHandler.AppGroupRestHandlerImpl)),
834+
appGroup.NewAppGroupServiceImpl,
835+
wire.Bind(new(appGroup.AppGroupService), new(*appGroup.AppGroupServiceImpl)),
836+
appGroup2.NewAppGroupRepositoryImpl,
837+
wire.Bind(new(appGroup2.AppGroupRepository), new(*appGroup2.AppGroupRepositoryImpl)),
838+
appGroup2.NewAppGroupMappingRepositoryImpl,
839+
wire.Bind(new(appGroup2.AppGroupMappingRepository), new(*appGroup2.AppGroupMappingRepositoryImpl)),
830840
)
831841
return &App{}, nil
832842
}
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
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/appGroup"
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+
"gopkg.in/go-playground/validator.v9"
30+
"net/http"
31+
"strconv"
32+
"strings"
33+
)
34+
35+
type AppGroupRestHandler interface {
36+
GetActiveAppGroupList(w http.ResponseWriter, r *http.Request)
37+
GetApplicationsForAppGroup(w http.ResponseWriter, r *http.Request)
38+
CreateAppGroup(w http.ResponseWriter, r *http.Request)
39+
UpdateAppGroup(w http.ResponseWriter, r *http.Request)
40+
DeleteAppGroup(w http.ResponseWriter, r *http.Request)
41+
}
42+
43+
type AppGroupRestHandlerImpl struct {
44+
logger *zap.SugaredLogger
45+
enforcer casbin.Enforcer
46+
userService user.UserService
47+
appGroupService appGroup.AppGroupService
48+
validator *validator.Validate
49+
}
50+
51+
func NewAppGroupRestHandlerImpl(logger *zap.SugaredLogger, enforcer casbin.Enforcer,
52+
userService user.UserService, appGroupService appGroup.AppGroupService,
53+
validator *validator.Validate) *AppGroupRestHandlerImpl {
54+
userAuthHandler := &AppGroupRestHandlerImpl{
55+
logger: logger,
56+
enforcer: enforcer,
57+
userService: userService,
58+
appGroupService: appGroupService,
59+
validator: validator,
60+
}
61+
return userAuthHandler
62+
}
63+
64+
func (handler AppGroupRestHandlerImpl) GetActiveAppGroupList(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+
user, err := handler.userService.GetById(userId)
71+
if userId == 0 || err != nil {
72+
common.WriteJsonResp(w, err, "Unauthorized User", http.StatusUnauthorized)
73+
return
74+
}
75+
emailId := strings.ToLower(user.EmailId)
76+
vars := mux.Vars(r)
77+
envId, err := strconv.Atoi(vars["envId"])
78+
if err != nil {
79+
common.WriteJsonResp(w, err, nil, http.StatusBadRequest)
80+
return
81+
}
82+
res, err := handler.appGroupService.GetActiveAppGroupList(emailId, handler.checkAuthBatch, envId)
83+
if err != nil {
84+
handler.logger.Errorw("service err, GetActiveAppGroupList", "err", err)
85+
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
86+
return
87+
}
88+
common.WriteJsonResp(w, nil, res, http.StatusOK)
89+
}
90+
func (handler AppGroupRestHandlerImpl) GetApplicationsForAppGroup(w http.ResponseWriter, r *http.Request) {
91+
userId, err := handler.userService.GetLoggedInUser(r)
92+
if userId == 0 || err != nil {
93+
common.WriteJsonResp(w, err, "Unauthorized User", http.StatusUnauthorized)
94+
return
95+
}
96+
token := r.Header.Get("token")
97+
if ok := handler.enforcer.Enforce(token, casbin.ResourceGlobal, casbin.ActionGet, "*"); !ok {
98+
common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden)
99+
return
100+
}
101+
vars := mux.Vars(r)
102+
id, err := strconv.Atoi(vars["appGroupId"])
103+
if err != nil {
104+
common.WriteJsonResp(w, err, "", http.StatusBadRequest)
105+
return
106+
}
107+
res, err := handler.appGroupService.GetApplicationsForAppGroup(id)
108+
if err != nil {
109+
handler.logger.Errorw("service err, GetApplicationsForAppGroup", "err", err)
110+
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
111+
return
112+
}
113+
common.WriteJsonResp(w, nil, res, http.StatusOK)
114+
}
115+
func (handler AppGroupRestHandlerImpl) CreateAppGroup(w http.ResponseWriter, r *http.Request) {
116+
userId, err := handler.userService.GetLoggedInUser(r)
117+
if userId == 0 || err != nil {
118+
common.WriteJsonResp(w, err, "Unauthorized User", http.StatusUnauthorized)
119+
return
120+
}
121+
decoder := json.NewDecoder(r.Body)
122+
var request appGroup.AppGroupDto
123+
err = decoder.Decode(&request)
124+
if err != nil {
125+
handler.logger.Errorw("request err, CreateAppGroup", "err", err, "payload", request)
126+
common.WriteJsonResp(w, err, nil, http.StatusBadRequest)
127+
return
128+
}
129+
request.UserId = userId
130+
vars := mux.Vars(r)
131+
envId, err := strconv.Atoi(vars["envId"])
132+
if err != nil {
133+
common.WriteJsonResp(w, err, nil, http.StatusBadRequest)
134+
return
135+
}
136+
request.EnvironmentId = envId
137+
err = handler.validator.Struct(request)
138+
if err != nil {
139+
handler.logger.Errorw("validation error", "err", err, "payload", request)
140+
common.WriteJsonResp(w, err, nil, http.StatusBadRequest)
141+
return
142+
}
143+
token := r.Header.Get("token")
144+
if ok := handler.enforcer.Enforce(token, casbin.ResourceGlobal, casbin.ActionCreate, "*"); !ok {
145+
common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden)
146+
return
147+
}
148+
handler.logger.Infow("request payload, CreateAppGroup", "payload", request)
149+
resp, err := handler.appGroupService.CreateAppGroup(&request)
150+
if err != nil {
151+
handler.logger.Errorw("service err, CreateAppGroup", "err", err, "payload", request)
152+
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
153+
return
154+
}
155+
common.WriteJsonResp(w, nil, resp, http.StatusOK)
156+
}
157+
func (handler AppGroupRestHandlerImpl) UpdateAppGroup(w http.ResponseWriter, r *http.Request) {
158+
userId, err := handler.userService.GetLoggedInUser(r)
159+
if userId == 0 || err != nil {
160+
common.WriteJsonResp(w, err, "Unauthorized User", http.StatusUnauthorized)
161+
return
162+
}
163+
decoder := json.NewDecoder(r.Body)
164+
var request appGroup.AppGroupDto
165+
err = decoder.Decode(&request)
166+
if err != nil {
167+
handler.logger.Errorw("request err, UpdateAppGroup", "err", err, "payload", request)
168+
common.WriteJsonResp(w, err, nil, http.StatusBadRequest)
169+
return
170+
}
171+
request.UserId = userId
172+
err = handler.validator.Struct(request)
173+
if err != nil {
174+
handler.logger.Errorw("validation error", "err", err, "payload", request)
175+
common.WriteJsonResp(w, err, nil, http.StatusBadRequest)
176+
return
177+
}
178+
token := r.Header.Get("token")
179+
if ok := handler.enforcer.Enforce(token, casbin.ResourceGlobal, casbin.ActionUpdate, "*"); !ok {
180+
common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden)
181+
return
182+
}
183+
handler.logger.Infow("request payload, UpdateAppGroup", "payload", request)
184+
resp, err := handler.appGroupService.UpdateAppGroup(&request)
185+
if err != nil {
186+
handler.logger.Errorw("service err, UpdateAppGroup", "err", err, "payload", request)
187+
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
188+
return
189+
}
190+
common.WriteJsonResp(w, nil, resp, http.StatusOK)
191+
}
192+
func (handler AppGroupRestHandlerImpl) DeleteAppGroup(w http.ResponseWriter, r *http.Request) {
193+
userId, err := handler.userService.GetLoggedInUser(r)
194+
if userId == 0 || err != nil {
195+
common.WriteJsonResp(w, err, "Unauthorized User", http.StatusUnauthorized)
196+
return
197+
}
198+
vars := mux.Vars(r)
199+
appGroupId, err := strconv.Atoi(vars["appGroupId"])
200+
if err != nil {
201+
common.WriteJsonResp(w, err, "", http.StatusBadRequest)
202+
return
203+
}
204+
token := r.Header.Get("token")
205+
if ok := handler.enforcer.Enforce(token, casbin.ResourceGlobal, casbin.ActionDelete, "*"); !ok {
206+
common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden)
207+
return
208+
}
209+
handler.logger.Infow("request payload, DeleteAppGroup", "appGroupId", appGroupId)
210+
resp, err := handler.appGroupService.DeleteAppGroup(appGroupId)
211+
if err != nil {
212+
handler.logger.Errorw("service err, DeleteAppGroup", "err", err, "appGroupId", appGroupId)
213+
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
214+
return
215+
}
216+
common.WriteJsonResp(w, nil, resp, http.StatusOK)
217+
}
218+
219+
func (handler AppGroupRestHandlerImpl) checkAuthBatch(emailId string, appObject []string) map[string]bool {
220+
var appResult map[string]bool
221+
if len(appObject) > 0 {
222+
appResult = handler.enforcer.EnforceByEmailInBatch(emailId, casbin.ResourceApplications, casbin.ActionGet, appObject)
223+
}
224+
return appResult
225+
}

api/restHandler/AppWorkflowRestHandler.go

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"github.com/devtron-labs/devtron/internal/sql/repository/app"
2424
appWorkflow2 "github.com/devtron-labs/devtron/internal/sql/repository/appWorkflow"
2525
"github.com/devtron-labs/devtron/internal/util"
26+
appGroup2 "github.com/devtron-labs/devtron/pkg/appGroup"
2627
"github.com/devtron-labs/devtron/pkg/appWorkflow"
2728
"github.com/devtron-labs/devtron/pkg/bean"
2829
"github.com/devtron-labs/devtron/pkg/pipeline"
@@ -242,9 +243,42 @@ func (impl AppWorkflowRestHandlerImpl) FindAppWorkflowByEnvironment(w http.Respo
242243
common.WriteJsonResp(w, err, nil, http.StatusBadRequest)
243244
return
244245
}
246+
247+
v := r.URL.Query()
248+
appIdsString := v.Get("appIds")
249+
var appIds []int
250+
if len(appIdsString) > 0 {
251+
appIdsSlices := strings.Split(appIdsString, ",")
252+
for _, appId := range appIdsSlices {
253+
id, err := strconv.Atoi(appId)
254+
if err != nil {
255+
common.WriteJsonResp(w, err, "please provide valid appIds", http.StatusBadRequest)
256+
return
257+
}
258+
appIds = append(appIds, id)
259+
}
260+
}
261+
var appGroupId int
262+
appGroupIdStr := v.Get("appGroupId")
263+
if len(appGroupIdStr) > 0 {
264+
appGroupId, err = strconv.Atoi(appGroupIdStr)
265+
if err != nil {
266+
common.WriteJsonResp(w, err, "please provide valid appGroupId", http.StatusBadRequest)
267+
return
268+
}
269+
}
270+
request := appGroup2.AppGroupingRequest{
271+
EnvId: envId,
272+
AppGroupId: appGroupId,
273+
AppIds: appIds,
274+
EmailId: userEmailId,
275+
CheckAuthBatch: impl.checkAuthBatch,
276+
UserId: userId,
277+
Ctx: r.Context(),
278+
}
245279
workflows := make(map[string]interface{})
246280
_, span := otel.Tracer("orchestrator").Start(r.Context(), "ciHandler.FetchAppWorkflowsInAppGrouping")
247-
workflowsList, err := impl.appWorkflowService.FindAppWorkflowsByEnvironmentId(envId, userEmailId, impl.checkAuthBatch)
281+
workflowsList, err := impl.appWorkflowService.FindAppWorkflowsByEnvironmentId(request)
248282
span.End()
249283
if err != nil {
250284
impl.Logger.Errorw("error in fetching workflows for app", "err", err)

0 commit comments

Comments
 (0)