Skip to content

Commit 769d986

Browse files
committed
fix(datadog): tolerate non-string list inputs and keep the shipped mute subblock ids
Both defects were introduced by this branch. - splitCommaList called .split on its argument, so routing create_downtime's monitorId through it turned a legitimate numeric input into a TypeError before the request was built. A <Block.output> reference to get_monitor or list_monitors resolves to a number, and an LLM tool call can pass a number or an array, so the helper now normalizes all three shapes. The previous Number.parseInt path had accepted a number by coercion. - Adding the unmute operation renamed the mute subblock ids scope/end to muteScope/muteEnd. Workflow state is persisted by subblock id, so every existing Mute Monitor block would have kept the old keys and silently lost its scope and end time. Restored the shipped ids; both are still unique block-wide and no operation reads another operation's value.
1 parent a078ac3 commit 769d986

3 files changed

Lines changed: 58 additions & 18 deletions

File tree

apps/sim/blocks/blocks/datadog.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,12 @@ export const DatadogBlock: BlockConfig<DatadogResponse> = {
5353
],
5454
datadog_mute_monitor: [
5555
{ text: 'Mute monitor', field: 'muteMonitorId', core: true },
56-
{ text: ', for scope', field: 'muteScope' },
57-
{ text: ', until', field: 'muteEnd' },
56+
{ text: ', for scope', field: 'scope' },
57+
{ text: ', until', field: 'end' },
5858
],
5959
datadog_unmute_monitor: [
6060
{ text: 'Unmute monitor', field: 'muteMonitorId', core: true },
61-
{ text: ', for scope', field: 'muteScope' },
61+
{ text: ', for scope', field: 'scope' },
6262
],
6363
datadog_query_logs: [
6464
{ text: 'Search logs matching', field: 'logQuery', core: true },
@@ -528,7 +528,7 @@ Return ONLY valid JSON - no explanations, no markdown code blocks.`,
528528
required: { field: 'operation', value: ['datadog_mute_monitor', 'datadog_unmute_monitor'] },
529529
},
530530
{
531-
id: 'muteScope',
531+
id: 'scope',
532532
title: 'Scope',
533533
type: 'short-input',
534534
placeholder: 'host:myhost (leave blank for all scopes)',
@@ -539,7 +539,7 @@ Return ONLY valid JSON - no explanations, no markdown code blocks.`,
539539
mode: 'advanced',
540540
},
541541
{
542-
id: 'muteEnd',
542+
id: 'end',
543543
title: 'Mute Until (Unix Timestamp)',
544544
type: 'short-input',
545545
placeholder: 'Leave empty to mute until unmuted',
@@ -1995,15 +1995,15 @@ Return ONLY the search query string - no explanations.`,
19951995
return {
19961996
...baseParams,
19971997
monitorId: params.muteMonitorId,
1998-
scope: params.muteScope || undefined,
1999-
end: params.muteEnd ? Number(params.muteEnd) : undefined,
1998+
scope: params.scope || undefined,
1999+
end: params.end ? Number(params.end) : undefined,
20002000
}
20012001

20022002
case 'datadog_unmute_monitor':
20032003
return {
20042004
...baseParams,
20052005
monitorId: params.muteMonitorId,
2006-
scope: params.muteScope || undefined,
2006+
scope: params.scope || undefined,
20072007
allScopes: toSwitchBoolean(params.unmuteAllScopes),
20082008
}
20092009

@@ -2323,8 +2323,8 @@ Return ONLY the search query string - no explanations.`,
23232323
monitorId: { type: 'string', description: 'Monitor ID' },
23242324
// Logs
23252325
muteMonitorId: { type: 'string', description: 'Monitor ID to mute or unmute' },
2326-
muteScope: { type: 'string', description: 'Scope to mute or unmute' },
2327-
muteEnd: { type: 'number', description: 'Unix timestamp when the mute ends' },
2326+
scope: { type: 'string', description: 'Scope to mute or unmute' },
2327+
end: { type: 'number', description: 'Unix timestamp when the mute ends' },
23282328
unmuteAllScopes: { type: 'boolean', description: 'Clear mute settings for every scope' },
23292329
logQuery: { type: 'string', description: 'Log search query' },
23302330
logFrom: { type: 'string', description: 'Log start time' },

apps/sim/tools/datadog/datadog.test.ts

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@ import { submitMetricsTool } from '@/tools/datadog/submit_metrics'
1616
import { unmuteMonitorTool } from '@/tools/datadog/unmute_monitor'
1717
import { updateIncidentTool } from '@/tools/datadog/update_incident'
1818
import { updateSloTool } from '@/tools/datadog/update_slo'
19-
import { buildSloPayload, datadogErrorMessage, mergeSloUpdatePayload } from '@/tools/datadog/utils'
19+
import {
20+
buildSloPayload,
21+
datadogErrorMessage,
22+
mergeSloUpdatePayload,
23+
splitCommaList,
24+
} from '@/tools/datadog/utils'
2025

2126
const auth = { apiKey: 'key', applicationKey: 'app-key' } as const
2227

@@ -401,6 +406,36 @@ describe('create_downtime monitor targeting', () => {
401406
const body = callBody(createDowntimeTool, { ...auth, scope: '*', monitorId: '123' } as any)
402407
expect(body.data.attributes.monitor_identifier).toEqual({ monitor_id: 123 })
403408
})
409+
410+
/**
411+
* A `<Block.output>` reference to get_monitor resolves to a number, and an LLM tool
412+
* call can pass one too, so the parser must not assume a string.
413+
*/
414+
it('accepts a monitor id that arrives as a number', () => {
415+
const body = callBody(createDowntimeTool, { ...auth, scope: '*', monitorId: 123 } as any)
416+
expect(body.data.attributes.monitor_identifier).toEqual({ monitor_id: 123 })
417+
})
418+
419+
it('accepts monitor tags that arrive as an array', () => {
420+
const body = callBody(createDowntimeTool, {
421+
...auth,
422+
scope: '*',
423+
monitorTags: ['team:backend', 'priority:high'],
424+
} as any)
425+
expect(body.data.attributes.monitor_identifier).toEqual({
426+
monitor_tags: ['team:backend', 'priority:high'],
427+
})
428+
})
429+
})
430+
431+
describe('splitCommaList input tolerance', () => {
432+
it('handles strings, numbers, and arrays without throwing', () => {
433+
expect(splitCommaList('a, b')).toEqual(['a', 'b'])
434+
expect(splitCommaList(123)).toEqual(['123'])
435+
expect(splitCommaList([1, 2])).toEqual(['1', '2'])
436+
expect(splitCommaList(undefined)).toBeUndefined()
437+
expect(splitCommaList('')).toBeUndefined()
438+
})
404439
})
405440

