exceptionfactory commented on a change in pull request #319: URL: https://github.com/apache/nifi-registry/pull/319#discussion_r626113532
########## File path: nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketFlowResource.java ########## @@ -291,6 +294,42 @@ public Response createFlowVersion( return Response.status(Response.Status.OK).entity(createdSnapshot).build(); } + @POST + @Path("{flowId}/versions/import") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @ApiOperation( + value = "Upload flow version", Review comment: Should this be changed to Import flow version? ```suggestion value = "Import flow version", ``` ########## File path: nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketFlowResource.java ########## @@ -291,6 +294,42 @@ public Response createFlowVersion( return Response.status(Response.Status.OK).entity(createdSnapshot).build(); } + @POST + @Path("{flowId}/versions/import") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @ApiOperation( + value = "Upload flow version", + notes = "Uploads the next version of a flow. The version number of the object being created must be the " + + "next available version integer. Flow versions are immutable after they are created.", Review comment: The notes make it sound like the version number must be specified, as opposed to being generated. ```suggestion notes = "Import the next version of a flow. The version number of the object being created will be the " + "next available version integer. Flow versions are immutable after they are created.", ``` ########## File path: nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/service/StandardServiceFacade.java ########## @@ -360,6 +362,48 @@ public VersionedFlowSnapshot getLatestFlowSnapshot(final String flowIdentifier) return lastSnapshot; } + @Override + public VersionedFlowSnapshot importVersionedFlowSnapshot(VersionedFlowSnapshot versionedFlowSnapshot, String bucketIdentifier, + String flowIdentifier, String comments) { + // set new snapshotMetadata + final VersionedFlowSnapshotMetadata metadata = new VersionedFlowSnapshotMetadata(); + metadata.setBucketIdentifier(bucketIdentifier); + metadata.setFlowIdentifier(flowIdentifier); + metadata.setVersion(LATEST_VERSION); + + // if there are new comments, then set it + // otherwise, keep the original comments + if (!StringUtils.isBlank(comments)) { + metadata.setComments(comments); + } else if (versionedFlowSnapshot.getSnapshotMetadata() != null && versionedFlowSnapshot.getSnapshotMetadata().getComments() != null) { Review comment: Should this line check for blank comments? ```suggestion } else if (versionedFlowSnapshot.getSnapshotMetadata() != null && StringUtils.isNotBlank(versionedFlowSnapshot.getSnapshotMetadata().getComments())) { ``` ########## File path: nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/explorer/grid-list/dialogs/import-new-flow/nf-registry-import-new-flow.js ########## @@ -0,0 +1,192 @@ +/* + * 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. + */ + +import { Component } from '@angular/core'; + +import NfRegistryApi from 'services/nf-registry.api'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material'; +import { FdsSnackBarService } from '@nifi-fds/core'; + +/** + * NfRegistryImportNewFlow constructor. + * + * @param nfRegistryApi The api service. + * @param fdsSnackBarService The FDS snack bar service module. + * @param matDialogRef The angular material dialog ref. + * @param data The data passed into this component. + * @constructor + */ +function NfRegistryImportNewFlow(nfRegistryApi, fdsSnackBarService, matDialogRef, data) { + // Services + this.snackBarService = fdsSnackBarService; + this.nfRegistryApi = nfRegistryApi; + this.dialogRef = matDialogRef; + // local state + this.keepDialogOpen = false; + this.protocol = location.protocol; + this.buckets = data.buckets; + this.activeBucket = data.activeBucket.identifier; + this.writableBuckets = []; + this.fileToUpload = null; + this.fileName = null; + this.name = ''; + this.description = ''; + this.selectedBucket = {}; + this.hoverValidity = ''; + this.extensions = 'application/json'; + this.multiple = false; +} + +NfRegistryImportNewFlow.prototype = { + constructor: NfRegistryImportNewFlow, + + ngOnInit: function () { + this.writableBuckets = this.filterWritableBuckets(this.buckets); + + // if there's only 1 writable bucket, always set as the initial value in the bucket dropdown + // if opening the dialog from the explorer/grid-list, there is no active bucket + if (typeof this.activeBucket === 'undefined') { Review comment: Can this be simplified to check against `undefined` without `typeof`? ```suggestion if (this.activeBucket === undefined) { ``` ########## File path: nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketFlowResource.java ########## @@ -291,6 +294,42 @@ public Response createFlowVersion( return Response.status(Response.Status.OK).entity(createdSnapshot).build(); } + @POST + @Path("{flowId}/versions/import") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @ApiOperation( + value = "Upload flow version", + notes = "Uploads the next version of a flow. The version number of the object being created must be the " + + "next available version integer. Flow versions are immutable after they are created.", + response = VersionedFlowSnapshot.class, + extensions = { + @Extension(name = "access-policy", properties = { + @ExtensionProperty(name = "action", value = "write"), + @ExtensionProperty(name = "resource", value = "/buckets/{bucketId}") }) + } + ) + @ApiResponses({ + @ApiResponse(code = 400, message = HttpStatusMessages.MESSAGE_400), + @ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401), + @ApiResponse(code = 403, message = HttpStatusMessages.MESSAGE_403), + @ApiResponse(code = 404, message = HttpStatusMessages.MESSAGE_404), + @ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409) }) + public Response importVersionedFlow( + @PathParam("bucketId") + @ApiParam("The bucket identifier") + final String bucketId, + @PathParam("flowId") + @ApiParam(value = "The flow identifier") + final String flowId, + @ApiParam("file") final VersionedFlowSnapshot versionedFlowSnapshot, + @HeaderParam("comments") final String comments) { Review comment: Recommend capitalizing this header to follow HTTP conventions: ```suggestion @HeaderParam("Comments") final String comments) { ``` ########## File path: nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/FlowsIT.java ########## @@ -541,4 +544,171 @@ public void testFlowNameUniquePerBucket() throws Exception { } + @Test + public void testImportVersionedFlowSnapshot() { + final RevisionInfo initialRevision = new RevisionInfo("FlowsIT", 0L); + + // Create a versionedFlowSnapshot to export + // Given: an empty Bucket "3" (see FlowsIT.sql) with a newly created flow + + final String bucketId = "3"; + final VersionedFlow flow = new VersionedFlow(); + flow.setBucketIdentifier(bucketId); + flow.setName("Test Flow for creating snapshots"); + flow.setDescription("This is a randomly named flow created by an integration test for the purpose of holding snapshots."); + flow.setRevision(initialRevision); + + final VersionedFlow createdFlow = client + .target(createURL("buckets/{bucketId}/flows")) + .resolveTemplate("bucketId", bucketId) + .request() + .post(Entity.entity(flow, MediaType.APPLICATION_JSON), VersionedFlow.class); + final String flowId = createdFlow.getIdentifier(); + + // Create snapshotMetadata + final VersionedFlowSnapshotMetadata flowSnapshotMetadata = new VersionedFlowSnapshotMetadata(); + flowSnapshotMetadata.setBucketIdentifier(bucketId); + flowSnapshotMetadata.setFlowIdentifier(flowId); + flowSnapshotMetadata.setComments("This is a snapshot created by an integration test."); + + // Create a VersionedFlowSnapshot + final VersionedFlowSnapshot flowSnapshot = new VersionedFlowSnapshot(); + flowSnapshot.setSnapshotMetadata(flowSnapshotMetadata); + flowSnapshot.setFlowContents(new VersionedProcessGroup()); // an empty root process group + flowSnapshot.getFlowContents().setName("Test Flow name"); + flowSnapshot.getSnapshotMetadata().setVersion(-1); Review comment: Recommend setting a private static final variable named LATEST_VERSION and reusing here and in the other test method. ########## File path: nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/service/StandardServiceFacade.java ########## @@ -360,6 +362,48 @@ public VersionedFlowSnapshot getLatestFlowSnapshot(final String flowIdentifier) return lastSnapshot; } + @Override + public VersionedFlowSnapshot importVersionedFlowSnapshot(VersionedFlowSnapshot versionedFlowSnapshot, String bucketIdentifier, + String flowIdentifier, String comments) { + // set new snapshotMetadata + final VersionedFlowSnapshotMetadata metadata = new VersionedFlowSnapshotMetadata(); + metadata.setBucketIdentifier(bucketIdentifier); + metadata.setFlowIdentifier(flowIdentifier); + metadata.setVersion(LATEST_VERSION); + + // if there are new comments, then set it + // otherwise, keep the original comments + if (!StringUtils.isBlank(comments)) { Review comment: This could be changed to `StringUtils.isNotBlank()` ```suggestion if (StringUtils.isNotBlank(comments)) { ``` ########## File path: nifi-registry-core/nifi-registry-web-ui/src/main/webapp/services/nf-registry.api.js ########## @@ -75,6 +75,137 @@ NfRegistryApi.prototype = { ); }, + /** + * Retrieves the specified versioned flow snapshot for an existing droplet the registry has stored. + * + * @param {string} dropletUri The uri of the droplet to request. + * @param {number} versionNumber The version of the flow to request. + * @returns {*} + */ + exportDropletVersionedSnapshot: function (dropletUri, versionNumber) { + var self = this; + var url = '../nifi-registry-api/' + dropletUri + '/versions/' + versionNumber + '/export'; + var options = { + headers: headers, + observe: 'response' + }; + + return this.http.get(url, options).pipe( + map(function (response) { + // export the VersionedFlowSnapshot by creating a hidden anchor element + var stringSnapshot = encodeURIComponent(JSON.stringify(response.body, null, 2)); Review comment: Is it necessary to call `JSON.stringify()` or is it possible to just get the text content of response.body? If `response.body` is already JSON, that means it went through an unnecessary serialization step, which would be great to avoid if possible. -- 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. For queries about this service, please contact Infrastructure at: us...@infra.apache.org