Skip to content

Commit d52752b

Browse files
committed
fix(microsoft): address review round 2 — Dataverse nextLink table binding, offboarding roles, SharePoint hidden filters
1 parent f739046 commit d52752b

8 files changed

Lines changed: 47 additions & 14 deletions

File tree

‎apps/docs/content/docs/integrations/azure_data_explorer.mdx‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,11 @@ Run a Kusto Query Language (KQL) query against an Azure Data Explorer database a
8383
| `truncated` | boolean | Whether rows of the primary result table were dropped to stay within the row or size cap \(each additionalResults entry has its own truncated flag\) — narrow the query if true |
8484
| `additionalResults` | array | Further result tables, present only when a query batch \(statements separated by ;\) or fork returns more than one; the row and size caps are shared across all tables |
8585
| ↳ `tableName` | string | Result table name |
86-
| ↳ `columns` | array | Column metadata \(name, type, dataType\) |
87-
| ↳ `rows` | array | Rows as positional arrays |
86+
| ↳ `columns` | array | Column metadata |
87+
| ↳ `name` | string | Column name |
88+
| ↳ `type` | string | Kusto scalar type |
89+
| ↳ `dataType` | string | Approximate .NET type |
90+
| ↳ `rows` | array | Rows as positional arrays matching the columns order |
8891
| ↳ `records` | array | Rows keyed by column name |
8992
| ↳ `rowCount` | number | Rows carried after the caps |
9093
| ↳ `totalRowCount` | number | Rows Kusto returned |

‎apps/docs/content/docs/integrations/microsoft_dataverse.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ Query and list records from a Microsoft Dataverse table. Supports OData query op
345345
| `top` | number | No | Maximum number of records to return \(OData $top\) |
346346
| `expand` | string | No | Navigation properties to expand \(OData $expand\) |
347347
| `count` | string | No | Set to "true" to include total record count in response \(OData $count\) |
348-
| `pageSize` | number | No | Records per page \(Prefer: odata.maxpagesize, 1-5000\). Defaults to 100 when Max Results is not set. Dataverse ignores $top when a page size is sent. |
348+
| `pageSize` | number | No | Records per page \(Prefer: odata.maxpagesize, 1-5000\). Defaults to 100 when Max Results is not set. Dataverse ignores $top when a page size is sent. When following nextLink, pass the same pageSize used for the first page, as Dataverse requires. |
349349
| `nextLink` | string | No | nextLink returned by a previous page. When set, the other query options are ignored and the same page size must be used. |
350350

351351
#### Output

