From da79fe227f3d55608e99cd2276db888e17f38c18 Mon Sep 17 00:00:00 2001
From: Sjoerd Vink <sjoerdvink@Sjoerds-MacBook-Pro.local>
Date: Tue, 5 Nov 2024 08:59:17 -0500
Subject: [PATCH] Reapply "feat: fallback for all env variables to prevent
 crash"

This reverts commit 3c2b3ca5ef4d7e25f027b1c6838cbab24e6bf96a.
---
 apps/web/src/components/navbar/navbar.tsx     |  9 +--
 apps/web/src/main.tsx                         |  5 +-
 libs/config/src/featureFlags.ts               | 61 ++++++++++++-------
 libs/shared/lib/data-access/broker/broker.tsx |  3 +-
 .../security/useAuthentication.tsx            |  5 +-
 libs/shared/lib/inspector/InspectorPanel.tsx  |  3 +-
 libs/shared/lib/sidebar/index.tsx             |  5 +-
 7 files changed, 57 insertions(+), 34 deletions(-)

diff --git a/apps/web/src/components/navbar/navbar.tsx b/apps/web/src/components/navbar/navbar.tsx
index 5cb0e428e..ecd864a38 100644
--- a/apps/web/src/components/navbar/navbar.tsx
+++ b/apps/web/src/components/navbar/navbar.tsx
@@ -13,8 +13,9 @@ 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 { Button, useActiveSaveStateAuthorization } from '@graphpolaris/shared';
+import { useDispatch } from 'react-redux';
+import { getEnvVariable, showManagePermissions, showSharableExploration } from 'config';
+import { Button, Dialog, DialogContent, DialogTrigger, useActiveSaveStateAuthorization, useSessionCache } from '@graphpolaris/shared';
 import { ManagementTrigger, ManagementViews } from '@graphpolaris/shared/lib/management';
 
 const AuthURL = import.meta.env.GP_AUTH_URL;
@@ -25,7 +26,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');
 
