From ceef94fbb4d9cf65721bb6c180699e76af25bbe2 Mon Sep 17 00:00:00 2001 From: Sjoerd <svink@graphpolaris.com> Date: Tue, 12 Nov 2024 11:41:49 +0000 Subject: [PATCH] fix: environment variables for feature flags --- apps/web/.env | 27 ++++-- apps/web/.env.development | 27 ++++-- apps/web/.env.example | 27 ++++-- apps/web/.env.production | 27 ++++-- apps/web/src/components/navbar/navbar.tsx | 34 +++---- apps/web/src/main.tsx | 5 +- apps/web/tsconfig.json | 7 +- libs/config/src/envVariables.ts | 48 ++++++++++ libs/config/src/featureFlags.ts | 32 ------- libs/config/src/index.ts | 2 +- .../featureFlags/FeatureEnabled.tsx | 6 ++ .../components/featureFlags/featureFlags.ts | 45 +++++++++ .../lib/components/featureFlags/index.ts | 2 + libs/shared/lib/data-access/broker/broker.tsx | 3 +- .../security/useAuthentication.tsx | 5 +- libs/shared/lib/inspector/InspectorPanel.tsx | 3 +- .../panel/querysidepanel/QueryMLDialog.tsx | 93 ++++++++++--------- libs/shared/lib/sidebar/index.tsx | 8 +- .../lib/vis/components/VisualizationPanel.tsx | 20 ++-- 19 files changed, 268 insertions(+), 153 deletions(-) create mode 100644 libs/config/src/envVariables.ts delete mode 100644 libs/config/src/featureFlags.ts create mode 100644 libs/shared/lib/components/featureFlags/FeatureEnabled.tsx create mode 100644 libs/shared/lib/components/featureFlags/featureFlags.ts create mode 100644 libs/shared/lib/components/featureFlags/index.ts diff --git a/apps/web/.env b/apps/web/.env index 2458b1b74..e085c0ec9 100644 --- a/apps/web/.env +++ b/apps/web/.env @@ -11,14 +11,21 @@ SENTRY_URL= GP_AUTH_URL= -WIP_TABLEVIS=false -WIP_NODELINKVIS=false -WIP_RAWJSONVIS=false -WIP_PAOHVIS=true -WIP_MATRIXVIS=true -WIP_SEMANTICSUBSTRATESVIS=true -WIP_MAPVIS=true +LINK_PREDICTION=true +CENTRALITY=true +COMMUNITY_DETECTION=true +SHORTEST_PATH=true -WIP_INSIGHT_SHARING=true -WIP_VIEWER_PERMISSIONS=true -WIP_SHARABLE_EXPLORATION=true +TABLEVIS=true +NODELINKVIS=true +RAWJSONVIS=true +PAOHVIS=true +MATRIXVIS=true +SEMANTICSUBSTRATESVIS=true +MAPVIS=true +VIS0D=true +VIS1D=true + +INSIGHT_SHARING=true +VIEWER_PERMISSIONS=true +SHARABLE_EXPLORATION=true diff --git a/apps/web/.env.development b/apps/web/.env.development index f56103b54..0e5a47287 100644 --- a/apps/web/.env.development +++ b/apps/web/.env.development @@ -11,14 +11,21 @@ SENTRY_URL= GP_AUTH_URL= -WIP_TABLEVIS=false -WIP_NODELINKVIS=false -WIP_RAWJSONVIS=false -WIP_PAOHVIS=true -WIP_MATRIXVIS=true -WIP_SEMANTICSUBSTRATESVIS=true -WIP_MAPVIS=true +LINK_PREDICTION=true +CENTRALITY=true +COMMUNITY_DETECTION=true +SHORTEST_PATH=true -WIP_INSIGHT_SHARING=true -WIP_VIEWER_PERMISSIONS=true -WIP_SHARABLE_EXPLORATION=true \ No newline at end of file +TABLEVIS=true +NODELINKVIS=true +RAWJSONVIS=true +PAOHVIS=true +MATRIXVIS=true +SEMANTICSUBSTRATESVIS=true +MAPVIS=true +VIS0D=true +VIS1D=true + +INSIGHT_SHARING=true +VIEWER_PERMISSIONS=true +SHARABLE_EXPLORATION=true diff --git a/apps/web/.env.example b/apps/web/.env.example index 10dba96b2..8a53e6ecc 100644 --- a/apps/web/.env.example +++ b/apps/web/.env.example @@ -10,14 +10,21 @@ SENTRY_URL= GP_AUTH_URL= -WIP_TABLEVIS=false -WIP_NODELINKVIS=false -WIP_RAWJSONVIS=false -WIP_PAOHVIS=true -WIP_MATRIXVIS=true -WIP_SEMANTICSUBSTRATESVIS=true -WIP_MAPVIS=true +LINK_PREDICTION=true +CENTRALITY=true +COMMUNITY_DETECTION=true +SHORTEST_PATH=true -WIP_INSIGHT_SHARING=true -WIP_VIEWER_PERMISSIONS=true -WIP_SHARABLE_EXPLORATION=true \ No newline at end of file +TABLEVIS=true +NODELINKVIS=true +RAWJSONVIS=true +PAOHVIS=true +MATRIXVIS=true +SEMANTICSUBSTRATESVIS=true +MAPVIS=true +VIS0D=true +VIS1D=true + +INSIGHT_SHARING=true +VIEWER_PERMISSIONS=true +SHARABLE_EXPLORATION=true diff --git a/apps/web/.env.production b/apps/web/.env.production index b848c9cca..fc1f37a8c 100644 --- a/apps/web/.env.production +++ b/apps/web/.env.production @@ -12,14 +12,21 @@ SENTRY_URL= GP_AUTH_URL=https://auth.staging.graphpolaris.com/ -WIP_TABLEVIS=false -WIP_NODELINKVIS=false -WIP_RAWJSONVIS=false -WIP_PAOHVIS=true -WIP_MATRIXVIS=true -WIP_SEMANTICSUBSTRATESVIS=true -WIP_MAPVIS=true +LINK_PREDICTION=true +CENTRALITY=true +COMMUNITY_DETECTION=true +SHORTEST_PATH=true -WIP_INSIGHT_SHARING=true -WIP_VIEWER_PERMISSIONS=true -WIP_SHARABLE_EXPLORATION=true \ No newline at end of file +TABLEVIS=true +NODELINKVIS=true +RAWJSONVIS=true +PAOHVIS=false +MATRIXVIS=false +SEMANTICSUBSTRATESVIS=false +MAPVIS=false +VIS0D=false +VIS1D=false + +INSIGHT_SHARING=false +VIEWER_PERMISSIONS=false +SHARABLE_EXPLORATION=false diff --git a/apps/web/src/components/navbar/navbar.tsx b/apps/web/src/components/navbar/navbar.tsx index 5cb0e428e..a93578f0e 100644 --- a/apps/web/src/components/navbar/navbar.tsx +++ b/apps/web/src/components/navbar/navbar.tsx @@ -13,11 +13,10 @@ import { useAuthCache, useAuthentication } from '@graphpolaris/shared/lib/data-a import { DropdownItem } from '@graphpolaris/shared/lib/components/dropdowns'; import GpLogo from './gp-logo'; import { Popover, PopoverContent, PopoverTrigger } from '@graphpolaris/shared/lib/components/layout/Popover'; -import { showSharableExploration } from 'config'; +import { FeatureEnabled } from '@graphpolaris/shared/lib/components/featureFlags'; import { Button, useActiveSaveStateAuthorization } from '@graphpolaris/shared'; import { ManagementTrigger, ManagementViews } from '@graphpolaris/shared/lib/management'; - -const AuthURL = import.meta.env.GP_AUTH_URL; +import { getEnvVariable } from 'config'; export const Navbar = () => { const dropdownRef = useRef<HTMLDivElement>(null); @@ -25,7 +24,7 @@ export const Navbar = () => { const authCache = useAuthCache(); const authorization = useActiveSaveStateAuthorization(); const [menuOpen, setMenuOpen] = useState(false); - const buildInfo = import.meta.env.GRAPHPOLARIS_VERSION; + const buildInfo = getEnvVariable('GRAPHPOLARIS_VERSION'); const [managementOpen, setManagementOpen] = useState<boolean>(false); const [current, setCurrent] = useState<ManagementViews>('overview'); @@ -67,28 +66,30 @@ export const Navbar = () => { </div> {authCache.authentication?.authenticated ? ( <> - {showSharableExploration() && ( + <FeatureEnabled featureFlag="SHARABLE_EXPLORATION"> <DropdownItem value="Share" onClick={() => { auth.newShareRoom(); }} /> - )} - {authCache.authorization?.savestate?.W && authorization.database.W && ( - <DropdownItem - value="Viewer Permissions" - onClick={() => { - setManagementOpen(true); - setCurrent('members'); - }} - /> - )} + </FeatureEnabled> + <FeatureEnabled featureFlag="VIEWER_PERMISSIONS"> + {authCache.authorization?.savestate?.W && authorization.database.W && ( + <DropdownItem + value="Viewer Permissions" + onClick={() => { + setManagementOpen(true); + setCurrent('members'); + }} + /> + )} + </FeatureEnabled> <DropdownItem value="Settings" onClick={() => {}} /> <DropdownItem value="Log out" onClick={() => { - location.replace(`${AuthURL}/outpost.goauthentik.io/sign_out`); + location.replace(`${getEnvVariable('GP_AUTH_URL')}/outpost.goauthentik.io/sign_out`); }} /> </> @@ -97,6 +98,7 @@ export const Navbar = () => { <DropdownItem value="Login" onClick={() => {}} /> </> )} + {authCache.authentication?.roomID && ( <div className="p-2 border-b"> <h3 className="text-xs break-words">Share ID: {authCache.authentication?.roomID}</h3> diff --git a/apps/web/src/main.tsx b/apps/web/src/main.tsx index e25807e6d..cdd0079ed 100644 --- a/apps/web/src/main.tsx +++ b/apps/web/src/main.tsx @@ -7,10 +7,11 @@ import App from './app/App'; import { createRoot } from 'react-dom/client'; import './main.css'; import { ErrorBoundary } from '@graphpolaris/shared/lib/components/errorBoundary'; +import { getEnvVariable } from 'config'; -if (import.meta.env.SENTRY_ENABLED) { +if (getEnvVariable('SENTRY_ENABLED') === 'true') { Sentry.init({ - dsn: import.meta.env.SENTRY_URL, + dsn: getEnvVariable('SENTRY_URL'), integrations: [Sentry.browserTracingIntegration(), Sentry.replayIntegration()], tracesSampleRate: 1.0, replaysSessionSampleRate: 0.1, diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json index b5d6c7282..263a48be6 100644 --- a/apps/web/tsconfig.json +++ b/apps/web/tsconfig.json @@ -45,7 +45,12 @@ "postcss.config.js", // excludes PostCSS configuration file "tsconfig.tsbuildinfo" // excludes TypeScript build info file ], - "include": ["vite.config.ts", "src/**/*", "../../libs/shared/lib/error-boundary"], + "include": [ + "vite.config.ts", + "src/**/*", + "../../libs/shared/lib/error-boundary", + "../../libs/shared/lib/components/featureFlags/featureFlags.ts" + ], "files": ["vite.config.ts"], "references": [] } diff --git a/libs/config/src/envVariables.ts b/libs/config/src/envVariables.ts new file mode 100644 index 000000000..cc8b8bb21 --- /dev/null +++ b/libs/config/src/envVariables.ts @@ -0,0 +1,48 @@ +/// <reference types="vite/client" /> + +export const envVar = { + GRAPHPOLARIS_VERSION: import.meta.env.GRAPHPOLARIS_VERSION, + BACKEND_URL: import.meta.env.BACKEND_URL, + BACKEND_WSS_URL: import.meta.env.BACKEND_WSS_URL, + STAGING: import.meta.env.STAGING, + SKIP_LOGIN: import.meta.env.SKIP_LOGIN, + BACKEND_USER: import.meta.env.BACKEND_USER, + + SENTRY_ENABLED: import.meta.env.SENTRY_ENABLED, + SENTRY_URL: import.meta.env.SENTRY_URL, + + GP_AUTH_URL: import.meta.env.GP_AUTH_URL, + + LINK_PREDICTION: import.meta.env.LINK_PREDICTION, + CENTRALITY: import.meta.env.CENTRALITY, + COMMUNITY_DETECTION: import.meta.env.COMMUNITY_DETECTION, + SHORTEST_PATH: import.meta.env.SHORTEST_PATH, + + TABLEVIS: import.meta.env.TABLEVIS, + NODELINKVIS: import.meta.env.NODELINKVIS, + RAWJSONVIS: import.meta.env.RAWJSONVIS, + PAOHVIS: import.meta.env.PAOHVIS, + MATRIXVIS: import.meta.env.MATRIXVIS, + SEMANTICSUBSTRATESVIS: import.meta.env.SEMANTICSUBSTRATESVIS, + MAPVIS: import.meta.env.MAPVIS, + VIS0D: import.meta.env.VIS0D, + VIS1D: import.meta.env.VIS1D, + + INSIGHT_SHARING: import.meta.env.INSIGHT_SHARING, + VIEWER_PERMISSIONS: import.meta.env.VIEWER_PERMISSIONS, + SHARABLE_EXPLORATION: import.meta.env.SHARABLE_EXPLORATION, +}; + +type EnvVarKey = keyof typeof envVar; + +function getEnvVariable(key: EnvVarKey): string | undefined { + const value = envVar[key]; + if (value === undefined) { + console.error(`Environment variable ${key} is not defined`); + return; + } + return value; +} + +export { getEnvVariable }; +export type { EnvVarKey }; diff --git a/libs/config/src/featureFlags.ts b/libs/config/src/featureFlags.ts deleted file mode 100644 index b6364d037..000000000 --- a/libs/config/src/featureFlags.ts +++ /dev/null @@ -1,32 +0,0 @@ -// Safely retrieve environment variable values with a default fallback -const getEnvVariable = (key: string, defaultValue: string = 'false'): string => { - return import.meta.env[key] ?? defaultValue; -}; - -// Check if the environment is production -const isProduction = (): boolean => { - return getEnvVariable('GRAPHPOLARIS_VERSION', 'dev') === 'prod'; -}; - -// Check if the Manage Permissions feature is enabled -const showManagePermissions = (): boolean => { - return !isProduction() || (isProduction() && getEnvVariable('WIP_VIEWER_PERMISSIONS') === 'false'); -}; - -// Check if the Insight Sharing feature is enabled -const showInsightSharing = (): boolean => { - return !isProduction() || (isProduction() && getEnvVariable('WIP_INSIGHT_SHARING') === 'false'); -}; - -// Check if the Insight Sharing feature is enabled -const showSharableExploration = (): boolean => { - return !isProduction() || (isProduction() && getEnvVariable('WIP_SHARABLE_EXPLORATION') === 'false'); -}; - -// Utility to check if a specific visualization is released based on environment variables -const isVisualizationReleased = (visualizationName: string): boolean => { - const visualizationFlag = getEnvVariable(`WIP_${visualizationName.toUpperCase()}`, 'false'); - return !isProduction() || (isProduction() && visualizationFlag === 'false'); -}; - -export { isProduction, showManagePermissions, showInsightSharing, showSharableExploration, isVisualizationReleased }; diff --git a/libs/config/src/index.ts b/libs/config/src/index.ts index caecba356..867698fe2 100644 --- a/libs/config/src/index.ts +++ b/libs/config/src/index.ts @@ -1,2 +1,2 @@ export * from './colors'; -export * from './featureFlags'; +export * from './envVariables'; diff --git a/libs/shared/lib/components/featureFlags/FeatureEnabled.tsx b/libs/shared/lib/components/featureFlags/FeatureEnabled.tsx new file mode 100644 index 000000000..75ba6e0b2 --- /dev/null +++ b/libs/shared/lib/components/featureFlags/FeatureEnabled.tsx @@ -0,0 +1,6 @@ +import { canViewFeature, FeatureFlagName } from '.'; +import { ReactNode } from 'react'; + +export function FeatureEnabled({ featureFlag, children }: { featureFlag: FeatureFlagName; children: ReactNode }) { + return canViewFeature(featureFlag) ? children : null; +} diff --git a/libs/shared/lib/components/featureFlags/featureFlags.ts b/libs/shared/lib/components/featureFlags/featureFlags.ts new file mode 100644 index 000000000..1f98e9b85 --- /dev/null +++ b/libs/shared/lib/components/featureFlags/featureFlags.ts @@ -0,0 +1,45 @@ +import { getEnvVariable, EnvVarKey } from 'config'; + +const getFeatureVariable = (flagId: EnvVarKey): boolean => { + // TODO: move feature flag storage to database instead of env variables + const value = getEnvVariable(flagId); + if (value === undefined) { + return false; + } + return String(value) === 'true'; +}; + +export const ML_FLAGS = { + LINK_PREDICTION: getFeatureVariable('LINK_PREDICTION'), + CENTRALITY: getFeatureVariable('CENTRALITY'), + COMMUNITY_DETECTION: getFeatureVariable('COMMUNITY_DETECTION'), + SHORTEST_PATH: getFeatureVariable('SHORTEST_PATH'), +}; + +export const VISUALIZATIONS_FLAGS = { + RAWJSONVIS: getFeatureVariable('RAWJSONVIS'), + NODELINKVIS: getFeatureVariable('NODELINKVIS'), + TABLEVIS: getFeatureVariable('TABLEVIS'), + PAOHVIS: getFeatureVariable('PAOHVIS'), + MATRIXVIS: getFeatureVariable('MATRIXVIS'), + SEMANTICSUBSTRATESVIS: getFeatureVariable('SEMANTICSUBSTRATESVIS'), + MAPVIS: getFeatureVariable('MAPVIS'), + VIS0D: getFeatureVariable('VIS0D'), + VIS1D: getFeatureVariable('VIS1D'), +}; + +export const FEATURE_FLAGS = { + ...ML_FLAGS, + ...VISUALIZATIONS_FLAGS, + INSIGHT_SHARING: getFeatureVariable('INSIGHT_SHARING'), + VIEWER_PERMISSIONS: getFeatureVariable('VIEWER_PERMISSIONS'), + SHARABLE_EXPLORATION: getFeatureVariable('SHARABLE_EXPLORATION'), +} as const satisfies Record<string, boolean>; + +export type FeatureFlagName = keyof typeof FEATURE_FLAGS; + +const canViewFeature = (flagId: FeatureFlagName): boolean => { + return FEATURE_FLAGS[flagId]; +}; + +export { canViewFeature }; diff --git a/libs/shared/lib/components/featureFlags/index.ts b/libs/shared/lib/components/featureFlags/index.ts new file mode 100644 index 000000000..20f816cf1 --- /dev/null +++ b/libs/shared/lib/components/featureFlags/index.ts @@ -0,0 +1,2 @@ +export * from './featureFlags'; +export * from './FeatureEnabled'; diff --git a/libs/shared/lib/data-access/broker/broker.tsx b/libs/shared/lib/data-access/broker/broker.tsx index f915a2f24..1f577c33f 100644 --- a/libs/shared/lib/data-access/broker/broker.tsx +++ b/libs/shared/lib/data-access/broker/broker.tsx @@ -4,6 +4,7 @@ * © Copyright Utrecht University (Department of Information and Computing Sciences) */ +import { getEnvVariable } from 'config'; import { UseIsAuthorizedState } from '../store/authSlice'; import { ReceiveMessageI, SendMessageI, SendMessageWithSessionI } from './types'; @@ -38,7 +39,7 @@ export class Broker { /** Get the singleton instance of the Broker. */ public static instance(): Broker { - if (!this.singletonInstance) this.singletonInstance = new Broker(import.meta.env.BACKEND_WSS_URL); + if (!this.singletonInstance) this.singletonInstance = new Broker(String(getEnvVariable('BACKEND_WSS_URL'))); return this.singletonInstance; } diff --git a/libs/shared/lib/data-access/security/useAuthentication.tsx b/libs/shared/lib/data-access/security/useAuthentication.tsx index a14824d96..0da759abb 100644 --- a/libs/shared/lib/data-access/security/useAuthentication.tsx +++ b/libs/shared/lib/data-access/security/useAuthentication.tsx @@ -1,9 +1,10 @@ +import { getEnvVariable } from 'config'; import { useAppDispatch, useAuthCache } from '../store'; import { authenticated, changeRoom, UserAuthenticationHeader } from '../store/authSlice'; import { addInfo, addError } from '../store/configSlice'; -const domain = import.meta.env.BACKEND_URL; -const userURI = import.meta.env.BACKEND_USER; +const domain = getEnvVariable('BACKEND_URL'); +const userURI = getEnvVariable('BACKEND_USER'); export const fetchSettings: RequestInit = { method: 'GET', diff --git a/libs/shared/lib/inspector/InspectorPanel.tsx b/libs/shared/lib/inspector/InspectorPanel.tsx index 074ad172e..66a88f64b 100644 --- a/libs/shared/lib/inspector/InspectorPanel.tsx +++ b/libs/shared/lib/inspector/InspectorPanel.tsx @@ -9,9 +9,10 @@ import { SelectionConfig } from '../vis/components/config/SelectionConfig'; import { SchemaSettings } from '../schema/panel/SchemaSettings'; import { QuerySettings } from '../querybuilder/panel/querysidepanel/QuerySettings'; import { useActiveVisualization } from '@graphpolaris/shared/lib/data-access'; +import { getEnvVariable } from 'config'; export function InspectorPanel(props: { children?: React.ReactNode }) { - const buildInfo = import.meta.env.GRAPHPOLARIS_VERSION; + const buildInfo = getEnvVariable('GRAPHPOLARIS_VERSION') === 'prod'; const selection = useSelection(); const focus = useFocus(); const dispatch = useDispatch(); diff --git a/libs/shared/lib/querybuilder/panel/querysidepanel/QueryMLDialog.tsx b/libs/shared/lib/querybuilder/panel/querysidepanel/QueryMLDialog.tsx index 1d2860ced..54f65c205 100644 --- a/libs/shared/lib/querybuilder/panel/querysidepanel/QueryMLDialog.tsx +++ b/libs/shared/lib/querybuilder/panel/querysidepanel/QueryMLDialog.tsx @@ -8,6 +8,7 @@ import { } from '@graphpolaris/shared/lib/data-access/store/mlSlice'; import { FormCard, FormBody, FormTitle, FormHBar } from '@graphpolaris/shared/lib/components/forms'; import { Input } from '@graphpolaris/shared/lib/components/inputs'; +import { FeatureEnabled } from '@graphpolaris/shared/lib/components/featureFlags'; export const QueryMLDialog = () => { const dispatch = useAppDispatch(); @@ -25,51 +26,59 @@ export const QueryMLDialog = () => { <FormHBar /> <div className="px-5"> - <Input - type="boolean" - label="Link Prediction" - value={ml.linkPrediction.enabled} - onChange={() => dispatch(setLinkPredictionEnabled(!ml.linkPrediction.enabled))} - /> - {ml.linkPrediction.enabled && ml.linkPrediction.result && <span># of predictions: {ml.linkPrediction.result.length}</span>} - {ml.linkPrediction.enabled && !ml.linkPrediction.result && <span>Loading...</span>} + <FeatureEnabled featureFlag="LINK_PREDICTION"> + <Input + type="boolean" + label="Link Prediction" + value={ml.linkPrediction.enabled} + onChange={() => dispatch(setLinkPredictionEnabled(!ml.linkPrediction.enabled))} + /> + {ml.linkPrediction.enabled && ml.linkPrediction.result && <span># of predictions: {ml.linkPrediction.result.length}</span>} + {ml.linkPrediction.enabled && !ml.linkPrediction.result && <span>Loading...</span>} + </FeatureEnabled> - <Input - type="boolean" - label="Centrality" - value={ml.centrality.enabled} - onChange={() => dispatch(setCentralityEnabled(!ml.centrality.enabled))} - /> - {ml.centrality.enabled && Object.values(ml.centrality.result).length > 0 && ( - <span> - sum of centers: - {Object.values(ml.centrality.result) - .reduce((a, b) => b + a) - .toFixed(2)} - </span> - )} - {ml.centrality.enabled && Object.values(ml.centrality.result).length === 0 && <span>No Centers Found</span>} + <FeatureEnabled featureFlag="CENTRALITY"> + <Input + type="boolean" + label="Centrality" + value={ml.centrality.enabled} + onChange={() => dispatch(setCentralityEnabled(!ml.centrality.enabled))} + /> + {ml.centrality.enabled && Object.values(ml.centrality.result).length > 0 && ( + <span> + sum of centers: + {Object.values(ml.centrality.result) + .reduce((a, b) => b + a) + .toFixed(2)} + </span> + )} + {ml.centrality.enabled && Object.values(ml.centrality.result).length === 0 && <span>No Centers Found</span>} + </FeatureEnabled> - <Input - type="boolean" - label="Community detection" - value={ml.communityDetection.enabled} - onChange={() => dispatch(setCommunityDetectionEnabled(!ml.communityDetection.enabled))} - /> - {ml.communityDetection.enabled && ml.communityDetection.result && ( - <span># of communities: {ml.communityDetection.result.length}</span> - )} - {ml.communityDetection.enabled && !ml.communityDetection.result && <span>Loading...</span>} + <FeatureEnabled featureFlag="COMMUNITY_DETECTION"> + <Input + type="boolean" + label="Community detection" + value={ml.communityDetection.enabled} + onChange={() => dispatch(setCommunityDetectionEnabled(!ml.communityDetection.enabled))} + /> + {ml.communityDetection.enabled && ml.communityDetection.result && ( + <span># of communities: {ml.communityDetection.result.length}</span> + )} + {ml.communityDetection.enabled && !ml.communityDetection.result && <span>Loading...</span>} + </FeatureEnabled> - <Input - type="boolean" - label="Shortest path" - value={ml.shortestPath.enabled} - onChange={() => dispatch(setShortestPathEnabled(!ml.shortestPath.enabled))} - /> - {ml.shortestPath.enabled && ml.shortestPath.result?.length > 0 && <span># of hops: {ml.shortestPath.result.length}</span>} - {ml.shortestPath.enabled && !ml.shortestPath.srcNode && <span>Please select source node</span>} - {ml.shortestPath.enabled && ml.shortestPath.srcNode && !ml.shortestPath.trtNode && <span>Please select target node</span>} + <FeatureEnabled featureFlag="SHORTEST_PATH"> + <Input + type="boolean" + label="Shortest path" + value={ml.shortestPath.enabled} + onChange={() => dispatch(setShortestPathEnabled(!ml.shortestPath.enabled))} + /> + {ml.shortestPath.enabled && ml.shortestPath.result?.length > 0 && <span># of hops: {ml.shortestPath.result.length}</span>} + {ml.shortestPath.enabled && !ml.shortestPath.srcNode && <span>Please select source node</span>} + {ml.shortestPath.enabled && ml.shortestPath.srcNode && !ml.shortestPath.trtNode && <span>Please select target node</span>} + </FeatureEnabled> </div> </FormBody> </FormCard> diff --git a/libs/shared/lib/sidebar/index.tsx b/libs/shared/lib/sidebar/index.tsx index 7c2f42419..c4f160cf1 100644 --- a/libs/shared/lib/sidebar/index.tsx +++ b/libs/shared/lib/sidebar/index.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { Button, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../components'; import ColorMode from '../components/color-mode'; +import { FeatureEnabled } from '@graphpolaris/shared/lib/components/featureFlags'; export type SideNavTab = 'Schema' | 'Search' | undefined; @@ -29,9 +30,6 @@ export function Sidebar({ tab: SideNavTab; openMonitoringDialog: () => void; }) { - const isProd = import.meta.env.GRAPHPOLARIS_VERSION === 'prod'; - const isInsightSharingWIP = import.meta.env.WIP_INSIGHT_SHARING === 'true'; - return ( <div className="side-bar w-fit h-full flex shrink"> <TooltipProvider delayDuration={100}> @@ -58,7 +56,7 @@ export function Sidebar({ </Tooltip> ))} - {(!isProd || (isProd && !isInsightSharingWIP)) && ( + <FeatureEnabled featureFlag="INSIGHT_SHARING"> <Tooltip placement={'right'}> <TooltipTrigger asChild> <Button @@ -71,7 +69,7 @@ export function Sidebar({ </TooltipTrigger> <TooltipContent>Insight Sharing</TooltipContent> </Tooltip> - )} + </FeatureEnabled> <div className="mt-auto mb-2"> <ColorMode /> diff --git a/libs/shared/lib/vis/components/VisualizationPanel.tsx b/libs/shared/lib/vis/components/VisualizationPanel.tsx index 506a38c79..21f04168e 100644 --- a/libs/shared/lib/vis/components/VisualizationPanel.tsx +++ b/libs/shared/lib/vis/components/VisualizationPanel.tsx @@ -17,21 +17,21 @@ import { updateVisualization, addVisualization } from '../../data-access/store/v import { VisualizationPropTypes, VISComponentType } from '../common'; import { ErrorBoundary } from '../../components/errorBoundary'; import { addError } from '../../data-access/store/configSlice'; -import { isVisualizationReleased } from 'config'; +import { canViewFeature } from '@graphpolaris/shared/lib/components/featureFlags/featureFlags'; type PromiseFunc = () => Promise<{ default: VISComponentType<any> }>; export const Visualizations: Record<string, PromiseFunc> = { - ...(isVisualizationReleased('TableVis') && { TableVis: () => import('../visualizations/tablevis/tablevis') }), - ...(isVisualizationReleased('PaohVis') && { PaohVis: () => import('../visualizations/paohvis/paohvis') }), - ...(isVisualizationReleased('RawJSONVis') && { RawJSONVis: () => import('../visualizations/rawjsonvis/rawjsonvis') }), - ...(isVisualizationReleased('NodeLinkVis') && { NodeLinkVis: () => import('../visualizations/nodelinkvis/nodelinkvis') }), - ...(isVisualizationReleased('MatrixVis') && { MatrixVis: () => import('../visualizations/matrixvis/matrixvis') }), - ...(isVisualizationReleased('SemanticSubstratesVis') && { + ...(canViewFeature('TABLEVIS') && { TableVis: () => import('../visualizations/tablevis/tablevis') }), + ...(canViewFeature('PAOHVIS') && { PaohVis: () => import('../visualizations/paohvis/paohvis') }), + ...(canViewFeature('RAWJSONVIS') && { RawJSONVis: () => import('../visualizations/rawjsonvis/rawjsonvis') }), + ...(canViewFeature('NODELINKVIS') && { NodeLinkVis: () => import('../visualizations/nodelinkvis/nodelinkvis') }), + ...(canViewFeature('MATRIXVIS') && { MatrixVis: () => import('../visualizations/matrixvis/matrixvis') }), + ...(canViewFeature('SEMANTICSUBSTRATESVIS') && { SemanticSubstratesVis: () => import('../visualizations/semanticsubstratesvis/semanticsubstratesvis'), }), - ...(isVisualizationReleased('MapVis') && { MapVis: () => import('../visualizations/mapvis/mapvis') }), - ...(isVisualizationReleased('Vis0D') && { Vis0D: () => import('../visualizations/vis0D/Vis0D') }), - ...(isVisualizationReleased('Vis1D') && { Vis1D: () => import('../visualizations/vis1D/Vis1D') }), + ...(canViewFeature('MAPVIS') && { MapVis: () => import('../visualizations/mapvis/mapvis') }), + ...(canViewFeature('VIS0D') && { Vis0D: () => import('../visualizations/Vis0D/Vis0D') }), + ...(canViewFeature('VIS1D') && { Vis1D: () => import('../visualizations/vis1D/Vis1D') }), }; export const VISUALIZATION_TYPES: string[] = Object.keys(Visualizations); -- GitLab