Obtain upload lock before uploading 64/127264/4
authorandre.schmid <andre.schmid@est.tech>
Wed, 23 Feb 2022 21:09:56 +0000 (21:09 +0000)
committerandre.schmid <andre.schmid@est.tech>
Thu, 3 Mar 2022 10:41:23 +0000 (10:41 +0000)
Before uploading, the system will now set the status as uploading in
order to have more control of the upload process and status.
Without that the UI status updates could show up incorrectly.
Also, this behaviour removes the need to upload a file to set the
upload in progress, which avoids a concurrent upload try to upload
a file if there is another upload in progress.

Change-Id: Ic008560aa57e1ee7a50389ad26f1a8890f1cf198
Issue-ID: SDC-3888
Signed-off-by: andre.schmid <andre.schmid@est.tech>
openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/OrchestrationTemplateCandidateUploadManagerController.java
openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/controllers/OrchestrationTemplateCandidateUploadManagerControllerImpl.java
openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/exception/OrchestrationTemplateCandidateUploadManagerExceptionSupplier.java
openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImpl.java
openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImplTest.java
openecomp-ui/src/nfvo-utils/i18n/en.json
openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js
openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPage.js
openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageView.jsx

index 971a1c4..19c27e4 100644 (file)
@@ -31,6 +31,7 @@ import javax.validation.constraints.NotNull;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.GET;
 import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
@@ -62,4 +63,18 @@ public interface OrchestrationTemplateCandidateUploadManagerController extends V
                              @Parameter(description = "Vendor Software Product version id") @PathParam("versionId") String versionId,
                              @NotNull(message = USER_MISSING_ERROR_MSG) @HeaderParam(USER_ID_HEADER_PARAM) String user);
 
+    /**
+     * Creates the upload lock, setting the status to upload in progress.
+     *
+     * @param vspId     the vsp id
+     * @param versionId the vsp version id
+     * @param user      the username accessing the API
+     * @return if successful, an OK response with the created VspUploadStatus information
+     */
+    @POST
+    @Path("/")
+    Response createUploadLock(@Parameter(description = "Vendor Software Product id") @PathParam("vspId") String vspId,
+                              @Parameter(description = "Vendor Software Product version id") @PathParam("versionId") String versionId,
+                              @NotNull(message = USER_MISSING_ERROR_MSG) @HeaderParam(USER_ID_HEADER_PARAM) String user);
+
 }
index f96522a..aec877d 100644 (file)
@@ -55,6 +55,15 @@ public class OrchestrationTemplateCandidateUploadManagerControllerImpl implement
         return Response.ok(vspUploadStatus.get()).build();
     }
 
