NIFI-14064 Fixed Display of Descriptions for Python Processors

- Set Tags in Extension definitions from Python Processor Details to avoid errors
- Added and updated tests for manifests

This closes #9565

Signed-off-by: David Handermann <exceptionfactory@apache.org>
This commit is contained in:
Rajmund Takacs 2024-12-04 16:48:41 +01:00 committed by exceptionfactory
parent c4420b410b
commit 3c9ecd876d
No known key found for this signature in database
GPG Key ID: 29B6A52D2AAE8DBA
4 changed files with 111 additions and 0 deletions

View File

@ -160,6 +160,7 @@ public class PythonControllerInteractionIT {
.orElseThrow(() -> new RuntimeException("Could not find ConvertCsvToExcel"));
assertEquals("0.0.1-SNAPSHOT", convertCsvToExcel.getProcessorVersion());
assertEquals(List.of("csv", "excel"), convertCsvToExcel.getTags());
assertEquals(new File("target/python/extensions/ConvertCsvToExcel.py").getAbsolutePath(),
new File(convertCsvToExcel.getSourceLocation()).getAbsolutePath());
}

View File

@ -191,6 +191,7 @@ public class StandardRuntimeManifestService implements RuntimeManifestService {
final Extension extension = new Extension();
extension.setDescription(pythonProcessorDetails.getCapabilityDescription());
extension.setName(pythonProcessorDetails.getProcessorType());
extension.setTags(pythonProcessorDetails.getTags());
extension.setInputRequirement(InputRequirement.INPUT_REQUIRED);
extension.setSupportsBatching(true);
extension.setType(ExtensionType.PROCESSOR);

View File

@ -25,7 +25,12 @@ import org.apache.nifi.c2.protocol.component.api.ProcessorDefinition;
import org.apache.nifi.c2.protocol.component.api.RuntimeManifest;
import org.apache.nifi.extension.manifest.parser.ExtensionManifestParser;
import org.apache.nifi.extension.manifest.parser.jaxb.JAXBExtensionManifestParser;
import org.apache.nifi.nar.ExtensionDefinition;
import org.apache.nifi.nar.ExtensionDefinition.ExtensionRuntime;
import org.apache.nifi.nar.ExtensionManager;
import org.apache.nifi.nar.PythonBundle;
import org.apache.nifi.processor.Processor;
import org.apache.nifi.python.PythonProcessorDetails;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -33,7 +38,9 @@ import java.io.File;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static java.util.Collections.emptySet;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.Mockito.mock;
@ -43,6 +50,7 @@ public class StandardRuntimeManifestServiceTest {
private Bundle frameworkBundle;
private Bundle testComponentsBundle;
private Bundle testPythonComponentsBundle;
private ExtensionManager extensionManager;
private ExtensionManifestParser extensionManifestParser;
private RuntimeManifestService runtimeManifestService;
@ -69,6 +77,13 @@ public class StandardRuntimeManifestServiceTest {
testComponentsBundle = new Bundle(testComponentsBundleDetails, this.getClass().getClassLoader());
final BundleDetails testPythonComponentsBundleDetails = new BundleDetails.Builder()
.coordinate(PythonBundle.PYTHON_BUNDLE_COORDINATE)
.workingDir(new File("src/test/resources/TestRuntimeManifest/nifi-test-python-components-nar"))
.build();
testPythonComponentsBundle = new Bundle(testPythonComponentsBundleDetails, this.getClass().getClassLoader());
extensionManager = mock(ExtensionManager.class);
extensionManifestParser = new JAXBExtensionManifestParser();
runtimeManifestService = new TestableRuntimeManifestService(extensionManager, extensionManifestParser, frameworkBundle);
@ -105,6 +120,49 @@ public class StandardRuntimeManifestServiceTest {
assertEquals(1, controllerServiceDefinitions.size());
}
@Test
public void testGetPythonManifest() {
final String processorClassName = "ClassName";
final List<String> expectedTags = List.of("tag1", "tag2");
when(extensionManager.getAllBundles()).thenReturn(emptySet());
when(extensionManager.getExtensions(Processor.class)).thenReturn(Set.of(
new ExtensionDefinition.Builder()
.implementationClassName(processorClassName)
.runtime(ExtensionRuntime.PYTHON)
.bundle(testPythonComponentsBundle)
.extensionType(Processor.class)
.tags(expectedTags)
.version(PythonBundle.VERSION)
.build()
));
final PythonProcessorDetails pythonProcessorDetails = mock(PythonProcessorDetails.class);
when(pythonProcessorDetails.getTags()).thenReturn(expectedTags);
when(extensionManager.getPythonProcessorDetails(processorClassName, PythonBundle.VERSION)).thenReturn(pythonProcessorDetails);
final List<org.apache.nifi.c2.protocol.component.api.Bundle> bundles = runtimeManifestService.getManifest().getBundles();
assertNotNull(bundles);
assertEquals(1, bundles.size());
final org.apache.nifi.c2.protocol.component.api.Bundle testPythonComponentsManifestBundle = bundles.getFirst();
assertEquals(PythonBundle.GROUP_ID, testPythonComponentsManifestBundle.getGroup());
assertEquals(PythonBundle.ARTIFACT_ID, testPythonComponentsManifestBundle.getArtifact());
assertEquals(PythonBundle.VERSION, testPythonComponentsManifestBundle.getVersion());
final ComponentManifest testComponentsManifest = testPythonComponentsManifestBundle.getComponentManifest();
assertNotNull(testComponentsManifest);
final List<ProcessorDefinition> processorDefinitions = testComponentsManifest.getProcessors();
assertEquals(1, processorDefinitions.size());
assertEquals(new HashSet<>(expectedTags), processorDefinitions.getFirst().getTags());
final List<ControllerServiceDefinition> controllerServiceDefinitions = testComponentsManifest.getControllerServices();
assertEquals(0, controllerServiceDefinitions.size());
}
/**
* Override getFrameworkBundle to provide a mocked Bundle.
*/

View File

@ -0,0 +1,51 @@
<!--
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.
-->
<extensionManifest>
<systemApiVersion>1.8.0</systemApiVersion>
<extensions>
<extension>
<name>TestProcessor1</name>
<type>PROCESSOR</type>
<description>Test processor 1.</description>
<tags>
<tag>test</tag>
<tag>processor</tag>
</tags>
<triggerSerially>true</triggerSerially>
<triggerWhenEmpty>true</triggerWhenEmpty>
<triggerWhenAnyDestinationAvailable>true</triggerWhenAnyDestinationAvailable>
<primaryNodeOnly>true</primaryNodeOnly>
<supportsBatching>true</supportsBatching>
<sideEffectFree>true</sideEffectFree>
<defaultSettings>
<yieldDuration>10 secs</yieldDuration>
<penaltyDuration>20 secs</penaltyDuration>
<bulletinLevel>DEBUG</bulletinLevel>
</defaultSettings>
<defaultSchedule>
<strategy>CRON_DRIVEN</strategy>
<period>* 1 * * *</period>
<concurrentTasks>5</concurrentTasks>
</defaultSchedule>
</extension>
<extension>
<name>TestProcessor2</name>
<type>PROCESSOR</type>
<description/>
<tags>
</tags>
</extension>
</extensions>
</extensionManifest>