diff --git a/application-engine/pom.xml b/application-engine/pom.xml index a9669b0e11..d63f7f0177 100644 --- a/application-engine/pom.xml +++ b/application-engine/pom.xml @@ -270,18 +270,6 @@ ${junit-jupiter.version} test - - org.junit.jupiter - junit-jupiter-api - ${junit-jupiter.version} - test - - - org.junit.jupiter - junit-jupiter-engine - ${junit-jupiter.version} - test - diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/DataService.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/DataService.java index b7eb4d4855..672a229447 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/DataService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/DataService.java @@ -71,6 +71,8 @@ public class DataService implements IDataService { public static final int MONGO_ID_LENGTH = 24; + private static final Set setDataForbiddenFieldTypes = Set.of(FieldType.TASK_REF, FieldType.CASE_REF); + @Autowired protected ApplicationEventPublisher publisher; @@ -220,6 +222,22 @@ public SetDataEventOutcome setData(Task task, ObjectNode values) { @Override public SetDataEventOutcome setData(Task task, ObjectNode values, Map params) { + return setData(task, values, params, false); + } + + /** + * Updates the data field's attributes of the provided task. + * + * @param task the task object of which the data are updated + * @param values information about how to update the data fields + * @param params additional information to be injected to the action delegate context + * @param runStrict if set to true, additional validations are going to be applied when updating the data fields. If + * set to false, minimal restrictions are considered. + * + * @return outcome containing Case, Task and changes that have been made. + * */ + @Override + public SetDataEventOutcome setData(Task task, ObjectNode values, Map params, boolean runStrict) { Case useCase = workflowService.findOne(task.getCaseId()); AbstractUser user = userService.getLoggedOrSystem(); @@ -232,70 +250,84 @@ public SetDataEventOutcome setData(Task task, ObjectNode values, Map { String fieldId = entry.getKey(); DataField dataField = useCase.getDataSet().get(fieldId); - if (dataField != null) { - Field field = useCase.getPetriNet().getField(fieldId).get(); - outcome.addOutcomes(resolveDataEvents(field, DataEventType.SET, EventPhase.PRE, useCase, task, params)); - if (outcome.getMessage() == null) { - Map dataSet = useCase.getPetriNet().getTransition(task.getTransitionId()).getDataSet(); - if (field.getEvents().containsKey(DataEventType.SET) && - ((DataEvent) field.getEvents().get(DataEventType.SET)).getMessage() != null) { - outcome.setMessage(((DataEvent) field.getEvents().get(DataEventType.SET)).getMessage()); - } else if (dataSet.containsKey(fieldId) - && dataSet.get(fieldId).getEvents().containsKey(DataEventType.SET) - && dataSet.get(fieldId).getEvents().get(DataEventType.SET).getMessage() != null) { - outcome.setMessage(dataSet.get(fieldId).getEvents().get(DataEventType.SET).getMessage()); - } - } - boolean modified = false; - ChangedField changedField = new ChangedField(); - changedField.setId(fieldId); - Object newValue = parseFieldsValues(entry.getValue(), dataField, task.getStringId()); - if (entry.getValue().has("value") || getFieldTypeFromNode((ObjectNode) entry.getValue()).equals("button")) { - dataField.setValue(newValue); - changedField.addAttribute("value", newValue); - modified = true; - } - List allowedNets = parseAllowedNetsValue(entry.getValue()); - if (allowedNets != null) { - dataField.setAllowedNets(allowedNets); - changedField.addAttribute("allowedNets", allowedNets); - modified = true; - } - String fieldType = getFieldTypeFromNode((ObjectNode) entry.getValue()); - Map filterMetadata = parseFilterMetadataValue((ObjectNode) entry.getValue(), fieldType); - if (filterMetadata != null) { - dataField.setFilterMetadata(filterMetadata); - changedField.addAttribute("filterMetadata", filterMetadata); - modified = true; - } - Map options = parseOptionsNode(entry.getValue(), fieldType); - if (options != null) { - setDataFieldOptions(options, dataField, changedField, fieldType); - modified = true; - } - Set choices = parseChoicesNode((ObjectNode) entry.getValue(), fieldType); - if (choices != null) { - dataField.setChoices(choices); - changedField.addAttribute("choices", choices.stream().map(i18nString -> i18nString.getTranslation(LocaleContextHolder.getLocale())).collect(Collectors.toSet())); - modified = true; + if (dataField == null) { + return; + } + if (runStrict) { + if (!isDataFieldEditable(dataField, task.getTransitionId())) { + throw new IllegalArgumentException("Cannot edit data field [" + fieldId + "], which is not editable on transition [" + task.getTransitionId() + "]."); } - Map properties = parseProperties(entry.getValue()); - if (properties != null) { - outcome.addOutcome(this.changeComponentProperties(useCase, task, field.getStringId(), properties)); - modified = true; + Field field = useCase.getField(fieldId); + if (field == null) { + throw new IllegalArgumentException("Such field with id [" + fieldId + "] does not exist in petri net [" + useCase.getPetriNetId() + "]"); } - if (modified) { - dataField.setLastModified(LocalDateTime.now()); + if (setDataForbiddenFieldTypes.contains(field.getType())) { + return; } - if (dataConfigurationProperties.getValidation().isSetDataEnabled()) { - validation.valid(useCase.getPetriNet().getDataSet().get(entry.getKey()), dataField); + } + + Field field = useCase.getPetriNet().getField(fieldId).get(); + outcome.addOutcomes(resolveDataEvents(field, DataEventType.SET, EventPhase.PRE, useCase, task, params)); + if (outcome.getMessage() == null) { + Map dataSet = useCase.getPetriNet().getTransition(task.getTransitionId()).getDataSet(); + if (field.getEvents().containsKey(DataEventType.SET) && + ((DataEvent) field.getEvents().get(DataEventType.SET)).getMessage() != null) { + outcome.setMessage(((DataEvent) field.getEvents().get(DataEventType.SET)).getMessage()); + } else if (dataSet.containsKey(fieldId) + && dataSet.get(fieldId).getEvents().containsKey(DataEventType.SET) + && dataSet.get(fieldId).getEvents().get(DataEventType.SET).getMessage() != null) { + outcome.setMessage(dataSet.get(fieldId).getEvents().get(DataEventType.SET).getMessage()); } - outcome.addChangedField(fieldId, changedField); - workflowService.save(useCase); - publisher.publishEvent(new SetDataEvent(outcome, EventPhase.PRE, user)); - outcome.addOutcomes(resolveDataEvents(field, DataEventType.SET, EventPhase.POST, useCase, task, params)); - applyFieldConnectedChanges(useCase, field); } + boolean modified = false; + ChangedField changedField = new ChangedField(); + changedField.setId(fieldId); + Object newValue = parseFieldsValues(entry.getValue(), dataField, task.getStringId()); + if (entry.getValue().has("value") || getFieldTypeFromNode((ObjectNode) entry.getValue()).equals("button")) { + dataField.setValue(newValue); + changedField.addAttribute("value", newValue); + modified = true; + } + List allowedNets = parseAllowedNetsValue(entry.getValue()); + if (allowedNets != null) { + dataField.setAllowedNets(allowedNets); + changedField.addAttribute("allowedNets", allowedNets); + modified = true; + } + String fieldType = getFieldTypeFromNode((ObjectNode) entry.getValue()); + Map filterMetadata = parseFilterMetadataValue((ObjectNode) entry.getValue(), fieldType); + if (filterMetadata != null) { + dataField.setFilterMetadata(filterMetadata); + changedField.addAttribute("filterMetadata", filterMetadata); + modified = true; + } + Map options = parseOptionsNode(entry.getValue(), fieldType); + if (options != null) { + setDataFieldOptions(options, dataField, changedField, fieldType); + modified = true; + } + Set choices = parseChoicesNode((ObjectNode) entry.getValue(), fieldType); + if (choices != null) { + dataField.setChoices(choices); + changedField.addAttribute("choices", choices.stream().map(i18nString -> i18nString.getTranslation(LocaleContextHolder.getLocale())).collect(Collectors.toSet())); + modified = true; + } + Map properties = parseProperties(entry.getValue()); + if (properties != null) { + outcome.addOutcome(this.changeComponentProperties(useCase, task, field.getStringId(), properties)); + modified = true; + } + if (modified) { + dataField.setLastModified(LocalDateTime.now()); + } + if (dataConfigurationProperties.getValidation().isSetDataEnabled()) { + validation.valid(useCase.getPetriNet().getDataSet().get(entry.getKey()), dataField); + } + outcome.addChangedField(fieldId, changedField); + workflowService.save(useCase); + publisher.publishEvent(new SetDataEvent(outcome, EventPhase.PRE, user)); + outcome.addOutcomes(resolveDataEvents(field, DataEventType.SET, EventPhase.POST, useCase, task, params)); + applyFieldConnectedChanges(useCase, field); }); updateDataset(useCase); outcome.setCase(workflowService.save(useCase)); @@ -303,6 +335,15 @@ public SetDataEventOutcome setData(Task task, ObjectNode values, Map> behaviorMap = dataField.getBehavior(); + if (behaviorMap == null) { + return false; + } + Set behaviorSet = behaviorMap.get(transId); + return behaviorSet != null && behaviorSet.contains(FieldBehavior.EDITABLE); + } + @Override public GetDataGroupsEventOutcome getDataGroups(String taskId, Locale locale) { return getDataGroups(taskId, locale, new HashSet<>(), 0, null); diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/TaskService.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/TaskService.java index ac21c61ae5..cc81367878 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/TaskService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/TaskService.java @@ -976,6 +976,9 @@ public SetDataEventOutcome getMainOutcome(Map outco } } mainOutcome = outcomes.remove(key); + if (mainOutcome == null) { + return null; + } mainOutcome.addOutcomes(new ArrayList<>(outcomes.values())); return mainOutcome; } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IDataService.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IDataService.java index 4426b3500a..7f1f5038a6 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IDataService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IDataService.java @@ -41,6 +41,8 @@ public interface IDataService { SetDataEventOutcome setData(Task task, ObjectNode values, Map params); + SetDataEventOutcome setData(Task task, ObjectNode values, Map params, boolean runStrict); + FileFieldInputStream getFile(Case useCase, Task task, FileField field, boolean forPreview) throws FileNotFoundException; FileFieldInputStream getFile(Case useCase, Task task, FileField field, boolean forPreview, Map params) throws FileNotFoundException; diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/AbstractTaskController.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/AbstractTaskController.java index eafa862bf8..bbde665db3 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/AbstractTaskController.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/AbstractTaskController.java @@ -7,6 +7,8 @@ import com.netgrif.application.engine.elastic.service.interfaces.IElasticTaskService; import com.netgrif.application.engine.elastic.web.requestbodies.singleaslist.SingleElasticTaskSearchRequestAsList; import com.netgrif.application.engine.eventoutcomes.LocalisedEventOutcomeFactory; +import com.netgrif.application.engine.objects.petrinet.domain.dataset.FieldType; +import com.netgrif.application.engine.objects.petrinet.domain.dataset.localised.LocalisedField; import com.netgrif.application.engine.workflow.web.responsebodies.LocalisedTaskResource; import com.netgrif.application.engine.objects.petrinet.domain.throwable.TransitionNotExecutableException; import com.netgrif.application.engine.workflow.domain.IllegalArgumentWithChangedFieldsException; @@ -19,6 +21,7 @@ import com.netgrif.application.engine.workflow.service.FileFieldInputStream; import com.netgrif.application.engine.workflow.service.interfaces.IDataService; import com.netgrif.application.engine.workflow.service.interfaces.ITaskService; +import com.netgrif.application.engine.workflow.service.interfaces.IWorkflowService; import com.netgrif.application.engine.workflow.web.requestbodies.file.FileFieldRequest; import com.netgrif.application.engine.workflow.web.requestbodies.singleaslist.SingleTaskSearchRequestAsList; import com.netgrif.application.engine.workflow.web.responsebodies.*; @@ -41,10 +44,8 @@ import org.springframework.web.multipart.MultipartFile; import java.io.FileNotFoundException; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; +import java.util.*; +import java.util.stream.Collectors; public abstract class AbstractTaskController { @@ -54,6 +55,8 @@ public abstract class AbstractTaskController { private final IDataService dataService; + private final IWorkflowService workflowService; + private final IElasticTaskService searchService; private final UserService userService; @@ -61,10 +64,12 @@ public abstract class AbstractTaskController { public AbstractTaskController(ITaskService taskService, IDataService dataService, IElasticTaskService searchService, + IWorkflowService workflowService, UserService userService) { this.taskService = taskService; this.dataService = dataService; this.searchService = searchService; + this.workflowService = workflowService; this.userService = userService; } @@ -216,9 +221,32 @@ public EntityModel getData(String taskId, Locale locale public EntityModel setData(String taskId, ObjectNode dataBody, Locale locale) { try { + List dataGroups = dataService.getDataGroups(taskId, locale).getData(); + Set referencedTaskIds = new HashSet<>(); + referencedTaskIds.add(taskId); + for (com.netgrif.application.engine.objects.petrinet.domain.DataGroup dataGroup : dataGroups) { + Set referencedTaskIdsByDataGroup = dataGroup.getFields().getContent().stream() + .filter(someField -> someField instanceof LocalisedField localisedField + && localisedField.getType() == FieldType.TASK_REF + && localisedField.getValue() instanceof List + && !((List) localisedField.getValue()).isEmpty()) + .map(localisedField -> (List) ((LocalisedField) localisedField).getValue()) + .flatMap(List::stream) + .collect(Collectors.toSet()); + referencedTaskIds.addAll(referencedTaskIdsByDataGroup); + } Map outcomes = new HashMap<>(); - dataBody.fields().forEachRemaining(it -> outcomes.put(it.getKey(), dataService.setData(it.getKey(), it.getValue().deepCopy()))); + dataBody.fields().forEachRemaining(fieldChangesEntry -> { + String taskIdToChangeWith = fieldChangesEntry.getKey(); + if (!referencedTaskIds.contains(taskIdToChangeWith)) { + return; + } + Task taskToChangeWith = taskService.findOne(taskIdToChangeWith); + outcomes.put(taskIdToChangeWith, dataService.setData(taskToChangeWith, + fieldChangesEntry.getValue().deepCopy(), new HashMap<>(), true)); + }); SetDataEventOutcome mainOutcome = taskService.getMainOutcome(outcomes, taskId); + mainOutcome = handleMainSetDataEventOutcome(mainOutcome, taskId); return EventOutcomeWithMessageResource.successMessage("Data field values have been successfully set", LocalisedEventOutcomeFactory.from(mainOutcome, LocaleContextHolder.getLocale())); } catch (IllegalArgumentWithChangedFieldsException e) { @@ -236,6 +264,7 @@ public EntityModel saveFile(String taskId, MultipartFil Map outcomes = new HashMap<>(); outcomes.put(dataBody.getParentTaskId(), dataService.saveFile(dataBody.getParentTaskId(), dataBody.getFieldId(), multipartFile)); SetDataEventOutcome mainOutcome = taskService.getMainOutcome(outcomes, taskId); + mainOutcome = handleMainSetDataEventOutcome(mainOutcome, taskId); return EventOutcomeWithMessageResource.successMessage("Data field values have been successfully set", LocalisedEventOutcomeFactory.from(mainOutcome, LocaleContextHolder.getLocale())); } catch (IllegalArgumentWithChangedFieldsException e) { @@ -268,7 +297,8 @@ public EntityModel deleteFile(String taskId, String fie Map outcomes = new HashMap<>(); outcomes.put(taskId, dataService.deleteFile(taskId, fieldId)); SetDataEventOutcome mainOutcome = taskService.getMainOutcome(outcomes, taskId); - return EventOutcomeWithMessageResource.successMessage("Data field values have been sucessfully set", + mainOutcome = handleMainSetDataEventOutcome(mainOutcome, taskId); + return EventOutcomeWithMessageResource.successMessage("Data field values have been successfully set", LocalisedEventOutcomeFactory.from(mainOutcome, LocaleContextHolder.getLocale())); } @@ -276,7 +306,8 @@ public EntityModel saveFiles(String taskId, MultipartFi Map outcomes = new HashMap<>(); outcomes.put(requestBody.getParentTaskId(), dataService.saveFiles(requestBody.getParentTaskId(), requestBody.getFieldId(), multipartFiles)); SetDataEventOutcome mainOutcome = taskService.getMainOutcome(outcomes, taskId); - return EventOutcomeWithMessageResource.successMessage("Data field values have been sucessfully set", + mainOutcome = handleMainSetDataEventOutcome(mainOutcome, taskId); + return EventOutcomeWithMessageResource.successMessage("Data field values have been successfully set", LocalisedEventOutcomeFactory.from(mainOutcome, LocaleContextHolder.getLocale())); } @@ -300,7 +331,8 @@ public EntityModel deleteNamedFile(String taskId, Strin Map outcomes = new HashMap<>(); outcomes.put(taskId, dataService.deleteFileByName(taskId, fieldId, name)); SetDataEventOutcome mainOutcome = taskService.getMainOutcome(outcomes, taskId); - return EventOutcomeWithMessageResource.successMessage("Data field values have been sucessfully set", + mainOutcome = handleMainSetDataEventOutcome(mainOutcome, taskId); + return EventOutcomeWithMessageResource.successMessage("Data field values have been successfully set", LocalisedEventOutcomeFactory.from(mainOutcome, LocaleContextHolder.getLocale())); } @@ -316,4 +348,13 @@ public ResponseEntity getFilePreview(String taskId, String fieldId) th .headers(headers) .body(fileFieldInputStream != null ? new InputStreamResource(fileFieldInputStream.getInputStream()) : null); } + + protected SetDataEventOutcome handleMainSetDataEventOutcome(SetDataEventOutcome mainOutcome, String taskId) { + if (mainOutcome != null) { + return mainOutcome; + } + + Task task = taskService.findOne(taskId); + return new SetDataEventOutcome(workflowService.findOne(task.getCaseId()), task); + } } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/PublicTaskController.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/PublicTaskController.java index e6623f3dd9..932db377fe 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/PublicTaskController.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/PublicTaskController.java @@ -9,6 +9,7 @@ import com.netgrif.application.engine.workflow.domain.eventoutcomes.response.EventOutcomeWithMessage; import com.netgrif.application.engine.workflow.service.interfaces.IDataService; import com.netgrif.application.engine.workflow.service.interfaces.ITaskService; +import com.netgrif.application.engine.workflow.service.interfaces.IWorkflowService; import com.netgrif.application.engine.workflow.web.requestbodies.file.FileFieldRequest; import com.netgrif.application.engine.workflow.web.requestbodies.singleaslist.SingleTaskSearchRequestAsList; import com.netgrif.application.engine.workflow.web.responsebodies.LocalisedTaskResource; @@ -54,8 +55,9 @@ public class PublicTaskController extends AbstractTaskController { public PublicTaskController(ITaskService taskService, IDataService dataService, + IWorkflowService workflowService, UserService userService) { - super(taskService, dataService, null, userService); + super(taskService, dataService, null, workflowService, userService); this.taskService = taskService; this.dataService = dataService; this.userService = userService; diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/TaskController.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/TaskController.java index bab31f35cf..2de3c52fed 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/TaskController.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/TaskController.java @@ -9,6 +9,7 @@ import com.netgrif.application.engine.workflow.domain.eventoutcomes.response.EventOutcomeWithMessage; import com.netgrif.application.engine.workflow.service.interfaces.IDataService; import com.netgrif.application.engine.workflow.service.interfaces.ITaskService; +import com.netgrif.application.engine.workflow.service.interfaces.IWorkflowService; import com.netgrif.application.engine.workflow.web.requestbodies.file.FileFieldRequest; import com.netgrif.application.engine.workflow.web.requestbodies.singleaslist.SingleTaskSearchRequestAsList; import com.netgrif.application.engine.workflow.web.responsebodies.CountResponse; @@ -55,8 +56,9 @@ public class TaskController extends AbstractTaskController { public TaskController(ITaskService taskService, IDataService dataService, IElasticTaskService searchService, + IWorkflowService workflowService, UserService userService) { - super(taskService, dataService, searchService, userService); + super(taskService, dataService, searchService, workflowService, userService); } @Override diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/workflow/TaskControllerTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/workflow/TaskControllerTest.groovy index 569de8e894..8fd652ee55 100644 --- a/application-engine/src/test/groovy/com/netgrif/application/engine/workflow/TaskControllerTest.groovy +++ b/application-engine/src/test/groovy/com/netgrif/application/engine/workflow/TaskControllerTest.groovy @@ -1,23 +1,27 @@ package com.netgrif.application.engine.workflow -import com.netgrif.application.engine.objects.auth.domain.ActorTransformer -import com.netgrif.application.engine.objects.auth.domain.User -import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService -import com.netgrif.application.engine.adapter.spring.petrinet.service.ProcessRoleService +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.databind.node.ObjectNode import com.netgrif.application.engine.TestHelper -import com.netgrif.application.engine.objects.auth.domain.Authority -import com.netgrif.application.engine.objects.auth.domain.enums.UserState import com.netgrif.application.engine.auth.service.AuthorityService import com.netgrif.application.engine.auth.service.UserService import com.netgrif.application.engine.elastic.service.interfaces.IElasticTaskService +import com.netgrif.application.engine.objects.auth.domain.ActorTransformer +import com.netgrif.application.engine.objects.auth.domain.Authority +import com.netgrif.application.engine.objects.auth.domain.User +import com.netgrif.application.engine.objects.auth.domain.enums.UserState import com.netgrif.application.engine.objects.petrinet.domain.PetriNet import com.netgrif.application.engine.objects.petrinet.domain.VersionType import com.netgrif.application.engine.objects.petrinet.domain.dataset.FileListFieldValue +import com.netgrif.application.engine.objects.petrinet.domain.roles.ProcessRole +import com.netgrif.application.engine.objects.workflow.domain.Case +import com.netgrif.application.engine.objects.workflow.domain.DataField +import com.netgrif.application.engine.objects.workflow.domain.Task +import com.netgrif.application.engine.petrinet.service.ProcessRoleService +import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService import com.netgrif.application.engine.startup.ImportHelper import com.netgrif.application.engine.startup.runner.SuperCreatorRunner import com.netgrif.application.engine.utils.FullPageRequest -import com.netgrif.application.engine.objects.workflow.domain.Case -import com.netgrif.application.engine.objects.workflow.domain.Task import com.netgrif.application.engine.workflow.service.TaskSearchService import com.netgrif.application.engine.workflow.service.TaskService import com.netgrif.application.engine.workflow.service.interfaces.IDataService @@ -25,7 +29,6 @@ import com.netgrif.application.engine.workflow.service.interfaces.IWorkflowServi import com.netgrif.application.engine.workflow.web.TaskController import com.netgrif.application.engine.workflow.web.WorkflowController import com.netgrif.application.engine.workflow.web.requestbodies.TaskSearchRequest -import com.netgrif.application.engine.objects.petrinet.domain.roles.ProcessRole import com.netgrif.application.engine.workflow.web.responsebodies.TaskReference import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -86,7 +89,9 @@ class TaskControllerTest { @Autowired private TaskController taskController - private PetriNet net + private PetriNet allDataNet + + private PetriNet setDataNet private Case useCase @@ -106,7 +111,7 @@ class TaskControllerTest { state: UserState.ACTIVE, authoritySet: [authorityService.getOrCreate(Authority.user)] as Set, processRoles: [] as Set), null) - importNet() + importNets() } @Test @@ -118,7 +123,7 @@ class TaskControllerTest { @Test void testDeleteFile() { - Case testCase = helper.createCase("My case", net) + Case testCase = helper.createCase("My case", allDataNet) String taskId = testCase.tasks.find {it.transition == "1"}.task dataService.saveFile(taskId, "file", new MockMultipartFile("test", new byte[] {})) @@ -132,7 +137,7 @@ class TaskControllerTest { @Test void testDeleteFileByName() { - Case testCase = helper.createCase("My case", net) + Case testCase = helper.createCase("My case", allDataNet) String taskId = testCase.tasks.find {it.transition == "1"}.task dataService.saveFiles(taskId, "fileList", new MockMultipartFile[] {new MockMultipartFile("test", "test", null, new byte[] {})}) @@ -144,6 +149,79 @@ class TaskControllerTest { assert ((FileListFieldValue) testCase.dataSet["fileList"].value).namesPaths == null || ((FileListFieldValue) testCase.dataSet["fileList"].value).namesPaths.size() == 0 } + @Test + void testSetDataFieldTypeRestriction() { + Case testCase = helper.createCase("My case", setDataNet) + String taskId = testCase.tasks.find { it.transition == "testSetDataFieldTypeRestriction" }.task + + ObjectNode dataSet = populateNestedDataset([(taskId):["taskRef_0": ["type": "taskRef", "value": [taskId]]]]) + def response = taskController.setData(taskId, dataSet, Locale.default) + assert response != null && response.content.outcome != null + assert response.content.outcome.changedFields.changedFields.isEmpty() + assert ((List) workflowService.findOne(testCase.stringId).getDataField("taskRef_0").getValue()).isEmpty() + + dataSet = populateNestedDataset([(taskId):["caseRef_0": ["type": "caseRef", "value": [testCase.stringId]]]]) + response = taskController.setData(taskId, dataSet, Locale.default) + assert response != null && response.content.outcome != null + assert response.content.outcome.changedFields.changedFields.isEmpty() + assert ((List) workflowService.findOne(testCase.stringId).getDataField("caseRef_0").getValue()).isEmpty() + } + + @Test + void testSetDataVisibleField() { + Case testCase = helper.createCase("My case", setDataNet) + String taskId = testCase.tasks.find { it.transition == "data" }.task + + ObjectNode dataSet = populateNestedDataset([(taskId):["text_1": ["type": "text", "value": "awd"]]]) + def response = taskController.setData(taskId, dataSet, Locale.default) + assert response != null && response.content.outcome == null + assert response.content.error != null + + // todo: test visible behavior based on parent taskRef behavior + } + + @Test + void testSetDataNestedTaskRefRestrictions() { + Case testCase1 = helper.createCase("testCase1", setDataNet) + String taskId = testCase1.tasks.find { it.transition == "testSetDataNestedTaskRefRestrictions" }.task + Case testCase2 = helper.createCase("testCase2", setDataNet) + Case testCase3 = helper.createCase("testCase3", setDataNet) + + DataField case1DataField = testCase1.getDataField("taskRef_0") + case1DataField.setValue(List.of(testCase2.tasks.find { it.transition == "testSetDataNestedTaskRefRestrictions" }.task)) + workflowService.save(testCase1) + + DataField case2DataField = testCase2.getDataField("taskRef_0") + case2DataField.setValue(List.of(testCase3.tasks.find { it.transition == "data" }.task)) + workflowService.save(testCase2) + + String nestedOtherTaskId = testCase2.tasks.find { it.transition == "data" }.task + ObjectNode dataSet = populateNestedDataset([(nestedOtherTaskId):["text_0": ["type": "text", "value": "awd"]]]) + def response = taskController.setData(taskId, dataSet, Locale.default) + assert response != null && response.content.outcome != null + assert response.content.outcome.changedFields.changedFields.isEmpty() + assert workflowService.findOne(testCase2.stringId).getDataField("text_0").getValue() == null + + String nestedTaskId = testCase3.tasks.find { it.transition == "data" }.task + dataSet = populateNestedDataset([(nestedTaskId):["text_0": ["type": "text", "value": "awd"]]]) + response = taskController.setData(taskId, dataSet, Locale.default) + assert response != null && response.content.outcome != null + assert !response.content.outcome.changedFields.changedFields.isEmpty() + assert workflowService.findOne(testCase3.stringId).getDataField("text_0").getValue() == "awd" + } + + @Test + void testSetDataNonReferencedField() { + Case testCase = helper.createCase("My case", setDataNet) + String taskId = testCase.tasks.find { it.transition == "testSetDataNonReferencedField" }.task + + ObjectNode dataSet = populateNestedDataset([(taskId):["text_1": ["type": "text", "value": "awd"]]]) + def response = taskController.setData(taskId, dataSet, Locale.default) + + assert response != null && response.content.outcome == null + assert response.content.error != null + } + void testWithRoleAndUserref() { createCase() @@ -167,15 +245,19 @@ class TaskControllerTest { assert !findTasksByMongo().empty } - void importNet() { - PetriNet netOptional = helper.createNet("all_data_refs.xml", VersionType.MAJOR).get() - assert netOptional != null - net = netOptional + void importNets() { + PetriNet allDataNet = helper.createNet("all_data_refs.xml", VersionType.MAJOR).get() + assert allDataNet != null + this.allDataNet = allDataNet + + PetriNet setDataNet = helper.createNet("task_controller_set_data.xml", VersionType.MAJOR).get() + assert setDataNet != null + this.setDataNet = setDataNet } void createCase() { useCase = null - useCase = helper.createCase("My case", net) + useCase = helper.createCase("My case", allDataNet) assert useCase != null } @@ -203,7 +285,7 @@ class TaskControllerTest { } void setUserRole() { - List roles = processRoleService.findAllByNetStringId(net.stringId) + List roles = processRoleService.findAllByNetStringId(allDataNet.stringId) for (ProcessRole role : roles) { if (role.importId == "process_role") { @@ -219,4 +301,10 @@ class TaskControllerTest { Page tasks = taskService.search(taskSearchRequestList, new FullPageRequest(), ActorTransformer.toLoggedUser(userService.findUserByUsername(DUMMY_USER_MAIL, null).get()), Locale.ENGLISH, false) return tasks } + + static ObjectNode populateNestedDataset(Map>> data) { + ObjectMapper mapper = new ObjectMapper() + String json = mapper.writeValueAsString(data) + return mapper.readTree(json) as ObjectNode + } } diff --git a/application-engine/src/test/resources/petriNets/task_controller_set_data.xml b/application-engine/src/test/resources/petriNets/task_controller_set_data.xml new file mode 100644 index 0000000000..60743a813a --- /dev/null +++ b/application-engine/src/test/resources/petriNets/task_controller_set_data.xml @@ -0,0 +1,154 @@ + + task_controller_set_data + 1.0.0 + TCS + task_controller_set_data + device_hub + true + true + false + + taskRef_0 + + </data> + <data type="caseRef"> + <id>caseRef_0</id> + <title/> + </data> + <data type="text"> + <id>text_0</id> + <title/> + </data> + <data type="text"> + <id>text_1</id> + <title/> + </data> + <transition> + <id>testSetDataFieldTypeRestriction</id> + <x>720</x> + <y>336</y> + <label>testSetDataFieldTypeRestriction</label> + <dataGroup> + <id>testSetDataFieldTypeRestriction_0</id> + <cols>4</cols> + <layout>grid</layout> + <dataRef> + <id>taskRef_0</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>1</x> + <y>1</y> + <rows>1</rows> + <cols>2</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>caseRef_0</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>1</x> + <y>2</y> + <rows>1</rows> + <cols>2</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + </dataGroup> + </transition> + <transition> + <id>testSetDataNestedTaskRefRestrictions</id> + <x>720</x> + <y>436</y> + <label>testSetDataNestedTaskRefRestrictions</label> + <dataGroup> + <id>testSetDataNestedTaskRefRestrictions_0</id> + <cols>4</cols> + <layout>grid</layout> + <dataRef> + <id>taskRef_0</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>1</x> + <y>1</y> + <rows>1</rows> + <cols>2</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + </dataGroup> + </transition> + <transition> + <id>data</id> + <x>720</x> + <y>564</y> + <label>data</label> + <dataGroup> + <id>data_0</id> + <cols>4</cols> + <layout>grid</layout> + <dataRef> + <id>text_0</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>1</x> + <y>1</y> + <rows>1</rows> + <cols>2</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>text_1</id> + <logic> + <behavior>visible</behavior> + </logic> + <layout> + <x>1</x> + <y>2</y> + <rows>1</rows> + <cols>2</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + </dataGroup> + </transition> + <transition> + <id>testSetDataNonReferencedField</id> + <x>720</x> + <y>664</y> + <label>testSetDataNonReferencedField</label> + <dataGroup> + <id>data_0</id> + <cols>4</cols> + <layout>grid</layout> + <dataRef> + <id>text_0</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>1</x> + <y>1</y> + <rows>1</rows> + <cols>2</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + </dataGroup> + </transition> +</document> \ No newline at end of file