406441
describe('registry surface', () => {

apps/sim/tools/datadog/utils.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,17 @@ export async function datadogErrorMessage(response: Response): Promise<string> {
6262
return messages.length > 0 ? messages.join('; ') : fallback
6363
}
6464

65-
/** Splits a comma-separated user input into a trimmed, non-empty list. */
66-
export function splitCommaList(value: string | undefined): string[] | undefined {
67-
if (!value) return undefined
68-
const items = value
69-
.split(',')
70-
.map((item) => item.trim())
65+
/**
66+
* Splits a comma-separated user input into a trimmed, non-empty list.
67+
*
68+
* Accepts `unknown` because a `<Block.output>` reference can resolve to a non-string:
69+
* a monitor ID read from `get_monitor` arrives as a number, and an LLM tool call can
70+
* pass an array. Calling `.split` on those would throw before the request is built.
71+
*/
72+
export function splitCommaList(value: unknown): string[] | undefined {
73+
if (value === undefined || value === null || value === '') return undefined
74+
const items = (Array.isArray(value) ? value : String(value).split(','))
75+
.map((item) => String(item).trim())
7176
.filter((item) => item.length > 0)
7277
return items.length > 0 ? items : undefined
7378
}
@@ -92,7 +97,7 @@ export function parseJsonParam<T>(value: unknown, fieldName: string): T | undefi
9297
* `Number('abc')` yields `NaN`, which `JSON.stringify` writes as `null` and Datadog
9398
* rejects with a message that names nothing the user typed, so bad input is rejected here.
9499
*/
95-
export function parseMonitorIds(value: string | undefined): number[] | undefined {
100+
export function parseMonitorIds(value: unknown): number[] | undefined {
96101
const items = splitCommaList(value)
97102
if (!items) return undefined
98103
return items.map((id) => {

0 commit comments

Comments
 (0)