@@ -88,7 +89,7 @@ export const Navbar = () => {
                   <DropdownItem
                     value="Log out"
                     onClick={() => {
-                      location.replace(`${AuthURL}/outpost.goauthentik.io/sign_out`);
+                      location.replace(`${getEnvVariable('GP_AUTH_URL')}/outpost.goauthentik.io/sign_out`);
                     }}
                   />
                 </>
diff --git a/apps/web/src/main.tsx b/apps/web/src/main.tsx
index e25807e6d..f7029e4d6 100644
--- a/apps/web/src/main.tsx
+++ b/apps/web/src/main.tsx
@@ -5,12 +5,13 @@ import * as Sentry from '@sentry/react';
 import { store } from '@graphpolaris/shared/lib/data-access/store';
 import App from './app/App';
 import { createRoot } from 'react-dom/client';
+import { getEnvVariable } from 'config';
 import './main.css';
 import { ErrorBoundary } from '@graphpolaris/shared/lib/components/errorBoundary';
 
-if (import.meta.env.SENTRY_ENABLED) {
+if (getEnvVariable('SENTRY_ENABLED')) {
   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/libs/config/src/featureFlags.ts b/libs/config/src/featureFlags.ts
index b6364d037..21d94384f 100644
--- a/libs/config/src/featureFlags.ts
+++ b/libs/config/src/featureFlags.ts
@@ -1,32 +1,49 @@
-// Safely retrieve environment variable values with a default fallback
-const getEnvVariable = (key: string, defaultValue: string = 'false'): string => {
-  return import.meta.env[key] ?? defaultValue;
+const envFallbacks: Record<string, any> = {
+  SENTRY_ENABLED: false,
+  SENTRY_URL: '',
+  GRAPHPOLARIS_VERSION: 'prod',
+  GP_AUTH_URL: '',
+  BACKEND_WSS_URL: '',
+  BACKEND_URL: '',
+  BACKEND_USER: '',
+  WIP_TABLEVIS: true,
+  WIP_NODELINKVIS: true,
+  WIP_RAWJSONVIS: true,
+  WIP_PAOHVIS: true,
+  WIP_MATRIXVIS: true,
+  WIP_SEMANTICSUBSTRATESVIS: true,
+  WIP_MAPVIS: true,
+  WIP_INSIGHT_SHARING: true,
+  WIP_VIEWER_PERMISSIONS: true,
+  WIP_SHARABLE_EXPLORATION: true,
 };
 
-// Check if the environment is production
-const isProduction = (): boolean => {
-  return getEnvVariable('GRAPHPOLARIS_VERSION', 'dev') === 'prod';
-};
+type EnvFallbackKey = keyof typeof envFallbacks;
 
-// Check if the Manage Permissions feature is enabled
-const showManagePermissions = (): boolean => {
-  return !isProduction() || (isProduction() && getEnvVariable('WIP_VIEWER_PERMISSIONS') === 'false');
+// Safely retrieve environment variable values with a default fallback
+const getEnvVariable = (key: EnvFallbackKey): any => {
+  const value = import.meta.env[key];
+  if (value === undefined) {
+    console.error(`Environment variable ${key} is missing, using fallback value.`);
+    return envFallbacks[key];
+  }
+  return value;
 };
 
-// Check if the Insight Sharing feature is enabled
-const showInsightSharing = (): boolean => {
-  return !isProduction() || (isProduction() && getEnvVariable('WIP_INSIGHT_SHARING') === 'false');
+// Check if the environment is production
+const isProduction = (): boolean => {
+  return getEnvVariable('GRAPHPOLARIS_VERSION') === 'prod';
 };
 
-// Check if the Insight Sharing feature is enabled
-const showSharableExploration = (): boolean => {
-  return !isProduction() || (isProduction() && getEnvVariable('WIP_SHARABLE_EXPLORATION') === 'false');
+// Utility to check if a WIP feature is enabled
+const isWIPFeatureEnabled = (featureKey: string): boolean => {
+  return getEnvVariable(`WIP_${featureKey.toUpperCase()}`) === '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');
-};
+// Feature flags with checks for production and WIP feature flags
+const showManagePermissions = (): boolean => !isProduction() || isWIPFeatureEnabled('viewer_permissions');
+const showInsightSharing = (): boolean => !isProduction() || isWIPFeatureEnabled('insight_sharing');
+const showSharableExploration = (): boolean => !isProduction() || isWIPFeatureEnabled('sharable_exploration');
+const isVisualizationReleased = (visualizationName: string): boolean => !isProduction() || isWIPFeatureEnabled(visualizationName);
 
-export { isProduction, showManagePermissions, showInsightSharing, showSharableExploration, isVisualizationReleased };
+export { getEnvVariable, isProduction, showManagePermissions, showInsightSharing, showSharableExploration, isVisualizationReleased };
diff --git a/libs/shared/lib/data-access/broker/broker.tsx b/libs/shared/lib/data-access/broker/broker.tsx
index f915a2f24..361434842 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(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 fcb507b86..18310f738 100644
--- a/libs/shared/lib/data-access/security/useAuthentication.tsx
+++ b/libs/shared/lib/data-access/security/useAuthentication.tsx
@@ -1,8 +1,9 @@
+import { getEnvVariable } from 'config';
 import { useAppDispatch, useAuthCache } from '../store';
 import { authenticated, changeRoom, UserAuthenticationHeader } from '../store/authSlice';
 
-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..d1c881bc6 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');
   const selection = useSelection();
   const focus = useFocus();
   const dispatch = useDispatch();
diff --git a/libs/shared/lib/sidebar/index.tsx b/libs/shared/lib/sidebar/index.tsx
index 7c2f42419..8307fea03 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 { getEnvVariable } from 'config';
 
 export type SideNavTab = 'Schema' | 'Search' | undefined;
 
@@ -29,8 +30,8 @@ export function Sidebar({
   tab: SideNavTab;
   openMonitoringDialog: () => void;
 }) {
-  const isProd = import.meta.env.GRAPHPOLARIS_VERSION === 'prod';
-  const isInsightSharingWIP = import.meta.env.WIP_INSIGHT_SHARING === 'true';
+  const isProd = getEnvVariable('GRAPHPOLARIS_VERSION') === 'prod';
+  const isInsightSharingWIP = getEnvVariable('WIP_INSIGHT_SHARING') === true;
 
   return (
     <div className="side-bar w-fit h-full flex shrink">
-- 
GitLab