diff --git a/Wire.go b/Wire.go index 82fb3abad2..23170f301d 100644 --- a/Wire.go +++ b/Wire.go @@ -178,6 +178,7 @@ func InitializeApp() (*App, error) { chartProvider.AppStoreChartProviderWireSet, appStoreValues.AppStoreValuesWireSet, appStoreDeployment.AppStoreDeploymentWireSet, + appStoreDeployment.TestAppStoreDeploymentWireSet, server.ServerWireSet, module.ModuleWireSet, apiToken.ApiTokenWireSet, diff --git a/api/appStore/InstalledAppRestHandler.go b/api/appStore/InstalledAppRestHandler.go index a209d1f5b0..6dcf2b7fa0 100644 --- a/api/appStore/InstalledAppRestHandler.go +++ b/api/appStore/InstalledAppRestHandler.go @@ -31,7 +31,6 @@ import ( "time" bean2 "github.com/devtron-labs/devtron/api/bean" - openapi "github.com/devtron-labs/devtron/api/helm-app/openapiClient" "github.com/devtron-labs/devtron/api/restHandler/common" "github.com/devtron-labs/devtron/client/argocdServer/application" "github.com/devtron-labs/devtron/client/cron" @@ -260,7 +259,7 @@ func (handler InstalledAppRestHandlerImpl) GetAllInstalledApp(w http.ResponseWri return } - appIdToAppMap := make(map[string]openapi.HelmApp) + appIdToAppMap := make(map[string]appStoreBean.HelmAppDetails) //the value of this map is array of strings because the GetHelmObjectByAppNameAndEnvId method may return "//" for error cases //so different apps may contain same object, to handle that we are using (map[string] []string) @@ -316,7 +315,7 @@ func (handler InstalledAppRestHandlerImpl) GetAllInstalledApp(w http.ResponseWri } } - authorizedApps := make([]openapi.HelmApp, 0) + authorizedApps := make([]appStoreBean.HelmAppDetails, 0) for appId, _ := range authorizedAppIdSet { authorizedApp := appIdToAppMap[appId] authorizedApps = append(authorizedApps, authorizedApp) @@ -716,6 +715,10 @@ func (handler *InstalledAppRestHandlerImpl) FetchResourceTree(w http.ResponseWri common.WriteJsonResp(w, err, "App not found in database", http.StatusBadRequest) return } + if installedApp.Environment.IsVirtualEnvironment { + common.WriteJsonResp(w, nil, nil, http.StatusOK) + return + } token := r.Header.Get("token") object, object2 := handler.enforcerUtil.GetHelmObjectByAppNameAndEnvId(installedApp.App.AppName, installedApp.EnvironmentId) var ok bool @@ -746,7 +749,7 @@ func (handler *InstalledAppRestHandlerImpl) FetchResourceTree(w http.ResponseWri apiError, ok := err.(*util2.ApiError) if ok && apiError != nil { if apiError.Code == constants.AppDetailResourceTreeNotFound && installedApp.DeploymentAppDeleteRequest == true { - // TODO refactoring: should be performed in go routine + // TODO refactoring: should be performed through nats err = handler.appStoreDeploymentService.MarkGitOpsInstalledAppsDeletedIfArgoAppIsDeleted(installedAppId, envId) appDeleteErr, appDeleteErrOk := err.(*util2.ApiError) if appDeleteErrOk && appDeleteErr != nil { diff --git a/api/appStore/deployment/AppStoreDeploymentRestHandler.go b/api/appStore/deployment/AppStoreDeploymentRestHandler.go index ab7faf4943..6585310b79 100644 --- a/api/appStore/deployment/AppStoreDeploymentRestHandler.go +++ b/api/appStore/deployment/AppStoreDeploymentRestHandler.go @@ -57,6 +57,7 @@ type AppStoreDeploymentRestHandler interface { UpdateInstalledApp(w http.ResponseWriter, r *http.Request) GetInstalledAppVersion(w http.ResponseWriter, r *http.Request) UpdateProjectHelmApp(w http.ResponseWriter, r *http.Request) + CheckAndUpdatePermissions(token string, installedApp *appStoreBean.InstallAppVersionDTO, request appStoreBean.UpdateProjectHelmAppDTO) bool } type AppStoreDeploymentRestHandlerImpl struct { @@ -592,10 +593,7 @@ func (handler AppStoreDeploymentRestHandlerImpl) UpdateProjectHelmApp(w http.Res handler.Logger.Errorw("service err, InstalledAppId", "err", err, "InstalledAppId", request.InstalledAppId) common.WriteJsonResp(w, fmt.Errorf("Unable to fetch installed app details"), nil, http.StatusBadRequest) } - rbacObjectForCurrentProject, rbacObjectForCurrentProject2 := handler.enforcerUtilHelm.GetHelmObjectByClusterIdNamespaceAndAppName(installedApp.ClusterId, installedApp.Namespace, installedApp.AppName) - ok := handler.enforcer.Enforce(token, casbin.ResourceHelmApp, casbin.ActionUpdate, rbacObjectForCurrentProject) || handler.enforcer.Enforce(token, casbin.ResourceHelmApp, casbin.ActionUpdate, rbacObjectForCurrentProject2) - rbacObjectForRequestedProject := handler.enforcerUtilHelm.GetHelmObjectByTeamIdAndClusterId(request.TeamId, installedApp.ClusterId, installedApp.Namespace, installedApp.AppName) - ok = handler.enforcer.Enforce(token, casbin.ResourceHelmApp, casbin.ActionUpdate, rbacObjectForRequestedProject) + ok := handler.CheckAndUpdatePermissions(token, installedApp, request) if !ok { common.WriteJsonResp(w, fmt.Errorf("unauthorized user"), nil, http.StatusForbidden) return @@ -610,3 +608,11 @@ func (handler AppStoreDeploymentRestHandlerImpl) UpdateProjectHelmApp(w http.Res common.WriteJsonResp(w, nil, "Project Updated", http.StatusOK) } } + +func (handler AppStoreDeploymentRestHandlerImpl) CheckAndUpdatePermissions(token string, installedApp *appStoreBean.InstallAppVersionDTO, request appStoreBean.UpdateProjectHelmAppDTO) bool { + rbacObjectForCurrentProject, rbacObjectForCurrentProject2 := handler.enforcerUtilHelm.GetHelmObjectByClusterIdNamespaceAndAppName(installedApp.ClusterId, installedApp.Namespace, installedApp.AppName) + ok := handler.enforcer.Enforce(token, casbin.ResourceHelmApp, casbin.ActionUpdate, rbacObjectForCurrentProject) || handler.enforcer.Enforce(token, casbin.ResourceHelmApp, casbin.ActionUpdate, rbacObjectForCurrentProject2) + rbacObjectForRequestedProject := handler.enforcerUtilHelm.GetHelmObjectByTeamIdAndClusterId(request.TeamId, installedApp.ClusterId, installedApp.Namespace, installedApp.AppName) + ok = handler.enforcer.Enforce(token, casbin.ResourceHelmApp, casbin.ActionUpdate, rbacObjectForRequestedProject) + return ok +} diff --git a/api/appStore/deployment/wire_TestAppStoreDeploymentRestHandler.go b/api/appStore/deployment/wire_TestAppStoreDeploymentRestHandler.go new file mode 100644 index 0000000000..7f3406c638 --- /dev/null +++ b/api/appStore/deployment/wire_TestAppStoreDeploymentRestHandler.go @@ -0,0 +1,8 @@ +package appStoreDeployment + +import "github.com/google/wire" + +var TestAppStoreDeploymentWireSet = wire.NewSet( + NewAppStoreDeploymentRestHandlerImpl, + wire.Bind(new(AppStoreDeploymentRestHandler), new(*AppStoreDeploymentRestHandlerImpl)), +) diff --git a/api/appStore/deployment/wire_appStoreDeployment.go b/api/appStore/deployment/wire_appStoreDeployment.go index 7897c45032..bff7f1731c 100644 --- a/api/appStore/deployment/wire_appStoreDeployment.go +++ b/api/appStore/deployment/wire_appStoreDeployment.go @@ -19,8 +19,6 @@ var AppStoreDeploymentWireSet = wire.NewSet( wire.Bind(new(EAMode.EAModeDeploymentService), new(*EAMode.EAModeDeploymentServiceImpl)), service.NewAppStoreDeploymentServiceImpl, wire.Bind(new(service.AppStoreDeploymentService), new(*service.AppStoreDeploymentServiceImpl)), - NewAppStoreDeploymentRestHandlerImpl, - wire.Bind(new(AppStoreDeploymentRestHandler), new(*AppStoreDeploymentRestHandlerImpl)), NewAppStoreDeploymentRouterImpl, wire.Bind(new(AppStoreDeploymentRouter), new(*AppStoreDeploymentRouterImpl)), repository.NewInstalledAppVersionHistoryRepositoryImpl, diff --git a/api/bean/AppView.go b/api/bean/AppView.go index 85054ab6b7..27a1b60317 100644 --- a/api/bean/AppView.go +++ b/api/bean/AppView.go @@ -44,13 +44,14 @@ type JobContainerResponse struct { } type DeploymentGroupDTO struct { - Id int `json:"id"` - Name string `json:"name"` - AppCount int `json:"appCount"` - NoOfApps string `json:"noOfApps"` - EnvironmentId int `json:"environmentId"` - CiPipelineId int `json:"ciPipelineId"` - CiMaterialDTOs []CiMaterialDTO `json:"ciMaterialDTOs"` + Id int `json:"id"` + Name string `json:"name"` + AppCount int `json:"appCount"` + NoOfApps string `json:"noOfApps"` + EnvironmentId int `json:"environmentId"` + CiPipelineId int `json:"ciPipelineId"` + CiMaterialDTOs []CiMaterialDTO `json:"ciMaterialDTOs"` + IsVirtualEnvironment bool `json:"isVirtualEnvironment"` } type CiMaterialDTO struct { @@ -136,6 +137,7 @@ type AppEnvironmentContainer struct { TeamName string `json:"teamName"` Description string `json:"description" validate:"max=40"` TotalCount int `json:"-"` + IsVirtualEnvironment bool `json:"isVirtualEnvironment"` } type DeploymentDetailContainer struct { @@ -173,7 +175,11 @@ type DeploymentDetailContainer struct { IpsAccessProvided bool `json:"ipsAccessProvided"` DeploymentAppDeleteRequest bool `json:"deploymentAppDeleteRequest"` Description string `json:"description" validate:"max=40"` + UserApprovalConfig string `json:"userApprovalConfig"` IsVirtualEnvironment bool `json:"isVirtualEnvironment"` + Image string `json:"image"` + ImageTag string `json:"imageTag"` + HelmPackageName string `json:"helmPackageName"` HelmReleaseInstallStatus string `json:"-"` } @@ -194,6 +200,26 @@ type Notes struct { Notes string `json:"gitOpsNotes,omitempty"` } +type EnvironmentForDependency struct { + AppStatus string `json:"appStatus"` //this is not the status of environment , this make sense with a specific app only + AppName string `json:"appName"` + AppId int `json:"appId"` + EnvironmentId int `json:"environmentId"` + EnvironmentName string `json:"environmentName"` + 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"` + PipelineName string `json:"pipelineName"` + LatestCdWorkflowRunnerId int `json:"latestCdWorkflowRunnerId,omitempty"` +} + 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"` diff --git a/cmd/external-app/wire.go b/cmd/external-app/wire.go index 2e10e85fae..c28faf1e3c 100644 --- a/cmd/external-app/wire.go +++ b/cmd/external-app/wire.go @@ -83,6 +83,7 @@ func InitializeApp() (*App, error) { chartProvider.AppStoreChartProviderWireSet, appStoreValues.AppStoreValuesWireSet, appStoreDeployment.AppStoreDeploymentWireSet, + appStoreDeployment.TestAppStoreDeploymentWireSet, server.ServerWireSet, module.ModuleWireSet, apiToken.ApiTokenWireSet, diff --git a/pkg/appStore/bean/bean.go b/pkg/appStore/bean/bean.go index 86456bcfb9..c8d2839fa1 100644 --- a/pkg/appStore/bean/bean.go +++ b/pkg/appStore/bean/bean.go @@ -19,6 +19,7 @@ package appStoreBean import ( "encoding/json" + openapi "github.com/devtron-labs/devtron/api/helm-app/openapiClient" repository2 "github.com/devtron-labs/devtron/pkg/cluster/repository" "time" ) @@ -99,6 +100,9 @@ type InstallAppVersionDTO struct { DeploymentAppType string `json:"deploymentAppType"` // TODO: instead of string, use enum AcdPartialDelete bool `json:"acdPartialDelete"` InstalledAppDeleteResponse *InstalledAppDeleteResponseDTO `json:"deleteResponse,omitempty"` + UpdatedOn time.Time `json:"updatedOn"` + IsVirtualEnvironment bool `json:"isVirtualEnvironment"` + HelmPackageName string `json:"helmPackageName"` AppStoreApplicationVersionId int } @@ -368,9 +372,61 @@ type ChartComponent struct { } const ( + DEFAULT_CLUSTER_ID = 1 + DEFAULT_NAMESPACE = "default" DEFAULT_ENVIRONMENT_OR_NAMESPACE_OR_PROJECT = "devtron" CLUSTER_COMPONENT_DIR_PATH = "/cluster/component" HELM_RELEASE_STATUS_FAILED = "Failed" HELM_RELEASE_STATUS_PROGRESSING = "Progressing" HELM_RELEASE_STATUS_UNKNOWN = "Unknown" ) + +type EnvironmentDetails struct { + EnvironmentName *string `json:"environmentName,omitempty"` + // id in which app is deployed + EnvironmentId *int32 `json:"environmentId,omitempty"` + // namespace corresponding to the environemnt + Namespace *string `json:"namespace,omitempty"` + // if given environemnt is marked as production or not, nullable + IsPrduction *bool `json:"isPrduction,omitempty"` + // cluster corresponding to the environemt where application is deployed + ClusterName *string `json:"clusterName,omitempty"` + // clusterId corresponding to the environemt where application is deployed + ClusterId *int32 `json:"clusterId,omitempty"` + + IsVirtualEnvironment *bool `json:"isVirtualEnvironment"` +} + +type HelmAppDetails struct { + // time when this application was last deployed/updated + LastDeployedAt *time.Time `json:"lastDeployedAt,omitempty"` + // name of the helm application/helm release name + AppName *string `json:"appName,omitempty"` + // unique identifier for app + AppId *string `json:"appId,omitempty"` + // name of the chart + ChartName *string `json:"chartName,omitempty"` + // url/location of the chart icon + ChartAvatar *string `json:"chartAvatar,omitempty"` + // unique identifier for the project, APP with no project will have id `0` + ProjectId *int32 `json:"projectId,omitempty"` + // chart version + ChartVersion *string `json:"chartVersion,omitempty"` + EnvironmentDetail *EnvironmentDetails `json:"environmentDetail,omitempty"` + AppStatus *string `json:"appStatus,omitempty"` +} + +type AppListDetail struct { + // clusters to which result corresponds + ClusterIds *[]int32 `json:"clusterIds,omitempty"` + // application type inside the array + ApplicationType *string `json:"applicationType,omitempty"` + // if data fetch for that cluster produced error + Errored *bool `json:"errored,omitempty"` + // error msg if client failed to fetch + ErrorMsg *string `json:"errorMsg,omitempty"` + // all helm app list, EA+ devtronapp + HelmApps *[]HelmAppDetails `json:"helmApps,omitempty"` + // all helm app list, EA+ devtronapp + DevtronApps *[]openapi.DevtronApp `json:"devtronApps,omitempty"` +} diff --git a/pkg/appStore/installedApp/repository/InstalledAppModels.go b/pkg/appStore/installedApp/repository/InstalledAppModels.go index 56ea99d0d7..320f0fbe48 100644 --- a/pkg/appStore/installedApp/repository/InstalledAppModels.go +++ b/pkg/appStore/installedApp/repository/InstalledAppModels.go @@ -37,6 +37,7 @@ type InstalledAppsWithChartDetails struct { AppOfferingMode string `json:"app_offering_mode"` AppStatus string `json:"app_status"` DeploymentAppDeleteRequest bool `json:"deploymentAppDeleteRequest"` + IsVirtualEnvironment bool `json:"is_virtual_environment"` } // InstalledAppAndEnvDetails is used to operate on native query; This should be avoided. diff --git a/pkg/appStore/installedApp/service/EAMode/InstalledAppDBService.go b/pkg/appStore/installedApp/service/EAMode/InstalledAppDBService.go index 8f61cdcc7f..d474af3151 100644 --- a/pkg/appStore/installedApp/service/EAMode/InstalledAppDBService.go +++ b/pkg/appStore/installedApp/service/EAMode/InstalledAppDBService.go @@ -18,17 +18,13 @@ package EAMode import ( - "github.com/devtron-labs/devtron/pkg/appStore/adapter" - "strconv" - "strings" - "time" - + "fmt" "github.com/Pallinder/go-randomdata" bean2 "github.com/devtron-labs/devtron/api/bean" - openapi "github.com/devtron-labs/devtron/api/helm-app/openapiClient" "github.com/devtron-labs/devtron/internal/middleware" "github.com/devtron-labs/devtron/internal/sql/repository/app" "github.com/devtron-labs/devtron/internal/util" + "github.com/devtron-labs/devtron/pkg/appStore/adapter" appStoreBean "github.com/devtron-labs/devtron/pkg/appStore/bean" repository2 "github.com/devtron-labs/devtron/pkg/appStore/installedApp/repository" "github.com/devtron-labs/devtron/pkg/auth/user" @@ -36,10 +32,13 @@ import ( util3 "github.com/devtron-labs/devtron/util" "github.com/go-pg/pg" "go.uber.org/zap" + "strconv" + "strings" + "time" ) type InstalledAppDBService interface { - GetAll(filter *appStoreBean.AppStoreFilter) (openapi.AppList, error) + GetAll(filter *appStoreBean.AppStoreFilter) (appStoreBean.AppListDetail, error) CheckAppExists(appNames []*appStoreBean.AppNames) ([]*appStoreBean.AppNames, error) FindAppDetailsForAppstoreApplication(installedAppId, envId int) (bean2.AppDetailContainer, error) CheckAppExistsByInstalledAppId(installedAppId int) (*repository2.InstalledApps, error) @@ -69,13 +68,13 @@ func NewInstalledAppDBServiceImpl(logger *zap.SugaredLogger, } } -func (impl *InstalledAppDBServiceImpl) GetAll(filter *appStoreBean.AppStoreFilter) (openapi.AppList, error) { +func (impl *InstalledAppDBServiceImpl) GetAll(filter *appStoreBean.AppStoreFilter) (appStoreBean.AppListDetail, error) { applicationType := "DEVTRON-CHART-STORE" var clusterIdsConverted []int32 for _, clusterId := range filter.ClusterIds { clusterIdsConverted = append(clusterIdsConverted, int32(clusterId)) } - installedAppsResponse := openapi.AppList{ + installedAppsResponse := appStoreBean.AppListDetail{ ApplicationType: &applicationType, ClusterIds: &clusterIdsConverted, } @@ -86,7 +85,7 @@ func (impl *InstalledAppDBServiceImpl) GetAll(filter *appStoreBean.AppStoreFilte impl.Logger.Error(err) return installedAppsResponse, err } - var helmAppsResponse []openapi.HelmApp + var helmAppsResponse []appStoreBean.HelmAppDetails for _, a := range installedApps { appLocal := a // copied data from here because value is passed as reference if appLocal.TeamId == 0 && appLocal.AppOfferingMode != util3.SERVER_MODE_HYPERION { @@ -97,14 +96,15 @@ func (impl *InstalledAppDBServiceImpl) GetAll(filter *appStoreBean.AppStoreFilte projectId := int32(appLocal.TeamId) envId := int32(appLocal.EnvironmentId) clusterId := int32(appLocal.ClusterId) - environmentDetails := openapi.AppEnvironmentDetail{ - EnvironmentName: &appLocal.EnvironmentName, - EnvironmentId: &envId, - Namespace: &appLocal.Namespace, - ClusterName: &appLocal.ClusterName, - ClusterId: &clusterId, + environmentDetails := appStoreBean.EnvironmentDetails{ + EnvironmentName: &appLocal.EnvironmentName, + EnvironmentId: &envId, + Namespace: &appLocal.Namespace, + ClusterName: &appLocal.ClusterName, + ClusterId: &clusterId, + IsVirtualEnvironment: &appLocal.IsVirtualEnvironment, } - helmAppResp := openapi.HelmApp{ + helmAppResp := appStoreBean.HelmAppDetails{ AppName: &appLocal.AppName, ChartName: &appLocal.AppStoreApplicationName, AppId: &appId, @@ -163,6 +163,9 @@ func (impl *InstalledAppDBServiceImpl) FindAppDetailsForAppstoreApplication(inst } else { chartName = installedAppVerison.AppStoreApplicationVersion.AppStore.DockerArtifactStore.Id } + updateTime := installedAppVerison.InstalledApp.UpdatedOn + timeStampTag := updateTime.Format(bean.LayoutDDMMYY_HHMM12hr) + deploymentContainer := bean2.DeploymentDetailContainer{ InstalledAppId: installedAppVerison.InstalledApp.Id, AppId: installedAppVerison.InstalledApp.App.Id, @@ -181,6 +184,7 @@ func (impl *InstalledAppDBServiceImpl) FindAppDetailsForAppstoreApplication(inst DeploymentAppType: installedAppVerison.InstalledApp.DeploymentAppType, DeploymentAppDeleteRequest: installedAppVerison.InstalledApp.DeploymentAppDeleteRequest, IsVirtualEnvironment: installedAppVerison.InstalledApp.Environment.IsVirtualEnvironment, + HelmPackageName: fmt.Sprintf("%s-%s-%s (GMT)", installedAppVerison.InstalledApp.App.AppName, installedAppVerison.InstalledApp.Environment.Name, timeStampTag), HelmReleaseInstallStatus: helmReleaseInstallStatus, Status: status, } diff --git a/pkg/bean/app.go b/pkg/bean/app.go index 8ae77b8afd..ff98c9d32d 100644 --- a/pkg/bean/app.go +++ b/pkg/bean/app.go @@ -32,9 +32,10 @@ import ( ) const ( - LayoutISO = "2006-01-02 15:04:05" - LayoutUS = "January 2, 2006 15:04:05" - LayoutRFC3339 = "2006-01-02T15:04:05Z07:00" + LayoutISO = "2006-01-02 15:04:05" + LayoutUS = "January 2, 2006 15:04:05" + LayoutRFC3339 = "2006-01-02T15:04:05Z07:00" + LayoutDDMMYY_HHMM12hr = "2 January,2006 15.04PM" ) type SourceTypeConfig struct { diff --git a/wire_gen.go b/wire_gen.go index b1eb9adffa..de03358235 100644 --- a/wire_gen.go +++ b/wire_gen.go @@ -765,7 +765,7 @@ func InitializeApp() (*App, error) { deployedConfigurationHistoryServiceImpl := history.NewDeployedConfigurationHistoryServiceImpl(sugaredLogger, userServiceImpl, deploymentTemplateHistoryServiceImpl, pipelineStrategyHistoryServiceImpl, configMapHistoryServiceImpl, cdWorkflowRepositoryImpl) pipelineHistoryRestHandlerImpl := history2.NewPipelineHistoryRestHandlerImpl(sugaredLogger, userServiceImpl, enforcerImpl, pipelineStrategyHistoryServiceImpl, deploymentTemplateHistoryServiceImpl, configMapHistoryServiceImpl, prePostCiScriptHistoryServiceImpl, prePostCdScriptHistoryServiceImpl, enforcerUtilImpl, deployedConfigurationHistoryServiceImpl) pipelineHistoryRouterImpl := history3.NewPipelineHistoryRouterImpl(pipelineHistoryRestHandlerImpl) - pipelineStatusTimelineRestHandlerImpl := status2.NewPipelineStatusTimelineRestHandlerImpl(sugaredLogger, pipelineStatusTimelineServiceImpl, enforcerUtilImpl, enforcerImpl) + pipelineStatusTimelineRestHandlerImpl := status2.NewPipelineStatusTimelineRestHandlerImpl(sugaredLogger, userServiceImpl, pipelineStatusTimelineServiceImpl, enforcerUtilImpl, enforcerImpl, cdApplicationStatusUpdateHandlerImpl, pipelineBuilderImpl) pipelineStatusRouterImpl := status3.NewPipelineStatusRouterImpl(pipelineStatusTimelineRestHandlerImpl) appWorkflowRestHandlerImpl := workflow.NewAppWorkflowRestHandlerImpl(sugaredLogger, userServiceImpl, appWorkflowServiceImpl, teamServiceImpl, enforcerImpl, pipelineBuilderImpl, appRepositoryImpl, enforcerUtilImpl) appWorkflowRouterImpl := workflow2.NewAppWorkflowRouterImpl(appWorkflowRestHandlerImpl)