Skip to content

Commit 52ad626

Browse files
committed
Add explicit validation checks for component schema fields and update google-adk dependency
1 parent 38ef32e commit 52ad626

3 files changed

Lines changed: 816 additions & 75 deletions

File tree

agent_sdks/conformance/validator.yaml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2597,3 +2597,28 @@
25972597
next:
25982598
level: 55
25992599
expect_error: Global recursion limit exceeded
2600+
2601+
- name: test_validate_invalid_child_type
2602+
description: Tests that invalid child type (number instead of string) throws.
2603+
catalog:
2604+
version: "0.8"
2605+
s2c_schema: "simplified_s2c_v08.json"
2606+
catalog_schema:
2607+
catalogId: standard
2608+
components:
2609+
Container:
2610+
type: object
2611+
properties:
2612+
child: {type: string}
2613+
validate:
2614+
- payload:
2615+
- surfaceUpdate:
2616+
surfaceId: s1
2617+
components:
2618+
- id: c1
2619+
component:
2620+
Container:
2621+
child: 123
2622+
expect_error: "string"
2623+
2624+

agent_sdks/cpp/src/schema/validator.cc

Lines changed: 112 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,12 @@ void A2uiValidator::check_component_integrity(const std::optional<std::string>&
193193
throw std::runtime_error("Missing root component: No component has id='" + *root_id + "'");
194194
}
195195

