Skip to content

Commit f830da9

Browse files
committed
fix(dynatrace): make the synthetic enabled filter tri-state
Review catch. `enabled` on List Synthetic Monitors is a three-way filter — enabled, disabled, or either — and I had it as a switch. The URL builder deliberately serializes `false` (there is a test pinning that `evaluate=false` survives), so leaving "Enabled Only" unchecked sent `enabled=false` and returned only the disabled monitors: exactly backwards. Made it a dropdown with Any / Enabled only / Disabled only, matching the monitorType field directly above it, which had the same shape and already used an empty-id "Any" option. The params mapper sends nothing for "Any". Checked the other nine switches rather than assuming. None share the bug: for each of them off genuinely means false, and false is Dynatrace's own default, so serializing it is correct. A test now pins that list so the trap cannot be re-introduced by converting one of them, alongside a test covering all three states of the filter.
1 parent 637b056 commit f830da9

2 files changed

Lines changed: 63 additions & 3 deletions

File tree

apps/sim/blocks/blocks/dynatrace.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1086,9 +1086,17 @@ Return ONLY the selector string - no explanations, no surrounding quotes.`,
10861086
condition: { field: 'operation', value: 'dynatrace_list_synthetic_monitors' },
10871087
},
10881088
{
1089+
// A tri-state filter, not a boolean: leaving it unset must send no
1090+
// `enabled` param at all. A switch would serialize its off position as
1091+
// `enabled=false` and silently return only the disabled monitors.
10891092
id: 'monitorEnabled',
1090-
title: 'Enabled Only',
1091-
type: 'switch',
1093+
title: 'Enabled State',
1094+
type: 'dropdown',
1095+
options: [
1096+
{ label: 'Any', id: '' },
1097+
{ label: 'Enabled only', id: 'true' },
1098+
{ label: 'Disabled only', id: 'false' },
1099+
],
10921100
mode: 'advanced',
10931101
condition: { field: 'operation', value: 'dynatrace_list_synthetic_monitors' },
10941102
},
@@ -1710,7 +1718,8 @@ Return ONLY the selector string - no explanations, no surrounding quotes.`,
17101718
return {
17111719
...baseParams,
17121720
type: params.monitorType || undefined,
1713-
enabled: params.monitorEnabled,
1721+
// '' means "any", so send no filter rather than enabled=false.
1722+
enabled: params.monitorEnabled ? params.monitorEnabled === 'true' : undefined,
17141723
location: params.monitorLocation || undefined,
17151724
tag: params.monitorTag || undefined,
17161725
managementZone: toNumber(params.monitorManagementZone),

apps/sim/tools/dynatrace/dynatrace.test.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
* @vitest-environment node
33
*/
44
import { describe, expect, it } from 'vitest'
5+
import { DynatraceBlock } from '@/blocks/blocks/dynatrace'
56
import { addTagsTool } from '@/tools/dynatrace/add_tags'
67
import { closeProblemTool } from '@/tools/dynatrace/close_problem'
78
import { createSettingsObjectTool } from '@/tools/dynatrace/create_settings_object'
@@ -128,6 +129,56 @@ describe('new-surface request shaping', () => {
128129
)
129130
})
130131

132+
it('treats the synthetic enabled filter as tri-state, not a boolean', () => {
133+
const params = (DynatraceBlock.tools.config?.params ?? (() => ({}))) as (
134+
p: Record<string, unknown>
135+
) => Record<string, unknown>
136+
const call = (monitorEnabled: string | undefined) =>
137+
params({
138+
operation: 'dynatrace_list_synthetic_monitors',
139+
environmentUrl: ENV,
140+
apiToken: TOKEN,
141+
monitorEnabled,
142+
})
143+
144+
// "Any" must send no filter — `enabled=false` would return only the
145+
// disabled monitors, which is the opposite of what the user asked for.
146+
expect(call('').enabled).toBeUndefined()
147+
expect(call(undefined).enabled).toBeUndefined()
148+
expect(call('true').enabled).toBe(true)
149+
expect(call('false').enabled).toBe(false)
150+
151+
expect(url(listSyntheticMonitorsTool, { environmentUrl: ENV, apiToken: TOKEN })).toBe(
152+
`${ENV}/api/v1/synthetic/monitors`
153+
)
154+
expect(
155+
url(listSyntheticMonitorsTool, { environmentUrl: ENV, apiToken: TOKEN, enabled: true })
156+
).toBe(`${ENV}/api/v1/synthetic/monitors?enabled=true`)
157+
})
158+
159+
it('keeps every remaining switch a real boolean, since off means false for each', () => {
160+
// monitorEnabled was the only tri-state filter. For the rest, Dynatrace's
161+
// own default is false, so serializing the off position is correct — this
162+
// pins that they stay switches rather than drifting into the same trap.
163+
const switches = DynatraceBlock.subBlocks
164+
.filter((sb) => sb.type === 'switch')
165+
.map((sb) => sb.id)
166+
expect(switches).not.toContain('monitorEnabled')
167+
expect(switches.sort()).toEqual(
168+
[
169+
'burnRateVisualizationEnabled',
170+
'deleteAllWithKey',
171+
'evaluate',
172+
'failOnPerformanceIssue',
173+
'showGlobalSlos',
174+
'sloEnabled',
175+
'stopOnProblem',
176+
'takeScreenshotsOnSuccess',
177+
'validateOnly',
178+
].sort()
179+
)
180+
})
181+
131182
it('repeats the synthetic tag param once per value', () => {
132183
expect(url(listSyntheticMonitorsTool, { ...base, tag: 'a, b' })).toBe(
133184
`${ENV}/api/v1/synthetic/monitors?tag=a&tag=b`

0 commit comments

Comments
 (0)