From 20a28d259fe13aa0476fed25022a625eabbd9935 Mon Sep 17 00:00:00 2001 From: Stephen Adams Date: Fri, 25 Oct 2024 11:34:19 -0400 Subject: [PATCH 1/5] feat: Add a widget model specifically for FR env The FR env has a limited set of features when compared to the commercial environment. Because of this, we should be limiting those within code so that we only return the useful widgets in itless environments. This commit adds a WidgetMappingFR type that specifies a subset of the commercial widgets. Signed-off-by: Stephen Adams --- rest/service/dashboardTemplate.go | 60 +++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/rest/service/dashboardTemplate.go b/rest/service/dashboardTemplate.go index 12b41034..849f693a 100644 --- a/rest/service/dashboardTemplate.go +++ b/rest/service/dashboardTemplate.go @@ -209,6 +209,66 @@ var ( }, }, } + WidgetMappingFR models.WidgetModuleFederationMapping = models.WidgetModuleFederationMapping{ + models.Rhel: models.ModuleFederationMetadata{ + Scope: "landing", + Module: "./RhelWidget", + Defaults: models.BaseWidgetDimensions.InitDimensions(models.BaseWidgetDimensions{}, 1, 4, 10, 1), + Config: models.WidgetConfiguration{ + Icon: models.RhelIcon, + Title: "Red Hat Enterprise Linux", + }, + }, + models.OpenShift: models.ModuleFederationMetadata{ + Scope: "landing", + Module: "./OpenShiftWidget", + Defaults: models.BaseWidgetDimensions.InitDimensions(models.BaseWidgetDimensions{}, 1, 4, 10, 1), + Config: models.WidgetConfiguration{ + Icon: models.OpenShiftIcon, + Title: "Red Hat OpenShift", + }, + }, + models.RecentlyVisited: models.ModuleFederationMetadata{ + Scope: "landing", + Module: "./RecentlyVisited", + Defaults: models.BaseWidgetDimensions.InitDimensions(models.BaseWidgetDimensions{}, 1, 7, 10, 1), + Config: models.WidgetConfiguration{ + Icon: models.HistoryIcon, + Title: "Recently visited", + }, + }, + models.FavoriteServices: models.ModuleFederationMetadata{ + Scope: "chrome", + Module: "./DashboardFavorites", + Defaults: models.BaseWidgetDimensions.InitDimensions(models.BaseWidgetDimensions{}, 1, 6, 10, 1), + Config: models.WidgetConfiguration{ + HeaderLink: models.WidgetHeaderLink{ + Title: "View all services", + Href: "/allservices", + }, + Icon: models.StarIcon, + Title: "My favorite services", + }, + }, + models.NotificationsEvents: models.ModuleFederationMetadata{ + Scope: "notifications", + Module: "./DashboardWidget", + Defaults: models.BaseWidgetDimensions.InitDimensions(models.BaseWidgetDimensions{}, 1, 3, 10, 1), + Config: models.WidgetConfiguration{ + HeaderLink: models.WidgetHeaderLink{ + Title: "View event log", + Href: "/settings/notifications/eventlog", + }, + Icon: models.BellIcon, + Title: "Events", + Permissions: []models.WidgetPermission{ + models.WidgetPermission{ + Method: models.OrgAdmin, + }, + }, + }, + }, + } ) func ForkBaseTemplate(userId uint, dashboard models.AvailableTemplates) (models.DashboardTemplate, error) { From 4f2ffbd8d1608a048a817e029fd992b9090e345c Mon Sep 17 00:00:00 2001 From: Stephen Adams Date: Fri, 25 Oct 2024 12:56:21 -0400 Subject: [PATCH 2/5] feat: Add FR based widget mappings via unleash If we're running in an ITless environment, we need to use the route that hands us the FR default widgets instead of the commercial ones. Here we're using an unleash flag to do that. Signed-off-by: Stephen Adams --- rest/routes/dashboardTemplate.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/rest/routes/dashboardTemplate.go b/rest/routes/dashboardTemplate.go index 15552965..e29c89a6 100644 --- a/rest/routes/dashboardTemplate.go +++ b/rest/routes/dashboardTemplate.go @@ -9,6 +9,7 @@ import ( "github.com/RedHatInsights/chrome-service-backend/rest/models" "github.com/RedHatInsights/chrome-service-backend/rest/service" "github.com/RedHatInsights/chrome-service-backend/rest/util" + "github.com/RedHatInsights/chrome-service-backend/rest/featureflags" "github.com/go-chi/chi/v5" "github.com/sirupsen/logrus" "gorm.io/gorm" @@ -280,6 +281,15 @@ func GetWidgetMappings(w http.ResponseWriter, r *http.Request) { handleDashboardResponse[models.WidgetModuleFederationMapping](resp, err, w) } +func GetWidgetMappingsFR(w http.ResponseWriter, r *http.Request) { + var err error + resp := util.EntityResponse[models.WidgetModuleFederationMapping]{ + Data: service.WidgetMappingFR, + } + + handleDashboardResponse[models.WidgetModuleFederationMapping](resp, err, w) +} + func ResetDashboardTemplate(w http.ResponseWriter, r *http.Request) { user := r.Context().Value(util.USER_CTX_KEY).(models.UserIdentity) userID := user.ID @@ -323,5 +333,9 @@ func MakeDashboardTemplateRoutes(sub chi.Router) { sub.Get("/base-template", GetBaseDashboardTemplates) sub.Get("/base-template/fork", ForkBaseTemplate) - sub.Get("/widget-mapping", GetWidgetMappings) + if featureflags.IsEnabled("chrome-service.itless.enabled") { + sub.Get("/widget-mapping", GetWidgetMappingsFR) + } else { + sub.Get("/widget-mapping", GetWidgetMappings) + } } From ad8ece9832d05763cff667086aed2be47cad3e95 Mon Sep 17 00:00:00 2001 From: Stephen Adams Date: Wed, 30 Oct 2024 10:13:40 -0400 Subject: [PATCH 3/5] fix: use an existing feature flag It turns out we have a feature flag for itless envs already, so we can leverage it rather than creating a new one. Signed-off-by: Stephen Adams --- rest/routes/dashboardTemplate.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest/routes/dashboardTemplate.go b/rest/routes/dashboardTemplate.go index e29c89a6..a062d60b 100644 --- a/rest/routes/dashboardTemplate.go +++ b/rest/routes/dashboardTemplate.go @@ -333,7 +333,7 @@ func MakeDashboardTemplateRoutes(sub chi.Router) { sub.Get("/base-template", GetBaseDashboardTemplates) sub.Get("/base-template/fork", ForkBaseTemplate) - if featureflags.IsEnabled("chrome-service.itless.enabled") { + if featureflags.IsEnabled("platform.chrome.itless") { sub.Get("/widget-mapping", GetWidgetMappingsFR) } else { sub.Get("/widget-mapping", GetWidgetMappings) From 5b462281abb12a4835402a9487799c7983b63470 Mon Sep 17 00:00:00 2001 From: Stephen Adams Date: Fri, 8 Nov 2024 09:33:47 -0500 Subject: [PATCH 4/5] refactor: rework filter widgets - Added a string to each widget to identify its corresponding featureflag - If the 'chrome-service.filterWidgets.enable' flag is set, then a function filters through the widgets to see which ones are meant to be hidden. By using this flag, we prevent searching for disabled widgets when we don't need to. It will simply load the defaults. Signed-off-by: Stephen Adams --- rest/models/DashboardTemplate.go | 1 + rest/routes/dashboardTemplate.go | 35 ++++++++------ rest/service/dashboardTemplate.go | 76 ++++++------------------------- 3 files changed, 38 insertions(+), 74 deletions(-) diff --git a/rest/models/DashboardTemplate.go b/rest/models/DashboardTemplate.go index f1cfb866..82c0ac15 100644 --- a/rest/models/DashboardTemplate.go +++ b/rest/models/DashboardTemplate.go @@ -353,6 +353,7 @@ type ModuleFederationMetadata struct { Scope string `json:"scope"` Module string `json:"module"` ImportName string `json:"importName,omitempty"` + FeatureFlag string `json:"featureFlag,omitempty"` Defaults BaseWidgetDimensions `json:"defaults"` Config WidgetConfiguration `json:"config"` } diff --git a/rest/routes/dashboardTemplate.go b/rest/routes/dashboardTemplate.go index a062d60b..5cbdc1a4 100644 --- a/rest/routes/dashboardTemplate.go +++ b/rest/routes/dashboardTemplate.go @@ -272,19 +272,32 @@ func DecodeDashboardTemplate(w http.ResponseWriter, r *http.Request) { handleDashboardResponse[models.DashboardTemplate](resp, err, w) } -func GetWidgetMappings(w http.ResponseWriter, r *http.Request) { - var err error - resp := util.EntityResponse[models.WidgetModuleFederationMapping]{ - Data: service.WidgetMapping, +func FilterWidgetMapping(widgetMapping models.WidgetModuleFederationMapping) models.WidgetModuleFederationMapping { + filteredWidgets := make(map[models.AvailableWidgets]models.ModuleFederationMetadata) + + for key, value := range widgetMapping { + if !featureflags.IsEnabled(value.FeatureFlag) { + filteredWidgets[key] = value + } } - handleDashboardResponse[models.WidgetModuleFederationMapping](resp, err, w) + return filteredWidgets } -func GetWidgetMappingsFR(w http.ResponseWriter, r *http.Request) { +func GetWidgetMappings(w http.ResponseWriter, r *http.Request) { var err error - resp := util.EntityResponse[models.WidgetModuleFederationMapping]{ - Data: service.WidgetMappingFR, + var resp util.EntityResponse[models.WidgetModuleFederationMapping] + + if featureflags.IsEnabled("chrome-service.filterWidgets.enable") { + filteredWidgetMapping := FilterWidgetMapping(service.WidgetMapping) + + resp = util.EntityResponse[models.WidgetModuleFederationMapping]{ + Data: filteredWidgetMapping, + } + } else { + resp = util.EntityResponse[models.WidgetModuleFederationMapping]{ + Data: service.WidgetMapping, + } } handleDashboardResponse[models.WidgetModuleFederationMapping](resp, err, w) @@ -333,9 +346,5 @@ func MakeDashboardTemplateRoutes(sub chi.Router) { sub.Get("/base-template", GetBaseDashboardTemplates) sub.Get("/base-template/fork", ForkBaseTemplate) - if featureflags.IsEnabled("platform.chrome.itless") { - sub.Get("/widget-mapping", GetWidgetMappingsFR) - } else { - sub.Get("/widget-mapping", GetWidgetMappings) - } + sub.Get("/widget-mapping", GetWidgetMappings) } diff --git a/rest/service/dashboardTemplate.go b/rest/service/dashboardTemplate.go index 849f693a..51e5b738 100644 --- a/rest/service/dashboardTemplate.go +++ b/rest/service/dashboardTemplate.go @@ -27,7 +27,7 @@ var ( Scope: "landing", Module: "./ExploreCapabilities", Defaults: models.BaseWidgetDimensions.InitDimensions(models.BaseWidgetDimensions{}, 3, 5, 10, 1), - + FeatureFlag: "widget.exploreCapabilities.hidden", Config: models.WidgetConfiguration{ Icon: models.RocketIcon, Title: "Explore capabilities", @@ -37,6 +37,7 @@ var ( Scope: "landing", Module: "./EdgeWidget", Defaults: models.BaseWidgetDimensions.InitDimensions(models.BaseWidgetDimensions{}, 1, 4, 10, 1), + FeatureFlag: "widget.edge.hidden", Config: models.WidgetConfiguration{ Icon: models.EdgeIcon, Title: "Edge Management", @@ -46,6 +47,7 @@ var ( Scope: "landing", Module: "./AnsibleWidget", Defaults: models.BaseWidgetDimensions.InitDimensions(models.BaseWidgetDimensions{}, 1, 4, 10, 1), + FeatureFlag: "widget.ansible.hidden", Config: models.WidgetConfiguration{ Icon: models.AnsibleIcon, Title: "Ansible Automation Platform", @@ -55,6 +57,7 @@ var ( Scope: "landing", Module: "./RhelWidget", Defaults: models.BaseWidgetDimensions.InitDimensions(models.BaseWidgetDimensions{}, 1, 4, 10, 1), + FeatureFlag: "widget.rhel.hidden", Config: models.WidgetConfiguration{ Icon: models.RhelIcon, Title: "Red Hat Enterprise Linux", @@ -64,6 +67,7 @@ var ( Scope: "landing", Module: "./OpenShiftWidget", Defaults: models.BaseWidgetDimensions.InitDimensions(models.BaseWidgetDimensions{}, 1, 4, 10, 1), + FeatureFlag: "widget.openshift.hidden", Config: models.WidgetConfiguration{ Icon: models.OpenShiftIcon, Title: "Red Hat OpenShift", @@ -73,6 +77,7 @@ var ( Scope: "landing", Module: "./QuayWidget", Defaults: models.BaseWidgetDimensions.InitDimensions(models.BaseWidgetDimensions{}, 1, 4, 10, 1), + FeatureFlag: "widget.quay.hidden", Config: models.WidgetConfiguration{ Icon: models.QuayIcon, Title: "Quay.io", @@ -82,6 +87,7 @@ var ( Scope: "landing", Module: "./AcsWidget", Defaults: models.BaseWidgetDimensions.InitDimensions(models.BaseWidgetDimensions{}, 1, 4, 10, 1), + FeatureFlag: "widget.acs.hidden", Config: models.WidgetConfiguration{ Icon: models.ACSIcon, Title: "Advanced Cluster Security", @@ -91,6 +97,7 @@ var ( Scope: "landing", Module: "./OpenShiftAiWidget", Defaults: models.BaseWidgetDimensions.InitDimensions(models.BaseWidgetDimensions{}, 1, 4, 10, 1), + FeatureFlag: "widget.openshiftAI.hidden", Config: models.WidgetConfiguration{ Icon: models.OpenShiftAiIcon, Title: "Red Hat OpenShift AI", @@ -100,6 +107,7 @@ var ( Scope: "landing", Module: "./RecentlyVisited", Defaults: models.BaseWidgetDimensions.InitDimensions(models.BaseWidgetDimensions{}, 1, 7, 10, 1), + FeatureFlag: "widget.recentlyVisited.hidden", Config: models.WidgetConfiguration{ Icon: models.HistoryIcon, Title: "Recently visited", @@ -109,6 +117,7 @@ var ( Scope: "chrome", Module: "./DashboardFavorites", Defaults: models.BaseWidgetDimensions.InitDimensions(models.BaseWidgetDimensions{}, 1, 6, 10, 1), + FeatureFlag: "widget.favoriteServices.hidden", Config: models.WidgetConfiguration{ HeaderLink: models.WidgetHeaderLink{ Title: "View all services", @@ -122,6 +131,7 @@ var ( Scope: "notifications", Module: "./DashboardWidget", Defaults: models.BaseWidgetDimensions.InitDimensions(models.BaseWidgetDimensions{}, 1, 3, 10, 1), + FeatureFlag: "widget.notificationsEvents.hidden", Config: models.WidgetConfiguration{ HeaderLink: models.WidgetHeaderLink{ Title: "View event log", @@ -140,6 +150,7 @@ var ( Scope: "learningResources", Module: "./BookmarkedLearningResourcesWidget", Defaults: models.BaseWidgetDimensions.InitDimensions(models.BaseWidgetDimensions{}, 2, 4, 10, 1), + FeatureFlag: "widget.learningResources.hidden", Config: models.WidgetConfiguration{ Icon: models.OutlinedBookmarkIcon, Title: "Bookmarked learning resources", @@ -149,6 +160,7 @@ var ( Scope: "landing", Module: "./SupportCaseWidget", Defaults: models.BaseWidgetDimensions.InitDimensions(models.BaseWidgetDimensions{}, 2, 4, 10, 1), + FeatureFlag: "widget.supportCases.hidden", Config: models.WidgetConfiguration{ HeaderLink: models.WidgetHeaderLink{ Title: "Open a support case", @@ -162,6 +174,7 @@ var ( Scope: "subscriptionInventory", Module: "./SubscriptionsWidget", Defaults: models.BaseWidgetDimensions.InitDimensions(models.BaseWidgetDimensions{}, 4, 4, 10, 1), + FeatureFlag: "widget.subscriptions.hidden", Config: models.WidgetConfiguration{ HeaderLink: models.WidgetHeaderLink{ Title: "Manage subscriptions", @@ -190,6 +203,7 @@ var ( Scope: "sources", Module: "./IntegrationsWidget", Defaults: models.BaseWidgetDimensions.InitDimensions(models.BaseWidgetDimensions{}, 2, 4, 10, 1), + FeatureFlag: "widget.integrations.hidden", Config: models.WidgetConfiguration{ HeaderLink: models.WidgetHeaderLink{ Title: "Explore integrations", @@ -209,66 +223,6 @@ var ( }, }, } - WidgetMappingFR models.WidgetModuleFederationMapping = models.WidgetModuleFederationMapping{ - models.Rhel: models.ModuleFederationMetadata{ - Scope: "landing", - Module: "./RhelWidget", - Defaults: models.BaseWidgetDimensions.InitDimensions(models.BaseWidgetDimensions{}, 1, 4, 10, 1), - Config: models.WidgetConfiguration{ - Icon: models.RhelIcon, - Title: "Red Hat Enterprise Linux", - }, - }, - models.OpenShift: models.ModuleFederationMetadata{ - Scope: "landing", - Module: "./OpenShiftWidget", - Defaults: models.BaseWidgetDimensions.InitDimensions(models.BaseWidgetDimensions{}, 1, 4, 10, 1), - Config: models.WidgetConfiguration{ - Icon: models.OpenShiftIcon, - Title: "Red Hat OpenShift", - }, - }, - models.RecentlyVisited: models.ModuleFederationMetadata{ - Scope: "landing", - Module: "./RecentlyVisited", - Defaults: models.BaseWidgetDimensions.InitDimensions(models.BaseWidgetDimensions{}, 1, 7, 10, 1), - Config: models.WidgetConfiguration{ - Icon: models.HistoryIcon, - Title: "Recently visited", - }, - }, - models.FavoriteServices: models.ModuleFederationMetadata{ - Scope: "chrome", - Module: "./DashboardFavorites", - Defaults: models.BaseWidgetDimensions.InitDimensions(models.BaseWidgetDimensions{}, 1, 6, 10, 1), - Config: models.WidgetConfiguration{ - HeaderLink: models.WidgetHeaderLink{ - Title: "View all services", - Href: "/allservices", - }, - Icon: models.StarIcon, - Title: "My favorite services", - }, - }, - models.NotificationsEvents: models.ModuleFederationMetadata{ - Scope: "notifications", - Module: "./DashboardWidget", - Defaults: models.BaseWidgetDimensions.InitDimensions(models.BaseWidgetDimensions{}, 1, 3, 10, 1), - Config: models.WidgetConfiguration{ - HeaderLink: models.WidgetHeaderLink{ - Title: "View event log", - Href: "/settings/notifications/eventlog", - }, - Icon: models.BellIcon, - Title: "Events", - Permissions: []models.WidgetPermission{ - models.WidgetPermission{ - Method: models.OrgAdmin, - }, - }, - }, - }, - } ) func ForkBaseTemplate(userId uint, dashboard models.AvailableTemplates) (models.DashboardTemplate, error) { From 3ddf227fe168b89e4e20db900d55c11fb96b5199 Mon Sep 17 00:00:00 2001 From: Stephen Adams Date: Mon, 11 Nov 2024 10:50:46 -0500 Subject: [PATCH 5/5] fix: delete widget from mapping when hidden This change modifies the logic for widgets so that instead of creating a new mapping and returning it, we just delete the widget from the original map. This makes it so that we're not using inverse logic and creating confusion where we don't need it. Signed-off-by: Stephen Adams --- rest/routes/dashboardTemplate.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rest/routes/dashboardTemplate.go b/rest/routes/dashboardTemplate.go index 5cbdc1a4..a611f6dc 100644 --- a/rest/routes/dashboardTemplate.go +++ b/rest/routes/dashboardTemplate.go @@ -272,16 +272,16 @@ func DecodeDashboardTemplate(w http.ResponseWriter, r *http.Request) { handleDashboardResponse[models.DashboardTemplate](resp, err, w) } -func FilterWidgetMapping(widgetMapping models.WidgetModuleFederationMapping) models.WidgetModuleFederationMapping { - filteredWidgets := make(map[models.AvailableWidgets]models.ModuleFederationMetadata) +// FilterWidgetMapping removes hidden widgets from the mapping by using feature flags stored with the widget definition. +func FilterWidgetMapping(widgetMapping models.WidgetModuleFederationMapping) models.WidgetModuleFederationMapping { for key, value := range widgetMapping { - if !featureflags.IsEnabled(value.FeatureFlag) { - filteredWidgets[key] = value + if featureflags.IsEnabled(value.FeatureFlag) { + delete(widgetMapping, key) } } - return filteredWidgets + return widgetMapping } func GetWidgetMappings(w http.ResponseWriter, r *http.Request) {