From 2c97db78b89a3fad91dbfbd7bb3059c14f9634f2 Mon Sep 17 00:00:00 2001 From: Cyril Misev Date: Fri, 28 Aug 2026 16:17:33 +0200 Subject: [PATCH 1/7] SED-4905 order enforcement on non-simple yaml artefacts --- .../java/step/core/yaml/YamlFieldOrder.java | 31 +++++++++++++++++++ .../step/core/yaml/YamlFieldPriority.java | 29 +++++++++++++++++ .../step/core/artefacts/AbstractArtefact.java | 9 +++++- .../core/yaml/model/AbstractYamlArtefact.java | 17 +++++++--- .../automation/YamlCallFunction.java | 11 +++++-- .../artefacts/automation/YamlCallPlan.java | 4 +++ .../plans/parser/yaml/YamlPlanReader.java | 27 ++++++++++++++++ 7 files changed, 120 insertions(+), 8 deletions(-) create mode 100644 step-core-model/src/main/java/step/core/yaml/YamlFieldOrder.java create mode 100644 step-core-model/src/main/java/step/core/yaml/YamlFieldPriority.java diff --git a/step-core-model/src/main/java/step/core/yaml/YamlFieldOrder.java b/step-core-model/src/main/java/step/core/yaml/YamlFieldOrder.java new file mode 100644 index 0000000000..c5f0e1481e --- /dev/null +++ b/step-core-model/src/main/java/step/core/yaml/YamlFieldOrder.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (C) 2026, exense GmbH + * + * This file is part of STEP + * + * STEP is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * STEP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with STEP. If not, see . + ******************************************************************************/ +package step.core.yaml; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Target(ElementType.FIELD) +@Retention(RUNTIME) +public @interface YamlFieldOrder { + YamlFieldPriority value(); +} diff --git a/step-core-model/src/main/java/step/core/yaml/YamlFieldPriority.java b/step-core-model/src/main/java/step/core/yaml/YamlFieldPriority.java new file mode 100644 index 0000000000..c5474182de --- /dev/null +++ b/step-core-model/src/main/java/step/core/yaml/YamlFieldPriority.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (C) 2026, exense GmbH + * + * This file is part of STEP + * + * STEP is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * STEP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with STEP. If not, see . + ******************************************************************************/ +package step.core.yaml; + +/** + * With equal priority, the ordering is as follows: common fields to all artefacts come first, then come artefact specific fields. + * Use this prioritization to override the field order + */ +public enum YamlFieldPriority { + Top, + Normal, + Low, +} diff --git a/step-core/src/main/java/step/core/artefacts/AbstractArtefact.java b/step-core/src/main/java/step/core/artefacts/AbstractArtefact.java index 6f54efe77b..01c3a884c6 100644 --- a/step-core/src/main/java/step/core/artefacts/AbstractArtefact.java +++ b/step-core/src/main/java/step/core/artefacts/AbstractArtefact.java @@ -31,6 +31,8 @@ import step.core.dynamicbeans.DynamicValue; import step.core.entities.EntityConstants; import step.core.entities.EntityReference; +import step.core.yaml.YamlFieldOrder; +import step.core.yaml.YamlFieldPriority; import java.util.ArrayList; import java.util.HashMap; @@ -48,7 +50,6 @@ public abstract class AbstractArtefact extends AbstractOrganizableObject { protected String description; - protected List children = new ArrayList<>(); @JsonSerialize(using = MapSerializer.class) @JsonDeserialize(using = MapDeserializer.class) @@ -61,7 +62,13 @@ public abstract class AbstractArtefact extends AbstractOrganizableObject { private DynamicValue continueParentNodeExecutionOnError = new DynamicValue<>(false); private boolean isWorkArtefact = false; + @YamlFieldOrder(YamlFieldPriority.Low) private ChildrenBlock before; + + @YamlFieldOrder(YamlFieldPriority.Low) + protected List children = new ArrayList<>(); + + @YamlFieldOrder(YamlFieldPriority.Low) private ChildrenBlock after; public AbstractArtefact() { diff --git a/step-core/src/main/java/step/core/yaml/model/AbstractYamlArtefact.java b/step-core/src/main/java/step/core/yaml/model/AbstractYamlArtefact.java index 3f98c455d9..b09b585e71 100644 --- a/step-core/src/main/java/step/core/yaml/model/AbstractYamlArtefact.java +++ b/step-core/src/main/java/step/core/yaml/model/AbstractYamlArtefact.java @@ -29,11 +29,12 @@ import step.core.yaml.AbstractYamlModel; import step.core.yaml.YamlArtefactsLookuper; import step.core.yaml.YamlFieldCustomCopy; +import step.core.yaml.YamlFieldOrder; +import step.core.yaml.YamlFieldPriority; import step.core.yaml.schema.YamlJsonSchemaHelper; import step.jsonschema.JsonSchema; import step.jsonschema.JsonSchemaDefaultValueProvider; - import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; @@ -58,20 +59,28 @@ public abstract class AbstractYamlArtefact extends A @JsonSchema(defaultProvider = DefaultYamlArtefactNameProvider.class) @YamlFieldCustomCopy + @YamlFieldOrder(YamlFieldPriority.Top) protected DynamicValue nodeName; + @YamlFieldOrder(YamlFieldPriority.Top) + protected String description; + protected DynamicValue skipNode = new DynamicValue<>(false); protected DynamicValue instrumentNode = new DynamicValue<>(false); protected DynamicValue continueParentNodeExecutionOnError = new DynamicValue<>(false); - protected String description; @JsonSchema(ref = YamlJsonSchemaHelper.DEFS_PREFIX + ARTEFACT_ARRAY_DEF) - @YamlFieldCustomCopy - protected List children = new ArrayList<>(); @YamlFieldCustomCopy + @YamlFieldOrder(YamlFieldPriority.Low) protected YamlChildrenBlock before; + + @YamlFieldCustomCopy + @YamlFieldOrder(YamlFieldPriority.Low) + protected List children = new ArrayList<>(); + @YamlFieldCustomCopy + @YamlFieldOrder(YamlFieldPriority.Low) protected YamlChildrenBlock after; public AbstractYamlArtefact() { diff --git a/step-plans/step-plans-base-artefacts/src/main/java/step/artefacts/automation/YamlCallFunction.java b/step-plans/step-plans-base-artefacts/src/main/java/step/artefacts/automation/YamlCallFunction.java index 1bc0b1ac94..171c7a46c5 100644 --- a/step-plans/step-plans-base-artefacts/src/main/java/step/artefacts/automation/YamlCallFunction.java +++ b/step-plans/step-plans-base-artefacts/src/main/java/step/artefacts/automation/YamlCallFunction.java @@ -23,16 +23,21 @@ import step.core.artefacts.AbstractArtefact; import step.core.dynamicbeans.DynamicValue; import step.core.yaml.YamlFieldCustomCopy; +import step.core.yaml.YamlFieldOrder; +import step.core.yaml.YamlFieldPriority; public class YamlCallFunction extends YamlTokenSelector { - protected DynamicValue resultMap = new DynamicValue<>(); + @YamlFieldCustomCopy + @YamlFieldOrder(YamlFieldPriority.Top) + protected YamlKeywordDefinition keyword = new YamlKeywordDefinition(null, null, "{}"); @YamlFieldCustomCopy + @YamlFieldOrder(YamlFieldPriority.Top) protected YamlDynamicInputs inputs = new YamlDynamicInputs("{}"); - @YamlFieldCustomCopy - protected YamlKeywordDefinition keyword = new YamlKeywordDefinition(null, null, "{}"); + @YamlFieldOrder(YamlFieldPriority.Normal) + protected DynamicValue resultMap = new DynamicValue<>(); public YamlCallFunction() { super(CallFunction.class); diff --git a/step-plans/step-plans-base-artefacts/src/main/java/step/artefacts/automation/YamlCallPlan.java b/step-plans/step-plans-base-artefacts/src/main/java/step/artefacts/automation/YamlCallPlan.java index 3e7a16eb9e..e7f88a8176 100644 --- a/step-plans/step-plans-base-artefacts/src/main/java/step/artefacts/automation/YamlCallPlan.java +++ b/step-plans/step-plans-base-artefacts/src/main/java/step/artefacts/automation/YamlCallPlan.java @@ -20,6 +20,8 @@ import step.artefacts.CallPlan; import step.core.yaml.YamlFieldCustomCopy; +import step.core.yaml.YamlFieldOrder; +import step.core.yaml.YamlFieldPriority; import step.core.yaml.model.AbstractYamlArtefact; public class YamlCallPlan extends AbstractYamlArtefact { @@ -27,9 +29,11 @@ public class YamlCallPlan extends AbstractYamlArtefact { private String planId; @YamlFieldCustomCopy + @YamlFieldOrder(YamlFieldPriority.Top) private YamlDynamicInputs selectionAttributes = new YamlDynamicInputs("{}"); @YamlFieldCustomCopy + @YamlFieldOrder(YamlFieldPriority.Top) protected YamlDynamicInputs input = new YamlDynamicInputs("{}"); public YamlCallPlan() { diff --git a/step-plans/step-plans-yaml-parser/src/main/java/step/plans/parser/yaml/YamlPlanReader.java b/step-plans/step-plans-yaml-parser/src/main/java/step/plans/parser/yaml/YamlPlanReader.java index 063df0d9ed..2d5215a835 100644 --- a/step-plans/step-plans-yaml-parser/src/main/java/step/plans/parser/yaml/YamlPlanReader.java +++ b/step-plans/step-plans-yaml-parser/src/main/java/step/plans/parser/yaml/YamlPlanReader.java @@ -24,10 +24,13 @@ import com.fasterxml.jackson.databind.DeserializationConfig; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationConfig; import com.fasterxml.jackson.databind.deser.BeanDeserializer; import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier; import com.fasterxml.jackson.databind.deser.std.CollectionDeserializer; import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.databind.ser.BeanPropertyWriter; +import com.fasterxml.jackson.databind.ser.BeanSerializerModifier; import com.fasterxml.jackson.databind.type.CollectionType; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator; @@ -44,6 +47,8 @@ import step.core.scanner.CachedAnnotationScanner; import step.core.yaml.PatchableYamlModel; import step.core.yaml.PatchingContext; +import step.core.yaml.YamlFieldOrder; +import step.core.yaml.YamlFieldPriority; import step.core.yaml.deserialization.PatchableYamlList; import step.core.yaml.deserialization.PatchableYamlListDeserializer; import step.core.yaml.deserialization.PatchableYamlModelDeserializer; @@ -67,6 +72,7 @@ import java.io.OutputStream; import java.lang.reflect.InvocationTargetException; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -236,6 +242,27 @@ public SimpleModule registerAllSerializersAndDeserializers(ObjectMapper resultin public void setupModule(SetupContext context) { super.setupModule(context); + // Bean serializer to have control over field ordering using YamlFieldOrder annotation + context.addBeanSerializerModifier(new BeanSerializerModifier() { + @Override + public List orderProperties(SerializationConfig config, BeanDescription beanDesc, List beanProperties) { + + List orderedList = new ArrayList<>(); + + for (YamlFieldPriority priority : YamlFieldPriority.values()) { + for (BeanPropertyWriter property : beanProperties) { + YamlFieldOrder order = property.getAnnotation(YamlFieldOrder.class); + YamlFieldPriority fieldPriority = order == null ? YamlFieldPriority.Normal : order.value(); + if (priority == fieldPriority) { + orderedList.add(property); + } + } + } + + return orderedList; + } + }); + context.addBeanDeserializerModifier(new BeanDeserializerModifier() { @Override public JsonDeserializer modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer deserializer) { From 3e2d9f9ed3f788cc0db9fea52d7a9de3514daae8 Mon Sep 17 00:00:00 2001 From: Cyril Misev Date: Fri, 28 Aug 2026 18:22:46 +0200 Subject: [PATCH 2/7] SED-4905 order enforcement on simpleyamlartefacts --- .../java/step/core/artefacts/AbstractArtefact.java | 7 +------ .../step/core/yaml/model/SimpleYamlArtefact.java | 14 ++++++++++---- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/step-core/src/main/java/step/core/artefacts/AbstractArtefact.java b/step-core/src/main/java/step/core/artefacts/AbstractArtefact.java index 01c3a884c6..93daf7dac7 100644 --- a/step-core/src/main/java/step/core/artefacts/AbstractArtefact.java +++ b/step-core/src/main/java/step/core/artefacts/AbstractArtefact.java @@ -31,8 +31,6 @@ import step.core.dynamicbeans.DynamicValue; import step.core.entities.EntityConstants; import step.core.entities.EntityReference; -import step.core.yaml.YamlFieldOrder; -import step.core.yaml.YamlFieldPriority; import java.util.ArrayList; import java.util.HashMap; @@ -62,13 +60,10 @@ public abstract class AbstractArtefact extends AbstractOrganizableObject { private DynamicValue continueParentNodeExecutionOnError = new DynamicValue<>(false); private boolean isWorkArtefact = false; - @YamlFieldOrder(YamlFieldPriority.Low) private ChildrenBlock before; - @YamlFieldOrder(YamlFieldPriority.Low) protected List children = new ArrayList<>(); - - @YamlFieldOrder(YamlFieldPriority.Low) + private ChildrenBlock after; public AbstractArtefact() { diff --git a/step-core/src/main/java/step/core/yaml/model/SimpleYamlArtefact.java b/step-core/src/main/java/step/core/yaml/model/SimpleYamlArtefact.java index 680254d39d..fb01e64bb8 100644 --- a/step-core/src/main/java/step/core/yaml/model/SimpleYamlArtefact.java +++ b/step-core/src/main/java/step/core/yaml/model/SimpleYamlArtefact.java @@ -18,15 +18,17 @@ ******************************************************************************/ package step.core.yaml.model; -import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import step.core.artefacts.AbstractArtefact; -import step.core.yaml.YamlModel; import step.core.yaml.SerializationUtils; +import step.core.yaml.YamlModel; import java.io.IOException; import java.util.List; +import java.util.Map; +import java.util.stream.Stream; /** * The yaml representation for {@link AbstractArtefact} classes having the yaml representation (annotated with {@link YamlModel}), @@ -37,7 +39,6 @@ */ public class SimpleYamlArtefact extends AbstractYamlArtefact { - @JsonIgnore protected ObjectNode fieldValues; public SimpleYamlArtefact(Class techArtefactClass, ObjectNode fieldValues, ObjectMapper yamlObjectMapper) { @@ -71,7 +72,12 @@ protected void fillYamlArtefactFields(T artefact) { public ObjectNode toFullJson() { ObjectNode jsonNode = yamlObjectMapper.valueToTree(this); - jsonNode.setAll(fieldValues); + // Flatten fieldValues into existing serialization to preserve order + List> list = jsonNode.propertyStream() + .flatMap(e -> e.getKey().equals("fieldValues") ? e.getValue().propertyStream() : Stream.of(e)) + .toList(); + jsonNode.removeAll(); + list.forEach(e -> jsonNode.set(e.getKey(), e.getValue())); return jsonNode; } } From a2a58136394365fa21dea9a862a0ae6ea40ad227 Mon Sep 17 00:00:00 2001 From: Cyril Misev Date: Fri, 28 Aug 2026 21:12:08 +0200 Subject: [PATCH 3/7] SED-4905 fix unit tests --- .../expected/plan1AfterModification.yml | 2 +- .../expected/plan1AfterModifyAndAdd.yml | 2 +- .../resources/expected/plan1AfterRename.yml | 2 +- ...ep-automation-package-schema-os-1.2.0.json | 360 +++++++++--------- .../core/yaml/model/AbstractYamlArtefact.java | 2 +- .../core/yaml/model/SimpleYamlArtefact.java | 4 +- 6 files changed, 187 insertions(+), 185 deletions(-) diff --git a/step-automation-packages/step-automation-packages-collections/src/test/resources/expected/plan1AfterModification.yml b/step-automation-packages/step-automation-packages-collections/src/test/resources/expected/plan1AfterModification.yml index 9a4a97b961..3b2a0975c6 100644 --- a/step-automation-packages/step-automation-packages-collections/src/test/resources/expected/plan1AfterModification.yml +++ b/step-automation-packages/step-automation-packages-collections/src/test/resources/expected/plan1AfterModification.yml @@ -15,9 +15,9 @@ plans: expression: "mySimpleKey" - callKeyword: nodeName: "CallMyKeyword2" + keyword: "MyKeyword2" inputs: - myInput: "myValue" - keyword: "MyKeyword2" categories: - "Yaml Plan" plansPlainText: diff --git a/step-automation-packages/step-automation-packages-collections/src/test/resources/expected/plan1AfterModifyAndAdd.yml b/step-automation-packages/step-automation-packages-collections/src/test/resources/expected/plan1AfterModifyAndAdd.yml index d6ee24d376..bb6eae1c6e 100644 --- a/step-automation-packages/step-automation-packages-collections/src/test/resources/expected/plan1AfterModifyAndAdd.yml +++ b/step-automation-packages/step-automation-packages-collections/src/test/resources/expected/plan1AfterModifyAndAdd.yml @@ -15,9 +15,9 @@ plans: expression: "mySimpleKey" - callKeyword: nodeName: "CallMyKeyword2" + keyword: "MyKeyword2" inputs: - myInput: "myValue" - keyword: "MyKeyword2" categories: - "Yaml Plan" - name: "New Name" diff --git a/step-automation-packages/step-automation-packages-collections/src/test/resources/expected/plan1AfterRename.yml b/step-automation-packages/step-automation-packages-collections/src/test/resources/expected/plan1AfterRename.yml index 91fc8259f6..ee51c2c55a 100644 --- a/step-automation-packages/step-automation-packages-collections/src/test/resources/expected/plan1AfterRename.yml +++ b/step-automation-packages/step-automation-packages-collections/src/test/resources/expected/plan1AfterRename.yml @@ -14,9 +14,9 @@ plans: expression: "mySimpleKey" - callKeyword: nodeName: "CallMyKeyword2" + keyword: "MyKeyword2" inputs: - myInput: "myValue" - keyword: "MyKeyword2" categories: - "Yaml Plan" plansPlainText: diff --git a/step-automation-packages/step-automation-packages-yaml/src/main/resources/step/automation/packages/yaml/step-automation-package-schema-os-1.2.0.json b/step-automation-packages/step-automation-packages-yaml/src/main/resources/step/automation/packages/yaml/step-automation-package-schema-os-1.2.0.json index d9d1a2cff8..2c391de1aa 100644 --- a/step-automation-packages/step-automation-packages-yaml/src/main/resources/step/automation/packages/yaml/step-automation-package-schema-os-1.2.0.json +++ b/step-automation-packages/step-automation-packages-yaml/src/main/resources/step/automation/packages/yaml/step-automation-package-schema-os-1.2.0.json @@ -292,6 +292,9 @@ } } }, + "children": { + "$ref": "#/$defs/ArtefactArrayDef" + }, "before": { "type": "object", "properties": { @@ -303,12 +306,6 @@ } } }, - "children": { - "$ref": "#/$defs/ArtefactArrayDef" - }, - "description": { - "type": "string" - }, "continueParentNodeExecutionOnError": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, @@ -318,6 +315,9 @@ "skipNode": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, + "description": { + "type": "string" + }, "nodeName": { "$ref": "#/$defs/SmartDynamicValueStringDef", "default": "Assert" @@ -370,6 +370,9 @@ } } }, + "children": { + "$ref": "#/$defs/ArtefactArrayDef" + }, "before": { "type": "object", "properties": { @@ -381,12 +384,6 @@ } } }, - "children": { - "$ref": "#/$defs/ArtefactArrayDef" - }, - "description": { - "type": "string" - }, "continueParentNodeExecutionOnError": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, @@ -396,6 +393,9 @@ "skipNode": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, + "description": { + "type": "string" + }, "nodeName": { "default": "CallKeyword", "$ref": "#/$defs/SmartDynamicValueStringDef" @@ -406,6 +406,12 @@ "routing": { "$ref": "#/$defs/DynamicKeywordInputsDef" }, + "resultMap": { + "$ref": "#/$defs/SmartDynamicValueStringDef" + }, + "inputs": { + "$ref": "#/$defs/DynamicKeywordInputsDef" + }, "keyword": { "keyword": { "oneOf": [ @@ -445,12 +451,6 @@ } ] } - }, - "inputs": { - "$ref": "#/$defs/DynamicKeywordInputsDef" - }, - "resultMap": { - "$ref": "#/$defs/SmartDynamicValueStringDef" } } } @@ -474,6 +474,9 @@ } } }, + "children": { + "$ref": "#/$defs/ArtefactArrayDef" + }, "before": { "type": "object", "properties": { @@ -485,12 +488,6 @@ } } }, - "children": { - "$ref": "#/$defs/ArtefactArrayDef" - }, - "description": { - "type": "string" - }, "continueParentNodeExecutionOnError": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, @@ -500,6 +497,9 @@ "skipNode": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, + "description": { + "type": "string" + }, "nodeName": { "default": "CallPlan", "$ref": "#/$defs/SmartDynamicValueStringDef" @@ -535,6 +535,9 @@ } } }, + "children": { + "$ref": "#/$defs/ArtefactArrayDef" + }, "before": { "type": "object", "properties": { @@ -546,12 +549,6 @@ } } }, - "children": { - "$ref": "#/$defs/ArtefactArrayDef" - }, - "description": { - "type": "string" - }, "continueParentNodeExecutionOnError": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, @@ -561,6 +558,9 @@ "skipNode": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, + "description": { + "type": "string" + }, "nodeName": { "$ref": "#/$defs/SmartDynamicValueStringDef", "default": "Case" @@ -590,6 +590,9 @@ } } }, + "children": { + "$ref": "#/$defs/ArtefactArrayDef" + }, "before": { "type": "object", "properties": { @@ -601,12 +604,6 @@ } } }, - "children": { - "$ref": "#/$defs/ArtefactArrayDef" - }, - "description": { - "type": "string" - }, "continueParentNodeExecutionOnError": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, @@ -616,6 +613,9 @@ "skipNode": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, + "description": { + "type": "string" + }, "nodeName": { "default": "Check", "$ref": "#/$defs/SmartDynamicValueStringDef" @@ -645,6 +645,9 @@ } } }, + "children": { + "$ref": "#/$defs/ArtefactArrayDef" + }, "before": { "type": "object", "properties": { @@ -656,12 +659,6 @@ } } }, - "children": { - "$ref": "#/$defs/ArtefactArrayDef" - }, - "description": { - "type": "string" - }, "continueParentNodeExecutionOnError": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, @@ -671,6 +668,9 @@ "skipNode": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, + "description": { + "type": "string" + }, "nodeName": { "default": "DataSet", "$ref": "#/$defs/SmartDynamicValueStringDef" @@ -948,6 +948,9 @@ } } }, + "children": { + "$ref": "#/$defs/ArtefactArrayDef" + }, "before": { "type": "object", "properties": { @@ -959,12 +962,6 @@ } } }, - "children": { - "$ref": "#/$defs/ArtefactArrayDef" - }, - "description": { - "type": "string" - }, "continueParentNodeExecutionOnError": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, @@ -974,6 +971,9 @@ "skipNode": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, + "description": { + "type": "string" + }, "nodeName": { "$ref": "#/$defs/SmartDynamicValueStringDef", "default": "Echo" @@ -1003,6 +1003,9 @@ } } }, + "children": { + "$ref": "#/$defs/ArtefactArrayDef" + }, "before": { "type": "object", "properties": { @@ -1014,12 +1017,6 @@ } } }, - "children": { - "$ref": "#/$defs/ArtefactArrayDef" - }, - "description": { - "type": "string" - }, "continueParentNodeExecutionOnError": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, @@ -1029,6 +1026,9 @@ "skipNode": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, + "description": { + "type": "string" + }, "nodeName": { "$ref": "#/$defs/SmartDynamicValueStringDef", "default": "Export" @@ -1067,6 +1067,9 @@ } } }, + "children": { + "$ref": "#/$defs/ArtefactArrayDef" + }, "before": { "type": "object", "properties": { @@ -1078,12 +1081,6 @@ } } }, - "children": { - "$ref": "#/$defs/ArtefactArrayDef" - }, - "description": { - "type": "string" - }, "continueParentNodeExecutionOnError": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, @@ -1093,6 +1090,9 @@ "skipNode": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, + "description": { + "type": "string" + }, "nodeName": { "$ref": "#/$defs/SmartDynamicValueStringDef", "default": "Failure" @@ -1125,6 +1125,9 @@ } } }, + "children": { + "$ref": "#/$defs/ArtefactArrayDef" + }, "before": { "type": "object", "properties": { @@ -1136,12 +1139,6 @@ } } }, - "children": { - "$ref": "#/$defs/ArtefactArrayDef" - }, - "description": { - "type": "string" - }, "continueParentNodeExecutionOnError": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, @@ -1151,6 +1148,9 @@ "skipNode": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, + "description": { + "type": "string" + }, "nodeName": { "default": "For", "$ref": "#/$defs/SmartDynamicValueStringDef" @@ -1201,6 +1201,9 @@ } } }, + "children": { + "$ref": "#/$defs/ArtefactArrayDef" + }, "before": { "type": "object", "properties": { @@ -1212,12 +1215,6 @@ } } }, - "children": { - "$ref": "#/$defs/ArtefactArrayDef" - }, - "description": { - "type": "string" - }, "continueParentNodeExecutionOnError": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, @@ -1227,6 +1224,9 @@ "skipNode": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, + "description": { + "type": "string" + }, "nodeName": { "default": "ForEach", "$ref": "#/$defs/SmartDynamicValueStringDef" @@ -1474,6 +1474,9 @@ } } }, + "children": { + "$ref": "#/$defs/ArtefactArrayDef" + }, "before": { "type": "object", "properties": { @@ -1485,12 +1488,6 @@ } } }, - "children": { - "$ref": "#/$defs/ArtefactArrayDef" - }, - "description": { - "type": "string" - }, "continueParentNodeExecutionOnError": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, @@ -1500,6 +1497,9 @@ "skipNode": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, + "description": { + "type": "string" + }, "nodeName": { "$ref": "#/$defs/SmartDynamicValueStringDef", "default": "If" @@ -1529,6 +1529,9 @@ } } }, + "children": { + "$ref": "#/$defs/ArtefactArrayDef" + }, "before": { "type": "object", "properties": { @@ -1540,12 +1543,6 @@ } } }, - "children": { - "$ref": "#/$defs/ArtefactArrayDef" - }, - "description": { - "type": "string" - }, "continueParentNodeExecutionOnError": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, @@ -1555,6 +1552,9 @@ "skipNode": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, + "description": { + "type": "string" + }, "nodeName": { "default": "PerformanceAssert", "$ref": "#/$defs/SmartDynamicValueStringDef" @@ -1604,6 +1604,9 @@ } } }, + "children": { + "$ref": "#/$defs/ArtefactArrayDef" + }, "before": { "type": "object", "properties": { @@ -1615,12 +1618,6 @@ } } }, - "children": { - "$ref": "#/$defs/ArtefactArrayDef" - }, - "description": { - "type": "string" - }, "continueParentNodeExecutionOnError": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, @@ -1630,6 +1627,9 @@ "skipNode": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, + "description": { + "type": "string" + }, "nodeName": { "$ref": "#/$defs/SmartDynamicValueStringDef", "default": "Placeholder" @@ -1656,6 +1656,9 @@ } } }, + "children": { + "$ref": "#/$defs/ArtefactArrayDef" + }, "before": { "type": "object", "properties": { @@ -1667,12 +1670,6 @@ } } }, - "children": { - "$ref": "#/$defs/ArtefactArrayDef" - }, - "description": { - "type": "string" - }, "continueParentNodeExecutionOnError": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, @@ -1682,6 +1679,9 @@ "skipNode": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, + "description": { + "type": "string" + }, "nodeName": { "$ref": "#/$defs/SmartDynamicValueStringDef", "default": "RetryIfFails" @@ -1723,6 +1723,9 @@ } } }, + "children": { + "$ref": "#/$defs/ArtefactArrayDef" + }, "before": { "type": "object", "properties": { @@ -1734,12 +1737,6 @@ } } }, - "children": { - "$ref": "#/$defs/ArtefactArrayDef" - }, - "description": { - "type": "string" - }, "continueParentNodeExecutionOnError": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, @@ -1749,6 +1746,9 @@ "skipNode": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, + "description": { + "type": "string" + }, "nodeName": { "default": "Return", "$ref": "#/$defs/SmartDynamicValueStringDef" @@ -1778,6 +1778,9 @@ } } }, + "children": { + "$ref": "#/$defs/ArtefactArrayDef" + }, "before": { "type": "object", "properties": { @@ -1789,12 +1792,6 @@ } } }, - "children": { - "$ref": "#/$defs/ArtefactArrayDef" - }, - "description": { - "type": "string" - }, "continueParentNodeExecutionOnError": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, @@ -1804,6 +1801,9 @@ "skipNode": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, + "description": { + "type": "string" + }, "nodeName": { "$ref": "#/$defs/SmartDynamicValueStringDef", "default": "Script" @@ -1833,6 +1833,9 @@ } } }, + "children": { + "$ref": "#/$defs/ArtefactArrayDef" + }, "before": { "type": "object", "properties": { @@ -1844,12 +1847,6 @@ } } }, - "children": { - "$ref": "#/$defs/ArtefactArrayDef" - }, - "description": { - "type": "string" - }, "continueParentNodeExecutionOnError": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, @@ -1859,6 +1856,9 @@ "skipNode": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, + "description": { + "type": "string" + }, "nodeName": { "default": "Sequence", "$ref": "#/$defs/SmartDynamicValueStringDef" @@ -1891,6 +1891,9 @@ } } }, + "children": { + "$ref": "#/$defs/ArtefactArrayDef" + }, "before": { "type": "object", "properties": { @@ -1902,12 +1905,6 @@ } } }, - "children": { - "$ref": "#/$defs/ArtefactArrayDef" - }, - "description": { - "type": "string" - }, "continueParentNodeExecutionOnError": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, @@ -1917,6 +1914,9 @@ "skipNode": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, + "description": { + "type": "string" + }, "nodeName": { "default": "Session", "$ref": "#/$defs/SmartDynamicValueStringDef" @@ -1949,6 +1949,9 @@ } } }, + "children": { + "$ref": "#/$defs/ArtefactArrayDef" + }, "before": { "type": "object", "properties": { @@ -1960,12 +1963,6 @@ } } }, - "children": { - "$ref": "#/$defs/ArtefactArrayDef" - }, - "description": { - "type": "string" - }, "continueParentNodeExecutionOnError": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, @@ -1975,6 +1972,9 @@ "skipNode": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, + "description": { + "type": "string" + }, "nodeName": { "$ref": "#/$defs/SmartDynamicValueStringDef", "default": "Set" @@ -2007,6 +2007,9 @@ } } }, + "children": { + "$ref": "#/$defs/ArtefactArrayDef" + }, "before": { "type": "object", "properties": { @@ -2018,12 +2021,6 @@ } } }, - "children": { - "$ref": "#/$defs/ArtefactArrayDef" - }, - "description": { - "type": "string" - }, "continueParentNodeExecutionOnError": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, @@ -2033,6 +2030,9 @@ "skipNode": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, + "description": { + "type": "string" + }, "nodeName": { "$ref": "#/$defs/SmartDynamicValueStringDef", "default": "Sleep" @@ -2068,6 +2068,9 @@ } } }, + "children": { + "$ref": "#/$defs/ArtefactArrayDef" + }, "before": { "type": "object", "properties": { @@ -2079,12 +2082,6 @@ } } }, - "children": { - "$ref": "#/$defs/ArtefactArrayDef" - }, - "description": { - "type": "string" - }, "continueParentNodeExecutionOnError": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, @@ -2094,6 +2091,9 @@ "skipNode": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, + "description": { + "type": "string" + }, "nodeName": { "$ref": "#/$defs/SmartDynamicValueStringDef", "default": "StreamingArtefact" @@ -2120,6 +2120,9 @@ } } }, + "children": { + "$ref": "#/$defs/ArtefactArrayDef" + }, "before": { "type": "object", "properties": { @@ -2131,12 +2134,6 @@ } } }, - "children": { - "$ref": "#/$defs/ArtefactArrayDef" - }, - "description": { - "type": "string" - }, "continueParentNodeExecutionOnError": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, @@ -2146,6 +2143,9 @@ "skipNode": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, + "description": { + "type": "string" + }, "nodeName": { "$ref": "#/$defs/SmartDynamicValueStringDef", "default": "Switch" @@ -2175,6 +2175,9 @@ } } }, + "children": { + "$ref": "#/$defs/ArtefactArrayDef" + }, "before": { "type": "object", "properties": { @@ -2186,12 +2189,6 @@ } } }, - "children": { - "$ref": "#/$defs/ArtefactArrayDef" - }, - "description": { - "type": "string" - }, "continueParentNodeExecutionOnError": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, @@ -2201,6 +2198,9 @@ "skipNode": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, + "description": { + "type": "string" + }, "nodeName": { "$ref": "#/$defs/SmartDynamicValueStringDef", "default": "Synchronized" @@ -2239,6 +2239,9 @@ } } }, + "children": { + "$ref": "#/$defs/ArtefactArrayDef" + }, "before": { "type": "object", "properties": { @@ -2250,12 +2253,6 @@ } } }, - "children": { - "$ref": "#/$defs/ArtefactArrayDef" - }, - "description": { - "type": "string" - }, "continueParentNodeExecutionOnError": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, @@ -2265,6 +2262,9 @@ "skipNode": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, + "description": { + "type": "string" + }, "nodeName": { "$ref": "#/$defs/SmartDynamicValueStringDef", "default": "TestCase" @@ -2291,6 +2291,9 @@ } } }, + "children": { + "$ref": "#/$defs/ArtefactArrayDef" + }, "before": { "type": "object", "properties": { @@ -2302,12 +2305,6 @@ } } }, - "children": { - "$ref": "#/$defs/ArtefactArrayDef" - }, - "description": { - "type": "string" - }, "continueParentNodeExecutionOnError": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, @@ -2317,6 +2314,9 @@ "skipNode": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, + "description": { + "type": "string" + }, "nodeName": { "$ref": "#/$defs/SmartDynamicValueStringDef", "default": "TestScenario" @@ -2343,6 +2343,9 @@ } } }, + "children": { + "$ref": "#/$defs/ArtefactArrayDef" + }, "before": { "type": "object", "properties": { @@ -2354,12 +2357,6 @@ } } }, - "children": { - "$ref": "#/$defs/ArtefactArrayDef" - }, - "description": { - "type": "string" - }, "continueParentNodeExecutionOnError": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, @@ -2369,6 +2366,9 @@ "skipNode": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, + "description": { + "type": "string" + }, "nodeName": { "$ref": "#/$defs/SmartDynamicValueStringDef", "default": "TestSet" @@ -2398,6 +2398,9 @@ } } }, + "children": { + "$ref": "#/$defs/ArtefactArrayDef" + }, "before": { "type": "object", "properties": { @@ -2409,12 +2412,6 @@ } } }, - "children": { - "$ref": "#/$defs/ArtefactArrayDef" - }, - "description": { - "type": "string" - }, "continueParentNodeExecutionOnError": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, @@ -2424,6 +2421,9 @@ "skipNode": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, + "description": { + "type": "string" + }, "nodeName": { "default": "ThreadGroup", "$ref": "#/$defs/SmartDynamicValueStringDef" @@ -2502,6 +2502,9 @@ } } }, + "children": { + "$ref": "#/$defs/ArtefactArrayDef" + }, "before": { "type": "object", "properties": { @@ -2513,12 +2516,6 @@ } } }, - "children": { - "$ref": "#/$defs/ArtefactArrayDef" - }, - "description": { - "type": "string" - }, "continueParentNodeExecutionOnError": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, @@ -2528,6 +2525,9 @@ "skipNode": { "$ref": "#/$defs/SmartDynamicValueBooleanDef" }, + "description": { + "type": "string" + }, "nodeName": { "$ref": "#/$defs/SmartDynamicValueStringDef", "default": "While" diff --git a/step-core/src/main/java/step/core/yaml/model/AbstractYamlArtefact.java b/step-core/src/main/java/step/core/yaml/model/AbstractYamlArtefact.java index b09b585e71..2d1ccd37c8 100644 --- a/step-core/src/main/java/step/core/yaml/model/AbstractYamlArtefact.java +++ b/step-core/src/main/java/step/core/yaml/model/AbstractYamlArtefact.java @@ -69,7 +69,6 @@ public abstract class AbstractYamlArtefact extends A protected DynamicValue instrumentNode = new DynamicValue<>(false); protected DynamicValue continueParentNodeExecutionOnError = new DynamicValue<>(false); - @JsonSchema(ref = YamlJsonSchemaHelper.DEFS_PREFIX + ARTEFACT_ARRAY_DEF) @YamlFieldCustomCopy @YamlFieldOrder(YamlFieldPriority.Low) @@ -77,6 +76,7 @@ public abstract class AbstractYamlArtefact extends A @YamlFieldCustomCopy @YamlFieldOrder(YamlFieldPriority.Low) + @JsonSchema(ref = YamlJsonSchemaHelper.DEFS_PREFIX + ARTEFACT_ARRAY_DEF) protected List children = new ArrayList<>(); @YamlFieldCustomCopy diff --git a/step-core/src/main/java/step/core/yaml/model/SimpleYamlArtefact.java b/step-core/src/main/java/step/core/yaml/model/SimpleYamlArtefact.java index fb01e64bb8..37fed02d5a 100644 --- a/step-core/src/main/java/step/core/yaml/model/SimpleYamlArtefact.java +++ b/step-core/src/main/java/step/core/yaml/model/SimpleYamlArtefact.java @@ -23,6 +23,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import step.core.artefacts.AbstractArtefact; import step.core.yaml.SerializationUtils; +import step.core.yaml.YamlFieldCustomCopy; import step.core.yaml.YamlModel; import java.io.IOException; @@ -39,7 +40,8 @@ */ public class SimpleYamlArtefact extends AbstractYamlArtefact { - protected ObjectNode fieldValues; + @YamlFieldCustomCopy + private ObjectNode fieldValues; public SimpleYamlArtefact(Class techArtefactClass, ObjectNode fieldValues, ObjectMapper yamlObjectMapper) { this.artefactClass = techArtefactClass; From eb2778b800d96d693a11b2bde2df4a0679a568bc Mon Sep 17 00:00:00 2001 From: Cyril Misev Date: Fri, 4 Sep 2026 10:23:33 +0200 Subject: [PATCH 4/7] SED-4905 fix review comments --- .../step/core/yaml/YamlFieldPriority.java | 6 +++--- .../core/yaml/model/AbstractYamlArtefact.java | 10 +++++----- .../core/yaml/model/SimpleYamlArtefact.java | 10 ++++++++-- .../automation/YamlCallFunction.java | 6 +++--- .../artefacts/automation/YamlCallPlan.java | 4 ++-- .../plans/parser/yaml/YamlPlanReader.java | 20 +++++++++++-------- 6 files changed, 33 insertions(+), 23 deletions(-) diff --git a/step-core-model/src/main/java/step/core/yaml/YamlFieldPriority.java b/step-core-model/src/main/java/step/core/yaml/YamlFieldPriority.java index c5474182de..8a7b45d7f3 100644 --- a/step-core-model/src/main/java/step/core/yaml/YamlFieldPriority.java +++ b/step-core-model/src/main/java/step/core/yaml/YamlFieldPriority.java @@ -23,7 +23,7 @@ * Use this prioritization to override the field order */ public enum YamlFieldPriority { - Top, - Normal, - Low, + TOP, + NORMAL, + LOW, } diff --git a/step-core/src/main/java/step/core/yaml/model/AbstractYamlArtefact.java b/step-core/src/main/java/step/core/yaml/model/AbstractYamlArtefact.java index 2d1ccd37c8..1faa619c1f 100644 --- a/step-core/src/main/java/step/core/yaml/model/AbstractYamlArtefact.java +++ b/step-core/src/main/java/step/core/yaml/model/AbstractYamlArtefact.java @@ -59,10 +59,10 @@ public abstract class AbstractYamlArtefact extends A @JsonSchema(defaultProvider = DefaultYamlArtefactNameProvider.class) @YamlFieldCustomCopy - @YamlFieldOrder(YamlFieldPriority.Top) + @YamlFieldOrder(YamlFieldPriority.TOP) protected DynamicValue nodeName; - @YamlFieldOrder(YamlFieldPriority.Top) + @YamlFieldOrder(YamlFieldPriority.TOP) protected String description; protected DynamicValue skipNode = new DynamicValue<>(false); @@ -71,16 +71,16 @@ public abstract class AbstractYamlArtefact extends A @YamlFieldCustomCopy - @YamlFieldOrder(YamlFieldPriority.Low) + @YamlFieldOrder(YamlFieldPriority.LOW) protected YamlChildrenBlock before; @YamlFieldCustomCopy - @YamlFieldOrder(YamlFieldPriority.Low) + @YamlFieldOrder(YamlFieldPriority.LOW) @JsonSchema(ref = YamlJsonSchemaHelper.DEFS_PREFIX + ARTEFACT_ARRAY_DEF) protected List children = new ArrayList<>(); @YamlFieldCustomCopy - @YamlFieldOrder(YamlFieldPriority.Low) + @YamlFieldOrder(YamlFieldPriority.LOW) protected YamlChildrenBlock after; public AbstractYamlArtefact() { diff --git a/step-core/src/main/java/step/core/yaml/model/SimpleYamlArtefact.java b/step-core/src/main/java/step/core/yaml/model/SimpleYamlArtefact.java index 37fed02d5a..a2f05542e6 100644 --- a/step-core/src/main/java/step/core/yaml/model/SimpleYamlArtefact.java +++ b/step-core/src/main/java/step/core/yaml/model/SimpleYamlArtefact.java @@ -75,8 +75,14 @@ protected void fillYamlArtefactFields(T artefact) { public ObjectNode toFullJson() { ObjectNode jsonNode = yamlObjectMapper.valueToTree(this); // Flatten fieldValues into existing serialization to preserve order - List> list = jsonNode.propertyStream() - .flatMap(e -> e.getKey().equals("fieldValues") ? e.getValue().propertyStream() : Stream.of(e)) + List> list = jsonNode.properties().stream() + .flatMap(e -> { + if (e.getKey().equals("fieldValues")) { + return e.getValue() instanceof ObjectNode ? + ((ObjectNode) e.getValue()).properties().stream() : Stream.empty(); + } + return Stream.of(e); + }) .toList(); jsonNode.removeAll(); list.forEach(e -> jsonNode.set(e.getKey(), e.getValue())); diff --git a/step-plans/step-plans-base-artefacts/src/main/java/step/artefacts/automation/YamlCallFunction.java b/step-plans/step-plans-base-artefacts/src/main/java/step/artefacts/automation/YamlCallFunction.java index 171c7a46c5..581b3fffa5 100644 --- a/step-plans/step-plans-base-artefacts/src/main/java/step/artefacts/automation/YamlCallFunction.java +++ b/step-plans/step-plans-base-artefacts/src/main/java/step/artefacts/automation/YamlCallFunction.java @@ -29,14 +29,14 @@ public class YamlCallFunction extends YamlTokenSelector { @YamlFieldCustomCopy - @YamlFieldOrder(YamlFieldPriority.Top) + @YamlFieldOrder(YamlFieldPriority.TOP) protected YamlKeywordDefinition keyword = new YamlKeywordDefinition(null, null, "{}"); @YamlFieldCustomCopy - @YamlFieldOrder(YamlFieldPriority.Top) + @YamlFieldOrder(YamlFieldPriority.TOP) protected YamlDynamicInputs inputs = new YamlDynamicInputs("{}"); - @YamlFieldOrder(YamlFieldPriority.Normal) + @YamlFieldOrder(YamlFieldPriority.NORMAL) protected DynamicValue resultMap = new DynamicValue<>(); public YamlCallFunction() { diff --git a/step-plans/step-plans-base-artefacts/src/main/java/step/artefacts/automation/YamlCallPlan.java b/step-plans/step-plans-base-artefacts/src/main/java/step/artefacts/automation/YamlCallPlan.java index e7f88a8176..96469fcaf9 100644 --- a/step-plans/step-plans-base-artefacts/src/main/java/step/artefacts/automation/YamlCallPlan.java +++ b/step-plans/step-plans-base-artefacts/src/main/java/step/artefacts/automation/YamlCallPlan.java @@ -29,11 +29,11 @@ public class YamlCallPlan extends AbstractYamlArtefact { private String planId; @YamlFieldCustomCopy - @YamlFieldOrder(YamlFieldPriority.Top) + @YamlFieldOrder(YamlFieldPriority.TOP) private YamlDynamicInputs selectionAttributes = new YamlDynamicInputs("{}"); @YamlFieldCustomCopy - @YamlFieldOrder(YamlFieldPriority.Top) + @YamlFieldOrder(YamlFieldPriority.TOP) protected YamlDynamicInputs input = new YamlDynamicInputs("{}"); public YamlCallPlan() { diff --git a/step-plans/step-plans-yaml-parser/src/main/java/step/plans/parser/yaml/YamlPlanReader.java b/step-plans/step-plans-yaml-parser/src/main/java/step/plans/parser/yaml/YamlPlanReader.java index 2d5215a835..d511bae613 100644 --- a/step-plans/step-plans-yaml-parser/src/main/java/step/plans/parser/yaml/YamlPlanReader.java +++ b/step-plans/step-plans-yaml-parser/src/main/java/step/plans/parser/yaml/YamlPlanReader.java @@ -247,16 +247,20 @@ public void setupModule(SetupContext context) { @Override public List orderProperties(SerializationConfig config, BeanDescription beanDesc, List beanProperties) { - List orderedList = new ArrayList<>(); + Map> grouped = new java.util.EnumMap<>(YamlFieldPriority.class); + for (YamlFieldPriority priority : YamlFieldPriority.values()) { + grouped.put(priority, new ArrayList<>()); + } + + for (BeanPropertyWriter property : beanProperties) { + YamlFieldOrder order = property.getAnnotation(YamlFieldOrder.class); + YamlFieldPriority fieldPriority = order == null ? YamlFieldPriority.NORMAL : order.value(); + grouped.get(fieldPriority).add(property); + } + List orderedList = new ArrayList<>(); for (YamlFieldPriority priority : YamlFieldPriority.values()) { - for (BeanPropertyWriter property : beanProperties) { - YamlFieldOrder order = property.getAnnotation(YamlFieldOrder.class); - YamlFieldPriority fieldPriority = order == null ? YamlFieldPriority.Normal : order.value(); - if (priority == fieldPriority) { - orderedList.add(property); - } - } + orderedList.addAll(grouped.get(priority)); } return orderedList; From d61a71c5bffdfe95529bac0a1cc85bc97c8daf59 Mon Sep 17 00:00:00 2001 From: Cyril Misev Date: Fri, 4 Sep 2026 11:24:52 +0200 Subject: [PATCH 5/7] SED-4905 fix logging --- .../packages/yaml/AutomationPackageDescriptorReader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/step-automation-packages/step-automation-packages-yaml/src/main/java/step/automation/packages/yaml/AutomationPackageDescriptorReader.java b/step-automation-packages/step-automation-packages-yaml/src/main/java/step/automation/packages/yaml/AutomationPackageDescriptorReader.java index a24250f040..75ada88653 100644 --- a/step-automation-packages/step-automation-packages-yaml/src/main/java/step/automation/packages/yaml/AutomationPackageDescriptorReader.java +++ b/step-automation-packages/step-automation-packages-yaml/src/main/java/step/automation/packages/yaml/AutomationPackageDescriptorReader.java @@ -145,7 +145,7 @@ protected void logAfterRead(String pac log.info("{} plan(s) found in automation package {}", res.getPlans().size(), StringUtils.defaultString(packageName)); } if (!res.getPlansPlainText().isEmpty()) { - log.info("{} plain text plan(s) found in automation package {}", res.getPlans().size(), StringUtils.defaultString(packageName)); + log.info("{} plain text plan(s) found in automation package {}", res.getPlansPlainText().size(), StringUtils.defaultString(packageName)); } for (Map.Entry> additionalEntry : res.getAdditionalFields().entrySet()) { log.info("{} {} found in automation package {}", additionalEntry.getValue().size(), additionalEntry.getKey(), StringUtils.defaultString(packageName)); From 12300083eea332715235ee190eb87911a4a9bfe6 Mon Sep 17 00:00:00 2001 From: Cyril Misev Date: Fri, 4 Sep 2026 15:53:57 +0200 Subject: [PATCH 6/7] SED-4912 add unit test --- .../AutomationPackageCollectionTestBase.java | 7 +- ...mationPackageDataSourceCollectionTest.java | 3 +- ...utomationPackagePlanFieldOrderingTest.java | 60 +++++++++++++++ ...utomationPackageWithEmptyWildcardTest.java | 2 +- .../test/resources/expected/FieldOrdering.yml | 74 +++++++++++++++++++ .../ap-field-ordering/automation-package.yml | 5 ++ .../ap-field-ordering/keywords/Keyword.yml | 14 ++++ .../ap-field-ordering/plans/FieldOrdering.yml | 74 +++++++++++++++++++ .../dynamicbeans/DynamicBeanResolver.java | 1 - 9 files changed, 235 insertions(+), 5 deletions(-) create mode 100644 step-automation-packages/step-automation-packages-collections/src/test/java/step/core/collections/AutomationPackagePlanFieldOrderingTest.java create mode 100644 step-automation-packages/step-automation-packages-collections/src/test/resources/expected/FieldOrdering.yml create mode 100644 step-automation-packages/step-automation-packages-collections/src/test/resources/testdata/ap-field-ordering/automation-package.yml create mode 100644 step-automation-packages/step-automation-packages-collections/src/test/resources/testdata/ap-field-ordering/keywords/Keyword.yml create mode 100644 step-automation-packages/step-automation-packages-collections/src/test/resources/testdata/ap-field-ordering/plans/FieldOrdering.yml diff --git a/step-automation-packages/step-automation-packages-collections/src/test/java/step/core/collections/AutomationPackageCollectionTestBase.java b/step-automation-packages/step-automation-packages-collections/src/test/java/step/core/collections/AutomationPackageCollectionTestBase.java index 918753c3c6..d2b002de25 100644 --- a/step-automation-packages/step-automation-packages-collections/src/test/java/step/core/collections/AutomationPackageCollectionTestBase.java +++ b/step-automation-packages/step-automation-packages-collections/src/test/java/step/core/collections/AutomationPackageCollectionTestBase.java @@ -47,7 +47,7 @@ public class AutomationPackageCollectionTestBase { private final JavaAutomationPackageReader reader; // To use a different source directory, override in subclass constructor - protected File sourceDirectory = new File("src/test/resources/testdata/ap1"); + protected final File sourceDirectory; protected File destinationDirectory; protected Path expectedFilesPath = new File("src/test/resources/expected").toPath(); protected AutomationPackageYamlFragmentManager fragmentManager; @@ -55,6 +55,11 @@ public class AutomationPackageCollectionTestBase { protected ResourceManager resourceManager; public AutomationPackageCollectionTestBase() { + this(new File("src/test/resources/testdata/ap1")); + } + + public AutomationPackageCollectionTestBase(File sourceDirectory) { + this.sourceDirectory = sourceDirectory; AutomationPackageSerializationRegistry serializationRegistry = new AutomationPackageSerializationRegistry(); AutomationPackageHookRegistry hookRegistry = new AutomationPackageHookRegistry(); diff --git a/step-automation-packages/step-automation-packages-collections/src/test/java/step/core/collections/AutomationPackageDataSourceCollectionTest.java b/step-automation-packages/step-automation-packages-collections/src/test/java/step/core/collections/AutomationPackageDataSourceCollectionTest.java index 09d3fbb899..463f7f6602 100644 --- a/step-automation-packages/step-automation-packages-collections/src/test/java/step/core/collections/AutomationPackageDataSourceCollectionTest.java +++ b/step-automation-packages/step-automation-packages-collections/src/test/java/step/core/collections/AutomationPackageDataSourceCollectionTest.java @@ -46,8 +46,7 @@ public class AutomationPackageDataSourceCollectionTest extends AutomationPackage private Collection planCollection; public AutomationPackageDataSourceCollectionTest() { - super(); - sourceDirectory = new File("src/test/resources/testdata/ap-with-datasource"); + super(new File("src/test/resources/testdata/ap-with-datasource")); } @Before diff --git a/step-automation-packages/step-automation-packages-collections/src/test/java/step/core/collections/AutomationPackagePlanFieldOrderingTest.java b/step-automation-packages/step-automation-packages-collections/src/test/java/step/core/collections/AutomationPackagePlanFieldOrderingTest.java new file mode 100644 index 0000000000..d03c992f86 --- /dev/null +++ b/step-automation-packages/step-automation-packages-collections/src/test/java/step/core/collections/AutomationPackagePlanFieldOrderingTest.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (C) 2026, exense GmbH + * + * This file is part of STEP + * + * STEP is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * STEP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with STEP. If not, see . + ******************************************************************************/ +package step.core.collections; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import step.automation.packages.AutomationPackageReadingException; +import step.core.plans.Plan; +import step.plans.parser.yaml.YamlPlan; + +import java.io.File; +import java.io.IOException; +import java.util.Optional; +import java.util.Properties; + +public class AutomationPackagePlanFieldOrderingTest extends AutomationPackageCollectionTestBase { + + private Collection planCollection; + + public AutomationPackagePlanFieldOrderingTest() { + super(new File("src/test/resources/testdata/ap-field-ordering")); + } + + @Before + public void setUp() throws IOException, AutomationPackageReadingException { + super.setUp(); + AutomationPackageCollectionFactory collectionFactory = new AutomationPackageCollectionFactory(new Properties(), fragmentManager); + planCollection = collectionFactory.getCollection(YamlPlan.PLANS_ENTITY_NAME, Plan.class); + } + + @Test + public void testPlanFieldOrdering() throws IOException { + + Optional optionalPlan = planCollection.find(Filters.equals("attributes.name", "FieldOrdering"), null, null, null, 100).findFirst(); + + Assert.assertTrue(optionalPlan.isPresent()); + + Plan plan = optionalPlan.get(); + planCollection.save(plan); + + assertFilesEqual(expectedFilesPath.resolve("FieldOrdering.yml"), destinationDirectory.toPath().resolve("plans").resolve("FieldOrdering.yml")); + } +} diff --git a/step-automation-packages/step-automation-packages-collections/src/test/java/step/core/collections/AutomationPackageWithEmptyWildcardTest.java b/step-automation-packages/step-automation-packages-collections/src/test/java/step/core/collections/AutomationPackageWithEmptyWildcardTest.java index 3b87a0fab9..bf69b42f53 100644 --- a/step-automation-packages/step-automation-packages-collections/src/test/java/step/core/collections/AutomationPackageWithEmptyWildcardTest.java +++ b/step-automation-packages/step-automation-packages-collections/src/test/java/step/core/collections/AutomationPackageWithEmptyWildcardTest.java @@ -7,7 +7,7 @@ public class AutomationPackageWithEmptyWildcardTest extends AutomationPackageCollectionTestBase { public AutomationPackageWithEmptyWildcardTest() { - super.sourceDirectory = new File("src/test/resources/testdata/ap-with-empty-wildcard"); + super(new File("src/test/resources/testdata/ap-with-empty-wildcard")); } @Test diff --git a/step-automation-packages/step-automation-packages-collections/src/test/resources/expected/FieldOrdering.yml b/step-automation-packages/step-automation-packages-collections/src/test/resources/expected/FieldOrdering.yml new file mode 100644 index 0000000000..3c64ca6ab6 --- /dev/null +++ b/step-automation-packages/step-automation-packages-collections/src/test/resources/expected/FieldOrdering.yml @@ -0,0 +1,74 @@ +--- +plans: + - name: "FieldOrdering" + root: + testCase: + nodeName: "FieldOrdering" + instrumentNode: true + children: + - sequence: + nodeName: "Sequence - Sequence Name" + description: "Description" + skipNode: true + instrumentNode: true + continueParentNodeExecutionOnError: true + continueOnError: true + pacing: 5000 + before: + continueOnError: false + steps: + - echo: + nodeName: "Echo - Init" + description: "Bla" + skipNode: true + instrumentNode: true + continueParentNodeExecutionOnError: true + text: + expression: "Test" + children: + - callKeyword: + nodeName: "Keyword call" + description: "Description" + keyword: "Keyword" + inputs: + - parameter: "Test" + instrumentNode: true + continueParentNodeExecutionOnError: true + remote: false + children: + - assert: + actual: "returnValue" + operator: "EQUALS" + doNegate: true + expected: "1" + customErrorMessage: "afda" + - switch: + nodeName: "Switch - switch" + description: "Description" + expression: "testd" + children: + - case: + value: "Bla" + - case: + value: "Blub" + - for: + nodeName: "For - Loop" + item: "a" + maxFailedLoops: 1 + threads: 2 + globalCounter: "c" + userItem: "b" + start: 0 + end: 1 + inc: 2 + children: + - set: + nodeName: "Set - variable" + key: "variable" + value: "value" + after: + continueOnError: false + steps: + - echo: + nodeName: "Echo - After" + text: "After" diff --git a/step-automation-packages/step-automation-packages-collections/src/test/resources/testdata/ap-field-ordering/automation-package.yml b/step-automation-packages/step-automation-packages-collections/src/test/resources/testdata/ap-field-ordering/automation-package.yml new file mode 100644 index 0000000000..7cbaeb413d --- /dev/null +++ b/step-automation-packages/step-automation-packages-collections/src/test/resources/testdata/ap-field-ordering/automation-package.yml @@ -0,0 +1,5 @@ +schemaVersion: 1.2.0 +name: "YAML Field Ordering" +fragments: + - "keywords/*.yml" + - "plans/*.yml" diff --git a/step-automation-packages/step-automation-packages-collections/src/test/resources/testdata/ap-field-ordering/keywords/Keyword.yml b/step-automation-packages/step-automation-packages-collections/src/test/resources/testdata/ap-field-ordering/keywords/Keyword.yml new file mode 100644 index 0000000000..c344d35e9d --- /dev/null +++ b/step-automation-packages/step-automation-packages-collections/src/test/resources/testdata/ap-field-ordering/keywords/Keyword.yml @@ -0,0 +1,14 @@ +--- +keywords: + - Composite: + name: "Keyword" + schema: + properties: + parameter: + type: "string" + required: + - "parameter" + routing: {} + plan: + root: + sequence: {} diff --git a/step-automation-packages/step-automation-packages-collections/src/test/resources/testdata/ap-field-ordering/plans/FieldOrdering.yml b/step-automation-packages/step-automation-packages-collections/src/test/resources/testdata/ap-field-ordering/plans/FieldOrdering.yml new file mode 100644 index 0000000000..96ec4a08f9 --- /dev/null +++ b/step-automation-packages/step-automation-packages-collections/src/test/resources/testdata/ap-field-ordering/plans/FieldOrdering.yml @@ -0,0 +1,74 @@ +--- +plans: + - name: "FieldOrdering" + root: + testCase: + nodeName: "FieldOrdering" + instrumentNode: true + children: + - sequence: + continueParentNodeExecutionOnError: true + description: "Description" + nodeName: "Sequence - Sequence Name" + instrumentNode: true + continueOnError: true + pacing: 5000 + before: + continueOnError: false + steps: + - echo: + text: + expression: "Test" + nodeName: "Echo - Init" + description: "Bla" + skipNode: true + instrumentNode: true + continueParentNodeExecutionOnError: true + after: + continueOnError: false + steps: + - echo: + nodeName: "Echo - After" + text: "After" + children: + - callKeyword: + nodeName: "Keyword call" + description: "Description" + keyword: "Keyword" + inputs: + - parameter: "Test" + instrumentNode: true + children: + - assert: + actual: "returnValue" + operator: "EQUALS" + doNegate: true + expected: "1" + customErrorMessage: "afda" + - switch: + nodeName: "Switch - switch" + description: "Description" + expression: "testd" + children: + - case: + value: "Bla" + - case: + value: "Blub" + - for: + nodeName: "For - Loop" + item: "a" + threads: 2 + userItem: "b" + start: 0 + end: 1 + inc: 2 + children: + - set: + nodeName: "Set - variable" + key: "variable" + value: "value" + globalCounter: "c" + maxFailedLoops: 1 + continueParentNodeExecutionOnError: true + remote: false + skipNode: true diff --git a/step-core/src/main/java/step/core/dynamicbeans/DynamicBeanResolver.java b/step-core/src/main/java/step/core/dynamicbeans/DynamicBeanResolver.java index 48403d1432..d5548f0d12 100644 --- a/step-core/src/main/java/step/core/dynamicbeans/DynamicBeanResolver.java +++ b/step-core/src/main/java/step/core/dynamicbeans/DynamicBeanResolver.java @@ -20,7 +20,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import step.core.artefacts.AbstractArtefact; import java.beans.BeanInfo; import java.beans.Introspector; From 27bfca3a2f945a880c2a918948129c12708303a1 Mon Sep 17 00:00:00 2001 From: Cyril Misev Date: Tue, 8 Sep 2026 12:45:02 +0200 Subject: [PATCH 7/7] SED-4920 fix revert faulty ai recommendation for a simpler stream expression --- .../step/core/yaml/model/SimpleYamlArtefact.java | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/step-core/src/main/java/step/core/yaml/model/SimpleYamlArtefact.java b/step-core/src/main/java/step/core/yaml/model/SimpleYamlArtefact.java index a2f05542e6..bd8a20f5c3 100644 --- a/step-core/src/main/java/step/core/yaml/model/SimpleYamlArtefact.java +++ b/step-core/src/main/java/step/core/yaml/model/SimpleYamlArtefact.java @@ -76,13 +76,11 @@ public ObjectNode toFullJson() { ObjectNode jsonNode = yamlObjectMapper.valueToTree(this); // Flatten fieldValues into existing serialization to preserve order List> list = jsonNode.properties().stream() - .flatMap(e -> { - if (e.getKey().equals("fieldValues")) { - return e.getValue() instanceof ObjectNode ? - ((ObjectNode) e.getValue()).properties().stream() : Stream.empty(); - } - return Stream.of(e); - }) + .flatMap(e -> + e.getKey().equals("fieldValues") + ? e.getValue().propertyStream() + : Stream.of(e) + ) .toList(); jsonNode.removeAll(); list.forEach(e -> jsonNode.set(e.getKey(), e.getValue()));