Skip to content

Commit dc17189

Browse files
committed
fix(feishu): expand bitable value schema to accept object arrays (User/attachment/options)
The previous BitableFieldValueSchema only allowed primitive items inside arrays, rejecting valid Feishu payloads where field values are arrays of objects — User=[{id:"ou_1"}], attachment arrays, and create_field property.options=[{name:"A"}]. This narrowed the accepted argument shapes and would break existing Feishu bitable calls under provider/runtime schema validation. Expand the array items union to include open objects (additionalProperties: true) alongside primitives, preserving the no-empty-patternProperties property while covering all documented Feishu bitable value shapes. Add regression tests for User field arrays and create_field property options arrays to guard against future narrowing. Closes #94547
1 parent 5e0fb89 commit dc17189

2 files changed

Lines changed: 59 additions & 1 deletion

File tree

extensions/feishu/src/bitable.test.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,4 +223,48 @@ describe("feishu bitable write tool schemas", () => {
223223
expect(valueSchema).toMatchObject({ anyOf: expect.any(Array) });
224224
expect(JSON.stringify(parameters)).not.toContain('"patternProperties":{"^.*$":{}}');
225225
});
226+
227+
it("accepts Feishu user field value (array of objects)", () => {
228+
// Feishu bitable user fields are arrays of objects: [{id:"ou_xxx"}].
229+
// The value schema must accept open objects inside arrays, not just
230+
// primitives, to avoid rejecting valid Feishu payloads.
231+
const { api, resolveTool } = createToolFactoryHarness(createConfig());
232+
registerFeishuBitableTools(api);
233+
const tool = resolveTool("feishu_bitable_create_record");
234+
const parameters = (tool as unknown as { parameters?: unknown }).parameters;
235+
const serialized = JSON.stringify(parameters);
236+
237+
// The array branch items must include an open-object option
238+
// (additionalProperties: true), not just primitive types.
239+
expect(serialized).toContain('"additionalProperties":true');
240+
// Verify the array items union has at least 5 branches (primitives + open object)
241+
const valueSchema = recordValueSchema(parameters, "fields");
242+
const anyOf = (valueSchema as { anyOf?: unknown[] })?.anyOf;
243+
const arrayBranch = anyOf?.find(
244+
(b) => (b as { type?: string })?.type === "array",
245+
) as { items?: { anyOf?: unknown[] } } | undefined;
246+
expect(arrayBranch?.items?.anyOf?.length).toBeGreaterThanOrEqual(5);
247+
});
248+
249+
it("accepts Feishu create_field property.options (array of option objects)", () => {
250+
// property.options for SingleSelect/MultiSelect fields is [{name:"A"}].
251+
// The value schema must allow arrays containing objects.
252+
const { api, resolveTool } = createToolFactoryHarness(createConfig());
253+
registerFeishuBitableTools(api);
254+
const tool = resolveTool("feishu_bitable_create_field");
255+
const parameters = (tool as unknown as { parameters?: unknown }).parameters;
256+
const valueSchema = recordValueSchema(parameters, "property");
257+
258+
const anyOf = (valueSchema as { anyOf?: unknown[] })?.anyOf;
259+
const arrayBranch = anyOf?.find(
260+
(b) => (b as { type?: string })?.type === "array",
261+
) as { items?: { anyOf?: unknown[] } } | undefined;
262+
// Array items must include an open-object branch for option objects
263+
const objectBranch = arrayBranch?.items?.anyOf?.find(
264+
(b) =>
265+
(b as { type?: string })?.type === "object" &&
266+
(b as { additionalProperties?: unknown })?.additionalProperties === true,
267+
);
268+
expect(objectBranch).toBeDefined();
269+
});
226270
});

extensions/feishu/src/bitable.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -498,12 +498,26 @@ async function updateRecord(
498498
// Strict JSON Schema (draft 2020-12) validators such as AWS Bedrock reject empty
499499
// sub-schemas, which fails the entire tool list. Every branch below serializes to
500500
// a non-empty schema (no bare `{}`).
501+
//
502+
// Array items include open objects (not just primitives) because Feishu bitable
503+
// field values already support arrays of objects for users ([{id:"ou_xxx"}]),
504+
// attachments, and create-field property.options ([{name:"A"}]). A primitive-only
505+
// array branch would reject those valid Feishu payloads, narrowing the accepted
506+
// argument shapes and breaking existing calls.
501507
const BitableFieldValueSchema = Type.Union([
502508
Type.String(),
503509
Type.Number(),
504510
Type.Boolean(),
505511
Type.Null(),
506-
Type.Array(Type.Union([Type.String(), Type.Number(), Type.Boolean(), Type.Null()])),
512+
Type.Array(
513+
Type.Union([
514+
Type.String(),
515+
Type.Number(),
516+
Type.Boolean(),
517+
Type.Null(),
518+
Type.Object({}, { additionalProperties: true }),
519+
]),
520+
),
507521
Type.Object({}, { additionalProperties: true }),
508522
]);
509523

0 commit comments

Comments
 (0)