-
Notifications
You must be signed in to change notification settings - Fork 23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add ability to load custom Experiment Engine UI #101
Add ability to load custom Experiment Engine UI #101
Conversation
"react-scripts": "5.0.0-next.37" | ||
}, | ||
"resolutions": { | ||
"react-scripts/**/postcss-normalize": "10.0.1" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Context: facebook/create-react-app#11278 (comment)
Currently, this will lead to a warning on yarn install
:
warning Resolution field "[email protected]" is incompatible with requested version
"[email protected]"
@@ -1,28 +1,51 @@ | |||
const proxy = require("http-proxy-middleware"); | |||
const { createProxyMiddleware } = require("http-proxy-middleware"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
http-proxy-middleware
dependency version has been upgraded in the new CRA.
ui/.env.development
Outdated
# JSON string map, key being the (unroutable) path used by the remote app for API calls, | ||
# and value being the URL to proxy to. Used only in local development mode, to bypass CORS. | ||
# More info: https://create-react-app.dev/docs/proxying-api-requests-in-development/ | ||
REMOTE_COMPONENTS_PROXY_PATHS= |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Used by setupProxy.js
for local development. REACT_APP_
prefix is not required here.
ui/.gitignore
Outdated
@@ -15,3 +15,6 @@ yarn-error.log* | |||
|
|||
.env.development.local | |||
/.eslintcache | |||
|
|||
# linting | |||
.husky/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I realise now there is a .gitignore
inside this generated folder, so it's unnecessary to mention it here - removed.
ui/.env.development
Outdated
@@ -14,4 +14,13 @@ REACT_APP_USER_DOCS_URL=[{"href":"https://github.com/gojek/turing","label":"Turi | |||
# Link to the .proto file used by the Kafka Result Logger when using Protobuf serialization | |||
REACT_APP_RESULT_LOG_PROTO_URL= | |||
|
|||
# Default Experiment Engine Remote UI settings | |||
REACT_APP_DEFAULT_EXPERIMENT_ENGINE_REMOTE_APPNAME= | |||
REACT_APP_DEFAULT_EXPERIMENT_ENGINE_REMOTE_URL= |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since these two variables needs to be defined simultaneously (either both or none), they can be combined into a single env variable defined as JSON object, similar to REACT_APP_USER_DOCS_URL
.
} | ||
|
||
// Load component from remote host | ||
const Component = React.lazy( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can it be called ExperimentsLandingPage
then?
); | ||
|
||
return ( | ||
<React.Suspense fallback="Loading Experiments"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the difference between this fallback="Loading Experiments"
and "Loading Experiment Exgine ..."
at L:36? In what scenarios each of them will be shown?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Loading Experiment Engine
happens when the remoteEntry.js
file is being retrieved and appended to the html head. This part is now required because we are not setting the remote module's configurations in craco.config.js
at build time anymore.
fallback="Loading Experiments"
or later for the router form, may be fallback="Loading Experiment Configuration"
is a placeholder while React lazy-loads the component (using loadComponent
in the preceding lines). It's hardly noticeable but still required - without the fallback
property, the rendering will fail in the first attempt.
Refactored the file to use <FallbackView/>
for all scenarios where the remote component is not successfully loaded.
if (!ready || failed) { | ||
return ( | ||
<EuiPage> | ||
<EuiPageBody> | ||
<EuiPageHeader> | ||
<EuiPageHeaderSection> | ||
<PageTitle title="Experiments" /> | ||
</EuiPageHeaderSection> | ||
</EuiPageHeader> | ||
<EuiPageContent> | ||
<EuiText size="s" color="subdued"> | ||
{!defaultExperimentEngine.url | ||
? "No default Experiment Engine configured" | ||
: !ready | ||
? "Loading Experiment Exgine ..." | ||
: "Failed to load Experiment Exgine"} | ||
</EuiText> | ||
</EuiPageContent> | ||
</EuiPageBody> | ||
</EuiPage> | ||
); | ||
} | ||
|
||
// Load component from remote host | ||
const Component = React.lazy( | ||
loadComponent(defaultExperimentEngine.appName, "./ExperimentsLandingPage") | ||
); | ||
|
||
return ( | ||
<React.Suspense fallback="Loading Experiments"> | ||
<Component projectId={projectId} /> | ||
</React.Suspense> | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (!ready || failed) { | |
return ( | |
<EuiPage> | |
<EuiPageBody> | |
<EuiPageHeader> | |
<EuiPageHeaderSection> | |
<PageTitle title="Experiments" /> | |
</EuiPageHeaderSection> | |
</EuiPageHeader> | |
<EuiPageContent> | |
<EuiText size="s" color="subdued"> | |
{!defaultExperimentEngine.url | |
? "No default Experiment Engine configured" | |
: !ready | |
? "Loading Experiment Exgine ..." | |
: "Failed to load Experiment Exgine"} | |
</EuiText> | |
</EuiPageContent> | |
</EuiPageBody> | |
</EuiPage> | |
); | |
} | |
// Load component from remote host | |
const Component = React.lazy( | |
loadComponent(defaultExperimentEngine.appName, "./ExperimentsLandingPage") | |
); | |
return ( | |
<React.Suspense fallback="Loading Experiments"> | |
<Component projectId={projectId} /> | |
</React.Suspense> | |
); | |
// Load component from remote host | |
const ExperimentsLandingPage = React.lazy( | |
loadComponent(defaultExperimentEngine.appName, "./ExperimentsLandingPage") | |
); | |
return ready | |
? ( | |
<React.Suspense fallback="Loading Experiments"> | |
<ExperimentsLandingPage projectId={projectId} /> | |
</React.Suspense> | |
) : ( | |
<EuiPage> | |
<EuiPageBody> | |
<EuiPageHeader> | |
<EuiPageHeaderSection> | |
<PageTitle title="Experiments" /> | |
</EuiPageHeaderSection> | |
</EuiPageHeader> | |
<EuiPageContent> | |
<EuiText size="s" color="subdued"> | |
{ failed | |
? (!!defaultExperimentEngine.url | |
? "Failed to load Experiment Exgine" | |
: "No default Experiment Engine configured" | |
) : "Loading Experiment Exgine ..." | |
} | |
</EuiText> | |
</EuiPageContent> | |
</EuiPageBody> | |
</EuiPage> | |
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But more specifically, I wonder if this component should be broke down into two:
- When
defaultExperimentEngine.url
is not configured at all then we don't need to try to load something from remote host and just render"No default Experiment Engine configured"
msg - When
defaultExperimentEngine.url
is configured, thenuseDynamicScript
andloadComponent
are called and proper remote component is rendered.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the suggestion. Refactored this file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, your changes introducing many formatting changes, which makes the review process less pleasant. We use prettier
for having a shared formatting configuration (it's defined here), and formatting is triggered with husky's pre-commit hook.
Seems like you are using a newer version of prettier
than I do and --jsx-bracket-same-line
option is deprecated since v2.40, hence the discrepancy. Can you please replace --jsx-bracket-same-line
in package.json
with --bracket-same-line
and reformat your branch with:
yarn prettier "src/**/*.{js,jsx,ts,tsx,json,css,scss,md}" --bracket-same-line --write
@romanwozniak thanks for the review. I've addressed your comments. Could you have another look? |
This PR adds the ability to load a remote UI component exposed by an experiment engine, at the
/experiments
path of the Turing app. This is made possible by the following 2 configurations (added toui/.env.development
):REACT_APP_DEFAULT_EXPERIMENT_ENGINE
REMOTE_COMPONENTS_PROXY_PATHS
The first one introduces the concept of a 'default' experiment engine, tied to the Turing app. Although Turing support multiple experiment engines when configuring routers, the
/experiments
endpoint is meant to configure the experiments themselves and this will only be possible for one experiment engine that is recognised as the default. In the future, this env var will be removed and the value will instead be obtained through the Turing API.The 2nd env var will be used only in the local environment, to proxy API requests required by the remote component so that CORS issues can be bypassed.
The remote component is created and consumed with the use of Module Federation. Some information on the main changes are described below.
Modifications
ui/package.json
- Upgrade to CRA5 alpha build, use craco to override the Webpack configsui/craco.config.js
- Add Module Federation settingsui/src/setupProxy.js
- Add proxy settings for API calls made by the remote componentui/src/utils/remoteComponent.js
,ui/src/hooks/useDynamicScript.js
- Make it possible to load the remote module dynamically, as opposed to specifying the experiment engine remote in the Craco configs (ref: module-federation-examples).ui/src/experiment/ExperimentsRouter.js
- Dynamically load the experiment engine's landing page or show an appropriate message if it is not configured or if the load fails.ui/src/App.js
- Register the/experiments
route.ui/src/bootstrap.js
- Moved the contents ofui/src/index.js
here to let it be loaded asynchronously.The other changes in this PR are automatic formatting fixes.
References