This is an automated email from the ASF dual-hosted git repository.

markap14 pushed a commit to branch NIFI-15258
in repository https://gitbox.apache.org/repos/asf/nifi-api.git

commit 7d13e70144afe71452374901d6b1167b8c3a0486
Author: Mark Payne <[email protected]>
AuthorDate: Tue Jan 27 09:03:52 2026 -0500

    NIFI-15467: Allow Connectors to specify bundle compatability when upd… (#54)
    
    * NIFI-15467: Allow Connectors to specify bundle compatability when 
updating flow
    
    * NIFI-15467: Have ComponentBundleLookup return Optional<Bundle> from 
getLatestBundle instead of a nullable Bundle
---
 .../components/connector/BundleCompatibility.java  | 47 ++++++++++++++++++++++
 .../connector/ComponentBundleLookup.java           | 36 +++++++++++++++++
 .../connector/ConnectorInitializationContext.java  | 28 ++++++++++++-
 ...ocumentationConnectorInitializationContext.java | 18 ++++++++-
 4 files changed, 125 insertions(+), 4 deletions(-)

diff --git 
a/src/main/java/org/apache/nifi/components/connector/BundleCompatibility.java 
b/src/main/java/org/apache/nifi/components/connector/BundleCompatibility.java
new file mode 100644
index 0000000..b17eb38
--- /dev/null
+++ 
b/src/main/java/org/apache/nifi/components/connector/BundleCompatibility.java
@@ -0,0 +1,47 @@
+/*
+ * 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.components.connector;
+
+/**
+ * Specifies how bundle resolution should be handled when updating a flow with 
components
+ * whose specified bundles may not be available in the NiFi instance.
+ */
+public enum BundleCompatibility {
+
+    /**
+     * Requires the exact bundle specified in the flow. If the bundle is not 
available,
+     * a ghosted component will be created. This is the default behavior.
+     */
+    REQUIRE_EXACT_BUNDLE,
+
+    /**
+     * If the specified bundle is not available, checks what bundles are 
available for
+     * the given component type. If exactly one bundle is available, resolves 
to that bundle.
+     * If no bundles or multiple bundles are available, uses the specified 
bundle and
+     * creates a ghosted component.
+     */
+    RESOLVE_BUNDLE,
+
+    /**
+     * If the specified bundle is not available, checks what bundles are 
available for
+     * the given component type. If no bundles are available, uses the 
specified bundle
+     * and creates a ghosted component. Otherwise, uses the newest available 
bundle
+     * based on version comparison.
+     */
+    RESOLVE_NEWEST_BUNDLE
+}
diff --git 
a/src/main/java/org/apache/nifi/components/connector/ComponentBundleLookup.java 
b/src/main/java/org/apache/nifi/components/connector/ComponentBundleLookup.java
index 7abe996..84cba63 100644
--- 
a/src/main/java/org/apache/nifi/components/connector/ComponentBundleLookup.java
+++ 
b/src/main/java/org/apache/nifi/components/connector/ComponentBundleLookup.java
@@ -20,6 +20,7 @@ package org.apache.nifi.components.connector;
 import org.apache.nifi.flow.Bundle;
 
 import java.util.List;
+import java.util.Optional;
 
 public interface ComponentBundleLookup {
 
@@ -30,4 +31,39 @@ public interface ComponentBundleLookup {
      */
     List<Bundle> getAvailableBundles(String componentType);
 
+    /**
+     * Determines the latest bundle available for the given component type 
based on version comparison.
+     *
+     * <p>Version comparison follows these rules:</p>
+     * <ol>
+     *   <li>Versions are split by dots (e.g., "2.1.0" becomes ["2", "1", 
"0"]).
+     *       This also supports calendar-date formats (e.g., 
"2026.01.01").</li>
+     *   <li>Each part is compared numerically when possible; numeric parts 
are considered
+     *       newer than non-numeric parts (e.g., "2.0.0" &gt; "2.0.next")</li>
+     *   <li>When base versions are equal, qualifiers are compared with the 
following precedence
+     *       (highest to lowest):
+     *       <ul>
+     *         <li>Release (no qualifier)</li>
+     *         <li>RC (Release Candidate) - e.g., "-RC1", "-RC2"</li>
+     *         <li>M (Milestone) - e.g., "-M1", "-M2"</li>
+     *         <li>Other/unknown qualifiers</li>
+     *         <li>SNAPSHOT</li>
+     *       </ul>
+     *   </li>
+     *   <li>Within the same qualifier type, numeric suffixes are compared
+     *       (e.g., "2.0.0-RC2" &gt; "2.0.0-RC1", "2.0.0-M4" &gt; 
"2.0.0-M1")</li>
+     * </ol>
+     *
+     * <p>Examples of version ordering (highest to lowest):</p>
+     * <ul>
+     *   <li>3.0.0 &gt; 2.1.0 &gt; 2.0.0 &gt; 2.0.0-RC2 &gt; 2.0.0-RC1 &gt; 
2.0.0-M4 &gt; 2.0.0-M1 &gt; 2.0.0-SNAPSHOT</li>
+     *   <li>2.1.0-SNAPSHOT &gt; 2.0.0 (higher base version wins)</li>
+     *   <li>2026.01.01 &gt; 2025.12.31 (calendar-date format)</li>
+     * </ul>
+     *
+     * @param componentType the component type
+     * @return an Optional containing the latest available bundle, or empty if 
no bundles are available
+     */
+    Optional<Bundle> getLatestBundle(String componentType);
+
 }
diff --git 
a/src/main/java/org/apache/nifi/components/connector/ConnectorInitializationContext.java
 
b/src/main/java/org/apache/nifi/components/connector/ConnectorInitializationContext.java
index 88880f9..fa422b4 100644
--- 
a/src/main/java/org/apache/nifi/components/connector/ConnectorInitializationContext.java
+++ 
b/src/main/java/org/apache/nifi/components/connector/ConnectorInitializationContext.java
@@ -70,7 +70,6 @@ public interface ConnectorInitializationContext {
      *   <li>Restarting all components</li>
      * </ul>
      *
-     *
      * <p>
      *     Depending on the changes required in order to update the flow to 
the provided VersionedProcessGroup, this
      *     could also result in stopping source processors and waiting for 
queues to drain, etc.
@@ -82,9 +81,34 @@ public interface ConnectorInitializationContext {
      *   be started.
      * </p>
      *
+     * <p>
+     *   This method uses the Bundle Compatability strategy of {@link 
BundleCompatibility#RESOLVE_BUNDLE}.
+     * </p>
+     *
+     * @param flowContext the context of the flow to be updated
+     * @param versionedExternalFlow the new representation of the flow
+     */
+    default void updateFlow(FlowContext flowContext, VersionedExternalFlow 
versionedExternalFlow) throws FlowUpdateException {
+        updateFlow(flowContext, versionedExternalFlow, 
BundleCompatibility.RESOLVE_BUNDLE);
+    }
+
+    /**
+     * <p>
+     *   Updates the Connector's flow to the given VersionedExternalFlow with 
the specified bundle compatibility strategy.
+     *   This method behaves like {@link #updateFlow(FlowContext, 
VersionedExternalFlow)} but allows control over how
+     *   component bundles are resolved when the specified bundle is not 
available.
+     * </p>
+     *
+     * <p>
+     *    Note that if Bundle Compatability is not set to {@link 
BundleCompatibility#REQUIRE_EXACT_BUNDLE}, this method may update the provided
+     *    VersionedExternalFlow to represent the actual bundles used during 
the update.
+     * </p>
+     *
      * @param flowContext the context of the flow to be updated
      * @param versionedExternalFlow the new representation of the flow
+     * @param bundleCompatability the strategy to use when resolving component 
bundles
+     * @throws FlowUpdateException if the flow update fails
      */
-    void updateFlow(FlowContext flowContext, VersionedExternalFlow 
versionedExternalFlow) throws FlowUpdateException;
+    void updateFlow(FlowContext flowContext, VersionedExternalFlow 
versionedExternalFlow, BundleCompatibility bundleCompatability) throws 
FlowUpdateException;
 
 }
diff --git 
a/src/main/java/org/apache/nifi/documentation/init/DocumentationConnectorInitializationContext.java
 
b/src/main/java/org/apache/nifi/documentation/init/DocumentationConnectorInitializationContext.java
index 75bc7e0..1eeb802 100644
--- 
a/src/main/java/org/apache/nifi/documentation/init/DocumentationConnectorInitializationContext.java
+++ 
b/src/main/java/org/apache/nifi/documentation/init/DocumentationConnectorInitializationContext.java
@@ -16,14 +16,17 @@
  */
 package org.apache.nifi.documentation.init;
 
+import org.apache.nifi.components.connector.BundleCompatibility;
 import org.apache.nifi.components.connector.ComponentBundleLookup;
 import org.apache.nifi.components.connector.ConnectorInitializationContext;
 import org.apache.nifi.components.connector.FlowUpdateException;
 import org.apache.nifi.components.connector.components.FlowContext;
+import org.apache.nifi.flow.Bundle;
 import org.apache.nifi.flow.VersionedExternalFlow;
 import org.apache.nifi.logging.ComponentLog;
 
 import java.util.List;
+import java.util.Optional;
 import java.util.UUID;
 
 /**
@@ -51,11 +54,22 @@ public class DocumentationConnectorInitializationContext 
implements ConnectorIni
 
     @Override
     public ComponentBundleLookup getComponentBundleLookup() {
-        return componentType -> List.of();
+        return new ComponentBundleLookup() {
+            @Override
+            public List<Bundle> getAvailableBundles(final String 
componentType) {
+                return List.of();
+            }
+
+            @Override
+            public Optional<Bundle> getLatestBundle(final String 
componentType) {
+                return Optional.empty();
+            }
+        };
     }
 
     @Override
-    public void updateFlow(final FlowContext flowContext, final 
VersionedExternalFlow versionedExternalFlow) throws FlowUpdateException {
+    public void updateFlow(final FlowContext flowContext, final 
VersionedExternalFlow versionedExternalFlow,
+                           final BundleCompatibility bundleCompatability) 
throws FlowUpdateException {
         // No-op for documentation purposes - we don't actually update any 
flows
     }
 }

Reply via email to