diff --git a/api/bean/AppView.go b/api/bean/AppView.go index ba54f8494c..e718d3bc96 100644 --- a/api/bean/AppView.go +++ b/api/bean/AppView.go @@ -138,6 +138,7 @@ type AppEnvironmentContainer struct { TeamName string `json:"teamName"` Description string `json:"description" validate:"max=40"` TotalCount int `json:"-"` + Commits []string `json:"commits"` } type DeploymentDetailContainer struct { @@ -197,22 +198,24 @@ type Notes struct { } type Environment struct { - AppStatus string `json:"appStatus"` //this is not the status of environment , this make sense with a specific app only - EnvironmentId int `json:"environmentId"` - EnvironmentName string `json:"environmentName"` - AppMetrics *bool `json:"appMetrics"` - InfraMetrics *bool `json:"infraMetrics"` - Prod bool `json:"prod"` - ChartRefId int `json:"chartRefId"` - LastDeployed string `json:"lastDeployed"` - LastDeployedBy string `json:"lastDeployedBy"` - LastDeployedImage string `json:"lastDeployedImage"` - DeploymentAppDeleteRequest bool `json:"deploymentAppDeleteRequest"` - Description string `json:"description" validate:"max=40"` - IsVirtualEnvironment bool `json:"isVirtualEnvironment"` - ClusterId int `json:"clusterId"` - PipelineId int `json:"pipelineId"` - LatestCdWorkflowRunnerId int `json:"latestCdWorkflowRunnerId,omitempty"` + AppStatus string `json:"appStatus"` //this is not the status of environment , this make sense with a specific app only + EnvironmentId int `json:"environmentId"` + EnvironmentName string `json:"environmentName"` + AppMetrics *bool `json:"appMetrics"` + InfraMetrics *bool `json:"infraMetrics"` + Prod bool `json:"prod"` + ChartRefId int `json:"chartRefId"` + LastDeployed string `json:"lastDeployed"` + LastDeployedBy string `json:"lastDeployedBy"` + LastDeployedImage string `json:"lastDeployedImage"` + DeploymentAppDeleteRequest bool `json:"deploymentAppDeleteRequest"` + Description string `json:"description" validate:"max=40"` + IsVirtualEnvironment bool `json:"isVirtualEnvironment"` + ClusterId int `json:"clusterId"` + PipelineId int `json:"pipelineId"` + LatestCdWorkflowRunnerId int `json:"latestCdWorkflowRunnerId,omitempty"` + CiArtifactId int `json:"ciArtifactId"` + Commits []string `json:"commits"` } type InstanceDetail struct { diff --git a/internal/sql/repository/AppListingRepository.go b/internal/sql/repository/AppListingRepository.go index 4de732409e..6b43d7bd8c 100644 --- a/internal/sql/repository/AppListingRepository.go +++ b/internal/sql/repository/AppListingRepository.go @@ -76,6 +76,7 @@ type AppNameTypeIdContainerDBResponse struct { } type LastDeployed struct { + CiArtifactId int `sql:"ci_artifact_id"` LastDeployedBy string `sql:"last_deployed_by"` LastDeployedImage string `sql:"last_deployed_image"` } @@ -157,7 +158,7 @@ func (impl AppListingRepositoryImpl) FetchOverviewAppsByEnvironment(envId, limit func (impl AppListingRepositoryImpl) FetchLastDeployedImage(appId, envId int) (*LastDeployed, error) { var lastDeployed []*LastDeployed // we are adding a case in the query to concatenate the string "(inactive)" to the users' email id when user is inactive - query := `select ca.image as last_deployed_image, + query := `select ca.id as ci_artifact_id,ca.image as last_deployed_image, case when u.active = false then u.email_id || ' (inactive)' else u.email_id @@ -604,8 +605,8 @@ func (impl AppListingRepositoryImpl) FetchOtherEnvironment(appId int) ([]*bean.E var otherEnvironments []*bean.Environment //TODO: remove infra metrics from query as it is not being used from here query := `select pcwr.pipeline_id, pcwr.last_deployed, pcwr.latest_cd_workflow_runner_id, pcwr.environment_id, pcwr.deployment_app_delete_request, - e.cluster_id, e.environment_name, e.default as prod, e.description, ca.image as last_deployed_image, - u.email_id as last_deployed_by, elam.app_metrics, elam.infra_metrics, ap.status as app_status + e.cluster_id, e.environment_name, e.default as prod, e.description, ca.image as last_deployed_image, + u.email_id as last_deployed_by, elam.app_metrics, elam.infra_metrics, ap.status as app_status,ca.id as ci_artifact_id from (select * from (select p.id as pipeline_id, p.app_id, cwr.started_on as last_deployed, cwr.triggered_by, cwr.id as latest_cd_workflow_runner_id, cw.ci_artifact_id, p.environment_id, p.deployment_app_delete_request, diff --git a/pkg/app/AppListingService.go b/pkg/app/AppListingService.go index f4b09f2e22..181e96ceed 100644 --- a/pkg/app/AppListingService.go +++ b/pkg/app/AppListingService.go @@ -143,6 +143,7 @@ type AppListingServiceImpl struct { dockerRegistryIpsConfigService dockerRegistry.DockerRegistryIpsConfigService userRepository userrepository.UserRepository deployedAppMetricsService deployedAppMetrics.DeployedAppMetricsService + ciArtifactRepository repository.CiArtifactRepository } func NewAppListingServiceImpl(Logger *zap.SugaredLogger, appListingRepository repository.AppListingRepository, @@ -153,7 +154,7 @@ func NewAppListingServiceImpl(Logger *zap.SugaredLogger, appListingRepository re argoUserService argo.ArgoUserService, envOverrideRepository chartConfig.EnvConfigOverrideRepository, chartRepository chartRepoRepository.ChartRepository, ciPipelineRepository pipelineConfig.CiPipelineRepository, dockerRegistryIpsConfigService dockerRegistry.DockerRegistryIpsConfigService, userRepository userrepository.UserRepository, - deployedAppMetricsService deployedAppMetrics.DeployedAppMetricsService) *AppListingServiceImpl { + deployedAppMetricsService deployedAppMetrics.DeployedAppMetricsService, ciArtifactRepository repository.CiArtifactRepository) *AppListingServiceImpl { serviceImpl := &AppListingServiceImpl{ Logger: Logger, appListingRepository: appListingRepository, @@ -172,6 +173,7 @@ func NewAppListingServiceImpl(Logger *zap.SugaredLogger, appListingRepository re dockerRegistryIpsConfigService: dockerRegistryIpsConfigService, userRepository: userRepository, deployedAppMetricsService: deployedAppMetricsService, + ciArtifactRepository: ciArtifactRepository, } return serviceImpl } @@ -234,6 +236,8 @@ func (impl AppListingServiceImpl) FetchOverviewAppsByEnvironment(envId, limit, o impl.Logger.Errorw("failed to fetch environment containers", "err", err, "envId", envId) return resp, err } + + artifactIds := make([]int, 0) for _, envContainer := range envContainers { lastDeployed, err := impl.appListingRepository.FetchLastDeployedImage(envContainer.AppId, envId) if err != nil { @@ -243,12 +247,44 @@ func (impl AppListingServiceImpl) FetchOverviewAppsByEnvironment(envId, limit, o if lastDeployed != nil { envContainer.LastDeployedImage = lastDeployed.LastDeployedImage envContainer.LastDeployedBy = lastDeployed.LastDeployedBy + envContainer.CiArtifactId = lastDeployed.CiArtifactId + artifactIds = append(artifactIds, lastDeployed.CiArtifactId) + } + } + uniqueArtifacts := getUniqueArtifacts(artifactIds) + + artifactWithGitCommit, err := impl.generateArtifactIDCommitMap(uniqueArtifacts) + if err != nil { + impl.Logger.Errorw("failed to fetch Artifacts to git Triggers ", "envId", envId, "err", err) + return resp, err + } + for _, envContainer := range envContainers { + envContainer.Commits = []string{} + if envContainer.CiArtifactId > 0 { + if commits, ok := artifactWithGitCommit[envContainer.CiArtifactId]; ok && commits != nil { + envContainer.Commits = commits + } } } resp.Apps = envContainers return resp, err } +func getUniqueArtifacts(artifactIds []int) (uniqueArtifactIds []int) { + uniqueArtifactIds = make([]int, 0) + + uniqueArtifactMap := make(map[int]bool) + + for _, artifactId := range artifactIds { + if ok := uniqueArtifactMap[artifactId]; !ok { + uniqueArtifactIds = append(uniqueArtifactIds, artifactId) + uniqueArtifactMap[artifactId] = true + } + } + + return uniqueArtifactIds +} + func (impl AppListingServiceImpl) FetchAllDevtronManagedApps() ([]AppNameTypeIdContainer, error) { impl.Logger.Debug("reached at FetchAllDevtronManagedApps:") apps := make([]AppNameTypeIdContainer, 0) @@ -770,6 +806,49 @@ func (impl AppListingServiceImpl) FetchAppStageStatus(appId int, appType int) ([ return appStageStatuses, err } +func (impl AppListingServiceImpl) generateArtifactIDCommitMap(artifactIds []int) (ciArtifactAndGitCommitsMap map[int][]string, err error) { + + if len(artifactIds) == 0 { + impl.Logger.Errorw("error in getting the ArtifactIds", "ArtifactIds", artifactIds, "err", err) + return make(map[int][]string), err + } + + artifacts, err := impl.ciArtifactRepository.GetByIds(artifactIds) + if err != nil { + return make(map[int][]string), err + } + + ciArtifactAndGitCommitsMap = make(map[int][]string) + ciArtifactWithModificationMap := make(map[int][]repository.Modification) + + for _, artifact := range artifacts { + materialInfo, err := repository.GetCiMaterialInfo(artifact.MaterialInfo, artifact.DataSource) + if err != nil { + impl.Logger.Errorw("error in getting the MaterialInfo", "ArtifactId", artifact.Id, "err", err) + return make(map[int][]string), err + } + if len(materialInfo) == 0 { + continue + } + for _, material := range materialInfo { + ciArtifactWithModificationMap[artifact.Id] = append(ciArtifactWithModificationMap[artifact.Id], material.Modifications...) + } + } + + for artifactId, modifications := range ciArtifactWithModificationMap { + + gitCommits := make([]string, 0) + + for _, modification := range modifications { + gitCommits = append(gitCommits, modification.Revision) + } + + ciArtifactAndGitCommitsMap[artifactId] = gitCommits + } + + return ciArtifactAndGitCommitsMap, nil +} + func (impl AppListingServiceImpl) FetchOtherEnvironment(ctx context.Context, appId int) ([]*bean.Environment, error) { newCtx, span := otel.Tracer("appListingRepository").Start(ctx, "FetchOtherEnvironment") envs, err := impl.appListingRepository.FetchOtherEnvironment(appId) @@ -793,6 +872,19 @@ func (impl AppListingServiceImpl) FetchOtherEnvironment(ctx context.Context, app impl.Logger.Errorw("error in fetching latest chart", "err", err) return envs, err } + + ciArtifacts := make([]int, 0) + for _, env := range envs { + ciArtifacts = append(ciArtifacts, env.CiArtifactId) + } + + uniqueArtifacts := getUniqueArtifacts(ciArtifacts) + + gitCommitsWithArtifacts, err := impl.generateArtifactIDCommitMap(uniqueArtifacts) + if err != nil { + impl.Logger.Errorw("Error in fetching the git commits of the ciArtifacts", "err", err, "ciArtifacts", ciArtifacts) + return envs, err + } for _, env := range envs { newCtx, span = otel.Tracer("envOverrideRepository").Start(newCtx, "FindLatestChartForAppByAppIdAndEnvId") envOverride, err := impl.envOverrideRepository.FindLatestChartForAppByAppIdAndEnvId(appId, env.EnvironmentId) @@ -809,6 +901,12 @@ func (impl AppListingServiceImpl) FetchOtherEnvironment(ctx context.Context, app if env.AppMetrics == nil { env.AppMetrics = &appLevelAppMetrics } + + if _, ok := gitCommitsWithArtifacts[env.CiArtifactId]; ok { + env.Commits = gitCommitsWithArtifacts[env.CiArtifactId] + } else { + env.Commits = make([]string, 0) + } env.InfraMetrics = &appLevelInfraMetrics //using default value, discarding value got from query } return envs, nil diff --git a/wire_gen.go b/wire_gen.go index 9b5f563127..14280cc724 100644 --- a/wire_gen.go +++ b/wire_gen.go @@ -576,7 +576,7 @@ func InitializeApp() (*App, error) { appWorkflowServiceImpl := appWorkflow2.NewAppWorkflowServiceImpl(sugaredLogger, appWorkflowRepositoryImpl, ciCdPipelineOrchestratorImpl, ciPipelineRepositoryImpl, pipelineRepositoryImpl, enforcerUtilImpl, resourceGroupServiceImpl, appRepositoryImpl, userAuthServiceImpl, chartServiceImpl) appListingViewBuilderImpl := app2.NewAppListingViewBuilderImpl(sugaredLogger) linkoutsRepositoryImpl := repository2.NewLinkoutsRepositoryImpl(sugaredLogger, db) - appListingServiceImpl := app2.NewAppListingServiceImpl(sugaredLogger, appListingRepositoryImpl, applicationServiceClientImpl, appRepositoryImpl, appListingViewBuilderImpl, pipelineRepositoryImpl, linkoutsRepositoryImpl, cdWorkflowRepositoryImpl, pipelineOverrideRepositoryImpl, environmentRepositoryImpl, argoUserServiceImpl, envConfigOverrideRepositoryImpl, chartRepositoryImpl, ciPipelineRepositoryImpl, dockerRegistryIpsConfigServiceImpl, userRepositoryImpl, deployedAppMetricsServiceImpl) + appListingServiceImpl := app2.NewAppListingServiceImpl(sugaredLogger, appListingRepositoryImpl, applicationServiceClientImpl, appRepositoryImpl, appListingViewBuilderImpl, pipelineRepositoryImpl, linkoutsRepositoryImpl, cdWorkflowRepositoryImpl, pipelineOverrideRepositoryImpl, environmentRepositoryImpl, argoUserServiceImpl, envConfigOverrideRepositoryImpl, chartRepositoryImpl, ciPipelineRepositoryImpl, dockerRegistryIpsConfigServiceImpl, userRepositoryImpl, deployedAppMetricsServiceImpl, ciArtifactRepositoryImpl) appCloneServiceImpl := appClone.NewAppCloneServiceImpl(sugaredLogger, pipelineBuilderImpl, chartServiceImpl, configMapServiceImpl, appWorkflowServiceImpl, appListingServiceImpl, propertiesConfigServiceImpl, pipelineStageServiceImpl, ciTemplateServiceImpl, appRepositoryImpl, ciPipelineRepositoryImpl, pipelineRepositoryImpl, ciPipelineConfigServiceImpl, gitOpsConfigReadServiceImpl) deploymentTemplateRepositoryImpl := repository2.NewDeploymentTemplateRepositoryImpl(db, sugaredLogger) generateManifestDeploymentTemplateServiceImpl := generateManifest.NewDeploymentTemplateServiceImpl(sugaredLogger, chartServiceImpl, appListingServiceImpl, deploymentTemplateRepositoryImpl, helmAppServiceImpl, chartTemplateServiceImpl, helmAppClientImpl, k8sServiceImpl, propertiesConfigServiceImpl, deploymentTemplateHistoryServiceImpl, environmentRepositoryImpl, appRepositoryImpl, scopedVariableManagerImpl, chartRefServiceImpl)