Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 107 additions & 5 deletions webapps/console/components/DataView/EventsBrowser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,13 @@ type EventsBrowserState = {
error?: string;
};

const ALL_ACTORS = "all";
/**
* Pseudo-entity for the "no connection selected" case: events of every connection of the workspace.
* `mode` is the default for the bulker view where the mode selector is bound to the selected entity
*/
const allConnectionsEntity = { id: ALL_ACTORS, name: "All Connections", type: "all", mode: "batch" };

const defaultState: EventsBrowserState = {
bulkerMode: undefined,
eventsLoading: false,
Expand Down Expand Up @@ -213,6 +220,7 @@ const EventsBrowser0 = ({
? streams
: streamType == "function"
? [
allConnectionsEntity,
Comment thread
vklimontovich marked this conversation as resolved.
...mappedConnections,
...profileBuilders
.filter(p => p.version > 0)
Expand All @@ -225,7 +233,7 @@ const EventsBrowser0 = ({
]
: streamType == "dead-letter"
? [
{ id: "all", name: "All Connections", type: "all" },
allConnectionsEntity,
...mappedConnections,
...profileBuilders
.filter(p => p.version > 0)
Expand All @@ -237,6 +245,7 @@ const EventsBrowser0 = ({
}),
]
: [
allConnectionsEntity,
...mappedConnections.filter(link => link.usesBulker || link.hybrid),
...(hasActiveProfileBuilder
? destinations
Expand Down Expand Up @@ -358,7 +367,10 @@ const EventsBrowser0 = ({
setConnection(connection);
setDebugEnabled(new Date(connection.data.debugTill) > new Date());
} else {
//"All Connections" or a profile builder - debug belongs to a single connection, and the
//banner would otherwise keep announcing it for a connection that is no longer selected
setConnection(undefined);
setDebugEnabled(false);
}
})();
}
Expand Down Expand Up @@ -420,7 +432,8 @@ const EventsBrowser0 = ({
const data = await eventsLogApi.get(
`${eventsLogStream}`,
level === "all" ? "all" : [level],
actorId,
//no actor selected - events of all the connections of the workspace
actorId === ALL_ACTORS ? undefined : actorId,
{
start: dates && dates[0] ? new Date(dates[0]) : undefined,
end: beforeDate || (dates && dates[1] ? new Date(dates[1]) : undefined),
Expand Down Expand Up @@ -735,6 +748,7 @@ const EventsBrowser0 = ({
entityType={entityType}
actorId={actorId}
mappedConnections={mappedConnectionsMap}
actorsMap={entitiesMap}
events={shownEvents}
loadEvents={() =>
dispatch({
Expand Down Expand Up @@ -762,10 +776,59 @@ type TableProps = {
entityType: string;
actorId: string;
mappedConnections: Record<string, any>;
/** All entities that can be selected in the connection selector, including profile builders */
actorsMap?: Record<string, any>;
loadEvents: () => void;
};

const FunctionsLogTable = ({ loadEvents, loading, streamType, entityType, actorId, events }: TableProps) => {
/** Both sides of a connection must fit into a narrow column - see TruncationPolicy */
const actorTruncation = 13;

/**
* Renders a link to the connection (or profile builder) an event belongs to. Used for the extra
* column that is shown when events of all connections are displayed at once
*/
const ActorLink: React.FC<{ actorId?: string; actorsMap?: Record<string, any> }> = ({ actorId, actorsMap }) => {
if (!actorId) {
return null;
}
const actor = actorsMap?.[actorId];
if (!actor || actor.type === "all") {
return <span className="font-mono text-xs">{trimMiddle(actorId, 16)}</span>;
}
const title =
actor.type === "profile-builder" ? (
<ProfileBuilderTitle profileBuilder={actor} destination={actor.destination} truncationPolicy={actorTruncation} />
) : (
<ConnectionTitle
connectionId={actorId}
stream={actor.stream}
service={actor.service}
destination={actor.destination}
truncationPolicy={actorTruncation}
/>
);
const href = actor.type === "profile-builder" ? `/profile-builder` : `/connections/edit?id=${actorId}`;
return (
<Tooltip title={actor.name} overlayClassName="min-w-fit">
{/* stopPropagation - otherwise a click opens the event drawer along with the navigation */}
<span onClick={e => e.stopPropagation()}>
<WLink href={href}>{title}</WLink>
</span>
</Tooltip>
);
};

/** Column shown instead of the connection selector when events of all connections are displayed */
const actorColumn = (actorsMap?: Record<string, any>) => ({
title: "Connection",
width: "20em",
dataIndex: "actorId",
key: "actorId",
render: (actorId: string) => <ActorLink actorId={actorId} actorsMap={actorsMap} />,
});

const FunctionsLogTable = ({ loadEvents, loading, streamType, entityType, actorId, events, actorsMap }: TableProps) => {
const workspace = useWorkspace();
const [funcsMap, setFuncsMap] = useState<Record<string, FunctionConfig>>({});

Expand Down Expand Up @@ -812,6 +875,7 @@ const FunctionsLogTable = ({ loadEvents, loading, streamType, entityType, actorI
width: "13em",
render: d => <UTCDate date={d} />,
},
...(actorId === ALL_ACTORS ? [actorColumn(actorsMap) as any] : []),
{
title: "Function",
width: "14em",
Expand Down Expand Up @@ -916,7 +980,7 @@ const FunctionsLogTable = ({ loadEvents, loading, streamType, entityType, actorI
);
};

const StreamEventsTable = ({ loadEvents, loading, streamType, entityType, actorId, events }: TableProps) => {
const StreamEventsTable = ({ loadEvents, loading, streamType, entityType, actorId, events, actorsMap }: TableProps) => {
const streamEvents = events
? events.map((e, i) => {
e = {
Expand All @@ -939,6 +1003,7 @@ const StreamEventsTable = ({ loadEvents, loading, streamType, entityType, actorI
width: "13em",
render: d => <UTCDate date={d} />,
},
...(actorId === ALL_ACTORS ? [actorColumn(actorsMap) as any] : []),
{
title: "Queue size",
width: "7em",
Expand Down Expand Up @@ -1012,7 +1077,36 @@ const StreamEventsTable = ({ loadEvents, loading, streamType, entityType, actorI
);
};

const BatchTable = ({ loadEvents, loading, streamType, entityType, actorId, events }: TableProps) => {
const estimatedCostNote =
"Estimation of how much the data warehouse charges for this load. The exact amount may be different due to " +
"discounts, committed use pricing, free tiers, etc.";

const EstimatedCostHeader: React.FC<{}> = () => (
<Tooltip overlayClassName="max-w-96" title={estimatedCostNote}>
<span className="whitespace-nowrap">
Est. cost <QuestionCircleOutlined />
</span>
</Tooltip>
);

const formatCost = (cost: number) =>
cost < 0.0001
? "< $0.0001"
: "$" + cost.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 4 });

const EstimatedCost: React.FC<{ cost?: number }> = ({ cost }) => {
//nothing charged (or nothing reported) - an empty cell is less noisy than a row of $0
if (typeof cost !== "number" || isNaN(cost) || cost === 0) {
return null;
}
return (
<Tooltip overlayClassName="max-w-96" title={`${estimatedCostNote} Exact value: ${cost}`}>
<span className="whitespace-nowrap">{formatCost(cost)}</span>
</Tooltip>
);
};

const BatchTable = ({ loadEvents, loading, streamType, entityType, actorId, events, actorsMap }: TableProps) => {
const batchEvents = (events || ([] as EventsLogRecord[])).map((e, i) => ({
...e,
id: e.date + "_" + i,
Expand All @@ -1025,6 +1119,7 @@ const BatchTable = ({ loadEvents, loading, streamType, entityType, actorId, even
width: "13em",
render: d => <UTCDate date={d} />,
},
...(actorId === ALL_ACTORS ? [actorColumn(actorsMap) as any] : []),
{
title: "Batch size",
width: "7em",
Expand All @@ -1037,6 +1132,13 @@ const BatchTable = ({ loadEvents, loading, streamType, entityType, actorId, even
dataIndex: ["content", "queueSize"],
key: "queue",
},
{
title: <EstimatedCostHeader />,
width: "9em",
dataIndex: ["content", "statistics", "estimatedCost"],
key: "cost",
render: (cost: number | undefined) => <EstimatedCost cost={cost} />,
},
{
title: "Status",
width: "8em",
Expand Down
Loading
Loading