markap14 commented on code in PR #9535:
URL: https://github.com/apache/nifi/pull/9535#discussion_r1854522197


##########
nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java:
##########
@@ -2836,6 +2950,331 @@ public Response importProcessGroup(
 
     }
 
+    /**
+     * Pastes the specified payload into the given Process Group.
+     *
+     * @param pasteRequestEntity A PasteResponseEntity.
+     * @return A pasteResponseEntity.
+     */
+    @PUT
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    @Path("{id}/paste")
+    @Operation(
+            summary = "Pastes into the specified process group",
+            responses = @ApiResponse(content = @Content(schema = 
@Schema(implementation = PasteResponseEntity.class))),
+            security = {
+                    @SecurityRequirement(name = "Write - 
/process-groups/{uuid}")
+            }
+    )
+    @ApiResponses(
+            value = {
+                    @ApiResponse(responseCode = "400", description = "NiFi was 
unable to complete the request because it was invalid. The request should not 
be retried without modification."),
+                    @ApiResponse(responseCode = "401", description = "Client 
could not be authenticated."),
+                    @ApiResponse(responseCode = "403", description = "Client 
is not authorized to make this request."),
+                    @ApiResponse(responseCode = "409", description = "The 
request was valid but NiFi was not in the appropriate state to process it.")
+            }
+    )
+    public Response paste(
+            @Parameter(
+                    description = "The process group id.",
+                    required = true
+            )
+            @PathParam("id") final String groupId,
+            @Parameter(
+                    description = "The request including the components to be 
pasted into the specified Process Group.",
+                    required = true
+            ) final PasteRequestEntity pasteRequestEntity) {
+
+        // verify the payload was specified
+        if (pasteRequestEntity == null) {
+            throw new IllegalArgumentException("The paste payload must be 
specified.");
+        }
+
+        // verify the revision is specified
+        if (pasteRequestEntity.getRevision() == null) {
+            throw new IllegalArgumentException("Revision must be specified.");
+        }
+
+        // verify the copy response is specified
+        if (pasteRequestEntity.getCopyResponse() == null) {
+            throw new IllegalArgumentException("The details of the copied 
components must be specified.");
+        }
+
+        if (isReplicateRequest()) {
+            return replicate(HttpMethod.PUT, pasteRequestEntity);
+        } else if (isDisconnectedFromCluster()) {
+            
verifyDisconnectedNodeModification(pasteRequestEntity.getDisconnectedNodeAcknowledged());
+        }
+
+        final CopyResponseEntity copyResponseEntity = 
pasteRequestEntity.getCopyResponse();
+        final VersionedProcessGroup versionedProcessGroup = 
getVersionedProcessGroup(copyResponseEntity);
+        mapVersionedIds(versionedProcessGroup, new HashMap<>(), new 
HashMap<>());
+
+        // resolve Bundle info
+        serviceFacade.discoverCompatibleBundles(versionedProcessGroup);
+
+        // prep a pasted flow snapshot to attempt to resolve external services 
and referenced parameter providers
+        final RegisteredFlowSnapshot pastedFlowSnapshot = new 
RegisteredFlowSnapshot();
+        
pastedFlowSnapshot.setExternalControllerServices(copyResponseEntity.getExternalControllerServiceReferences());
+        pastedFlowSnapshot.setFlowContents(versionedProcessGroup);
+        
pastedFlowSnapshot.setParameterContexts(copyResponseEntity.getParameterContexts());
+        
pastedFlowSnapshot.setParameterProviders(copyResponseEntity.getParameterProviders());
+
+        // if there are any Controller Services referenced that are inherited 
from the parent group,
+        // resolve those to point to the appropriate Controller Service, if we 
are able to.
+        final FlowSnapshotContainer flowSnapshotContainer = new 
FlowSnapshotContainer(pastedFlowSnapshot);
+        final Set<String> unresolvedControllerServices = 
serviceFacade.resolveInheritedControllerServices(flowSnapshotContainer, 
groupId, NiFiUserUtils.getNiFiUser());
+
+        // If there are any Parameter Providers referenced by Parameter 
Contexts, resolve these to point to the appropriate Parameter Provider, if we 
are able to.
+        final Set<String> unresolvedParameterProviders = 
serviceFacade.resolveParameterProviders(pastedFlowSnapshot, 
NiFiUserUtils.getNiFiUser());
+
+        final Revision requestRevision = 
getRevision(pasteRequestEntity.getRevision(), groupId);
+        return withWriteLock(
+                serviceFacade,
+                pasteRequestEntity,
+                requestRevision,
+                lookup -> {
+                    final NiFiUser user = NiFiUserUtils.getNiFiUser();
+
+                    // ensure the user can write to the current group
+                    final Authorizable processGroup = 
lookup.getProcessGroup(groupId).getAuthorizable();
+                    processGroup.authorize(authorizer, RequestAction.WRITE, 
user);
+
+                    // if the pasted content contains restricted components, 
ensure the user is allowed those restrictions
+                    final Set<ConfigurableComponent> restrictedComponents = 
FlowRegistryUtils.getRestrictedComponents(versionedProcessGroup, serviceFacade);
+                    restrictedComponents.forEach(restrictedComponent -> {
+                        final ComponentAuthorizable 
restrictedComponentAuthorizable = 
lookup.getConfigurableComponent(restrictedComponent);
+                        authorizeRestrictions(authorizer, 
restrictedComponentAuthorizable);
+                    });
+
+                    // authorize controller services
+                    
AuthorizeControllerServiceReference.authorizeUnresolvedControllerServiceReferences(groupId,
 unresolvedControllerServices, authorizer, lookup, user);
+
+                    // if the pasted content contains parameter contexts, 
ensure the user can create them or add to existing matching contexts
+                    final Map<String, VersionedParameterContext> 
parameterContexts = copyResponseEntity.getParameterContexts();
+                    if (parameterContexts != null) {
+                        parameterContexts.values().forEach(context -> 
AuthorizeParameterReference.authorizeParameterContextAddition(context, 
serviceFacade, authorizer, lookup, user));
+                    }
+
+                    // authorize parameter providers
+                    
AuthorizeParameterProviders.authorizeUnresolvedParameterProviders(unresolvedParameterProviders,
 authorizer, lookup, user);
+
+                    // if the pasted content contains instance ids, ensure the 
user can read those instances since sensitive values will be copied over
+                    authorizeInstanceIds(versionedProcessGroup, lookup);
+                },
+                () -> 
serviceFacade.verifyComponentTypes(versionedProcessGroup),
+                (revision, requestPasteRequestEntity) -> {
+                    final CopyResponseEntity requestCopyResponseEntity = 
requestPasteRequestEntity.getCopyResponse();
+
+                    // prepare the request to add versioned components
+                    final VersionedComponentAdditions additions = new 
VersionedComponentAdditions.Builder()
+                            
.setProcessors(requestCopyResponseEntity.getProcessors())
+                            
.setInputPorts(requestCopyResponseEntity.getInputPorts())
+                            
.setOutputPorts(requestCopyResponseEntity.getOutputPorts())
+                            .setFunnels(requestCopyResponseEntity.getFunnels())
+                            .setLabels(requestCopyResponseEntity.getLabels())
+                            
.setProcessGroups(requestCopyResponseEntity.getProcessGroups())
+                            
.setRemoteProcessGroups(requestCopyResponseEntity.getRemoteProcessGroups())
+                            
.setConnections(requestCopyResponseEntity.getConnections())
+                            
.setParameterContexts(requestCopyResponseEntity.getParameterContexts())
+                            
.setParameterProviders(requestCopyResponseEntity.getParameterProviders())
+                            .build();
+
+                    final PasteResponseEntity pasteResponseEntity = 
serviceFacade.pasteComponents(revision, groupId, additions, 
getIdGenerationSeed().orElse(null));
+
+                    // prune response as necessary
+                    for (ProcessGroupEntity childGroupEntity : 
pasteResponseEntity.getFlow().getProcessGroups()) {
+                        childGroupEntity.getComponent().setContents(null);
+                    }
+
+                    // create the response entity
+                    
populateRemainingSnippetContent(pasteResponseEntity.getFlow());
+
+                    return generateOkResponse(pasteResponseEntity).build();
+                }
+        );
+    }
+
+    private static VersionedProcessGroup getVersionedProcessGroup(final 
CopyResponseEntity copyResponse) {
+        final VersionedProcessGroup versionedProcessGroup = new 
VersionedProcessGroup();
+        versionedProcessGroup.setProcessors(new 
HashSet<>(copyResponse.getProcessors()));
+        versionedProcessGroup.setInputPorts(new 
HashSet<>(copyResponse.getInputPorts()));
+        versionedProcessGroup.setOutputPorts(new 
HashSet<>(copyResponse.getOutputPorts()));
+        versionedProcessGroup.setProcessGroups(new 
HashSet<>(copyResponse.getProcessGroups()));
+        versionedProcessGroup.setRemoteProcessGroups(new 
HashSet<>(copyResponse.getRemoteProcessGroups()));
+        versionedProcessGroup.setFunnels(new 
HashSet<>(copyResponse.getFunnels()));
+        versionedProcessGroup.setLabels(new 
HashSet<>(copyResponse.getLabels()));
+        versionedProcessGroup.setConnections(new 
HashSet<>(copyResponse.getConnections()));
+        return versionedProcessGroup;
+    }
+
+    private void mapVersionedIds(final VersionedProcessGroup group, final 
Map<String, String> idMapping, final Map<String, String> serviceIdMapping) {
+        group.getControllerServices().forEach(cs -> {
+            final String newId = generateUuid(cs.getIdentifier());
+            idMapping.put(cs.getIdentifier(), newId);
+            serviceIdMapping.put(cs.getIdentifier(), newId);
+            cs.setIdentifier(newId);
+        });
+        group.getControllerServices().forEach(cs -> {
+            cs.getProperties().entrySet().stream()
+                    .filter(propertyEntry -> {
+                        final Map<String, VersionedPropertyDescriptor> 
propertyDescriptors = cs.getPropertyDescriptors();
+                        if (propertyDescriptors != null) {
+                            final VersionedPropertyDescriptor 
propertyDescriptor = propertyDescriptors.get(propertyEntry.getKey());
+                            if (propertyDescriptor != null && 
propertyDescriptor.getIdentifiesControllerService()) {
+                                return 
serviceIdMapping.containsKey(propertyEntry.getValue());
+                            }
+                        }
+
+                        return false;
+                    })
+                    .findFirst()
+                    .ifPresent(serviceEntry -> 
serviceEntry.setValue(serviceIdMapping.get(serviceEntry.getValue())));

Review Comment:
   I think we want to use `.forEach` here instead of `.findFirst().ifPresent()` 
because we want to do this for all properties, right?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to