196+
auto check_ref = [&](const std::string& comp_id, const std::string& ref_id, const std::string& field_name) {
197+
if (ids.find(ref_id) == ids.end()) {
198+
throw std::runtime_error("Component '" + comp_id + "' references non-existent component '" + ref_id + "' in field '" + field_name + "'");
199+
}
200+
};
201+
196202
for (const auto& comp : components) {
197203
if (comp.contains("component") && comp["component"].is_object()) {
198204
auto comp_def = comp["component"];
@@ -222,76 +228,134 @@ void A2uiValidator::check_component_integrity(const std::optional<std::string>&
222228
}
223229
}
224230

225-
if (!skip_root_check && root_id.has_value()) {
226-
auto check_ref = [&](const std::string& ref_id, const std::string& field_name) {
227-
if (ids.find(ref_id) == ids.end()) {
228-
throw std::runtime_error("Component '" + comp.value("id", "") + "' references non-existent component '" + ref_id + "' in field '" + field_name + "'");
229-
}
230-
};
231+
std::string comp_id = comp.value("id", "unknown");
231232

232-
if (comp.contains("child") && comp["child"].is_string()) {
233-
check_ref(comp["child"].get<std::string>(), "child");
233+
if (comp.contains("child")) {
234+
if (!comp["child"].is_string()) {
235+
throw std::runtime_error("Validation failed: 'child' must be a string");
234236
}
235-
if (comp.contains("children")) {
236-
const auto& children = comp["children"];
237-
if (children.is_array()) {
238-
for (const auto& item : children) {
239-
if (item.is_string()) {
240-
check_ref(item.get<std::string>(), "children");
237+
if (!skip_root_check && root_id.has_value()) {
238+
check_ref(comp_id, comp["child"].get<std::string>(), "child");
239+
}
240+
}
241+
if (comp.contains("children")) {
242+
const auto& children = comp["children"];
243+
if (children.is_array()) {
244+
for (const auto& item : children) {
245+
if (item.is_string()) {
246+
if (!skip_root_check && root_id.has_value()) {
247+
check_ref(comp_id, item.get<std::string>(), "children");
241248
}
249+
} else {
250+
throw std::runtime_error("Validation failed: 'children' array items must be strings");
242251
}
243-
} else if (children.is_object()) {
244-
if (children.contains("explicitList") && children["explicitList"].is_array()) {
245-
for (const auto& item : children["explicitList"]) {
246-
if (item.is_string()) {
247-
check_ref(item.get<std::string>(), "children.explicitList");
252+
}
253+
} else if (children.is_object()) {
254+
if (children.contains("explicitList")) {
255+
if (!children["explicitList"].is_array()) {
256+
throw std::runtime_error("Validation failed: 'explicitList' must be an array");
257+
}
258+
for (const auto& item : children["explicitList"]) {
259+
if (item.is_string()) {
260+
if (!skip_root_check && root_id.has_value()) {
261+
check_ref(comp_id, item.get<std::string>(), "children.explicitList");
248262
}
263+
} else {
264+
throw std::runtime_error("Validation failed: 'explicitList' array items must be strings");
249265
}
250266
}
251-
if (children.contains("template") && children["template"].is_object()) {
252-
const auto& temp = children["template"];
253-
if (temp.contains("componentId") && temp["componentId"].is_string()) {
254-
check_ref(temp["componentId"].get<std::string>(), "children.template.componentId");
267+
}
268+
if (children.contains("template")) {
269+
if (!children["template"].is_object()) {
270+
throw std::runtime_error("Validation failed: 'template' must be an object");
271+
}
272+
const auto& temp = children["template"];
273+
if (temp.contains("componentId")) {
274+
if (!temp["componentId"].is_string()) {
275+
throw std::runtime_error("Validation failed: 'componentId' must be a string");
276+
}
277+
if (!skip_root_check && root_id.has_value()) {
278+
check_ref(comp_id, temp["componentId"].get<std::string>(), "children.template.componentId");
255279
}
256280
}
257-
if (children.contains("componentId") && children["componentId"].is_string()) {
258-
check_ref(children["componentId"].get<std::string>(), "children.componentId");
281+
}
282+
if (children.contains("componentId")) {
283+
if (!children["componentId"].is_string()) {
284+
throw std::runtime_error("Validation failed: 'componentId' must be a string");
285+
}
286+
if (!skip_root_check && root_id.has_value()) {
287+
check_ref(comp_id, children["componentId"].get<std::string>(), "children.componentId");
259288
}
260289
}
290+
} else {
291+
throw std::runtime_error("Validation failed: 'children' must be an array or object");
261292
}
262-
if (comp.contains("component") && comp["component"].is_object()) {
263-
for (auto it = comp["component"].begin(); it != comp["component"].end(); ++it) {
264-
if (it.value().is_object()) {
265-
auto props = it.value();
266-
if (props.contains("child") && props["child"].is_string()) {
267-
check_ref(props["child"].get<std::string>(), "child");
293+
}
294+
295+
if (comp.contains("component") && comp["component"].is_object()) {
296+
for (auto it = comp["component"].begin(); it != comp["component"].end(); ++it) {
297+
if (it.value().is_object()) {
298+
auto props = it.value();
299+
if (props.contains("child")) {
300+
if (!props["child"].is_string()) {
301+
throw std::runtime_error("Validation failed: 'child' must be a string");
268302
}
269-
if (props.contains("children")) {
270-
const auto& children = props["children"];
271-
if (children.is_array()) {
272-
for (const auto& item : children) {
273-
if (item.is_string()) {
274-
check_ref(item.get<std::string>(), "children");
303+
if (!skip_root_check && root_id.has_value()) {
304+
check_ref(comp_id, props["child"].get<std::string>(), "child");
305+
}
306+
}
307+
308+
if (props.contains("children")) {
309+
const auto& children = props["children"];
310+
if (children.is_array()) {
311+
for (const auto& item : children) {
312+
if (item.is_string()) {
313+
if (!skip_root_check && root_id.has_value()) {
314+
check_ref(comp_id, item.get<std::string>(), "children");
275315
}
316+
} else {
317+
throw std::runtime_error("Validation failed: 'children' array items must be strings");
276318
}
277-
} else if (children.is_object()) {
278-
if (children.contains("explicitList") && children["explicitList"].is_array()) {
279-
for (const auto& item : children["explicitList"]) {
280-
if (item.is_string()) {
281-
check_ref(item.get<std::string>(), "children.explicitList");
319+
}
320+
} else if (children.is_object()) {
321+
if (children.contains("explicitList")) {
322+
if (!children["explicitList"].is_array()) {
323+
throw std::runtime_error("Validation failed: 'explicitList' must be an array");
324+
}
325+
for (const auto& item : children["explicitList"]) {
326+
if (item.is_string()) {
327+
if (!skip_root_check && root_id.has_value()) {
328+
check_ref(comp_id, item.get<std::string>(), "children.explicitList");
282329
}
330+
} else {
331+
throw std::runtime_error("Validation failed: 'explicitList' array items must be strings");
283332
}
284333
}
285-
if (children.contains("template") && children["template"].is_object()) {
286-
const auto& temp = children["template"];
287-
if (temp.contains("componentId") && temp["componentId"].is_string()) {
288-
check_ref(temp["componentId"].get<std::string>(), "children.template.componentId");
334+
}
335+
if (children.contains("template")) {
336+
if (!children["template"].is_object()) {
337+
throw std::runtime_error("Validation failed: 'template' must be an object");
338+
}
339+
const auto& temp = children["template"];
340+
if (temp.contains("componentId")) {
341+
if (!temp["componentId"].is_string()) {
342+
throw std::runtime_error("Validation failed: 'componentId' must be a string");
343+
}
344+
if (!skip_root_check && root_id.has_value()) {
345+
check_ref(comp_id, temp["componentId"].get<std::string>(), "children.template.componentId");
289346
}
290347
}
291-
if (children.contains("componentId") && children["componentId"].is_string()) {
292-
check_ref(children["componentId"].get<std::string>(), "children.componentId");
348+
}
349+
if (children.contains("componentId")) {
350+
if (!children["componentId"].is_string()) {
351+
throw std::runtime_error("Validation failed: 'componentId' must be a string");
352+
}
353+
if (!skip_root_check && root_id.has_value()) {
354+
check_ref(comp_id, children["componentId"].get<std::string>(), "children.componentId");
293355
}
294356
}
357+
} else {
358+
throw std::runtime_error("Validation failed: 'children' must be an array or object");
295359
}
296360
}
297361
}

0 commit comments

Comments
 (0)