diff --git a/api/config/config.go b/api/config/config.go
index 7cb7e568..3394c37b 100644
--- a/api/config/config.go
+++ b/api/config/config.go
@@ -119,9 +119,8 @@ type UIConfig struct {
StaticPath string `validated:"required"`
IndexPath string `validated:"required"`
- ClockworkUIHomepage string `json:"REACT_APP_CLOCKWORK_UI_HOMEPAGE"`
- KubeflowUIHomepage string `json:"REACT_APP_KUBEFLOW_UI_HOMEPAGE"`
- CaramlAIStreamlitHomepage string `json:"REACT_APP_CARAML_AI_STREAMLIT_HOMEPAGE"`
+ ClockworkUIHomepage string `json:"REACT_APP_CLOCKWORK_UI_HOMEPAGE"`
+ KubeflowUIHomepage string `json:"REACT_APP_KUBEFLOW_UI_HOMEPAGE"`
AllowCustomStream bool `json:"REACT_APP_ALLOW_CUSTOM_STREAM"`
AllowCustomTeam bool `json:"REACT_APP_ALLOW_CUSTOM_TEAM"`
diff --git a/api/models/v2/application.go b/api/models/v2/application.go
index 95721f94..db6f183c 100644
--- a/api/models/v2/application.go
+++ b/api/models/v2/application.go
@@ -1,11 +1,12 @@
package models
type Application struct {
- Name string `json:"name" validate:"required"`
- Description string `json:"description"`
- Homepage string `json:"homepage"`
- Configuration *ApplicationConfig `json:"config" validate:"dive"`
- IsProjectAgnostic bool `json:"is_project_agnostic"`
+ Name string `json:"name" validate:"required"`
+ Description string `json:"description"`
+ Homepage string `json:"homepage"`
+ Configuration *ApplicationConfig `json:"config" validate:"dive"`
+ IsProjectAgnostic bool `json:"is_project_agnostic"`
+ StreamlitPlaceholderPageConfig *StreamlitPlaceholderPageConfig `json:"streamlit_placeholder_page_config"`
}
type ApplicationConfig struct {
@@ -18,3 +19,7 @@ type NavigationMenuItem struct {
Label string `json:"label"`
Destination string `json:"destination"`
}
+
+type StreamlitPlaceholderPageConfig struct {
+ StreamlitURL string `json:"streamlit_url"`
+}
diff --git a/ui/packages/app/src/App.js b/ui/packages/app/src/App.js
index e1008274..1e2dd943 100644
--- a/ui/packages/app/src/App.js
+++ b/ui/packages/app/src/App.js
@@ -3,18 +3,22 @@ import {
ErrorBoundary,
Login,
MlpApiContextProvider,
+ ApplicationsContextProvider,
+ ApplicationsContext,
Page404,
- Toast
+ Toast,
+ AuthContext
} from "@caraml-dev/ui-lib";
import { EuiProvider } from "@elastic/eui";
-import React from "react";
+import React, { useContext } from "react";
import { Route, Routes } from "react-router-dom";
import AppRoutes from "./AppRoutes";
import { PrivateLayout } from "./PrivateLayout";
import config from "./config";
-import { CaraMLAIPage } from "./caraml_ai/CaraMLAIPage";
+import { StreamlitPlaceholderPage } from "./streamlit_placeholder_page/StreamlitPlaceholderPage";
-const App = () => (
+const App = () => {
+ return (
(
timeout={config.TIMEOUT}
useMockData={config.USE_MOCK_DATA}>
-
- } />
-
- }>
- } />
-
-
- } />
-
- {
- config.CARAML_AI_STREAMLIT_HOMEPAGE &&
- }>
- } />
-
- }
-
-
+
+
+
+
-);
+)};
export default App;
+
+const MLPRoutes = () => {
+ const { apps, isLoaded } = useContext(ApplicationsContext);
+ const { state } = useContext(AuthContext);
+
+ // If the user is not authenticated, show the login page, WITHOUT rendering the additional streamlit app routes (they
+ // need to be retrieved from the MLP API v2/applications endpoint).
+ //
+ // If the user is already authenticated, check that the apps have been loaded into an array (the ApplicationsContext
+ // may still return a memoized empty object even if state.isAuthenticated is true because the API call happens
+ // asynchronously). If the apps have not been loaded yet, we return null until all the apps are loaded before
+ // rendering all the routes, including the streamlit app routes, at once (we do this because React routes are rendered
+ // synchronously and cannot be updated without reloading the entire page).
+ return (!state.isAuthenticated || (isLoaded && apps.constructor === Array)) ? (
+
+ } />
+
+ }>
+ } />
+ {/*If the user is authenticated, and all the apps have been loaded into an array, we construct a Route*/}
+ {/*corresponding to each Streamlit app*/}
+ {state.isAuthenticated &&
+ isLoaded && apps.constructor === Array &&
+ apps.map(app =>
+ } />
+ )
+ }
+
+
+ } />
+
+
+ ) : null;
+};
\ No newline at end of file
diff --git a/ui/packages/app/src/config.js b/ui/packages/app/src/config.js
index d2a7e8a0..3a301dc0 100644
--- a/ui/packages/app/src/config.js
+++ b/ui/packages/app/src/config.js
@@ -33,7 +33,6 @@ const config = {
),
CLOCKWORK_UI_HOMEPAGE: getEnv("REACT_APP_CLOCKWORK_UI_HOMEPAGE"),
KUBEFLOW_UI_HOMEPAGE: getEnv("REACT_APP_KUBEFLOW_UI_HOMEPAGE"),
- CARAML_AI_STREAMLIT_HOMEPAGE: getEnv("REACT_APP_CARAML_AI_STREAMLIT_HOMEPAGE"),
ALLOW_CUSTOM_STREAM:
getEnv("REACT_APP_ALLOW_CUSTOM_STREAM") != null
? getEnv("REACT_APP_ALLOW_CUSTOM_STREAM")
diff --git a/ui/packages/app/src/caraml_ai/CaraMLAIPage.js b/ui/packages/app/src/streamlit_placeholder_page/StreamlitPlaceholderPage.js
similarity index 66%
rename from ui/packages/app/src/caraml_ai/CaraMLAIPage.js
rename to ui/packages/app/src/streamlit_placeholder_page/StreamlitPlaceholderPage.js
index dc3892ad..b99a500f 100644
--- a/ui/packages/app/src/caraml_ai/CaraMLAIPage.js
+++ b/ui/packages/app/src/streamlit_placeholder_page/StreamlitPlaceholderPage.js
@@ -1,9 +1,8 @@
import React from "react";
import { EuiPageTemplate } from "@elastic/eui";
-import config from "./../config";
-export const CaraMLAIPage = () => {
- const iframe = ``
+export const StreamlitPlaceholderPage = ({ app }) => {
+ const iframe = ``
function Iframe(props) {
return (
);
@@ -12,8 +11,8 @@ export const CaraMLAIPage = () => {
diff --git a/ui/packages/lib/src/providers/application/context.js b/ui/packages/lib/src/providers/application/context.js
index d254fde7..7535b4f0 100644
--- a/ui/packages/lib/src/providers/application/context.js
+++ b/ui/packages/lib/src/providers/application/context.js
@@ -9,15 +9,15 @@ const ApplicationsContext = React.createContext({
export const ApplicationsContextProvider = ({ children }) => {
const location = useLocation();
- const [{ data: apps }] = useMlpApi("/v2/applications", {}, []);
+ const [{ data: apps, isLoaded }] = useMlpApi("/v2/applications", {}, []);
const currentApp = useMemo(
- () => apps.find(a => location.pathname.startsWith(a.homepage)),
- [apps, location.pathname]
+ () => isLoaded && apps.constructor === Array && apps.find(a => location.pathname.startsWith(a.homepage)),
+ [apps, isLoaded, location.pathname]
);
return (
-
+
{children}
);