+    public Response createUploadLock(String vspId, String versionId, String user) {
+        vspId = ValidationUtils.sanitizeInputString(vspId);
+        versionId = ValidationUtils.sanitizeInputString(versionId);
+        user = ValidationUtils.sanitizeInputString(user);
+
+        final VspUploadStatusDto vspUploadStatus = orchestrationTemplateCandidateUploadManager.putUploadInProgress(vspId, versionId, user);
+        return Response.status(Status.CREATED).entity(vspUploadStatus).build();
+    }
+
     /**
      * Builds the string representing the get API url.
      *
index 643f59b..bea478c 100644 (file)
@@ -44,7 +44,7 @@ public class OrchestrationTemplateCandidateUploadManagerExceptionSupplier {
     }
 
     public static Supplier<CoreException> vspUploadAlreadyInProgress(final String vspId, final String vspVersionId) {
-        final String errorMsg = String.format("There is a processing in progress for the VSP '%s', version '%s'", vspId, vspVersionId);
+        final String errorMsg = String.format("Upload already in progress for the VSP '%s', version '%s'", vspId, vspVersionId);
         return () -> new CoreException(new ErrorCodeBuilder().withId(VSP_PROCESSING_IN_PROGRESS).withMessage(errorMsg).build());
     }
 
index 01d2a59..93483f3 100644 (file)
@@ -30,6 +30,7 @@ import static org.openecomp.sdc.common.errors.Messages.ERROR_HAS_OCCURRED_WHILE_
 import static org.openecomp.sdc.common.errors.Messages.NO_FILE_WAS_UPLOADED_OR_FILE_NOT_EXIST;
 import static org.openecomp.sdc.common.errors.Messages.PACKAGE_PROCESS_ERROR;
 import static org.openecomp.sdc.common.errors.Messages.UNEXPECTED_PROBLEM_HAPPENED_WHILE_GETTING;
+import static org.openecomp.sdcrests.vsp.rest.exception.OrchestrationTemplateCandidateUploadManagerExceptionSupplier.vspUploadAlreadyInProgress;
 
 import java.io.ByteArrayInputStream;
 import java.io.FileInputStream;
@@ -144,7 +145,11 @@ public class OrchestrationTemplateCandidateImpl implements OrchestrationTemplate
         final Response response;
         VspUploadStatusDto vspUploadStatus = null;
         try {
-            vspUploadStatus = orchestrationTemplateCandidateUploadManager.putUploadInProgress(vspId, versionId, user);
+            vspUploadStatus = getVspUploadStatus(vspId, versionId, user);
+
+            if (vspUploadStatus.getStatus() != VspUploadStatus.UPLOADING) {
+                throw vspUploadAlreadyInProgress(vspId, versionId).get();
+            }
             final byte[] fileToUploadBytes;
             final DataHandler dataHandler = fileToUpload.getDataHandler();
             final var filename = ValidationUtils.sanitizeInputString(dataHandler.getName());
@@ -199,6 +204,16 @@ public class OrchestrationTemplateCandidateImpl implements OrchestrationTemplate
         return response;
     }
 
+    private VspUploadStatusDto getVspUploadStatus(final String vspId, final String versionId, final String user) {
+        final Optional<VspUploadStatusDto> vspUploadStatusOpt =
+            orchestrationTemplateCandidateUploadManager.findLatestStatus(vspId, versionId, user);
+        if (vspUploadStatusOpt.isEmpty() || vspUploadStatusOpt.get().isComplete()) {
+            return orchestrationTemplateCandidateUploadManager.putUploadInProgress(vspId, versionId, user);
+        }
+
+        return vspUploadStatusOpt.get();
+    }
+
     private ArtifactInfo handleArtifactStorage(final String vspId, final String versionId, final String filename,
                                                final DataHandler artifactDataHandler) {
         final Path tempArtifactPath;
index 802d6d8..8b31261 100644 (file)
@@ -32,6 +32,7 @@ import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+import static org.openecomp.sdcrests.vsp.rest.exception.OrchestrationTemplateCandidateUploadManagerExceptionSupplier.vspUploadAlreadyInProgress;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -50,6 +51,7 @@ import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.cxf.jaxrs.ext.multipart.Attachment;
 import org.apache.cxf.jaxrs.ext.multipart.ContentDisposition;
+import org.jetbrains.annotations.NotNull;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.mockito.ArgumentMatchers;
@@ -66,6 +68,7 @@ import org.openecomp.sdc.be.csar.storage.MinIoStorageArtifactStorageConfig.Crede
 import org.openecomp.sdc.be.csar.storage.MinIoStorageArtifactStorageConfig.EndPoint;
 import org.openecomp.sdc.be.csar.storage.PackageSizeReducer;
 import org.openecomp.sdc.be.csar.storage.StorageFactory;
+import org.openecomp.sdc.common.errors.CoreException;
 import org.openecomp.sdc.logging.api.Logger;
 import org.openecomp.sdc.logging.api.LoggerFactory;
 import org.openecomp.sdc.vendorsoftwareproduct.OrchestrationTemplateCandidateManager;
@@ -159,9 +162,14 @@ class OrchestrationTemplateCandidateImplTest {
     void uploadSignedTest() throws IOException {
         final String vspId = "vspId";
         final String versionId = "versionId";
-        when(orchestrationTemplateCandidateUploadManager.putUploadInProgress(vspId, versionId, user)).thenReturn(new VspUploadStatusDto());
-        when(orchestrationTemplateCandidateUploadManager.putUploadInValidation(vspId, versionId, user)).thenReturn(new VspUploadStatusDto());
-        when(orchestrationTemplateCandidateUploadManager.putUploadInProcessing(vspId, versionId, user)).thenReturn(new VspUploadStatusDto());
+        when(orchestrationTemplateCandidateUploadManager.findLatestStatus(vspId, versionId, user)).thenReturn(Optional.empty());
+        final UUID lockId = UUID.randomUUID();
+        when(orchestrationTemplateCandidateUploadManager.putUploadInProgress(vspId, versionId, user))
+            .thenReturn(createVspUploadStatus(lockId, VspUploadStatus.UPLOADING));
+        when(orchestrationTemplateCandidateUploadManager.putUploadInValidation(vspId, versionId, user))
+            .thenReturn(createVspUploadStatus(lockId, VspUploadStatus.VALIDATING));
+        when(orchestrationTemplateCandidateUploadManager.putUploadInProcessing(vspId, versionId, user))
+            .thenReturn(createVspUploadStatus(lockId, VspUploadStatus.PROCESSING));
         Response response = orchestrationTemplateCandidate
             .upload(vspId, versionId, mockAttachment("filename.zip", this.getClass().getResource("/files/sample-signed.zip")), user);
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
@@ -172,15 +180,28 @@ class OrchestrationTemplateCandidateImplTest {
     void uploadNotSignedTest() throws IOException {
         final String vspId = "vspId";
         final String versionId = "versionId";
-        when(orchestrationTemplateCandidateUploadManager.putUploadInProgress(vspId, versionId, user)).thenReturn(new VspUploadStatusDto());
-        when(orchestrationTemplateCandidateUploadManager.putUploadInValidation(vspId, versionId, user)).thenReturn(new VspUploadStatusDto());
-        when(orchestrationTemplateCandidateUploadManager.putUploadInProcessing(vspId, versionId, user)).thenReturn(new VspUploadStatusDto());
+        when(orchestrationTemplateCandidateUploadManager.findLatestStatus(vspId, versionId, user)).thenReturn(Optional.empty());
+        final UUID lockId = UUID.randomUUID();
+        when(orchestrationTemplateCandidateUploadManager.putUploadInProgress(vspId, versionId, user))
+            .thenReturn(createVspUploadStatus(lockId, VspUploadStatus.UPLOADING));
+        when(orchestrationTemplateCandidateUploadManager.putUploadInValidation(vspId, versionId, user))
+            .thenReturn(createVspUploadStatus(lockId, VspUploadStatus.VALIDATING));
+        when(orchestrationTemplateCandidateUploadManager.putUploadInProcessing(vspId, versionId, user))
+            .thenReturn(createVspUploadStatus(lockId, VspUploadStatus.PROCESSING));
         Response response = orchestrationTemplateCandidate.upload(vspId, versionId,
             mockAttachment("filename.csar", this.getClass().getResource("/files/sample-not-signed.csar")), user);
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
         assertTrue(((UploadFileResponseDto) response.getEntity()).getErrors().isEmpty());
     }
 
+    @NotNull
+    private VspUploadStatusDto createVspUploadStatus(final UUID lockId, final VspUploadStatus uploadStatus) {
+        final VspUploadStatusDto vspUploadStatusProcessing = new VspUploadStatusDto();
+        vspUploadStatusProcessing.setLockId(lockId);
+        vspUploadStatusProcessing.setStatus(uploadStatus);
+        return vspUploadStatusProcessing;
+    }
+
     @Test
     void uploadNotSignedArtifactStorageManagerIsEnabledTest() throws IOException {
         when(storageFactory.createArtifactStorageManager()).thenReturn(artifactStorageManager);
@@ -195,7 +216,9 @@ class OrchestrationTemplateCandidateImplTest {
         final byte[] bytes = Files.readAllBytes(path);
         when(packageSizeReducer.reduce(any())).thenReturn(bytes);
 
-        when(orchestrationTemplateCandidateUploadManager.putUploadInProgress(vspId, versionId, user)).thenReturn(new VspUploadStatusDto());
+        final VspUploadStatusDto vspUploadStatusDto = new VspUploadStatusDto();
+        vspUploadStatusDto.setStatus(VspUploadStatus.UPLOADING);
+        when(orchestrationTemplateCandidateUploadManager.findLatestStatus(vspId, versionId, user)).thenReturn(Optional.of(vspUploadStatusDto));
         when(orchestrationTemplateCandidateUploadManager.putUploadInValidation(vspId, versionId, user)).thenReturn(new VspUploadStatusDto());
         when(orchestrationTemplateCandidateUploadManager.putUploadInProcessing(vspId, versionId, user)).thenReturn(new VspUploadStatusDto());
 
@@ -230,6 +253,10 @@ class OrchestrationTemplateCandidateImplTest {
     @Test
     void uploadSignNotValidTest() throws IOException {
         //given
+        final VspUploadStatusDto vspUploadStatusDto = new VspUploadStatusDto();
+        vspUploadStatusDto.setStatus(VspUploadStatus.UPLOADING);
+        when(orchestrationTemplateCandidateUploadManager.findLatestStatus(candidateId, versionId, user))
+            .thenReturn(Optional.of(vspUploadStatusDto));
         when(orchestrationTemplateCandidateUploadManager.putUploadInValidation(candidateId, versionId, user)).thenReturn(new VspUploadStatusDto());
         //when
         Response response = orchestrationTemplateCandidate
@@ -318,8 +345,8 @@ class OrchestrationTemplateCandidateImplTest {
     @Test
     void finishUploadMustBeCalledWhenExceptionHappensTest() {
         //given
-        final VspUploadStatusDto vspUploadStatusDto = new VspUploadStatusDto();
-        vspUploadStatusDto.setLockId(UUID.randomUUID());
+        final VspUploadStatusDto vspUploadStatusDto = createVspUploadStatus(UUID.randomUUID(), VspUploadStatus.UPLOADING);
+        when(orchestrationTemplateCandidateUploadManager.findLatestStatus(candidateId, versionId, user)).thenReturn(Optional.empty());
         when(orchestrationTemplateCandidateUploadManager.putUploadInProgress(candidateId, versionId, user)).thenReturn(vspUploadStatusDto);
         final RuntimeException forcedException = new RuntimeException();
         when(fileToUpload.getDataHandler()).thenThrow(forcedException);
@@ -331,4 +358,42 @@ class OrchestrationTemplateCandidateImplTest {
         verify(orchestrationTemplateCandidateUploadManager)
             .putUploadAsFinished(candidateId, versionId, vspUploadStatusDto.getLockId(), VspUploadStatus.ERROR, user);
     }
+
+//    @Test
+//    void uploadTestWithLatestStatusComplete() {
+//        final VspUploadStatusDto vspUploadStatusDto = new VspUploadStatusDto();
+//        vspUploadStatusDto.setComplete(true);
+//        //given
+//        when(orchestrationTemplateCandidateUploadManager.findLatestStatus(candidateId, versionId, user)).thenReturn(Optional.of(vspUploadStatusDto));
+//        final Attachment mock = Mockito.mock(Attachment.class);
+//        when(mock.getDataHandler()).thenReturn(Mockito.mock(DataHandler.class));
+//        //when
+//        final CoreException actualException = assertThrows(CoreException.class,
+//            () -> orchestrationTemplateCandidate.upload(candidateId, versionId, mock, user));
+//        final CoreException expectedException = couldNotAcceptPackageNoUploadInProgress(candidateId, versionId).get();
+//        //then
+//        assertEquals(expectedException.code().id(), actualException.code().id());
+//        assertEquals(expectedException.code().message(), actualException.code().message());
+//        verify(orchestrationTemplateCandidateUploadManager).findLatestStatus(candidateId, versionId, user);
+//    }
+
+    @Test
+    void uploadTestWithUploadInProgress() {
+        final VspUploadStatusDto vspUploadStatusDto = new VspUploadStatusDto();
+        vspUploadStatusDto.setComplete(false);
+        vspUploadStatusDto.setStatus(VspUploadStatus.PROCESSING);
+        //given
+        when(orchestrationTemplateCandidateUploadManager.findLatestStatus(candidateId, versionId, user)).thenReturn(Optional.of(vspUploadStatusDto));
+        final Attachment mock = Mockito.mock(Attachment.class);
+        when(mock.getDataHandler()).thenReturn(Mockito.mock(DataHandler.class));
+        //when
+        final CoreException actualException = assertThrows(CoreException.class,
+            () -> orchestrationTemplateCandidate.upload(candidateId, versionId, mock, user));
+        final CoreException expectedException = vspUploadAlreadyInProgress(candidateId, versionId).get();
+        //then
+        assertEquals(expectedException.code().id(), actualException.code().id());
+        assertEquals(expectedException.code().message(), actualException.code().message());
+        verify(orchestrationTemplateCandidateUploadManager).findLatestStatus(candidateId, versionId, user);
+    }
+
 }
index ada011f..3a61fb3 100644 (file)
   "Upload will erase existing data. Do you want to continue?": "Upload will erase existing data. Do you want to continue?",
   "Continue": "Continue",
   "Upload validation failed": "Upload validation failed",
+  "upload.failed": "Upload failed",
   "Download HEAT": "Download HEAT",
   "Go to Overview": "Go to Overview",
   "Upload New HEAT": "Upload New HEAT",
index 3364edf..90bf9f9 100644 (file)
@@ -366,6 +366,16 @@ const SoftwareProductActionHelper = {
         );
     },
 
+    createUploadStatus(vspId, versionId) {
+        const options = {
+            noLoading: true
+        };
+        return RestAPIUtil.post(
+            `${baseUrl()}${vspId}/versions/${versionId}/orchestration-template-candidate/upload`,
+            options
+        );
+    },
+
     loadSoftwareProductAssociatedData(dispatch) {
         fetchSoftwareProductCategories(dispatch);
         fetchModelList(dispatch);
index 4b4c2fa..07a59ed 100644 (file)
@@ -22,6 +22,7 @@ import { onboardingMethod } from '../SoftwareProductConstants.js';
 import ScreensHelper from 'sdc-app/common/helpers/ScreensHelper.js';
 import { enums, screenTypes } from 'sdc-app/onboarding/OnboardingConstants.js';
 import VNFImportActionHelper from '../vnfMarketPlace/VNFImportActionHelper.js';
+import VspUploadStatus from 'sdc-app/onboarding/softwareProduct/landingPage/VspUploadStatus';
 
 export const mapStateToProps = ({
     features,
@@ -120,16 +121,34 @@ const mapActionsToProps = (dispatch, { version }) => {
                 // do nothing by default
             }
         ) => {
-            SoftwareProductActionHelper.uploadFile(dispatch, {
+            SoftwareProductActionHelper.createUploadStatus(
                 softwareProductId,
-                formData,
-                failedNotificationTitle: i18n('Upload validation failed'),
-                version,
-                onUploadProgress
-            }).finally(() => {
-                onUploadFinished();
-            });
-            onUploadStart();
+                version.id
+            )
+                .then(response => {
+                    const vspUploadStatus = new VspUploadStatus(response);
+                    onUploadStart(vspUploadStatus);
+                    SoftwareProductActionHelper.uploadFile(dispatch, {
+                        softwareProductId,
+                        formData,
+                        failedNotificationTitle: i18n(
+                            'Upload validation failed'
+                        ),
+                        version,
+                        onUploadProgress
+                    }).finally(() => {
+                        onUploadFinished();
+                    });
+                })
+                .catch(error => {
+                    dispatch({
+                        type: modalActionTypes.GLOBAL_MODAL_ERROR,
+                        data: {
+                            title: i18n('upload.failed'),
+                            msg: error.message
+                        }
+                    });
+                });
         },
 
         onUploadConfirmation: (
@@ -152,19 +171,39 @@ const mapActionsToProps = (dispatch, { version }) => {
                     confirmationButtonText: i18n('Continue'),
                     title: i18n('Warning'),
                     onConfirmed: () => {
-                        SoftwareProductActionHelper.uploadFile(dispatch, {
+                        SoftwareProductActionHelper.createUploadStatus(
                             softwareProductId,
-                            formData,
-                            failedNotificationTitle: i18n(
-                                'Upload validation failed'
-                            ),
-                            version,
-                            onUploadProgress
-                        }).finally(value => {
-                            console.log('upload finished', value);
-                            onUploadFinished();
-                        });
-                        onUploadStart();
+                            version.id
+                        )
+                            .then(response => {
+                                const vspUploadStatus = new VspUploadStatus(
+                                    response
+                                );
+                                onUploadStart(vspUploadStatus);
+                                SoftwareProductActionHelper.uploadFile(
+                                    dispatch,
+                                    {
+                                        softwareProductId,
+                                        formData,
+                                        failedNotificationTitle: i18n(
+                                            'Upload validation failed'
+                                        ),
+                                        version,
+                                        onUploadProgress
+                                    }
+                                ).finally(() => {
+                                    onUploadFinished();
+                                });
+                            })
+                            .catch(error => {
+                                dispatch({
+                                    type: modalActionTypes.GLOBAL_MODAL_ERROR,
+                                    data: {
+                                        title: i18n('upload.failed'),
+                                        msg: error.message
+                                    }
+                                });
+                            });
                     },
                     onDeclined: () =>
                         dispatch({
index c560a73..6403286 100644 (file)
@@ -261,7 +261,10 @@ class SoftwareProductLandingPageView extends React.Component {
         }
     }
 
-    onUploadStart = () => {
+    onUploadStart = vspUploadStatus => {
+        this.setState({
+            uploadStatus: vspUploadStatus
+        });
         this.stopUploadStatusChecking();
         this.showProgressBar();
     };
@@ -324,7 +327,7 @@ class SoftwareProductLandingPageView extends React.Component {
             onUploadConfirmation(
                 currentSoftwareProduct.id,
                 formData,
-                () => this.onUploadStart(),
+                vspUploadStatus => this.onUploadStart(vspUploadStatus),
                 this.onUploadProgress,
                 this.onUploadFinished
             );
@@ -332,7 +335,7 @@ class SoftwareProductLandingPageView extends React.Component {
             onUpload(
                 currentSoftwareProduct.id,
                 formData,
-                () => this.onUploadStart(),
+                vspUploadStatus => this.onUploadStart(vspUploadStatus),
                 this.onUploadProgress,
                 this.onUploadFinished
             );