diff --git a/api/bean/AppView/AppView.go b/api/bean/AppView/AppView.go index 8779691a25..cc93ff7221 100644 --- a/api/bean/AppView/AppView.go +++ b/api/bean/AppView/AppView.go @@ -64,6 +64,7 @@ type GenericNoteResponseBean struct { Description string `json:"description"` UpdatedBy string `json:"updatedBy"` UpdatedOn time.Time `json:"updatedOn"` + CreatedBy string `json:"createdBy"` } type JobContainer struct { @@ -100,6 +101,7 @@ type JobListingContainer struct { LastTriggeredEnvironmentName string `sql:"last_triggered_environment_name" json:"lastTriggeredEnvironmentName"` LastTriggeredEnvironmentId int `sql:"last_triggered_environment_id" json:"lastEnvironmentId"` ProjectId int `sql:"team_id" json:"projectId"` + CreatedBy int32 `sql:"created_by" json:"createdBy"` } type CiPipelineLastSucceededTime struct { diff --git a/api/restHandler/app/pipeline/AutoCompleteRestHandler.go b/api/restHandler/app/pipeline/AutoCompleteRestHandler.go index 165afaf581..8bae0d4b8e 100644 --- a/api/restHandler/app/pipeline/AutoCompleteRestHandler.go +++ b/api/restHandler/app/pipeline/AutoCompleteRestHandler.go @@ -18,6 +18,7 @@ package pipeline import ( "context" + "errors" "fmt" "github.com/devtron-labs/devtron/pkg/auth/user" "github.com/devtron-labs/devtron/pkg/build/git/gitProvider/read" @@ -93,6 +94,24 @@ func (handler DevtronAppAutoCompleteRestHandlerImpl) GetAppListForAutocomplete(w v := r.URL.Query() teamId := v.Get("teamId") appName := v.Get("appName") + offset := 0 + size := 0 // default value is 0, it means if not provided in query param it will fetch all + sizeStr := v.Get("size") + if sizeStr != "" { + size, err = strconv.Atoi(sizeStr) + if err != nil || size < 0 { + common.WriteJsonResp(w, errors.New("invalid size"), nil, http.StatusBadRequest) + return + } + } + offsetStr := v.Get("offset") + if offsetStr != "" { + offset, err = strconv.Atoi(offsetStr) + if err != nil || offset < 0 { + common.WriteJsonResp(w, errors.New("invalid offset"), nil, http.StatusBadRequest) + return + } + } appTypeParam := v.Get("appType") var appType int if appTypeParam != "" { @@ -131,6 +150,7 @@ func (handler DevtronAppAutoCompleteRestHandlerImpl) GetAppListForAutocomplete(w } } if isActionUserSuperAdmin { + apps = handler.getPaginatedResultsForApps(offset, size, apps) common.WriteJsonResp(w, err, apps, http.StatusOK) return } @@ -161,6 +181,7 @@ func (handler DevtronAppAutoCompleteRestHandlerImpl) GetAppListForAutocomplete(w } span.End() // RBAC + accessedApps = handler.getPaginatedResultsForApps(offset, size, accessedApps) common.WriteJsonResp(w, err, accessedApps, http.StatusOK) } @@ -290,3 +311,14 @@ func (handler DevtronAppAutoCompleteRestHandlerImpl) TeamListAutocomplete(w http common.WriteJsonResp(w, err, result, http.StatusOK) } + +func (handler DevtronAppAutoCompleteRestHandlerImpl) getPaginatedResultsForApps(offset int, size int, apps []*pipeline.AppBean) []*pipeline.AppBean { + if size > 0 { + if offset+size <= len(apps) { + apps = apps[offset : offset+size] + } else { + apps = apps[offset:] + } + } + return apps +} diff --git a/internal/sql/repository/helper/AppListingRepositoryQueryBuilder.go b/internal/sql/repository/helper/AppListingRepositoryQueryBuilder.go index b527185e19..8767773b5c 100644 --- a/internal/sql/repository/helper/AppListingRepositoryQueryBuilder.go +++ b/internal/sql/repository/helper/AppListingRepositoryQueryBuilder.go @@ -73,7 +73,7 @@ const ( func (impl AppListingRepositoryQueryBuilder) BuildJobListingQuery(appIDs []int, statuses []string, environmentIds []int, sortOrder string) (string, []interface{}) { var queryParams []interface{} query := `select ci_pipeline.name as ci_pipeline_name,ci_pipeline.id as ci_pipeline_id,app.id as job_id,app.display_name - as job_name, app.app_name,app.description,app.team_id,cwr.started_on,cwr.status,cem.environment_id,cwr.environment_id as last_triggered_environment_id from app left join ci_pipeline on + as job_name, app.app_name,app.description,app.created_by,app.team_id,cwr.started_on,cwr.status,cem.environment_id,cwr.environment_id as last_triggered_environment_id from app left join ci_pipeline on app.id = ci_pipeline.app_id and ci_pipeline.active=true left join (select cw.ci_pipeline_id, cw.status, cw.started_on, cw.environment_id from ci_workflow cw inner join (select ci_pipeline_id, MAX(started_on) max_started_on from ci_workflow group by ci_pipeline_id ) cws on cw.ci_pipeline_id = cws.ci_pipeline_id diff --git a/pkg/app/AppListingService.go b/pkg/app/AppListingService.go index a5d5654ff2..004bc158f2 100644 --- a/pkg/app/AppListingService.go +++ b/pkg/app/AppListingService.go @@ -360,9 +360,14 @@ func (impl AppListingServiceImpl) FetchJobs(fetchJobListingRequest FetchAppListi impl.Logger.Errorw("error in fetching job list", "error", err, jobListingFilter) return []*AppView.JobContainer{}, err } + userEmailMap, err := impl.extractEmailIdFromUserId(jobListingContainers) + if err != nil { + impl.Logger.Errorw("Error in extractEmailIdFromUserId", "jobContainers", jobListingContainers, "err", err) + return nil, err + } CiPipelineIDs := GetCIPipelineIDs(jobListingContainers) JobsLastSucceededOnTime, err := impl.appListingRepository.FetchJobsLastSucceededOn(CiPipelineIDs) - jobContainers := BuildJobListingResponse(jobListingContainers, JobsLastSucceededOnTime) + jobContainers := BuildJobListingResponse(jobListingContainers, JobsLastSucceededOnTime, userEmailMap) return jobContainers, nil } @@ -553,7 +558,7 @@ func GetCIPipelineIDs(jobContainers []*AppView.JobListingContainer) []int { } return ciPipelineIDs } -func BuildJobListingResponse(jobContainers []*AppView.JobListingContainer, JobsLastSucceededOnTime []*AppView.CiPipelineLastSucceededTime) []*AppView.JobContainer { +func BuildJobListingResponse(jobContainers []*AppView.JobListingContainer, JobsLastSucceededOnTime []*AppView.CiPipelineLastSucceededTime, userEmailMap map[int32]string) []*AppView.JobContainer { jobContainersMapping := make(map[int]AppView.JobContainer) var appIds []int @@ -572,6 +577,7 @@ func BuildJobListingResponse(jobContainers []*AppView.JobListingContainer, JobsL val.JobName = jobContainer.JobName val.JobActualName = jobContainer.JobActualName val.ProjectId = jobContainer.ProjectId + val.Description = AppView.GenericNoteResponseBean{Description: jobContainer.Description, CreatedBy: userEmailMap[jobContainer.CreatedBy]} } if len(val.JobCiPipelines) == 0 { @@ -881,3 +887,24 @@ func (impl AppListingServiceImpl) RedirectToLinkouts(Id int, appId int, envId in return link, nil } + +func (impl AppListingServiceImpl) extractEmailIdFromUserId(jobContainers []*AppView.JobListingContainer) (map[int32]string, error) { + var userIds []int32 + userEmailMap := make(map[int32]string) + for _, job := range jobContainers { + if job.CreatedBy != 0 { + userIds = append(userIds, job.CreatedBy) + } + } + if len(userIds) > 0 { + users, err := impl.userRepository.GetByIds(userIds) + if err != nil { + impl.Logger.Errorw("Error in getting users", "userIds", userIds, "err", err) + return userEmailMap, err + } + for _, user := range users { + userEmailMap[user.Id] = user.EmailId + } + } + return userEmailMap, nil +} diff --git a/pkg/pipeline/DevtronAppConfigService.go b/pkg/pipeline/DevtronAppConfigService.go index a8b4de8551..85ff9d772b 100644 --- a/pkg/pipeline/DevtronAppConfigService.go +++ b/pkg/pipeline/DevtronAppConfigService.go @@ -20,6 +20,7 @@ import ( "github.com/devtron-labs/devtron/internal/sql/repository/app" "github.com/devtron-labs/devtron/internal/sql/repository/helper" "github.com/devtron-labs/devtron/internal/sql/repository/pipelineConfig" + "github.com/devtron-labs/devtron/pkg/auth/user/repository" "github.com/devtron-labs/devtron/pkg/bean" "github.com/devtron-labs/devtron/pkg/resourceGroup" "github.com/devtron-labs/devtron/util/rbac" @@ -79,6 +80,7 @@ type DevtronAppConfigServiceImpl struct { enforcerUtil rbac.EnforcerUtil ciMaterialConfigService CiMaterialConfigService + userRepository repository.UserRepository } func NewDevtronAppConfigServiceImpl( @@ -88,7 +90,8 @@ func NewDevtronAppConfigServiceImpl( pipelineRepository pipelineConfig.PipelineRepository, resourceGroupService resourceGroup.ResourceGroupService, enforcerUtil rbac.EnforcerUtil, - ciMaterialConfigService CiMaterialConfigService) *DevtronAppConfigServiceImpl { + ciMaterialConfigService CiMaterialConfigService, + userRepository repository.UserRepository) *DevtronAppConfigServiceImpl { return &DevtronAppConfigServiceImpl{ logger: logger, ciCdPipelineOrchestrator: ciCdPipelineOrchestrator, @@ -97,6 +100,7 @@ func NewDevtronAppConfigServiceImpl( resourceGroupService: resourceGroupService, enforcerUtil: enforcerUtil, ciMaterialConfigService: ciMaterialConfigService, + userRepository: userRepository, } } @@ -179,12 +183,17 @@ func (impl *DevtronAppConfigServiceImpl) FindAllMatchesByAppName(appName string, impl.logger.Errorw("error while fetching app", "err", err) return nil, err } + userEmailMap, err := impl.getUserEmailFromApps(apps) + if err != nil { + impl.logger.Errorw("Error in getUserEmailFromApps", "apps", apps, "err", err) + return nil, err + } for _, app := range apps { name := app.AppName if appType == helper.Job { name = app.DisplayName } - appsRes = append(appsRes, &AppBean{Id: app.Id, Name: name}) + appsRes = append(appsRes, &AppBean{Id: app.Id, Name: name, Description: app.Description, CreatedBy: userEmailMap[app.CreatedBy]}) } return appsRes, err } @@ -240,8 +249,13 @@ func (impl *DevtronAppConfigServiceImpl) FindAppsByTeamId(teamId int) ([]*AppBea impl.logger.Errorw("error while fetching app", "err", err) return nil, err } + userEmailMap, err := impl.getUserEmailFromApps(apps) + if err != nil { + impl.logger.Errorw("Error in getUserEmailFromApps", "apps", apps, "err", err) + return nil, err + } for _, app := range apps { - appsRes = append(appsRes, &AppBean{Id: app.Id, Name: app.AppName}) + appsRes = append(appsRes, &AppBean{Id: app.Id, Name: app.AppName, Description: app.Description, CreatedBy: userEmailMap[app.CreatedBy]}) } return appsRes, err } @@ -258,3 +272,24 @@ func (impl *DevtronAppConfigServiceImpl) FindAppsByTeamName(teamName string) ([] } return appsRes, err } + +func (impl *DevtronAppConfigServiceImpl) getUserEmailFromApps(apps []*app.App) (map[int32]string, error) { + var userIds []int32 + userEmailMap := make(map[int32]string) + for _, app := range apps { + if app.CreatedBy != 0 { + userIds = append(userIds, app.CreatedBy) + } + } + if len(userIds) > 0 { + users, err := impl.userRepository.GetByIds(userIds) + if err != nil { + impl.logger.Errorw("Error in getting users", "userIds", userIds, "err", err) + return userEmailMap, err + } + for _, user := range users { + userEmailMap[user.Id] = user.EmailId + } + } + return userEmailMap, nil +} diff --git a/pkg/pipeline/PipelineBuilder.go b/pkg/pipeline/PipelineBuilder.go index 74d70fede1..6fe330b399 100644 --- a/pkg/pipeline/PipelineBuilder.go +++ b/pkg/pipeline/PipelineBuilder.go @@ -264,9 +264,11 @@ type TeamAppBean struct { } type AppBean struct { - Id int `json:"id"` - Name string `json:"name,notnull"` - TeamId int `json:"teamId,omitempty"` + Id int `json:"id"` + Name string `json:"name,notnull"` + TeamId int `json:"teamId,omitempty"` + CreatedBy string `json:"createdBy"` + Description string `json:"description"` } type PipelineStrategiesResponse struct { diff --git a/wire_gen.go b/wire_gen.go index 6e08af1332..69f21f246a 100644 --- a/wire_gen.go +++ b/wire_gen.go @@ -734,7 +734,7 @@ func InitializeApp() (*App, error) { return nil, err } appDeploymentTypeChangeManagerImpl := pipeline.NewAppDeploymentTypeChangeManagerImpl(sugaredLogger, pipelineRepositoryImpl, appServiceImpl, appStatusRepositoryImpl, helmAppServiceImpl, appArtifactManagerImpl, cdPipelineConfigServiceImpl, gitOpsConfigReadServiceImpl, chartServiceImpl, workflowEventPublishServiceImpl, deploymentConfigServiceImpl, chartReadServiceImpl, deploymentConfigReadServiceImpl) - devtronAppConfigServiceImpl := pipeline.NewDevtronAppConfigServiceImpl(sugaredLogger, ciCdPipelineOrchestratorImpl, appRepositoryImpl, pipelineRepositoryImpl, resourceGroupServiceImpl, enforcerUtilImpl, ciMaterialConfigServiceImpl) + devtronAppConfigServiceImpl := pipeline.NewDevtronAppConfigServiceImpl(sugaredLogger, ciCdPipelineOrchestratorImpl, appRepositoryImpl, pipelineRepositoryImpl, resourceGroupServiceImpl, enforcerUtilImpl, ciMaterialConfigServiceImpl, userRepositoryImpl) pipelineBuilderImpl := pipeline.NewPipelineBuilderImpl(sugaredLogger, gitMaterialReadServiceImpl, chartRepositoryImpl, ciPipelineConfigServiceImpl, ciMaterialConfigServiceImpl, appArtifactManagerImpl, devtronAppCMCSServiceImpl, devtronAppStrategyServiceImpl, appDeploymentTypeChangeManagerImpl, cdPipelineConfigServiceImpl, devtronAppConfigServiceImpl) deploymentTemplateValidationServiceEntImpl := validator.NewDeploymentTemplateValidationServiceEntImpl() deploymentTemplateValidationServiceImpl := validator.NewDeploymentTemplateValidationServiceImpl(sugaredLogger, chartRefServiceImpl, scopedVariableManagerImpl, deployedAppMetricsServiceImpl, deploymentTemplateValidationServiceEntImpl)