Skip to content
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 standard ensembler UI #37

Merged

Conversation

deadlycoconuts
Copy link
Contributor

@deadlycoconuts deadlycoconuts commented Sep 19, 2022

What this PR does / why we need it:
With the changes introduced in Turing to support the usage of Standard Ensemblers with Custom Experiment Engines, the Turing Experiments UI has been updated in this PR to expose Standard Ensembler components consumed by the Turing UI.

There are two new components that Turing Experiments UI will now expose - EditStandardEnsemblerConfig and StandardEnsemblerConfigDetails. As their names suggest, they are used respectively for the create/edit router form and the router details view in the Turing UI respectively.

StandardEnsemblerConfigDetails
Screenshot 2022-09-21 at 1 53 03 PM

EditStandardEnsemblerConfig
Screenshot 2022-09-21 at 5 44 32 PM

Sample User Workflow

Screen.Recording.2022-09-21.at.5.46.07.PM.mov

In addition, the appConfig now has a new routeNamePathPrefix field that specifies the path prefix that gets appended to a user-defined treatment configuration. This path prefix reflects the nesting of the treatment configuration within the final response payload that the treatment service, and/or subsequently the Turing Experiments plugin, sends back to the client.

For example, if the user-defined treatment configuration is:

{
    "route_name": "control"
    ...
}

and the client response is as follows:

{
    "treatment": {
        "configuration": {
            "route_name": "control"
            ...
        }
        ....
    }
    ...
}

then the path prefix will have to be specified as treatment.configuration. (including the final period).

Main Modifications

  • ui/craco.config.js - Addition of the exposed component paths to be consumed by the ModuleFederationPlugin
  • ui/src/config.js - Addition of the routeNamePathPrefix field to the appConfig
  • ui/src/turing/components/configuration/StandardEnsemblerConfigDetails.js - Addition of the new StandardEnsemblerConfigDetails component to be exposed
  • ui/src/turing/components/form/EditStandardEnsemblerConfig.js - Addition of the new EditStandardEnsemblerConfig

@deadlycoconuts deadlycoconuts self-assigned this Sep 19, 2022
@deadlycoconuts deadlycoconuts marked this pull request as ready for review September 21, 2022 06:18
@deadlycoconuts deadlycoconuts requested a review from a team September 21, 2022 09:51
Copy link
Contributor

@terryyylim terryyylim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM on the whole, left some comments to be addressed, thank you @deadlycoconuts !

</EuiButtonEmpty>
);

return isButtonPopoverOpen[item.id] ? (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use && and not have a ternary that returns null here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for catching that! I've just edited it!

Copy link
Contributor Author

@deadlycoconuts deadlycoconuts Sep 23, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correction: I remembered I need null as a return value because a function passed to the render prop needs to return minimally a null response as opposed to undefined which is what may happen if you use && and the first condition returns undefined
Screenshot 2022-09-23 at 4 20 55 PM

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be addressed by this suggestion - #37 (comment).

field: "status",
width: "5px",
render: (_, item) => {
const isAssigned = routeToExperimentMappings[item.id] ?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

status and route_name column computations seem to be duplicated here. Would it make sense to pre-aggregate all column values prior to passing them into the items prop of EuiInMemoryTable?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All the columns (with the exception of status) actually use multiple column values (of the rows in items, which represents the routes configured) to render the cells of the table. While it's easy to precompute the cells for status (isAssigned is the only value needed to determine status) and store them as a column, route_name for example needs both the value of isAssigned as well as the id of each route.

This poses a problem because it's not possible to pass 2 (or more) values into the field prop of EuiTableFieldDataColumnType for the EuiBasicTable to extract both values and then pass them as arguments to the function specified in the render prop. This means that whatever precomputed values aggregated in advance would only be useful for the partially rendering each cell, because the function of the render prop would still have to access or compute other values from each row on-the-fly to determine how the cell ultimately gets rendered.

That said, I think this makes aggregating the computations in advance less useful than expected, since we aren't just extracting a single value to display for each cell, and still relying on the render prop to perform the rendering.

On the other hand, I've not tried precomputing each column as a ReactNode component and then getting the table to extract these values as is (without having to rely on the render function) i.e. having the field-s set as "column_1", "column_2", "column_3" ..., and then having these keys within each of the elements in items corresponding to <Column1... />, <Column2... />, <Column3... />, ... 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a slight update on this, I think we can just use id in the field for all of the columns, and then get the render function to do something with the id since we're almost always using the routeToExperimentMappings with the route name.

@@ -13,7 +13,7 @@ export const getExperimentStatus = (experiment) => {
const statusMapping = {
Deactivated: {
label: "Deactivated",
color: "subdued",
color: "#6A717D",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed this colour which is no longer supported as part of the EUI colour palette; it now needs to be specified manually with a colour hex.

}) => {
const { appConfig } = useConfig();

const [isButtonPopoverOpen, setIsButtonPopoverOpen] = useState(routes.reduce((m, r) => {m[r.id] = {running: false, scheduled: false}; return m}, {}));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm having this state in the parent component makes the implementation a little more complex and less coupled with the button that it belongs to. Can this be defined directly inside LinkedExperimentsContextMenu component so that each clickable item has its own state? And can it simply track true/false ? Then, if any of them are true, we can use the routeToExperimentMappings to list the experiments in the popover, for that specific status - right?

We will be creating more state variables but since they are all lean, I don't think that should be a problem.

Copy link
Contributor Author

@deadlycoconuts deadlycoconuts Sep 23, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I remember making a conscious choice to manage all the states within the parent because creating the individual states within LinkedExperimentsContextMenu somehow threw some error or minimally a warning regarding the creation of individual states by looping through with the function in render. I'll try to reproduce the issue again and share that error again.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay I can't seem to get the error to reappear, maybe it was something else :/ I've made the changes to track the state of each button within each LinkedExperimentsContextMenu, and we'll be using useToggle to get the true/false state of the button.

@deadlycoconuts
Copy link
Contributor Author

Thanks everyone for all the comments! I should've most of them covered. There's some significant refactoring done to LinkedRouteTable and LinkedExperimentsContextMenu, so it'd be nice to take a look at that!

Copy link
Contributor

@terryyylim terryyylim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @deadlycoconuts , PR looks cleaner now! I left some small comments - feel free to merge after addressing them 😄


// this stringified value of routes below allows the React effect below to mimic a deep comparison when changes to the
// array routes are made
const stringifiedRoutes = JSON.stringify(routes)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be better to use the ids only?

const stringifiedRoutes = routes.map(e => e.id).join()

@deadlycoconuts
Copy link
Contributor Author

It seems like all of the issues on this PR has been resolved; I'll be merging this in a short while! Thanks a lot everyone for your time! :)

@deadlycoconuts deadlycoconuts merged commit 734b0ab into caraml-dev:main Sep 26, 2022
@deadlycoconuts deadlycoconuts deleted the add_standard_ensembler_ui branch September 26, 2022 10:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants