Actor Search Bar in Logical View (#8865)

This commit is contained in:
Max Fitton 2020-06-10 18:43:56 -07:00 committed by GitHub
parent 439dbb7822
commit 950b389581
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 127 additions and 80 deletions

View file

@ -109,6 +109,38 @@ export type NodeInfoResponse = {
export const getNodeInfo = () => get<NodeInfoResponse>("/api/node_info", {});
export type RayletActorInfo =
| {
actorId: string;
actorTitle: string;
averageTaskExecutionSpeed: number;
children: RayletInfoResponse["actors"];
// currentTaskFuncDesc: string[];
ipAddress: string;
jobId: string;
nodeId: string;
numExecutedTasks: number;
numLocalObjects: number;
numObjectIdsInScope: number;
pid: number;
port: number;
state: 0 | 1 | 2;
taskQueueLength: number;
timestamp: number;
usedObjectStoreMemory: number;
usedResources: { [key: string]: number };
currentTaskDesc?: string;
numPendingTasks?: number;
webuiDisplay?: Record<string, string>;
}
| {
actorId: string;
actorTitle: string;
requiredResources: { [key: string]: number };
state: -1;
invalidStateType?: "infeasibleActor" | "pendingActor";
};
export type RayletInfoResponse = {
nodes: {
[ip: string]: {
@ -120,37 +152,7 @@ export type RayletInfoResponse = {
};
};
actors: {
[actorId: string]:
| {
actorId: string;
actorTitle: string;
averageTaskExecutionSpeed: number;
children: RayletInfoResponse["actors"];
// currentTaskFuncDesc: string[];
ipAddress: string;
jobId: string;
nodeId: string;
numExecutedTasks: number;
numLocalObjects: number;
numObjectIdsInScope: number;
pid: number;
port: number;
state: 0 | 1 | 2;
taskQueueLength: number;
timestamp: number;
usedObjectStoreMemory: number;
usedResources: { [key: string]: number };
currentTaskDesc?: string;
numPendingTasks?: number;
webuiDisplay?: Record<string, string>;
}
| {
actorId: string;
actorTitle: string;
requiredResources: { [key: string]: number };
state: -1;
invalidStateType?: "infeasibleActor" | "pendingActor";
};
[actorId: string]: RayletActorInfo;
};
};

View file

@ -13,7 +13,7 @@ import {
getProfilingResultURL,
launchKillActor,
launchProfiling,
RayletInfoResponse,
RayletActorInfo,
} from "../../../api";
import Actors from "./Actors";
@ -61,7 +61,7 @@ const styles = (theme: Theme) =>
});
type Props = {
actor: RayletInfoResponse["actors"][keyof RayletInfoResponse["actors"]];
actor: RayletActorInfo;
};
type State = {

View file

@ -1,21 +1,18 @@
import { createStyles, Theme, withStyles, WithStyles } from "@material-ui/core";
import React from "react";
import React, { Fragment } from "react";
import { RayletInfoResponse } from "../../../api";
import Actor from "./Actor";
const styles = (theme: Theme) => createStyles({});
type Props = {
type ActorProps = {
actors: RayletInfoResponse["actors"];
};
class Actors extends React.Component<Props & WithStyles<typeof styles>> {
render() {
const { actors } = this.props;
return Object.entries(actors).map(([actorId, actor]) => (
<Actor actor={actor} key={actorId} />
));
}
}
const Actors = (props: ActorProps) => {
const { actors } = props;
export default withStyles(styles)(Actors);
const actorChildren = Object.entries(actors).map(([actorId, actor]) => (
<Actor actor={actor} key={actorId} />
));
return <Fragment>{actorChildren}</Fragment>;
};
export default Actors;

View file

@ -1,48 +1,96 @@
import {
createStyles,
Theme,
FormControl,
FormHelperText,
Input,
InputLabel,
Typography,
WithStyles,
withStyles,
} from "@material-ui/core";
import React from "react";
import React, { useState } from "react";
import { connect } from "react-redux";
import { RayletActorInfo, RayletInfoResponse } from "../../../api";
import { StoreState } from "../../../store";
import Actors from "./Actors";
const styles = (theme: Theme) =>
createStyles({
warning: {
fontSize: "0.8125rem",
marginBottom: theme.spacing(2),
},
warningIcon: {
fontSize: "1.25em",
verticalAlign: "text-bottom",
},
});
const actorMatchesSearch = (
actor: RayletActorInfo,
nameFilter: string,
): boolean => {
// Performs a case insensitive search for the name filter string within the
// actor and all of its nested subactors.
const actorTitles = getNestedActorTitles(actor);
const loweredNameFilter = nameFilter.toLowerCase();
const match = actorTitles.find(
(actorTitle) => actorTitle.toLowerCase().search(loweredNameFilter) !== -1,
);
return match !== undefined;
};
const getNestedActorTitles = (actor: RayletActorInfo): string[] => {
const actorTitle = actor.actorTitle;
const titles: string[] = actorTitle ? [actorTitle] : [];
// state of -1 indicates an actor data record that does not have children.
if (actor.state === -1) {
return titles;
}
const children = actor["children"];
if (children === undefined || Object.entries(children).length === 0) {
return titles;
}
const childrenTitles = Object.values(children).flatMap((actor) =>
getNestedActorTitles(actor),
);
return titles.concat(childrenTitles);
};
const mapStateToProps = (state: StoreState) => ({
rayletInfo: state.dashboard.rayletInfo,
});
class LogicalView extends React.Component<
WithStyles<typeof styles> & ReturnType<typeof mapStateToProps>
> {
render() {
const { rayletInfo } = this.props;
return (
<div>
{rayletInfo === null ? (
<Typography color="textSecondary">Loading...</Typography>
) : Object.entries(rayletInfo.actors).length === 0 ? (
<Typography color="textSecondary">No actors found.</Typography>
) : (
<Actors actors={rayletInfo.actors} />
)}
</div>
const filterObj = (obj: Object, filterFn: any) =>
Object.fromEntries(Object.entries(obj).filter(filterFn));
type LogicalViewProps = {
rayletInfo: RayletInfoResponse | null;
} & ReturnType<typeof mapStateToProps>;
const LogicalView = ({ rayletInfo }: LogicalViewProps) => {
const [nameFilter, setNameFilter] = useState("");
if (rayletInfo === null) {
return <Typography color="textSecondary">Loading...</Typography>;
}
let filteredActors = rayletInfo.actors;
if (nameFilter !== "") {
filteredActors = filterObj(
filteredActors,
([_, actor]: [any, RayletActorInfo]) =>
actorMatchesSearch(actor, nameFilter),
);
}
}
export default connect(mapStateToProps)(withStyles(styles)(LogicalView));
return (
<div>
{Object.entries(rayletInfo.actors).length === 0 ? (
<Typography color="textSecondary">No actors found.</Typography>
) : (
<div>
<FormControl>
<InputLabel htmlFor="actor-name-filter">Actor Search</InputLabel>
<Input
id="actor-name-filter"
aria-describedby="actor-name-helper-text"
value={nameFilter}
onChange={(event) => setNameFilter(event.target.value)}
/>
<FormHelperText id="actor-name-helper-text">
Search for an actor by name
</FormHelperText>
</FormControl>
<Actors actors={filteredActors} />
</div>
)}
</div>
);
};
export default connect(mapStateToProps)(LogicalView);