Skip to content

Commit 614eaf7

Browse files
authored
fix(tailscale): omit whitespace expiry and share response schemas (#9399)
1 parent 8911b77 commit 614eaf7

9 files changed

Lines changed: 216 additions & 369 deletions

File tree

‎apps/sim/blocks/blocks/tailscale.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -775,7 +775,7 @@ export const TailscaleBlock: BlockConfig = {
775775
if (
776776
params.operation === 'create_auth_key' &&
777777
params.expirySeconds != null &&
778-
params.expirySeconds !== ''
778+
!(typeof params.expirySeconds === 'string' && params.expirySeconds.trim() === '')
779779
) {
780780
const expirySeconds = Number(params.expirySeconds)
781781
if (!Number.isFinite(expirySeconds)) throw new Error('Expiry must be a finite number')

‎apps/sim/lib/workflows/editing/builders.test.ts‎

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { TailscaleBlock } from '@/blocks/blocks/tailscale'
2020
import { getAllBlocks, getBlock } from '@/blocks/registry'
2121
import { prepareBlockState } from '@/stores/workflows/prepare-block-state'
2222
import type { BlockState } from '@/stores/workflows/workflow/types'
23-
import { tailscaleAuthorizeDeviceTool } from '@/tools/tailscale'
23+
import { tailscaleAuthorizeDeviceTool, tailscaleCreateAuthKeyTool } from '@/tools/tailscale'
2424

2525
const mockIsIntegrationDeploymentAvailable =
2626
integrationsAvailabilityMockFns.mockIsIntegrationDeploymentAvailableForVisibility
@@ -214,6 +214,20 @@ describe('createBlockFromParams', () => {
214214
).toMatchObject({ authorized: false })
215215
})
216216

217+
it.each(['', ' ', null, undefined])(
218+
'omits cleared auth-key expiry %j so the provider default applies',
219+
(expirySeconds) => {
220+
const transform = TailscaleBlock.tools?.config?.params
221+
if (!transform) throw new Error('Tailscale has no parameter transform')
222+
const body = tailscaleCreateAuthKeyTool.request.body?.({
223+
apiKey: 'test-token',
224+
tailnet: '-',
225+
...transform({ operation: 'create_auth_key', expirySeconds }),
226+
})
227+
expect(JSON.stringify(body)).not.toContain('"expirySeconds"')
228+
}
229+
)
230+
217231
it('exposes the output ports of the seeded Agent default', () => {
218232
const block = createBlockFromParams(
219233
'agent-research-default',
Lines changed: 4 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
import { DNS_CONFIGURATION_OUTPUT } from '@/tools/tailscale/outputs'
12
import type {
23
TailscaleBaseParams,
34
TailscaleDnsConfiguration,
45
TailscaleGetDnsConfigurationResponse,
56
} from '@/tools/tailscale/types'
6-
import { assertTailscaleResponse } from '@/tools/tailscale/utils'
7+
import { assertTailscaleResponse, mapTailscaleDnsConfiguration } from '@/tools/tailscale/utils'
78
import type { ToolConfig } from '@/tools/types'
89
import { safeUrlPathSegment } from '@/tools/url-path'
910

@@ -42,72 +43,9 @@ export const tailscaleGetDnsConfigurationTool: ToolConfig<
4243
return {
4344
success: true,
4445
output: {
45-
dnsConfiguration: {
46-
nameservers: data.nameservers ?? [],
47-
splitDNS: data.splitDNS ?? null,
48-
searchPaths: data.searchPaths ?? [],
49-
preferences: data.preferences ?? null,
50-
},
46+
dnsConfiguration: mapTailscaleDnsConfiguration(data),
5147
},
5248
}
5349
},
54-
outputs: {
55-
dnsConfiguration: {
56-
type: 'json',
57-
description: 'Dns configuration',
58-
properties: {
59-
nameservers: {
60-
type: 'array',
61-
description: 'Global DNS resolvers to use',
62-
items: {
63-
type: 'json',
64-
properties: {
65-
address: {
66-
type: 'string',
67-
description: 'IPv4 or IPv6 address of the DNS resolver',
68-
optional: true,
69-
},
70-
useWithExitNode: {
71-
type: 'boolean',
72-
description:
73-
'If true, this resolver should still be used when a device is configured to use a Tailscale exit node',
74-
optional: true,
75-
},
76-
},
77-
},
78-
},
79-
splitDNS: {
80-
type: 'json',
81-
description:
82-
'Map of DNS name suffixes (domains) to lists of resolvers for Split DNS and advanced routing overlays',
83-
nullable: true,
84-
},
85-
searchPaths: {
86-
type: 'array',
87-
description: 'Search domain paths to apply',
88-
items: {
89-
type: 'string',
90-
},
91-
},
92-
preferences: {
93-
type: 'json',
94-
description: 'Preferences',
95-
properties: {
96-
overrideLocalDNS: {
97-
type: 'boolean',
98-
description:
99-
'If true, resolvers in `nameservers` override the local OS DNS configuration; if false, local resolvers are used',
100-
optional: true,
101-
},
102-
magicDNS: {
103-
type: 'boolean',
104-
description: 'Whether MagicDNS is enabled for this tailnet',
105-
optional: true,
106-
},
107-
},
108-
nullable: true,
109-
},
110-
},
111-
},
112-
},
50+
outputs: DNS_CONFIGURATION_OUTPUT,
11351
}
Lines changed: 4 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
import { TAILNET_SETTINGS_OUTPUT } from '@/tools/tailscale/outputs'
12
import type {
23
TailscaleBaseParams,
34
TailscaleGetTailnetSettingsResponse,
45
TailscaleTailnetSettings,
56
} from '@/tools/tailscale/types'
6-
import { assertTailscaleResponse } from '@/tools/tailscale/utils'
7+
import { assertTailscaleResponse, mapTailscaleTailnetSettings } from '@/tools/tailscale/utils'
78
import type { ToolConfig } from '@/tools/types'
89
import { safeUrlPathSegment } from '@/tools/url-path'
910

@@ -42,109 +43,9 @@ export const tailscaleGetTailnetSettingsTool: ToolConfig<
4243
return {
4344
success: true,
4445
output: {
45-
settings: {
46-
aclsExternallyManagedOn: data.aclsExternallyManagedOn ?? null,
47-
aclsExternalLink: data.aclsExternalLink ?? null,
48-
adminConsoleSessionTimeoutMinutes: data.adminConsoleSessionTimeoutMinutes ?? null,
49-
devicesApprovalOn: data.devicesApprovalOn ?? null,
50-
devicesAutoUpdatesOn: data.devicesAutoUpdatesOn ?? null,
51-
devicesKeyDurationDays: data.devicesKeyDurationDays ?? null,
52-
usersApprovalOn: data.usersApprovalOn ?? null,
53-
usersRoleAllowedToJoinExternalTailnets:
54-
data.usersRoleAllowedToJoinExternalTailnets ?? null,
55-
networkFlowLoggingOn: data.networkFlowLoggingOn ?? null,
56-
regionalRoutingOn: data.regionalRoutingOn ?? null,
57-
routeSelection: data.routeSelection ?? null,
58-
postureIdentityCollectionOn: data.postureIdentityCollectionOn ?? null,
59-
httpsEnabled: data.httpsEnabled ?? null,
60-
},
46+
settings: mapTailscaleTailnetSettings(data),
6147
},
6248
}
6349
},
64-
outputs: {
65-
settings: {
66-
type: 'json',
67-
description: 'Settings for a tailnet',
68-
properties: {
69-
aclsExternallyManagedOn: {
70-
type: 'boolean',
71-
description:
72-
'Prevents users from editing policies in the admin console to avoid conflicts with external management workflows like GitOps or Terraform',
73-
nullable: true,
74-
},
75-
aclsExternalLink: {
76-
type: 'string',
77-
description:
78-
'Link to the external tailnet policy definition or management solution for this tailnet',
79-
nullable: true,
80-
},
81-
adminConsoleSessionTimeoutMinutes: {
82-
type: 'number',
83-
description:
84-
'The [admin console session timeout](/docs/account/admin-console-session-timeout) for members of this tailnet',
85-
nullable: true,
86-
},
87-
devicesApprovalOn: {
88-
type: 'boolean',
89-
description:
90-
'Whether [device approval](/docs/features/access-control/device-management/device-approval) is enabled for the tailnet',
91-
nullable: true,
92-
},
93-
devicesAutoUpdatesOn: {
94-
type: 'boolean',
95-
description:
96-
'Whether [auto updates](/docs/features/client/update#auto-updates) are enabled for devices that belong to this tailnet',
97-
nullable: true,
98-
},
99-
devicesKeyDurationDays: {
100-
type: 'number',
101-
description:
102-
'The [key expiry](/docs/features/access-control/key-expiry) duration for devices on this tailnet',
103-
nullable: true,
104-
},
105-
usersApprovalOn: {
106-
type: 'boolean',
107-
description:
108-
'Whether [user approval](/docs/features/access-control/user-approval) is enabled for this tailnet',
109-
nullable: true,
110-
},
111-
usersRoleAllowedToJoinExternalTailnets: {
112-
type: 'string',
113-
description:
114-
'Which user roles are allowed to [join external tailnets](/docs/features/sharing/how-to/invite-any-user)',
115-
nullable: true,
116-
},
117-
networkFlowLoggingOn: {
118-
type: 'boolean',
119-
description:
120-
'Whether [network flow logs](/docs/features/logging/network-flow-logs) are enabled for the tailnet',
121-
nullable: true,
122-
},
123-
regionalRoutingOn: {
124-
type: 'boolean',
125-
description:
126-
'Whether [regional routing](/docs/features/route-selection#regional-routing) is enabled for the tailnet',
127-
nullable: true,
128-
},
129-
routeSelection: {
130-
type: 'string',
131-
description:
132-
'The [route selection](/docs/features/route-selection) algorithm used by the tailnet: * `active-passive-failover` - Active-passive failover (formerly known as "Failover") * `regional-routing` - Regional routing * `regional-routing-failover` - Regional routing with in-region failover * `magicroute` - MagicRoute A PATCH request must not specify both the `regionalRoutingOn` and `routeSelection` fields',
133-
nullable: true,
134-
},
135-
postureIdentityCollectionOn: {
136-
type: 'boolean',
137-
description:
138-
'Whether [identity collection](/docs/features/access-control/device-management/how-to/manage-identity) is enabled for [device posture](/docs/features/device-posture) integrations for the tailnet',
139-
nullable: true,
140-
},
141-
httpsEnabled: {
142-
type: 'boolean',
143-
description:
144-
'Whether provisioning of [HTTPS certificates](/docs/how-to/set-up-https-certificates) is enabled for this tailnet',
145-
nullable: true,
146-
},
147-
},
148-
},
149-
},
50+
outputs: TAILNET_SETTINGS_OUTPUT,
15051
}

0 commit comments

Comments
 (0)