mirror of
https://github.com/coder/coder.git
synced 2025-07-18 14:17:22 +00:00
fix: handle missed actions in workspace timings (#17593)
Fix https://github.com/coder/coder/issues/16409 Since the provisioner timings action is not strongly typed, but it is typed as a generic string, and we are not using `noUncheckedIndexedAccess`, we can miss some of the actions returned from the API, causing type errors. To avoid that, I changed the code to be extra safe by adding `undefined` into the return type.
This commit is contained in:
@ -57,7 +57,7 @@ export const ResourcesChart: FC<ResourcesChartProps> = ({
|
||||
const theme = useTheme();
|
||||
const legendsByAction = getLegendsByAction(theme);
|
||||
const visibleLegends = [...new Set(visibleTimings.map((t) => t.action))].map(
|
||||
(a) => legendsByAction[a],
|
||||
(a) => legendsByAction[a] ?? { label: a },
|
||||
);
|
||||
|
||||
return (
|
||||
@ -99,6 +99,7 @@ export const ResourcesChart: FC<ResourcesChartProps> = ({
|
||||
<XAxisSection>
|
||||
{visibleTimings.map((t) => {
|
||||
const duration = calcDuration(t.range);
|
||||
const legend = legendsByAction[t.action] ?? { label: t.action };
|
||||
|
||||
return (
|
||||
<XAxisRow
|
||||
@ -117,7 +118,7 @@ export const ResourcesChart: FC<ResourcesChartProps> = ({
|
||||
value={duration}
|
||||
offset={calcOffset(t.range, generalTiming)}
|
||||
scale={scale}
|
||||
colors={legendsByAction[t.action].colors}
|
||||
colors={legend.colors}
|
||||
/>
|
||||
</Tooltip>
|
||||
{formatTime(duration)}
|
||||
@ -139,11 +140,20 @@ export const isCoderResource = (resource: string) => {
|
||||
);
|
||||
};
|
||||
|
||||
function getLegendsByAction(theme: Theme): Record<string, ChartLegend> {
|
||||
// TODO: We should probably strongly type the action attribute on
|
||||
// ProvisionerTiming to catch missing actions in the record. As a "workaround"
|
||||
// for now, we are using undefined since we don't have noUncheckedIndexedAccess
|
||||
// enabled.
|
||||
function getLegendsByAction(
|
||||
theme: Theme,
|
||||
): Record<string, ChartLegend | undefined> {
|
||||
return {
|
||||
"state refresh": {
|
||||
label: "state refresh",
|
||||
},
|
||||
provision: {
|
||||
label: "provision",
|
||||
},
|
||||
create: {
|
||||
label: "create",
|
||||
colors: {
|
||||
|
@ -152,3 +152,79 @@ export const LongTimeRange = {
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
// We want to gracefully handle the case when the action is added in the BE but
|
||||
// not in the FE. This is a temporary fix until we can have strongly provisioner
|
||||
// timing action types in the BE.
|
||||
export const MissedAction: Story = {
|
||||
args: {
|
||||
agentConnectionTimings: [
|
||||
{
|
||||
ended_at: "2025-03-12T18:15:13.651163Z",
|
||||
stage: "connect",
|
||||
started_at: "2025-03-12T18:15:10.249068Z",
|
||||
workspace_agent_id: "41ab4fd4-44f8-4f3a-bb69-262ae85fba0b",
|
||||
workspace_agent_name: "Interface",
|
||||
},
|
||||
],
|
||||
agentScriptTimings: [
|
||||
{
|
||||
display_name: "Startup Script",
|
||||
ended_at: "2025-03-12T18:16:44.771508Z",
|
||||
exit_code: 0,
|
||||
stage: "start",
|
||||
started_at: "2025-03-12T18:15:13.847336Z",
|
||||
status: "ok",
|
||||
workspace_agent_id: "41ab4fd4-44f8-4f3a-bb69-262ae85fba0b",
|
||||
workspace_agent_name: "Interface",
|
||||
},
|
||||
],
|
||||
provisionerTimings: [
|
||||
{
|
||||
action: "create",
|
||||
ended_at: "2025-03-12T18:08:07.402358Z",
|
||||
job_id: "a7c4a05d-1c36-4264-8275-8107c93c5fc8",
|
||||
resource: "coder_agent.Interface",
|
||||
source: "coder",
|
||||
stage: "apply",
|
||||
started_at: "2025-03-12T18:08:07.194957Z",
|
||||
},
|
||||
{
|
||||
action: "create",
|
||||
ended_at: "2025-03-12T18:08:08.029908Z",
|
||||
job_id: "a7c4a05d-1c36-4264-8275-8107c93c5fc8",
|
||||
resource: "null_resource.validate_url",
|
||||
source: "null",
|
||||
stage: "apply",
|
||||
started_at: "2025-03-12T18:08:07.399387Z",
|
||||
},
|
||||
{
|
||||
action: "create",
|
||||
ended_at: "2025-03-12T18:08:07.440785Z",
|
||||
job_id: "a7c4a05d-1c36-4264-8275-8107c93c5fc8",
|
||||
resource: "module.emu_host.random_id.emulator_host_id",
|
||||
source: "random",
|
||||
stage: "apply",
|
||||
started_at: "2025-03-12T18:08:07.403171Z",
|
||||
},
|
||||
{
|
||||
action: "missed action",
|
||||
ended_at: "2025-03-12T18:08:08.029752Z",
|
||||
job_id: "a7c4a05d-1c36-4264-8275-8107c93c5fc8",
|
||||
resource: "null_resource.validate_url",
|
||||
source: "null",
|
||||
stage: "apply",
|
||||
started_at: "2025-03-12T18:08:07.410219Z",
|
||||
},
|
||||
],
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const user = userEvent.setup();
|
||||
const canvas = within(canvasElement);
|
||||
const applyButton = canvas.getByRole("button", {
|
||||
name: "View apply details",
|
||||
});
|
||||
await user.click(applyButton);
|
||||
await canvas.findByText("missed action");
|
||||
},
|
||||
};
|
||||
|
Reference in New Issue
Block a user