‎apps/sim/blocks/blocks/microsoft_ad.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1182,7 +1182,7 @@ export const MicrosoftAdBlockMeta = {
11821182
description:
11831183
'Disable an Azure AD account and strip its group memberships when someone leaves. Use for secure offboarding.',
11841184
content:
1185-
'# Offboard User\n\nRevoke access for a departing user.\n\n## Steps\n1. Resolve the account with Get User by user principal name or id.\n2. Use Update User to set Account Enabled to No so the user can no longer sign in.\n3. Use List User Memberships with the user id to enumerate the groups the user belongs to directly.\n4. For each group, call Remove Group Member with the user id.\n\n## Output\nReturn the disabled user id and the list of groups the user was removed from. Note any group where removal failed so it can be handled manually, and recommend writing the action to an audit record.',
1185+
'# Offboard User\n\nRevoke access for a departing user.\n\n## Steps\n1. Resolve the account with Get User by user principal name or id.\n2. Use Update User to set Account Enabled to No so the user can no longer sign in.\n3. Use List User Memberships with the user id to enumerate everything the user belongs to directly.\n4. For each membership whose odataType is #microsoft.graph.group, call Remove Group Member with the user id. For each one whose odataType is #microsoft.graph.directoryRole, call Remove Directory Role Member with the role id and user id. List any other types (such as administrative units) for manual follow-up.\n\n## Output\nReturn the disabled user id and the groups and directory roles the user was removed from. Note any group where removal failed so it can be handled manually, and recommend writing the action to an audit record.',
11861186
},
11871187
{
11881188
name: 'audit-group-membership',

‎apps/sim/blocks/blocks/sharepoint.ts‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1472,6 +1472,12 @@ Return ONLY the JSON object - no explanations, no markdown, no extra text.`,
14721472
top: cleanString(top) ? Number(cleanString(top)) : undefined,
14731473
}
14741474

1475+
if (result.includeColumns === true && rest.operation === 'sharepoint_get_list') {
1476+
result.filter = undefined
1477+
result.top = undefined
1478+
result.selectFields = undefined
1479+
}
1480+
14751481
if (columnDefinitions && rest.operation === 'sharepoint_create_list') {
14761482
result.pageContent = columnDefinitions
14771483
}

‎apps/sim/tools/azure_data_explorer/query.ts‎

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,9 +155,28 @@ export const azureDataExplorerQueryTool: InternalToolConfig<
155155
type: 'object',
156156
properties: {
157157
tableName: { type: 'string', description: 'Result table name', nullable: true },
158-
columns: { type: 'array', description: 'Column metadata (name, type, dataType)' },
159-
rows: { type: 'array', description: 'Rows as positional arrays' },
160-
records: { type: 'array', description: 'Rows keyed by column name' },
158+
columns: {
159+
type: 'array',
160+
description: 'Column metadata',
161+
items: {
162+
type: 'object',
163+
properties: {
164+
name: { type: 'string', description: 'Column name' },
165+
type: { type: 'string', description: 'Kusto scalar type', nullable: true },
166+
dataType: { type: 'string', description: 'Approximate .NET type', nullable: true },
167+
},
168+
},
169+
},
170+
rows: {
171+
type: 'array',
172+
description: 'Rows as positional arrays matching the columns order',
173+
items: { type: 'array' },
174+
},
175+
records: {
176+
type: 'array',
177+
description: 'Rows keyed by column name',
178+
items: { type: 'object' },
179+
},
161180
rowCount: { type: 'number', description: 'Rows carried after the caps' },
162181
totalRowCount: { type: 'number', description: 'Rows Kusto returned' },
163182
truncated: { type: 'boolean', description: 'Whether rows were dropped by the caps' },

‎apps/sim/tools/generated/tool-metadata.ts‎

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

‎apps/sim/tools/generated/tool-outputs.ts‎

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

‎apps/sim/tools/microsoft_dataverse/list_records.ts‎

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { createLogger } from '@sim/logger'
2+
import { escapeRegExp } from '@sim/utils/string'
23
import type {
34
DataverseListRecordsParams,
45
DataverseListRecordsResponse,
@@ -34,7 +35,7 @@ function environmentHost(url: URL): string {
3435
* environment this request is bound to, so a supplied link can never carry the bearer token to
3536
* another host.
3637
*/
37-
function assertNextLink(nextLink: string, baseUrl: string): string {
38+
function assertNextLink(nextLink: string, baseUrl: string, entitySetName: string): string {
3839
const value = nextLink.trim()
3940
if (!value || value.length > DATAVERSE_MAX_NEXT_LINK_LENGTH) {
4041
throw new Error(
@@ -52,9 +53,13 @@ function assertNextLink(nextLink: string, baseUrl: string): string {
5253
url.username !== '' ||
5354
url.password !== '' ||
5455
environmentHost(url) !== environmentHost(new URL(baseUrl)) ||
55-
!url.pathname.startsWith('/api/data/')
56+
!new RegExp(`^/api/data/v[0-9.]+/${escapeRegExp(entitySetName.trim())}$`, 'i').test(
57+
url.pathname
58+
)
5659
) {
57-
throw new Error('nextLink must be a Web API URL on the selected Dataverse environment')
60+
throw new Error(
61+
'nextLink must be a Web API URL for the selected table on the selected Dataverse environment'
62+
)
5863
}
5964
return value
6065
}
@@ -132,7 +137,7 @@ export const dataverseListRecordsTool: ToolConfig<
132137
required: false,
133138
visibility: 'user-or-llm',
134139
description:
135-
'Records per page (Prefer: odata.maxpagesize, 1-5000). Defaults to 100 when Max Results is not set. Dataverse ignores $top when a page size is sent.',
140+
'Records per page (Prefer: odata.maxpagesize, 1-5000). Defaults to 100 when Max Results is not set. Dataverse ignores $top when a page size is sent. When following nextLink, pass the same pageSize used for the first page, as Dataverse requires.',
136141
},
137142
nextLink: {
138143
type: 'string',
@@ -147,7 +152,7 @@ export const dataverseListRecordsTool: ToolConfig<
147152
url: (params) => {
148153
const baseUrl = getDataverseRequestBaseUrl(params)
149154
getExplicitPageSize(params)
150-
if (params.nextLink) return assertNextLink(params.nextLink, baseUrl)
155+
if (params.nextLink) return assertNextLink(params.nextLink, baseUrl, params.entitySetName)
151156
const queryParts: string[] = []
152157
if (params.select) queryParts.push(`$select=${encodeURIComponent(params.select)}`)
153158
if (params.filter) queryParts.push(`$filter=${encodeURIComponent(params.filter)}`)

0 commit comments

Comments
 (0)