Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: handling side-effects for displaying external helm apps with same name across diff namespaces and clusters #4951

Merged
merged 50 commits into from
May 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
0d1e135
store display name, along with unique appName(displayName-ns-clusterI…
prakash100198 Apr 16, 2024
01a346e
Merge branch 'main' into ext-app-fixes
prakash100198 Apr 19, 2024
723332f
comments
prakash100198 Apr 19, 2024
7c09365
appName and display name handling at other places as well for externa…
prakash100198 Apr 19, 2024
33bf9ee
Merge branch 'main' into ext-app-fixes
prakash100198 Apr 19, 2024
1e016f5
handling display name in GetHelmAppMetaInfo in AppCrudOperationServic…
prakash100198 Apr 21, 2024
f69202f
FetchAppDetailsForInstalledAppV2 send display name if present
prakash100198 Apr 21, 2024
e5212e1
DeleteDBLinkedHelmApplication deletion handling for ext app with uniq…
prakash100198 Apr 22, 2024
ed1248f
Merge branch 'main' into ext-app-fixes
prakash100198 Apr 22, 2024
b535b32
applist method incorporated checks on display name if exists instead …
prakash100198 Apr 22, 2024
bfc4031
query change in GetAppAndEnvDetailsForDeploymentAppTypeInstalledApps …
prakash100198 Apr 22, 2024
5c299a5
GenerateInstallAppVersionMinDTO if display name exists then send appn…
prakash100198 Apr 22, 2024
d5cadda
FindAppDetailsForAppstoreApplication, considering display name as wel…
prakash100198 Apr 22, 2024
3128fc8
Merge branch 'main' into ext-app-fixes
prakash100198 Apr 23, 2024
c37ed41
multiple fixes around corner cases around display names
prakash100198 Apr 23, 2024
47ede99
Merge branch 'main' into ext-app-fixes
prakash100198 Apr 23, 2024
bc9e81a
minor fix
prakash100198 Apr 23, 2024
df58a7b
fixes for fetching resoirce trees for ext apps using display name ins…
prakash100198 Apr 23, 2024
7abc37a
bug fixes
prakash100198 Apr 24, 2024
9795efc
linkHelmApplicationToChartStore, use displayName instead of appName f…
prakash100198 Apr 24, 2024
2e492c0
LinkHelmApplicationToChartStore: server mode full from hyperion when …
prakash100198 Apr 24, 2024
ed3ed9a
revert:- LinkHelmApplicationToChartStore: server mode full from hyper…
prakash100198 Apr 24, 2024
4d15cd8
on update proj, update app offering mode to full and, getAppNameForIn…
prakash100198 Apr 24, 2024
238b51e
handled corner cases in DeleteDBLinkedHelmApplication
prakash100198 Apr 24, 2024
26f429e
Merge branch 'main' into ext-app-fixes
prakash100198 Apr 24, 2024
f870ab6
code refactoring
prakash100198 Apr 24, 2024
7beee96
remove unwanted error check
prakash100198 Apr 24, 2024
d6cf6e3
Merge branch 'main' into ext-app-fixes
prakash100198 Apr 24, 2024
df5d335
removed logic to block update and deploy when not linked to devtron
prakash100198 Apr 25, 2024
031530d
Merge branch 'main' into ext-app-fixes
prakash100198 Apr 25, 2024
290452c
code review incorporations and code refactoring
prakash100198 Apr 25, 2024
99e89a3
handling display name in migrate app type and trigger for migrated ap…
prakash100198 Apr 25, 2024
e28f4fb
minor fix
prakash100198 May 1, 2024
3a6018e
Merge branch 'main' into ext-app-fixes
prakash100198 May 1, 2024
f6ac093
minor fix
prakash100198 May 1, 2024
22bacd8
Merge branch 'main' into ext-app-fixes
prakash100198 May 2, 2024
632f3da
code review incorporation and refactoring
prakash100198 May 2, 2024
8fbc78c
Merge branch 'main' into ext-app-fixes
prakash100198 May 2, 2024
bbd9fde
wire fix
prakash100198 May 2, 2024
063ba80
Merge branch 'main' into ext-app-fixes
prakash100198 May 7, 2024
c10aa81
fixes for multiple installed apps with same app pointer linked to cha…
prakash100198 May 16, 2024
676d1d4
Merge branch 'main' into ext-app-fixes
prakash100198 May 16, 2024
973c05d
remove unused code
prakash100198 May 16, 2024
cabcdc4
Merge branch 'main' into ext-app-fixes
prakash100198 May 19, 2024
4aa2feb
fix for showing unassigned proj to ext app that's already linked to d…
prakash100198 May 19, 2024
947a29d
fix
prakash100198 May 19, 2024
e8c0f8d
fix
prakash100198 May 19, 2024
be13836
revert
prakash100198 May 20, 2024
d7cf678
fix
prakash100198 May 20, 2024
e05b330
Merge branch 'main' into ext-app-fixes
prakash100198 May 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions api/appStore/InstalledAppRestHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/devtron-labs/devtron/pkg/appStore/installedApp/service/FullMode"
"github.com/devtron-labs/devtron/pkg/appStore/installedApp/service/FullMode/deploymentTypeChange"
"github.com/devtron-labs/devtron/pkg/appStore/installedApp/service/FullMode/resource"
util3 "github.com/devtron-labs/devtron/pkg/appStore/util"
"github.com/devtron-labs/devtron/pkg/bean"
"net/http"
"strconv"
Expand Down Expand Up @@ -603,6 +604,10 @@ func (handler *InstalledAppRestHandlerImpl) FetchAppDetailsForInstalledApp(w htt
common.WriteJsonResp(w, err, "App not found in database", http.StatusBadRequest)
return
}
if util3.IsExternalChartStoreApp(installedApp.App.DisplayName) {
//this is external app case where app_name is a unique identifier, and we want to fetch resource based on display_name
handler.installedAppService.ChangeAppNameToDisplayNameForInstalledApp(installedApp)
}

appDetail, err := handler.installedAppService.FindAppDetailsForAppstoreApplication(installedAppId, envId)
if err != nil {
Expand Down Expand Up @@ -724,6 +729,10 @@ func (handler *InstalledAppRestHandlerImpl) FetchResourceTree(w http.ResponseWri
common.WriteJsonResp(w, err, "App not found in database", http.StatusBadRequest)
return
}
if util3.IsExternalChartStoreApp(installedApp.App.DisplayName) {
//this is external app case where app_name is a unique identifier, and we want to fetch resource based on display_name
handler.installedAppService.ChangeAppNameToDisplayNameForInstalledApp(installedApp)
}
if installedApp.Environment.IsVirtualEnvironment {
common.WriteJsonResp(w, nil, nil, http.StatusOK)
return
Expand Down
3 changes: 2 additions & 1 deletion api/appStore/deployment/CommonDeploymentRestHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ func (handler *CommonDeploymentRestHandlerImpl) getAppOfferingMode(installedAppI
err = &util.ApiError{HttpStatusCode: http.StatusBadRequest, UserMessage: "invalid app id"}
return appOfferingMode, installedAppDto, err
}
installedAppDto, err = handler.installedAppService.GetInstalledAppByClusterNamespaceAndName(appIdentifier.ClusterId, appIdentifier.Namespace, appIdentifier.ReleaseName)
uniqueAppName := appIdentifier.GetUniqueAppNameIdentifier()
installedAppDto, err = handler.installedAppService.GetInstalledAppByClusterNamespaceAndName(appIdentifier.ClusterId, appIdentifier.Namespace, uniqueAppName)
if err != nil {
err = &util.ApiError{HttpStatusCode: http.StatusBadRequest, UserMessage: "unable to find app in database"}
return appOfferingMode, installedAppDto, err
Expand Down
4 changes: 2 additions & 2 deletions api/helm-app/HelmAppRestHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,15 +224,15 @@ func (handler *HelmAppRestHandlerImpl) GetReleaseInfo(w http.ResponseWriter, r *
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
return
}
installedApp, err := handler.installedAppService.GetInstalledAppByClusterNamespaceAndName(appIdentifier.ClusterId, appIdentifier.Namespace, appIdentifier.ReleaseName)
installedAppVersionDto, err := handler.installedAppService.GetReleaseInfo(appIdentifier)
if err != nil {
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
return
}

res := &bean.ReleaseAndInstalledAppInfo{
ReleaseInfo: releaseInfo,
InstalledAppInfo: bean.ConvertToInstalledAppInfo(installedApp),
InstalledAppInfo: bean.ConvertToInstalledAppInfo(installedAppVersionDto),
}

common.WriteJsonResp(w, err, res, http.StatusOK)
Expand Down
1 change: 1 addition & 0 deletions api/helm-app/HelmAppRouter.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ func (impl *HelmAppRouterImpl) InitAppListRouter(helmRouter *mux.Router) {
helmRouter.Path("/hibernate").HandlerFunc(impl.helmAppRestHandler.Hibernate).Methods("POST")
helmRouter.Path("/unhibernate").HandlerFunc(impl.helmAppRestHandler.UnHibernate).Methods("POST")

// GetReleaseInfo used only for external apps
helmRouter.Path("/release-info").Queries("appId", "{appId}").
HandlerFunc(impl.helmAppRestHandler.GetReleaseInfo).Methods("GET")

Expand Down
214 changes: 146 additions & 68 deletions api/helm-app/service/HelmAppService.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (
"net/http"
"reflect"
"strconv"
"strings"
"time"

"github.com/caarlos0/env/v6"
Expand Down Expand Up @@ -56,7 +55,7 @@ type HelmAppService interface {
GetValuesYaml(ctx context.Context, app *AppIdentifier) (*gRPC.ReleaseInfo, error)
GetDesiredManifest(ctx context.Context, app *AppIdentifier, resource *openapi.ResourceIdentifier) (*openapi.DesiredManifestResponse, error)
DeleteApplication(ctx context.Context, app *AppIdentifier) (*openapi.UninstallReleaseResponse, error)
DeleteDBLinkedHelmApplication(ctx context.Context, app *AppIdentifier, useId int32) (*openapi.UninstallReleaseResponse, error)
DeleteDBLinkedHelmApplication(ctx context.Context, appIdentifier *AppIdentifier, useId int32) (*openapi.UninstallReleaseResponse, error)
// UpdateApplication is a wrapper over helmAppClient.UpdateApplication, sends update request to kubelink for external chart store apps
UpdateApplication(ctx context.Context, app *AppIdentifier, request *bean.UpdateApplicationRequestDto) (*openapi.UpdateReleaseResponse, error)
GetDeploymentDetail(ctx context.Context, app *AppIdentifier, version int32) (*openapi.HelmAppDeploymentManifestDetail, error)
Expand Down Expand Up @@ -448,7 +447,56 @@ func (impl *HelmAppServiceImpl) GetDesiredManifest(ctx context.Context, app *App
return response, nil
}

func (impl *HelmAppServiceImpl) DeleteDBLinkedHelmApplication(ctx context.Context, app *AppIdentifier, userId int32) (*openapi.UninstallReleaseResponse, error) {
// getInstalledAppForAppIdentifier return installed_apps for app unique identifier or releaseName/displayName whichever exists else return pg.ErrNoRows
func (impl *HelmAppServiceImpl) getInstalledAppForAppIdentifier(appIdentifier *AppIdentifier) (*repository.InstalledApps, error) {
model := &repository.InstalledApps{}
var err error
//for ext apps search app from unique identifier
appUniqueIdentifier := appIdentifier.GetUniqueAppNameIdentifier()
model, err = impl.installedAppRepository.GetInstalledAppByAppName(appUniqueIdentifier)
if err != nil {
if util.IsErrNoRows(err) {
//if error is pg no rows, then find installed app via app.DisplayName because this can also happen that
//an ext-app is already linked to devtron, and it's entry in app_name col in app table will not be a unique
//identifier but the display name.
displayName := appIdentifier.ReleaseName
model, err = impl.installedAppRepository.GetInstalledAppByAppName(displayName)
if err != nil {
impl.logger.Errorw("error in fetching installed app from display name", "appDisplayName", displayName, "err", err)
return model, err
}
} else {
impl.logger.Errorw("error in fetching installed app by app unique identifier", "appUniqueIdentifier", appUniqueIdentifier, "err", err)
return model, err
}
}
return model, nil
}

func (impl *HelmAppServiceImpl) getAppForAppIdentifier(appIdentifier *AppIdentifier) (*app.App, error) {
//for ext apps search app from unique identifier
appUniqueIdentifier := appIdentifier.GetUniqueAppNameIdentifier()
model, err := impl.appRepository.FindActiveByName(appUniqueIdentifier)
if err != nil {
if util.IsErrNoRows(err) {
//if error is pg no rows, then find app via release name because this can also happen that a project is
//already assigned a project, and it's entry in app_name col in app table will not be a unique
//identifier but the display name i.e. release name.
displayName := appIdentifier.ReleaseName
model, err = impl.appRepository.FindActiveByName(displayName)
if err != nil {
impl.logger.Errorw("error in fetching app from display name", "appDisplayName", displayName, "err", err)
return nil, err
}
} else {
impl.logger.Errorw("error in fetching app by app unique identifier", "appUniqueIdentifier", appUniqueIdentifier, "err", err)
return nil, err
}
}
return model, nil
}

func (impl *HelmAppServiceImpl) DeleteDBLinkedHelmApplication(ctx context.Context, appIdentifier *AppIdentifier, userId int32) (*openapi.UninstallReleaseResponse, error) {
dbConnection := impl.appRepository.GetConnection()
tx, err := dbConnection.Begin()
if err != nil {
Expand All @@ -457,67 +505,94 @@ func (impl *HelmAppServiceImpl) DeleteDBLinkedHelmApplication(ctx context.Contex
}
// Rollback tx on error.
defer tx.Rollback()
var isAppLinkedToChartStore bool // if true, entry present in both app and installed_app table

// For Helm deployments ReleaseName is App.Name
model, err := impl.installedAppRepository.GetInstalledAppByAppName(app.ReleaseName)
if err != nil {
impl.logger.Errorw("error in fetching installed app", "appName", app.ReleaseName, "err", err)
installedAppModel, err := impl.getInstalledAppForAppIdentifier(appIdentifier)
if err != nil && !util.IsErrNoRows(err) {
impl.logger.Errorw("DeleteDBLinkedHelmApplication, error in fetching installed app for app identifier", "appIdentifier", appIdentifier, "err", err)
return nil, err
}

// If there are two releases with same name but in different namespace (eg: test -n demo-1 {Hyperion App}, test -n demo-2 {Externally Installed});
// And if the request is received for the externally installed app, the below condition will handle
if model.Environment.Namespace != app.Namespace {
return nil, pg.ErrNoRows
if installedAppModel.Id > 0 {
isAppLinkedToChartStore = true
}

// App Delete --> Start
//soft delete app
appModel := &model.App
appModel.Active = false
appModel.UpdatedBy = userId
appModel.UpdatedOn = time.Now()
err = impl.appRepository.UpdateWithTxn(appModel, tx)
if err != nil {
impl.logger.Errorw("error in deleting appModel", "app", appModel)
return nil, err
}
// App Delete --> End
if isAppLinkedToChartStore {
// If there are two releases with same name but in different namespace (eg: test -n demo-1 {Hyperion App}, test -n demo-2 {Externally Installed});
// And if the request is received for the externally installed app, the below condition will handle
if installedAppModel.Environment.Namespace != appIdentifier.Namespace {
return nil, pg.ErrNoRows
}

// InstalledApp Delete --> Start
// soft delete install app
model.Active = false
model.UpdatedBy = userId
model.UpdatedOn = time.Now()
_, err = impl.installedAppRepository.UpdateInstalledApp(model, tx)
if err != nil {
impl.logger.Errorw("error while deleting install app", "error", err)
return nil, err
}
// InstalledApp Delete --> End
// App Delete --> Start
//soft delete app
appModel := &installedAppModel.App
appModel.Active = false
appModel.UpdatedBy = userId
appModel.UpdatedOn = time.Now()
err = impl.appRepository.UpdateWithTxn(appModel, tx)
if err != nil {
impl.logger.Errorw("error in deleting appModel", "app", appModel)
return nil, err
}
// App Delete --> End

// InstalledApp Delete --> Start
// soft delete install app
installedAppModel.Active = false
installedAppModel.UpdatedBy = userId
installedAppModel.UpdatedOn = time.Now()
_, err = impl.installedAppRepository.UpdateInstalledApp(installedAppModel, tx)
if err != nil {
impl.logger.Errorw("error while deleting install app", "error", err)
return nil, err
}
// InstalledApp Delete --> End

// InstalledAppVersions Delete --> Start
models, err := impl.installedAppRepository.GetInstalledAppVersionByInstalledAppId(model.Id)
if err != nil {
impl.logger.Errorw("error while fetching install app versions", "error", err)
return nil, err
}
// InstalledAppVersions Delete --> Start
models, err := impl.installedAppRepository.GetInstalledAppVersionByInstalledAppId(installedAppModel.Id)
if err != nil {
impl.logger.Errorw("error while fetching install app versions", "error", err)
return nil, err
}

// soft delete install app versions
for _, item := range models {
item.Active = false
item.UpdatedBy = userId
item.UpdatedOn = time.Now()
_, err = impl.installedAppRepository.UpdateInstalledAppVersion(item, tx)
// soft delete install app versions
for _, item := range models {
item.Active = false
item.UpdatedBy = userId
item.UpdatedOn = time.Now()
_, err = impl.installedAppRepository.UpdateInstalledAppVersion(item, tx)
if err != nil {
impl.logger.Errorw("error while fetching from db", "error", err)
return nil, err
}
}
// InstalledAppVersions Delete --> End
} else {
//this means app not found in installed_apps, but a scenario where an external app is only
//assigned project and not linked to devtron, in that case only entry in app will be found.
appModel, err := impl.getAppForAppIdentifier(appIdentifier)
if err != nil {
impl.logger.Errorw("error while fetching from db", "error", err)
impl.logger.Errorw("DeleteDBLinkedHelmApplication, error in fetching app from appIdentifier", "appIdentifier", appIdentifier, "err", err)
return nil, err
}
if appModel != nil && appModel.Id > 0 {
// App Delete --> Start
//soft delete app
appModel.Active = false
appModel.UpdatedBy = userId
appModel.UpdatedOn = time.Now()
err = impl.appRepository.UpdateWithTxn(appModel, tx)
if err != nil {
impl.logger.Errorw("error in deleting appModel", "app", appModel)
return nil, err
}
// App Delete --> End
}
}
// InstalledAppVersions Delete --> End
res, err := impl.DeleteApplication(ctx, app)

res, err := impl.DeleteApplication(ctx, appIdentifier)
if err != nil {
impl.logger.Errorw("error in deleting helm application", "error", err, "appIdentifier", app)
impl.logger.Errorw("error in deleting helm application", "error", err, "appIdentifier", appIdentifier)
return nil, err
}

Expand Down Expand Up @@ -1005,29 +1080,32 @@ type AppIdentifier struct {
ReleaseName string `json:"releaseName"`
}

// GetUniqueAppNameIdentifier returns unique app name identifier, we store all helm releases in kubelink cache with key
// as what is returned from this func, this is the case where an app across diff namespace or cluster can have same name,
// so to identify then uniquely below implementation would serve as good unique identifier for an external app.
func (r *AppIdentifier) GetUniqueAppNameIdentifier() string {
return fmt.Sprintf("%s-%s-%s", r.ReleaseName, r.Namespace, strconv.Itoa(r.ClusterId))
}

func (r *AppIdentifier) GetUniqueAppIdentifierForGivenNamespaceAndCluster(namespace, clusterId string) string {
return fmt.Sprintf("%s-%s-%s", r.ReleaseName, namespace, clusterId)
}

func (impl *HelmAppServiceImpl) DecodeAppId(appId string) (*AppIdentifier, error) {
component := strings.Split(appId, "|")
if len(component) != 3 {
return nil, fmt.Errorf("malformed app id %s", appId)
}
clusterId, err := strconv.Atoi(component[0])
if err != nil {
return nil, err
}
if clusterId <= 0 {
return nil, fmt.Errorf("target cluster is not provided")
}
return &AppIdentifier{
ClusterId: clusterId,
Namespace: component[1],
ReleaseName: component[2],
}, nil
return DecodeExternalAppAppId(appId)
}

func (impl *HelmAppServiceImpl) EncodeAppId(appIdentifier *AppIdentifier) string {
return fmt.Sprintf("%d|%s|%s", appIdentifier.ClusterId, appIdentifier.Namespace, appIdentifier.ReleaseName)
}

func isSameAppName(deployedAppName string, appDto app.App) bool {
if len(appDto.DisplayName) > 0 {
return deployedAppName == appDto.DisplayName
}
return deployedAppName == appDto.AppName
}

func (impl *HelmAppServiceImpl) appListRespProtoTransformer(deployedApps *gRPC.DeployedAppList, token string, helmAuth func(token string, object string) bool, helmCdPipelines []*pipelineConfig.Pipeline, installedHelmApps []*repository.InstalledApps) openapi.AppList {
applicationType := "HELM-APP"
appList := openapi.AppList{ClusterIds: &[]int32{deployedApps.ClusterId}, ApplicationType: &applicationType}
Expand Down Expand Up @@ -1055,7 +1133,7 @@ func (impl *HelmAppServiceImpl) appListRespProtoTransformer(deployedApps *gRPC.D

// do not add helm apps in the list which are created using app_store
for _, installedHelmApp := range installedHelmApps {
if deployedapp.AppName == installedHelmApp.App.AppName && int(deployedapp.EnvironmentDetail.ClusterId) == installedHelmApp.Environment.ClusterId && deployedapp.EnvironmentDetail.Namespace == installedHelmApp.Environment.Namespace {
if isSameAppName(deployedapp.AppName, installedHelmApp.App) && int(deployedapp.EnvironmentDetail.ClusterId) == installedHelmApp.Environment.ClusterId && deployedapp.EnvironmentDetail.Namespace == installedHelmApp.Environment.Namespace {
toExcludeFromList = true
break
}
Expand Down
26 changes: 26 additions & 0 deletions api/helm-app/service/helper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package service

import (
"fmt"
"strconv"
"strings"
)

func DecodeExternalAppAppId(appId string) (*AppIdentifier, error) {
component := strings.Split(appId, "|")
if len(component) != 3 {
return nil, fmt.Errorf("malformed app id %s", appId)
}
clusterId, err := strconv.Atoi(component[0])
if err != nil {
return nil, err
}
if clusterId <= 0 {
return nil, fmt.Errorf("target cluster is not provided")
}
return &AppIdentifier{
ClusterId: clusterId,
Namespace: component[1],
ReleaseName: component[2],
}, nil
}
5 changes: 5 additions & 0 deletions api/restHandler/app/appList/AppListingRestHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
argoApplication "github.com/devtron-labs/devtron/client/argocdServer/bean"
"github.com/devtron-labs/devtron/pkg/appStore/installedApp/service/FullMode"
"github.com/devtron-labs/devtron/pkg/appStore/installedApp/service/FullMode/resource"
util4 "github.com/devtron-labs/devtron/pkg/appStore/util"
bean2 "github.com/devtron-labs/devtron/pkg/cluster/repository/bean"
"net/http"
"strconv"
Expand Down Expand Up @@ -909,6 +910,10 @@ func (handler AppListingRestHandlerImpl) GetHostUrlsByBatch(w http.ResponseWrite
common.WriteJsonResp(w, err, "App not found in database", http.StatusBadRequest)
return
}
if util4.IsExternalChartStoreApp(installedApp.App.DisplayName) {
//this is external app case where app_name is a unique identifier, and we want to fetch resource based on display_name
handler.installedAppService.ChangeAppNameToDisplayNameForInstalledApp(installedApp)
}
resourceTreeAndNotesContainer := bean.AppDetailsContainer{}
resourceTreeAndNotesContainer, err = handler.fetchResourceTreeFromInstallAppService(w, r, resourceTreeAndNotesContainer, *installedApp)
if err != nil {
Expand Down
Loading
Loading