Skip to content

Commit

Permalink
Merge pull request #7364 from NipuniBhagya/feature-labels
Browse files Browse the repository at this point in the history
Introduce configuration based feature status flag support
  • Loading branch information
NipuniBhagya authored Jan 25, 2025
2 parents 271cd66 + 96a0c97 commit c84cdb7
Show file tree
Hide file tree
Showing 20 changed files with 1,777 additions and 391 deletions.
11 changes: 11 additions & 0 deletions .changeset/seven-games-sell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
"@wso2is/admin.application-templates.v1": patch
"@wso2is/admin.server-configurations.v1": patch
"@wso2is/admin.feature-gate.v1": patch
"@wso2is/admin.branding.v1": patch
"@wso2is/admin.actions.v1": patch
"@wso2is/console": patch
"@wso2is/core": patch
---

Introduce configuration based feature status flag support
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,13 @@
{% endif %}
],
"enabled": {{ feature.enabled }},
"featureFlags": {
{% if feature.feature_flags is defined %}
{% for flag, value in feature.feature_flags.items() %}
"{{ flag }}": {{ value }}{{ "," if not loop.last }}
{% endfor %}
{% endif %}
},
"scopes": {
{% if feature.scopes is defined %}
{% for operation, scopes in feature.scopes.items() %}
Expand Down
18 changes: 6 additions & 12 deletions apps/console/src/configs/routes.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2023-2024, WSO2 LLC. (https://www.wso2.com).
* Copyright (c) 2023-2025, WSO2 LLC. (https://www.wso2.com).
*
* WSO2 LLC. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
Expand Down Expand Up @@ -34,8 +34,8 @@ import {
import { getSidePanelIcons } from "@wso2is/admin.core.v1/configs/ui";
import { AppConstants } from "@wso2is/admin.core.v1/constants";
import { commonConfig } from "@wso2is/admin.extensions.v1";
import FeatureFlagConstants from "@wso2is/admin.feature-gate.v1/constants/feature-flag-constants";
import FeatureGateConstants from "@wso2is/admin.feature-gate.v1/constants/feature-gate-constants";
import { FeatureStatusLabel } from "@wso2is/admin.feature-gate.v1/models/feature-status";
import { ServerConfigurationsConstants } from "@wso2is/admin.server-configurations.v1";
import { LegacyModeInterface, RouteInterface } from "@wso2is/core/models";
import compact from "lodash-es/compact";
Expand Down Expand Up @@ -71,8 +71,6 @@ import FullScreenLayout from "../layouts/full-screen-layout";

export const getAppViewRoutes = (): RouteInterface[] => {
const legacyMode: LegacyModeInterface = window["AppUtils"]?.getConfig()?.ui?.legacyMode;
const showStatusLabelForNewAuthzRuntimeFeatures: boolean =
window["AppUtils"]?.getConfig()?.ui?.showStatusLabelForNewAuthzRuntimeFeatures;

const defaultRoutes: RouteInterface[] = [
{
Expand Down Expand Up @@ -719,8 +717,7 @@ export const getAppViewRoutes = (): RouteInterface[] => {
import("@wso2is/admin.sms-templates.v1/pages/sms-customization")
),
exact: true,
featureStatus: "NEW",
featureStatusLabel: FeatureStatusLabel.NEW,
featureFlagKey: FeatureFlagConstants.FEATURE_FLAG_KEY_MAP.SMS_TEMPLATES,
icon: {
icon: getSidePanelIcons().sms
},
Expand Down Expand Up @@ -1049,9 +1046,8 @@ export const getAppViewRoutes = (): RouteInterface[] => {
category: "extensions:develop.sidePanel.categories.monitor",
component: lazy(() => import("@wso2is/admin.org-insights.v1/pages/org-insights")),
exact: true,
featureFlagKey: FeatureFlagConstants.FEATURE_FLAG_KEY_MAP.INSIGHTS,
featureGateIds: [ FeatureGateConstants.SAAS_FEATURES_IDENTIFIER ],
featureStatus: "BETA",
featureStatusLabel: "common:beta",
icon: {
icon: <LightbulbOnIcon fill="black" className="icon" />
},
Expand Down Expand Up @@ -1239,8 +1235,7 @@ export const getAppViewRoutes = (): RouteInterface[] => {
],
component: lazy(() => import("@wso2is/admin.roles.v2/pages/role")),
exact: true,
featureStatus: showStatusLabelForNewAuthzRuntimeFeatures ? "NEW" : "",
featureStatusLabel: showStatusLabelForNewAuthzRuntimeFeatures ? "common:new": "",
featureFlagKey: FeatureFlagConstants.FEATURE_FLAG_KEY_MAP.USER_ROLES,
icon: {
icon: getSidePanelIcons().applicationRoles
},
Expand Down Expand Up @@ -1300,8 +1295,7 @@ export const getAppViewRoutes = (): RouteInterface[] => {
import("@wso2is/admin.actions.v1/pages/actions")
),
exact: true,
featureStatus: "BETA",
featureStatusLabel: FeatureStatusLabel.BETA,
featureFlagKey: FeatureFlagConstants.FEATURE_FLAG_KEY_MAP.ACTIONS,
icon: {
icon: <ProgressFlowIcon className="icon" fill="black" />
},
Expand Down
86 changes: 60 additions & 26 deletions apps/console/src/layouts/dashboard-layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com).
* Copyright (c) 2023-2025, WSO2 LLC. (https://www.wso2.com).
*
* WSO2 LLC. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
Expand Down Expand Up @@ -27,6 +27,7 @@ import {
AppState,
AppUtils,
ConfigReducerStateInterface,
FeatureConfigInterface,
ProtectedRoute,
RouteUtils,
UIConstants,
Expand All @@ -37,15 +38,18 @@ import Header from "@wso2is/admin.core.v1/components/header";
import { CommonUtils as ConsoleCommonUtils } from "@wso2is/admin.core.v1/utils";
import { applicationConfig } from "@wso2is/admin.extensions.v1";
import FeatureGateConstants from "@wso2is/admin.feature-gate.v1/constants/feature-gate-constants";
import { FeatureStatusLabel } from "@wso2is/admin.feature-gate.v1/models/feature-status";
import {
AlertInterface,
AnnouncementBannerInterface,
CategorizedRouteInterface,
ChildRouteInterface,
FeatureAccessConfigInterface,
FeatureFlagsInterface,
NavRouteInterface,
ProfileInfoInterface,
RouteInterface
} from "@wso2is/core/models";
import { CategorizedRouteInterface } from "@wso2is/core/src/models";
import { initializeAlertSystem } from "@wso2is/core/store";
import { RouteUtils as CommonRouteUtils, CommonUtils } from "@wso2is/core/utils";
import {
Expand Down Expand Up @@ -138,6 +142,7 @@ const DashboardLayout: FunctionComponent<RouteComponentProps> = (
const organizationLoading: boolean = useSelector(
(state: AppState) => state?.organization?.getOrganizationLoading
);
const featureConfig: FeatureConfigInterface = useSelector((state: AppState) => state.config.ui.features);

const initLoad: MutableRefObject<boolean> = useRef(true);

Expand Down Expand Up @@ -296,6 +301,27 @@ const DashboardLayout: FunctionComponent<RouteComponentProps> = (
setAnnouncement(validAnnouncement);
};

/**
* Resolves the feature flag status of a given feature.
*
* @param featureName - Name of the feature.
* @param featureKey - Key of the feature.
*
* @returns Feature status label.
*/
const resolveFeatureFlag = (featureName: string, featureKey: string): FeatureStatusLabel=> {
const config: FeatureAccessConfigInterface = featureConfig?.[featureName];

if (!config) {
return null;
}

const featureFlag: FeatureFlagsInterface = config?.featureFlags?.find(
(featureFlag: FeatureFlagsInterface) => featureFlag.feature === featureKey);

return FeatureStatusLabel[featureFlag?.flag];
};

const generateNavbarItems = (): NavbarItems[] => {
const categorizedRoutes: CategorizedRouteInterface = {};

Expand All @@ -320,34 +346,42 @@ const DashboardLayout: FunctionComponent<RouteComponentProps> = (

return Object.entries(categorizedRoutes).map(
([ _navCategory, routes ]: [ navCategory: string, routes: NavRouteInterface[] ]) => {

return {
items: routes.map((route: NavRouteInterface) => ({
"data-componentid": `side-panel-items-${ kebabCase(route.id) }`,
"data-testid": `side-panel-items-${ kebabCase(route.id) }`,
icon: <GenericIcon
transparent
className="route-icon"
{ ...route.icon }
/>,
items: route.items?.map((subRoute: NavRouteInterface) => ({
"data-componentid": `side-panel-items-${ kebabCase(subRoute.id) }`,
"data-testid": `side-panel-items-${ kebabCase(subRoute.id) }`,
items: routes.map((route: NavRouteInterface) => {
const routeFlag: string = resolveFeatureFlag(route.id, route.featureFlagKey);

return {
"data-componentid": `side-panel-items-${ kebabCase(route.id) }`,
"data-testid": `side-panel-items-${ kebabCase(route.id) }`,
icon: <GenericIcon
transparent
className="route-sub-icon"
{ ...subRoute.icon }
className="route-icon"
{ ...route.icon }
/>,
label: t(subRoute.name),
onClick: () => history.push(subRoute.path),
selected: subRoute.selected ?? selectedRoute?.path === subRoute.path,
tag: t(subRoute.featureStatusLabel)
})),
label: t(route.name),
onClick: () => history.push(route.path),
selected: route.selected ?? isRouteActive(route.path),
tag: t(route.featureStatusLabel)
}))
items: route.items?.map((subRoute: NavRouteInterface) => {

const subRouteFlag: string = resolveFeatureFlag(subRoute.id, subRoute.featureFlagKey);

return {
"data-componentid": `side-panel-items-${ kebabCase(subRoute.id) }`,
"data-testid": `side-panel-items-${ kebabCase(subRoute.id) }`,
icon: <GenericIcon
transparent
className="route-sub-icon"
{ ...subRoute.icon }
/>,
label: t(subRoute.name),
onClick: () => history.push(subRoute.path),
selected: subRoute.selected ?? selectedRoute?.path === subRoute.path,
tag: t(subRouteFlag)
};
}),
label: t(route.name),
onClick: () => history.push(route.path),
selected: route.selected ?? isRouteActive(route.path),
tag: t(routeFlag)
};
})
};
}
);
Expand Down
Loading

0 comments on commit c84cdb7

Please sign in to comment.