luigidemasi commented on code in PR #26813: URL: https://github.com/apache/camel/pull/26813#discussion_r4090966364
########## dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/SemanticDefinitionDeserializer.java: ########## @@ -0,0 +1,176 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.dsl.yaml.deserializers; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +import org.apache.camel.CamelContext; +import org.apache.camel.dsl.yaml.common.YamlDeserializationContext; +import org.apache.camel.dsl.yaml.common.YamlDeserializerSupport; +import org.apache.camel.semantic.SemanticQuestion; +import org.apache.camel.semantic.SemanticQuestions; +import org.apache.camel.spi.CamelContextCustomizer; +import org.apache.camel.spi.annotations.YamlIn; +import org.apache.camel.spi.annotations.YamlProperty; +import org.apache.camel.spi.annotations.YamlType; +import org.snakeyaml.engine.v2.api.ConstructNode; +import org.snakeyaml.engine.v2.nodes.Node; +import org.snakeyaml.engine.v2.nodes.NodeTuple; +import org.snakeyaml.engine.v2.nodes.SequenceNode; + +/** Named semantic declarations are installed in a resource-wide pass before route references are resolved. */ +@YamlIn +@YamlType(nodes = "semantic", properties = { + @YamlProperty(name = "question", + type = "map:org.apache.camel.dsl.yaml.deserializers.SemanticDefinitionDeserializer$QuestionSchema", + required = true) +}) +public class SemanticDefinitionDeserializer extends YamlDeserializerSupport implements ConstructNode { + private static final Set<String> FIELDS + = Set.of("type", "instructions", "state", "criteria", "threshold", "uncertainty", "uncertaintyPolicy"); + + @Override + public Object construct(Node node) { + read(node); + // Registration happens once for the entire resource, including declarations after routes. + return (CamelContextCustomizer) context -> { + }; + } + + public static void configure(CamelContext context, YamlDeserializationContext dc, Node root) { + if (!(root instanceof SequenceNode sequence)) { + return; + } + Map<String, SemanticQuestion> definitions = new LinkedHashMap<>(); + for (Node node : sequence.getValue()) { + for (NodeTuple tuple : asMappingNode(node).getValue()) { + if ("semantic".equals(asText(tuple.getKeyNode()))) { + read(tuple.getValueNode()).forEach((name, question) -> { + if (definitions.putIfAbsent(name, question) != null) { + throw new IllegalArgumentException("Duplicate semantic question: " + name); + } + }); + } + } + } + SemanticQuestions.get(context).replace(dc.getResource(), definitions); + } + + private static Map<String, SemanticQuestion> read(Node node) { + Map<String, Node> semantic = fields(node); + if (!semantic.keySet().equals(Set.of("question"))) { + throw new IllegalArgumentException("Semantic declaration requires only question"); + } + Map<String, SemanticQuestion> result = new LinkedHashMap<>(); + fields(semantic.get("question")).forEach((name, definition) -> { + Map<String, Node> values = fields(definition); + if (!FIELDS.containsAll(values.keySet())) { + throw new IllegalArgumentException("Unknown property in semantic question: " + name); + } + String typeName = asText(values.get("type")); + if (typeName == null) { + throw new IllegalArgumentException("Semantic question type is required: " + name); + } + SemanticQuestion.Type type = SemanticQuestion.Type.valueOf(typeName.toUpperCase(Locale.ROOT)); + Map<String, String> criteria = new LinkedHashMap<>(); + List<String> levels = List.of(); + if (values.containsKey("criteria")) { + if (type == SemanticQuestion.Type.SCORE) { + levels = asSequenceNode(values.get("criteria")).getValue().stream().map(YamlDeserializerSupport::asText) + .toList(); + } else { + fields(values.get("criteria")).forEach((key, value) -> criteria.put(key, asText(value))); + } + } + if (type != SemanticQuestion.Type.BOOLEAN && (values.containsKey("threshold") || values.containsKey("uncertainty") + || values.containsKey("uncertaintyPolicy"))) { + throw new IllegalArgumentException("Threshold and uncertainty policy require a boolean question: " + name); + } + SemanticQuestion.UncertaintyPolicy policy = values.containsKey("uncertaintyPolicy") + ? SemanticQuestion.UncertaintyPolicy + .valueOf(asText(values.get("uncertaintyPolicy")).replace('-', '_').toUpperCase(Locale.ROOT)) + : SemanticQuestion.UncertaintyPolicy.FAIL; + result.put(name, new SemanticQuestion( + type, asText(values.get("instructions")), asText(values.get("state")), + criteria, levels, number(values, "threshold", 0.5), number(values, "uncertainty", 0), policy)); + }); + return result; + } + + private static double number(Map<String, Node> values, String name, double fallback) { + return values.containsKey(name) ? Double.parseDouble(asText(values.get(name))) : fallback; Review Comment: Fixed in [69222be0cbf8](https://github.com/apache/camel/commit/69222be0cbf8720f1f1b9dc7854e616625a172d4). Invalid `threshold` and `uncertainty` values now throw `YamlDeserializationException` naming both the question and field, with the YAML source position and original `NumberFormatException` preserved. For example: `Invalid numeric value for 'threshold' in semantic question 'spam': abc`. Parameterized tests load both invalid fields without schema validation and assert the resource name, question, field, line/column and cause. All 595 YAML reactor tests and the full 694-module build passed. _AI-generated by Codex on behalf of [luigidemasi](https://github.com/luigidemasi)._ ########## core/camel-api/src/main/java/org/apache/camel/spi/PropertyConfigurer.java: ########## @@ -37,6 +37,23 @@ */ public interface PropertyConfigurer { + /** + * Optionally binds a value before resolving bean or class references. Property placeholders have already been Review Comment: Fixed in [69222be0cbf8](https://github.com/apache/camel/commit/69222be0cbf8720f1f1b9dc7854e616625a172d4). The Javadoc now states that placeholders are resolved before `configureRaw` only when the caller requested placeholder resolution, matching the `withPlaceholder(false)` path. Binding behavior is unchanged. The `camel-api` build, formatter and import sorting passed after the documentation edit. _AI-generated by Codex on behalf of [luigidemasi](https://github.com/luigidemasi)._ -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
