WF-validation to empty artifact 78/75078/2
authorYarin Dekel <yarind@amdocs.com>
Wed, 26 Dec 2018 11:14:01 +0000 (13:14 +0200)
committerYarin Dekel <yarind@amdocs.com>
Wed, 26 Dec 2018 11:14:05 +0000 (13:14 +0200)
Issue-ID: SDC-2023
Change-Id: Idcc690cfad19da0d29232c93b202042f6230ffaf
Signed-off-by: Yarin Dekel <yarind@amdocs.com>
workflow-designer-be/src/main/java/org/onap/sdc/workflow/services/impl/WorkflowVersionManagerImpl.java
workflow-designer-be/src/test/java/org/onap/sdc/workflow/services/impl/WorkflowVersionManagerImplTest.java
workflow-designer-ui/src/main/frontend/src/features/version/composition/CompositionView.js
workflow-designer-ui/src/main/frontend/src/features/version/composition/compositionActions.js
workflow-designer-ui/src/main/frontend/src/features/version/composition/compositionConstants.js
workflow-designer-ui/src/main/frontend/src/features/version/composition/compositionReducer.js
workflow-designer-ui/src/main/frontend/src/features/version/composition/newDiagram.bpmn
workflow-designer-ui/src/main/frontend/src/features/version/versionApi.js
workflow-designer-ui/src/main/frontend/src/features/version/versionSaga.js
workflow-designer-ui/src/main/frontend/src/i18n/languages.json
workflow-designer-ui/src/main/frontend/src/services/restAPIUtil.js

index 0078d22..3321077 100644 (file)
@@ -232,9 +232,10 @@ public class WorkflowVersionManagerImpl implements WorkflowVersionManager {
                     workflowId, versionId);
             throw new VersionModificationException(workflowId, versionId);
         }
