This is an automated email from the ASF dual-hosted git repository. exceptionfactory pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/nifi.git
The following commit(s) were added to refs/heads/main by this push: new f3fed9f6d2 NIFI-14660 Git Flow normalization sorts Lists and Sets of of Strings and Enums (#10016) f3fed9f6d2 is described below commit f3fed9f6d20c4f625b868c9e62e81b86c1bdeb14 Author: Pierre Villard <pierre.villard...@gmail.com> AuthorDate: Tue Jun 17 16:22:33 2025 +0200 NIFI-14660 Git Flow normalization sorts Lists and Sets of of Strings and Enums (#10016) Signed-off-by: David Handermann <exceptionfact...@apache.org> --- .../serialize/JacksonFlowSnapshotSerializer.java | 1 + .../git/serialize/SortedEnumSetSerializer.java | 48 ++++++++++++++ .../serialize/SortedStringCollectionsModule.java | 60 +++++++++++++++++ .../git/serialize/SortedStringListSerializer.java | 75 ++++++++++++++++++++++ .../git/serialize/SortedStringSetSerializer.java | 46 +++++++++++++ .../JacksonFlowSnapshotSerializerTest.java | 17 +++++ 6 files changed, 247 insertions(+) diff --git a/nifi-extension-bundles/nifi-extension-utils/nifi-git-flow-registry/src/main/java/org/apache/nifi/registry/flow/git/serialize/JacksonFlowSnapshotSerializer.java b/nifi-extension-bundles/nifi-extension-utils/nifi-git-flow-registry/src/main/java/org/apache/nifi/registry/flow/git/serialize/JacksonFlowSnapshotSerializer.java index 683e96b50c..b39aec95bf 100644 --- a/nifi-extension-bundles/nifi-extension-utils/nifi-git-flow-registry/src/main/java/org/apache/nifi/registry/flow/git/serialize/JacksonFlowSnapshotSerializer.java +++ b/nifi-extension-bundles/nifi-extension-utils/nifi-git-flow-registry/src/main/java/org/apache/nifi/registry/flow/git/serialize/JacksonFlowSnapshotSerializer.java @@ -43,6 +43,7 @@ public class JacksonFlowSnapshotSerializer implements FlowSnapshotSerializer { .configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true) .enable(SerializationFeature.INDENT_OUTPUT) .addModule(new VersionedComponentModule()) + .addModule(new SortedStringCollectionsModule()) .build(); @Override diff --git a/nifi-extension-bundles/nifi-extension-utils/nifi-git-flow-registry/src/main/java/org/apache/nifi/registry/flow/git/serialize/SortedEnumSetSerializer.java b/nifi-extension-bundles/nifi-extension-utils/nifi-git-flow-registry/src/main/java/org/apache/nifi/registry/flow/git/serialize/SortedEnumSetSerializer.java new file mode 100644 index 0000000000..ed03030dcb --- /dev/null +++ b/nifi-extension-bundles/nifi-extension-utils/nifi-git-flow-registry/src/main/java/org/apache/nifi/registry/flow/git/serialize/SortedEnumSetSerializer.java @@ -0,0 +1,48 @@ +/* + * 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.nifi.registry.flow.git.serialize; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +import java.io.IOException; +import java.util.List; +import java.util.Set; + +public class SortedEnumSetSerializer extends StdSerializer<Set<? extends Enum<?>>> { + + public SortedEnumSetSerializer() { + super((Class<Set<? extends Enum<?>>>) (Class<?>) Set.class); + } + + @Override + public void serialize(final Set<? extends Enum<?>> value, final JsonGenerator gen, final SerializerProvider provider) throws IOException { + final List<String> sorted = value.stream() + .map(Enum::name) + .sorted() + .toList(); + + gen.writeStartArray(); + for (final String str : sorted) { + gen.writeString(str); + } + gen.writeEndArray(); + } +} + diff --git a/nifi-extension-bundles/nifi-extension-utils/nifi-git-flow-registry/src/main/java/org/apache/nifi/registry/flow/git/serialize/SortedStringCollectionsModule.java b/nifi-extension-bundles/nifi-extension-utils/nifi-git-flow-registry/src/main/java/org/apache/nifi/registry/flow/git/serialize/SortedStringCollectionsModule.java new file mode 100644 index 0000000000..215d7db1fe --- /dev/null +++ b/nifi-extension-bundles/nifi-extension-utils/nifi-git-flow-registry/src/main/java/org/apache/nifi/registry/flow/git/serialize/SortedStringCollectionsModule.java @@ -0,0 +1,60 @@ +/* + * 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.nifi.registry.flow.git.serialize; + +import com.fasterxml.jackson.databind.BeanDescription; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializationConfig; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.databind.ser.BeanSerializerModifier; +import com.fasterxml.jackson.databind.type.CollectionType; + +import java.util.List; +import java.util.Set; + +public class SortedStringCollectionsModule extends SimpleModule { + + final Set<String> fieldsToSkipSorting = Set.of("inheritedParameterContexts"); + + @Override + public void setupModule(final SetupContext context) { + super.setupModule(context); + context.addBeanSerializerModifier(new BeanSerializerModifier() { + @Override + public JsonSerializer<?> modifyCollectionSerializer(final SerializationConfig config, + final CollectionType valueType, + final BeanDescription beanDesc, + final JsonSerializer<?> serializer) { + // Only apply to List<String> + if (List.class.isAssignableFrom(valueType.getRawClass()) && valueType.getContentType().getRawClass() == String.class) { + return new SortedStringListSerializer((JsonSerializer<Object>) serializer, fieldsToSkipSorting); + } + // Only apply to Set<String> + if (Set.class.isAssignableFrom(valueType.getRawClass()) && valueType.getContentType().getRawClass() == String.class) { + return new SortedStringSetSerializer(); + } + // Only apply to set of enums + if (Set.class.isAssignableFrom(valueType.getRawClass()) && valueType.getContentType().isEnumType()) { + return new SortedEnumSetSerializer(); + } + return serializer; + } + }); + } +} + diff --git a/nifi-extension-bundles/nifi-extension-utils/nifi-git-flow-registry/src/main/java/org/apache/nifi/registry/flow/git/serialize/SortedStringListSerializer.java b/nifi-extension-bundles/nifi-extension-utils/nifi-git-flow-registry/src/main/java/org/apache/nifi/registry/flow/git/serialize/SortedStringListSerializer.java new file mode 100644 index 0000000000..348da9bb1b --- /dev/null +++ b/nifi-extension-bundles/nifi-extension-utils/nifi-git-flow-registry/src/main/java/org/apache/nifi/registry/flow/git/serialize/SortedStringListSerializer.java @@ -0,0 +1,75 @@ +/* + * 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.nifi.registry.flow.git.serialize; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.ContextualSerializer; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +public class SortedStringListSerializer extends StdSerializer<List<String>> implements ContextualSerializer { + + private final JsonSerializer<Object> defaultSerializer; + private final Set<String> fieldsToSkipSorting; + private final String currentFieldName; // for ContextualSerializer + + public SortedStringListSerializer(final JsonSerializer<Object> defaultSerializer, final Set<String> fieldsToSkipSorting) { + this(defaultSerializer, fieldsToSkipSorting, null); + } + + public SortedStringListSerializer(final JsonSerializer<Object> defaultSerializer, final Set<String> fieldsToSkipSorting, final String currentFieldName) { + super((Class<List<String>>) (Class<?>) List.class); + this.defaultSerializer = defaultSerializer; + this.fieldsToSkipSorting = fieldsToSkipSorting; + this.currentFieldName = currentFieldName; + } + + @Override + public void serialize(final List<String> value, final JsonGenerator gen, final SerializerProvider provider) throws IOException { + if (fieldsToSkipSorting.contains(currentFieldName)) { + // Skip sorting, delegate to default + defaultSerializer.serialize(value, gen, provider); + return; + } + + final List<String> sorted = new ArrayList<>(value); + Collections.sort(sorted); + + gen.writeStartArray(); + for (final String str : sorted) { + gen.writeString(str); + } + gen.writeEndArray(); + } + + @Override + public JsonSerializer<?> createContextual(final SerializerProvider prov, final BeanProperty property) throws JsonMappingException { + final JsonSerializer<Object> defaultSer = prov.findValueSerializer(prov.constructType(List.class), property); + final String fieldName = property != null ? property.getName() : null; + return new SortedStringListSerializer(defaultSer, fieldsToSkipSorting, fieldName); + } +} diff --git a/nifi-extension-bundles/nifi-extension-utils/nifi-git-flow-registry/src/main/java/org/apache/nifi/registry/flow/git/serialize/SortedStringSetSerializer.java b/nifi-extension-bundles/nifi-extension-utils/nifi-git-flow-registry/src/main/java/org/apache/nifi/registry/flow/git/serialize/SortedStringSetSerializer.java new file mode 100644 index 0000000000..1593941122 --- /dev/null +++ b/nifi-extension-bundles/nifi-extension-utils/nifi-git-flow-registry/src/main/java/org/apache/nifi/registry/flow/git/serialize/SortedStringSetSerializer.java @@ -0,0 +1,46 @@ +/* + * 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.nifi.registry.flow.git.serialize; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +public class SortedStringSetSerializer extends StdSerializer<Set<String>> { + + public SortedStringSetSerializer() { + super((Class<Set<String>>) (Class<?>) Set.class); + } + + @Override + public void serialize(final Set<String> value, final JsonGenerator gen, final SerializerProvider provider) throws IOException { + final List<String> sorted = new ArrayList<>(value); + Collections.sort(sorted); + gen.writeStartArray(); + for (final String str : sorted) { + gen.writeString(str); + } + gen.writeEndArray(); + } +} diff --git a/nifi-extension-bundles/nifi-extension-utils/nifi-git-flow-registry/src/test/java/org/apache/nifi/registry/flow/git/serialize/JacksonFlowSnapshotSerializerTest.java b/nifi-extension-bundles/nifi-extension-utils/nifi-git-flow-registry/src/test/java/org/apache/nifi/registry/flow/git/serialize/JacksonFlowSnapshotSerializerTest.java index 5553035be9..a0bd0370e8 100644 --- a/nifi-extension-bundles/nifi-extension-utils/nifi-git-flow-registry/src/test/java/org/apache/nifi/registry/flow/git/serialize/JacksonFlowSnapshotSerializerTest.java +++ b/nifi-extension-bundles/nifi-extension-utils/nifi-git-flow-registry/src/test/java/org/apache/nifi/registry/flow/git/serialize/JacksonFlowSnapshotSerializerTest.java @@ -23,10 +23,14 @@ import org.apache.nifi.flow.VersionedParameter; import org.apache.nifi.flow.VersionedParameterContext; import org.apache.nifi.flow.VersionedProcessGroup; import org.apache.nifi.flow.VersionedProcessor; +import org.apache.nifi.flow.VersionedPropertyDescriptor; +import org.apache.nifi.flow.VersionedResourceDefinition; +import org.apache.nifi.flow.VersionedResourceType; import org.apache.nifi.registry.flow.RegisteredFlowSnapshot; import org.junit.jupiter.api.Test; import java.io.IOException; +import java.util.List; import java.util.Map; import java.util.Set; @@ -45,6 +49,7 @@ public class JacksonFlowSnapshotSerializerTest { final VersionedParameterContext versionedParameterContext = new VersionedParameterContext(); versionedParameterContext.setIdentifier("myParamContext"); + versionedParameterContext.setInheritedParameterContexts(List.of("inheritedContext2", "inheritedContext3", "inheritedContext1")); VersionedParameter parameter1 = new VersionedParameter(); parameter1.setName("name1"); @@ -55,8 +60,15 @@ public class JacksonFlowSnapshotSerializerTest { versionedParameterContext.setParameters(Set.of(parameter2, parameter1, parameter3)); + final VersionedPropertyDescriptor descriptor = new VersionedPropertyDescriptor(); + final VersionedResourceDefinition resourceDefinition = new VersionedResourceDefinition(); + resourceDefinition.setResourceTypes(Set.of(VersionedResourceType.TEXT, VersionedResourceType.URL, VersionedResourceType.FILE)); + descriptor.setResourceDefinition(resourceDefinition); + final VersionedProcessor processor1 = new VersionedProcessor(); processor1.setIdentifier("proc1"); + processor1.setAutoTerminatedRelationships(Set.of("success", "failure")); + processor1.setPropertyDescriptors(Map.of("prop1", descriptor)); final VersionedProcessor processor2 = new VersionedProcessor(); processor2.setIdentifier("proc2"); final VersionedProcessor processor3 = new VersionedProcessor(); @@ -82,10 +94,15 @@ public class JacksonFlowSnapshotSerializerTest { assertEquals(3, processors.size()); assertEquals("proc1", processors.get(0).get("identifier").asText()); + assertEquals("[ \"failure\", \"success\" ]", processors.get(0).get("autoTerminatedRelationships").toPrettyString()); + assertEquals("[ \"FILE\", \"TEXT\", \"URL\" ]", processors.get(0).get("propertyDescriptors").get("prop1").get("resourceDefinition").get("resourceTypes").toPrettyString()); + assertEquals("proc2", processors.get(1).get("identifier").asText()); assertEquals("proc3", processors.get(2).get("identifier").asText()); assertEquals(1, parameterContexts.size()); + assertEquals("[ \"inheritedContext2\", \"inheritedContext3\", \"inheritedContext1\" ]", parameterContext.get("inheritedParameterContexts").toPrettyString()); + assertEquals(3, parameters.size()); assertEquals("name1", parameters.get(0).get("name").asText()); assertEquals("name2", parameters.get(1).get("name").asText());