-
-        artifactRepository.delete(workflowId, versionId);
-        versioningManager.publish(workflowId, new Version(versionId), "Delete Artifact");
+        if(retrievedVersion.isHasArtifact()) {
+            artifactRepository.delete(workflowId, versionId);
+            versioningManager.publish(workflowId, new Version(versionId), "Delete Artifact");
+        }
     }
 
     private void validateVersionExistAndCertified(String workflowId, List<Version> versions, String versionId) {
index 640d3e8..8e714f6 100644 (file)
@@ -399,15 +399,28 @@ public class WorkflowVersionManagerImplTest {
     }
 
     @Test
-    public void shouldDeleteArtifact() {
+    public void shouldNotDeleteArtifactIfNotExist() {
         doNothing().when(workflowVersionManager).validateWorkflowStatus(ITEM1_ID);
         Version version = new Version(VERSION1_ID);
         doReturn(version).when(versioningManagerMock).get(ITEM1_ID, version);
         WorkflowVersion workflowVersion = new WorkflowVersion(VERSION1_ID);
         doReturn(workflowVersion).when(versionMapperMock).versionToWorkflowVersion(version);
         workflowVersionManager.deleteArtifact(ITEM1_ID, VERSION1_ID);
-        verify(artifactRepositoryMock).delete(ITEM1_ID, VERSION1_ID);
-        verify(versioningManagerMock).publish(ITEM1_ID, version, "Delete Artifact");
+        verify(artifactRepositoryMock, times(0)).delete(ITEM1_ID, VERSION1_ID);
+        verify(versioningManagerMock, times(0)).publish(ITEM1_ID, version, "Delete Artifact");
+    }
+
+    @Test
+    public void shouldDeleteArtifactIfExist() {
+        doNothing().when(workflowVersionManager).validateWorkflowStatus(ITEM1_ID);
+        Version version = new Version(VERSION1_ID);
+        doReturn(version).when(versioningManagerMock).get(ITEM1_ID, version);
+        WorkflowVersion workflowVersion = new WorkflowVersion(VERSION1_ID);
+        doReturn(true).when(artifactRepositoryMock).isExist(ITEM1_ID, VERSION1_ID);
+        doReturn(workflowVersion).when(versionMapperMock).versionToWorkflowVersion(version);
+        workflowVersionManager.deleteArtifact(ITEM1_ID, VERSION1_ID);
+        verify(artifactRepositoryMock, times(1)).delete(ITEM1_ID, VERSION1_ID);
+        verify(versioningManagerMock, times(1)).publish(ITEM1_ID, version, "Delete Artifact");
     }
 
     private static Version eqVersion(String versionId) {
index 529627d..e444d98 100644 (file)
 import React, { Component } from 'react';
 import fileSaver from 'file-saver';
 import isEqual from 'lodash.isequal';
-import CustomModeler from './custom-modeler';
+import PropTypes from 'prop-types';
 import propertiesPanelModule from 'bpmn-js-properties-panel';
+import { I18n } from 'react-redux-i18n';
+
+import CustomModeler from './custom-modeler';
 import propertiesProviderModule from './custom-properties-provider/provider/camunda';
 import camundaModuleDescriptor from './custom-properties-provider/descriptors/camunda';
 import newDiagramXML from './newDiagram.bpmn';
-import PropTypes from 'prop-types';
 import CompositionButtons from './components/CompositionButtonsPanel';
 import { setElementInputsOutputs } from './bpmnUtils.js';
-import { I18n } from 'react-redux-i18n';
 import {
     PROCESS_DEFAULT_ID,
     COMPOSITION_ERROR_COLOR,
@@ -79,10 +80,6 @@ class CompositionView extends Component {
         this.generatedId = 'bpmn-container' + Date.now();
         this.fileInput = React.createRef();
         this.bpmnContainer = React.createRef();
-        this.selectedElement = false;
-        this.state = {
-            diagram: false
-        };
         this.versionChanged = false;
     }
     componentDidUpdate(prevProps) {
@@ -146,7 +143,7 @@ class CompositionView extends Component {
         });
 
         this.modeler.attachTo('#' + this.generatedId);
-        this.setDiagramToBPMN(composition ? composition : newDiagramXML);
+        this.setDiagramToBPMN(composition);
         this.modeler.on('element.out', () => this.exportDiagramToStore());
         this.modeler.on('element.click', this.handleCompositionStatus);
         this.modeler.on(
index b11c956..fe74ba0 100644 (file)
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
-import { SET_COMPOSITION, UPDATE_ERRORS } from './compositionConstants';
+import {
+    SET_COMPOSITION,
+    UPDATE_ERRORS,
+    DELETE_COMPOSITION
+} from './compositionConstants';
 
 export const updateComposition = payload => ({
     type: SET_COMPOSITION,
     payload
 });
 
+export const deleteCompositionArtifact = () => ({
+    type: DELETE_COMPOSITION
+});
+
 export const updateValidation = payload => ({
     type: UPDATE_ERRORS,
     payload
index 69c7fb8..1db4021 100644 (file)
@@ -14,6 +14,7 @@
 * limitations under the License.
 */
 export const SET_COMPOSITION = 'composition/SET_COMPOSITION';
+export const DELETE_COMPOSITION = 'composition/DELETE_COMPOSITION';
 export const UPDATE_ERRORS = 'composition/UPDATE_ERRORS';
 
 export const bpmnElementsTypes = {
index 53d9f44..9deb9cb 100644 (file)
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
-import { SET_COMPOSITION } from './compositionConstants';
+import { SET_COMPOSITION, DELETE_COMPOSITION } from './compositionConstants';
 import { UPDATE_ERRORS } from './compositionConstants';
+import newDiagramXML from './newDiagram.bpmn';
 
-export default (state = { diagram: false, errors: [] }, action) => {
+export default (state = { diagram: newDiagramXML, errors: [] }, action) => {
     switch (action.type) {
         case SET_COMPOSITION:
             return {
                 ...state,
                 diagram: action.payload
             };
+        case DELETE_COMPOSITION:
+            return {
+                ...state,
+                diagram: newDiagramXML
+            };
         case UPDATE_ERRORS: {
             const filteredErrors = state.errors.filter(
                 item => item.element.id !== action.payload.element.id
index f54671c..6abaf8d 100644 (file)
@@ -1,4 +1,4 @@
-export default `<?xml version="1.0" encoding="UTF-8"?>
+<?xml version="1.0" encoding="UTF-8"?>
 <bpmn2:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd" id="sample-diagram" targetNamespace="http://bpmn.io/schema/bpmn">
   <bpmn2:process id="Process_1" isExecutable="true">
   </bpmn2:process>
@@ -6,4 +6,4 @@ export default `<?xml version="1.0" encoding="UTF-8"?>
     <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
     </bpmndi:BPMNPlane>
   </bpmndi:BPMNDiagram>
-</bpmn2:definitions>`;
\ No newline at end of file
+</bpmn2:definitions>
index ab9394c..b3a2e13 100644 (file)
@@ -66,6 +66,11 @@ const Api = {
             formData
         );
     },
+    deleteVersionArtifact: ({ workflowId, versionId }) => {
+        return RestfulAPIUtil.delete(
+            `${baseUrl(workflowId)}/${versionId}/artifact`
+        );
+    },
     certifyVersion: ({ workflowId, versionId }) => {
         return RestfulAPIUtil.post(
             `${baseUrl(workflowId)}/${versionId}/state`,
index a7dabed..a476b41 100644 (file)
@@ -33,27 +33,46 @@ import { notificationActions } from 'shared/notifications/notificationsActions';
 import { versionState } from 'features/version/versionConstants';
 import overviewApi from '../workflow/overview/overviewApi';
 import { versionListFetchAction } from '../workflow/overview/overviewConstansts';
-import { updateComposition } from 'features/version/composition/compositionActions';
+import {
+    updateComposition,
+    deleteCompositionArtifact
+} from 'features/version/composition/compositionActions';
 import { getActivitiesList } from 'features/activities/activitiesActions';
 
+/**
+ * Composition validation - converting artifact string to xml
+ * and checking if bpmn diagram has only one child
+ * @param composition
+ * @returns {boolean}
+ */
+function validateCurrentArtifact(composition) {
+    const parser = new DOMParser();
+    const xml = parser.parseFromString(composition, 'text/xml');
+    return Boolean(
+        xml.getElementsByTagName('bpmndi:BPMNPlane').BPMNPlane_1.children.length
+    );
+}
+
 function* fetchVersion(action) {
     try {
         yield put(getActivitiesList());
         const data = yield call(versionApi.fetchVersion, action.payload);
         const { inputs, outputs, ...rest } = data;
-        let composition = false;
+        let composition;
 
         if (rest.hasArtifact) {
             composition = yield call(
                 versionApi.fetchVersionArtifact,
                 action.payload
             );
+        } else {
+            //Clearing the store from old artifact using init the default
+            yield put(deleteCompositionArtifact());
         }
-
         yield all([
             put(setWorkflowVersionAction(rest)),
             put(setInputsOutputs({ inputs, outputs })),
-            put(updateComposition(composition))
+            composition && put(updateComposition(composition))
         ]);
     } catch (error) {
         yield put(genericNetworkErrorAction(error));
@@ -81,12 +100,12 @@ function* watchUpdateVersion(action) {
             workflowName,
             params: { composition, ...versionData }
         } = action.payload;
-
-        yield call(versionApi.updateVersion, {
-            workflowId,
-            params: versionData
-        });
-        if (composition) {
+        const isArtifactValid = validateCurrentArtifact(composition);
+        if (isArtifactValid) {
+            yield call(versionApi.updateVersion, {
+                workflowId,
+                params: versionData
+            });
             yield call(versionApi.updateVersionArtifact, {
                 workflowId,
                 workflowName,
@@ -94,13 +113,21 @@ function* watchUpdateVersion(action) {
                 versionId: versionData.id,
                 payload: composition
             });
+            yield put(
+                notificationActions.showSuccess({
+                    title: I18n.t('workflow.confirmationMessages.updateTitle'),
+                    message: I18n.t(
+                        'workflow.confirmationMessages.updateMessage'
+                    )
+                })
+            );
+        } else {
+            yield call(versionApi.deleteVersionArtifact, {
+                workflowId,
+                versionId: versionData.id
+            });
         }
-        yield put(
-            notificationActions.showSuccess({
-                title: I18n.t('workflow.confirmationMessages.updateTitle'),
-                message: I18n.t('workflow.confirmationMessages.updateMessage')
-            })
-        );
+        return isArtifactValid;
     } catch (error) {
         yield put(genericNetworkErrorAction(error));
     }
@@ -108,18 +135,26 @@ function* watchUpdateVersion(action) {
 
 function* watchCertifyVersion(action) {
     try {
-        yield call(watchUpdateVersion, action);
+        const isArtifactValid = yield call(watchUpdateVersion, action);
+        if (!isArtifactValid)
+            throw new Error('Could not update empty artifact');
         yield call(versionApi.certifyVersion, {
             ...action.payload
         });
+        yield put(versionStateChangedAction({ state: versionState.CERTIFIED }));
         yield put(
             notificationActions.showSuccess({
                 title: I18n.t('workflow.confirmationMessages.certifyTitle'),
                 message: I18n.t('workflow.confirmationMessages.certifyMessage')
             })
         );
-        yield put(versionStateChangedAction({ state: versionState.CERTIFIED }));
     } catch (error) {
+        yield put(
+            notificationActions.showError({
+                title: I18n.t('workflow.confirmationMessages.certifyTitle'),
+                message: I18n.t('workflow.composition.certifyArtifact')
+            })
+        );
         yield put(genericNetworkErrorAction(error));
     }
 }
index b4c3826..9485681 100644 (file)
@@ -80,7 +80,8 @@
                 "bpmnError" : "BPMN.IO Error",
                 "exportErrorMsg": "Could not export diagram",
                 "saveErrorMsg": "Could not save diagram",
-                "importErrorMsg": "Could not import diagram"
+                "importErrorMsg": "Could not import diagram",
+                "certifyArtifact": "Could not certify empty artifact"
             },
             "confirmationMessages": {
                 "certifyTitle": "Certify Version",
index 05c0d79..08262fe 100644 (file)
@@ -136,7 +136,7 @@ class RestAPIUtil {
         return this.handleRequest(url, PUT, options, data);
     }
 
-    destroy(url, options) {
+    delete(url, options) {
         return this.handleRequest(url, DELETE, options);
     }