Search in sources :

Example 6 with UpdateDocument

use of io.lumeer.engine.api.event.UpdateDocument in project engine by Lumeer.

the class StateChangeDetector method detectChanges.

@Override
public void detectChanges(final DocumentEvent documentEvent, final Collection collection) {
    final CollectionPurpose purpose = collection.getPurpose();
    final String stateAttr = purpose.getStateAttributeId();
    if (StringUtils.isNotEmpty(stateAttr) && isAttributeChanged(documentEvent, stateAttr)) {
        final boolean doneState = isDoneState(documentEvent, collection);
        if (!(documentEvent instanceof CreateDocument)) {
            // delete previous due date and assignee events on the document
            if (documentEvent instanceof RemoveDocument || doneState) {
                delayedActionDao.deleteScheduledActions(getResourcePath(documentEvent), Set.of(NotificationType.DUE_DATE_SOON, NotificationType.PAST_DUE_DATE, NotificationType.TASK_ASSIGNED, NotificationType.TASK_REOPENED, NotificationType.STATE_UPDATE, NotificationType.DUE_DATE_CHANGED));
            }
        }
        if (documentEvent instanceof UpdateDocument) {
            // switched back to non-final state, reschedule due dates and assignees
            if (wasDoneState(documentEvent, collection) && !doneState) {
                // create new due date events on the document
                final ZonedDateTime dueDate = getDueDate(documentEvent, collection);
                if (dueDate != null) {
                    delayedActionDao.scheduleActions(getDelayedActions(documentEvent, collection, NotificationType.PAST_DUE_DATE, dueDate));
                    if (dueDate.minus(DUE_DATE_SOON_DAYS, ChronoUnit.DAYS).isAfter(ZonedDateTime.now())) {
                        delayedActionDao.scheduleActions(getDelayedActions(documentEvent, collection, NotificationType.DUE_DATE_SOON, dueDate.minus(DUE_DATE_SOON_DAYS, ChronoUnit.DAYS)));
                    }
                }
                delayedActionDao.scheduleActions(getDelayedActions(documentEvent, collection, NotificationType.TASK_REOPENED, nowPlus()));
            }
        }
        if (!(documentEvent instanceof RemoveDocument)) {
            // create new due date events on the document
            delayedActionDao.scheduleActions(getDelayedActions(documentEvent, collection, NotificationType.STATE_UPDATE, nowPlus()));
        }
    }
}
Also used : RemoveDocument(io.lumeer.engine.api.event.RemoveDocument) UpdateDocument(io.lumeer.engine.api.event.UpdateDocument) ZonedDateTime(java.time.ZonedDateTime) CreateDocument(io.lumeer.engine.api.event.CreateDocument) CollectionPurpose(io.lumeer.api.model.CollectionPurpose)

Example 7 with UpdateDocument

use of io.lumeer.engine.api.event.UpdateDocument in project engine by Lumeer.

the class AbstractPurposeChangeDetector method getRemovedAssignees.

protected Set<Assignee> getRemovedAssignees(final DocumentEvent documentEvent, final Collection collection) {
    if (documentEvent instanceof UpdateDocument) {
        final String assigneeAttributeId = collection.getPurpose().getAssigneeAttributeId();
        if (StringUtils.isNotEmpty(assigneeAttributeId)) {
            final Attribute assigneeAttribute = findAttribute(collection.getAttributes(), assigneeAttributeId);
            if (assigneeAttribute != null) {
                final Set<Assignee> originalUsers = new HashSet<>(DocumentUtils.getUsersList(((UpdateDocument) documentEvent).getOriginalDocument(), assigneeAttribute, getTeams(), getUsers()));
                final Set<Assignee> newUsers = DocumentUtils.getUsersList(documentEvent.getDocument(), assigneeAttribute, getTeams(), getUsers());
                newUsers.forEach(assignee -> {
                    originalUsers.remove(new Assignee(assignee.getEmail(), true));
                    originalUsers.remove(new Assignee(assignee.getEmail(), false));
                });
                return originalUsers;
            }
        }
    }
    return Set.of();
}
Also used : UpdateDocument(io.lumeer.engine.api.event.UpdateDocument) ResourceUtils.findAttribute(io.lumeer.api.util.ResourceUtils.findAttribute) Attribute(io.lumeer.api.model.Attribute) HashSet(java.util.HashSet)

Example 8 with UpdateDocument

use of io.lumeer.engine.api.event.UpdateDocument in project engine by Lumeer.

the class AbstractPurposeChangeDetector method isAttributeChanged.

protected boolean isAttributeChanged(final DocumentEvent documentEvent, final String attributeId) {
    if (documentEvent instanceof UpdateDocument) {
        final Document original = ((UpdateDocument) documentEvent).getOriginalDocument();
        final Document document = documentEvent.getDocument();
        if (original != null && document != null) {
            final Object originalAttr = original.getData() != null ? original.getData().get(attributeId) : null;
            final Object newAttr = document.getData() != null ? document.getData().get(attributeId) : null;
            if (originalAttr == null && newAttr == null) {
                return false;
            }
            if (originalAttr == null ^ newAttr == null) {
                return true;
            }
            return !originalAttr.equals(newAttr);
        } else {
            return false;
        }
    }
    return true;
}
Also used : UpdateDocument(io.lumeer.engine.api.event.UpdateDocument) DataDocument(io.lumeer.engine.api.data.DataDocument) Document(io.lumeer.api.model.Document) CreateDocument(io.lumeer.engine.api.event.CreateDocument) UpdateDocument(io.lumeer.engine.api.event.UpdateDocument)

Example 9 with UpdateDocument

use of io.lumeer.engine.api.event.UpdateDocument in project engine by Lumeer.

the class AssigneeChangeDetector method detectChanges.

@Override
public void detectChanges(final DocumentEvent documentEvent, final Collection collection) {
    final CollectionPurpose purpose = collection.getPurpose();
    final String assigneeAttr = purpose.getAssigneeAttributeId();
    final boolean doneState = isDoneState(documentEvent, collection);
    if (StringUtils.isNotEmpty(assigneeAttr) && isAttributeChanged(documentEvent, assigneeAttr)) {
        if (!(documentEvent instanceof CreateDocument)) {
            // delete previous due date and assignee events on the document
            delayedActionDao.deleteScheduledActions(getResourcePath(documentEvent), Set.of(NotificationType.DUE_DATE_SOON, NotificationType.PAST_DUE_DATE, NotificationType.TASK_ASSIGNED, NotificationType.TASK_REOPENED, NotificationType.DUE_DATE_CHANGED));
            if (!(documentEvent instanceof RemoveDocument) && !doneState) {
                final ZonedDateTime dueDate = getDueDate(documentEvent, collection);
                if (dueDate != null) {
                    delayedActionDao.scheduleActions(getDelayedActions(documentEvent, collection, NotificationType.PAST_DUE_DATE, dueDate));
                    if (dueDate.minus(DUE_DATE_SOON_DAYS, ChronoUnit.DAYS).isAfter(ZonedDateTime.now())) {
                        delayedActionDao.scheduleActions(getDelayedActions(documentEvent, collection, NotificationType.DUE_DATE_SOON, dueDate.minus(DUE_DATE_SOON_DAYS, ChronoUnit.DAYS)));
                    }
                }
            }
        }
        if (documentEvent instanceof UpdateDocument) {
            delayedActionDao.scheduleActions(getDelayedActions(documentEvent, collection, NotificationType.TASK_UNASSIGNED, nowPlus(), getRemovedAssignees(documentEvent, collection)));
        }
        if (!(documentEvent instanceof RemoveDocument) && !doneState) {
            // create new due date events on the document
            delayedActionDao.scheduleActions(getDelayedActions(documentEvent, collection, NotificationType.TASK_ASSIGNED, nowPlus(), getAddedAssignees(documentEvent, collection)));
        }
    }
}
Also used : RemoveDocument(io.lumeer.engine.api.event.RemoveDocument) ZonedDateTime(java.time.ZonedDateTime) UpdateDocument(io.lumeer.engine.api.event.UpdateDocument) CreateDocument(io.lumeer.engine.api.event.CreateDocument) CollectionPurpose(io.lumeer.api.model.CollectionPurpose)

Example 10 with UpdateDocument

use of io.lumeer.engine.api.event.UpdateDocument in project engine by Lumeer.

the class SingleStage method commitDocumentOperations.

private List<Document> commitDocumentOperations(final List<DocumentOperation> operations, final List<Document> createdDocuments, final Map<String, Collection> collectionsMapForCreatedDocuments) {
    if (operations.isEmpty() && collectionsMapForCreatedDocuments.isEmpty()) {
        return List.of();
    }
    final FunctionFacade functionFacade = task.getFunctionFacade();
    final TaskProcessingFacade taskProcessingFacade = task.getTaskProcessingFacade(taskExecutor, functionFacade);
    final PurposeChangeProcessor purposeChangeProcessor = task.getPurposeChangeProcessor();
    // Collection -> [Document]
    final Map<String, List<Document>> updatedDocuments = new HashMap<>();
    Map<String, Set<String>> documentIdsByCollection = operations.stream().map(Operation::getEntity).collect(Collectors.groupingBy(Document::getCollectionId, mapping(Document::getId, toSet())));
    final Map<String, Collection> collectionsMap = task.getDaoContextSnapshot().getCollectionDao().getCollectionsByIds(documentIdsByCollection.keySet()).stream().collect(Collectors.toMap(Collection::getId, coll -> coll));
    final Set<String> collectionsChanged = new HashSet<>();
    Map<String, Document> documentsByCorrelationId = createdDocuments.stream().collect(Collectors.toMap(doc -> doc.createIfAbsentMetaData().getString(Document.META_CORRELATION_ID), Function.identity()));
    // aggregate all operations to individual documents
    final Map<String, List<DocumentOperation>> changesByDocumentId = Utils.categorize(operations.stream(), change -> change.getEntity().getId());
    final Set<String> unprocessedCreatedDocuments = createdDocuments.stream().map(Document::getId).collect(toSet());
    createdDocuments.forEach(document -> {
        final Collection collection = collectionsMap.get(document.getCollectionId());
        final DataDocument newDataDecoded = constraintManager.encodeDataTypes(collection, document.getData());
        auditAdapter.registerCreate(collection.getId(), ResourceType.DOCUMENT, document.getId(), task.getInitiator(), automationName, null, newDataDecoded);
    });
    changesByDocumentId.forEach((id, changeList) -> {
        unprocessedCreatedDocuments.remove(id);
        final Document document = changeList.get(0).getEntity();
        final Document originalDocument = (task instanceof RuleTask) ? ((RuleTask) task).getOldDocument() : ((task instanceof FunctionTask) ? ((FunctionTask) task).getOriginalDocumentOrDefault(id, changeList.get(0).getOriginalDocument()) : changeList.get(0).getOriginalDocument());
        final Collection collection = collectionsMap.get(document.getCollectionId());
        final DataDocument aggregatedUpdate = new DataDocument();
        changeList.forEach(change -> aggregatedUpdate.put(change.getAttrId(), change.getValue()));
        final DataDocument newData = constraintManager.encodeDataTypes(collection, aggregatedUpdate);
        final DataDocument oldData = originalDocument != null ? new DataDocument(originalDocument.getData()) : new DataDocument();
        Set<String> attributesIdsToAdd = new HashSet<>(newData.keySet());
        attributesIdsToAdd.removeAll(oldData.keySet());
        if (attributesIdsToAdd.size() > 0) {
            collection.getAttributes().stream().filter(attr -> attributesIdsToAdd.contains(attr.getId())).forEach(attr -> {
                attr.setUsageCount(attr.getUsageCount() + 1);
                collection.setLastTimeUsed(ZonedDateTime.now());
                collectionsChanged.add(collection.getId());
            });
        }
        document.setUpdatedBy(task.getInitiator().getId());
        document.setUpdateDate(ZonedDateTime.now());
        final DataDocument beforePatch = task.getDaoContextSnapshot().getDataDao().getData(document.getCollectionId(), document.getId());
        DataDocument patchedData = task.getDaoContextSnapshot().getDataDao().patchData(document.getCollectionId(), document.getId(), newData);
        Document updatedDocument = task.getDaoContextSnapshot().getDocumentDao().updateDocument(document.getId(), document);
        updatedDocument.setData(patchedData);
        // notify delayed actions about data change
        if (collection.getPurposeType() == CollectionPurposeType.Tasks) {
            final Document original;
            if (originalDocument == null) {
                // when triggered by an action button, let's use the document from db
                original = new Document(document);
                original.setData(beforePatch);
            } else {
                original = originalDocument;
            }
            purposeChangeProcessor.processChanges(new UpdateDocument(updatedDocument, original), collection);
        }
        var oldDataDecoded = constraintManager.decodeDataTypes(collection, beforePatch);
        var patchedDataDecoded = constraintManager.decodeDataTypes(collection, patchedData);
        auditAdapter.registerDataChange(updatedDocument.getCollectionId(), ResourceType.DOCUMENT, updatedDocument.getId(), task.getInitiator(), automationName, null, beforePatch, oldDataDecoded, patchedData, patchedDataDecoded);
        // add patched data to new documents
        boolean created = false;
        if (StringUtils.isNotEmpty(document.createIfAbsentMetaData().getString(Document.META_CORRELATION_ID))) {
            final Document doc = documentsByCorrelationId.get(document.getMetaData().getString(Document.META_CORRELATION_ID));
            if (doc != null) {
                doc.setData(patchedData);
                created = true;
            }
        }
        if (task instanceof RuleTask) {
            if (created) {
                taskProcessingFacade.onCreateDocument(new CreateDocument(updatedDocument));
            } else {
                if (task.getRecursionDepth() == 0) {
                    // there are now 3 versions of the document:
                    // 1) the document before user triggered an update - original document (null when triggered by action button)
                    // 2) the document with the new user entered value - before patch
                    // 3) the document with the value computed by the rule based on the previous two - updated document
                    // this rule got executed because of change from 1 to 2
                    // for the recursive rules, we need to trigger rules for changes between 2 and 3
                    final UpdateDocument updateDocumentEvent;
                    final Document orig = new Document(document);
                    orig.setData(beforePatch);
                    updateDocumentEvent = new UpdateDocument(updatedDocument, orig);
                    taskProcessingFacade.onDocumentUpdate(updateDocumentEvent, ((RuleTask) task).getRule().getName());
                } else {
                    taskExecutor.submitTask(functionFacade.createTaskForUpdateDocument(collection, originalDocument, updatedDocument, aggregatedUpdate.keySet()));
                }
            }
        }
        patchedData = constraintManager.decodeDataTypes(collection, patchedData);
        updatedDocument.setData(patchedData);
        updatedDocuments.computeIfAbsent(document.getCollectionId(), key -> new ArrayList<>()).add(updatedDocument);
    });
    unprocessedCreatedDocuments.forEach(id -> {
        createdDocuments.stream().filter(d -> d.getId().equals(id)).findFirst().ifPresent(document -> {
            taskProcessingFacade.onCreateDocument(new CreateDocument(document));
        });
    });
    changesTracker.addCollections(collectionsChanged.stream().map(collectionsMap::get).collect(toSet()));
    changesTracker.addUpdatedDocuments(updatedDocuments.values().stream().flatMap(java.util.Collection::stream).collect(toSet()));
    changesTracker.updateCollectionsMap(collectionsMapForCreatedDocuments);
    changesTracker.updateCollectionsMap(collectionsMap);
    collectionsChanged.forEach(collectionId -> task.getDaoContextSnapshot().getCollectionDao().updateCollection(collectionId, collectionsMap.get(collectionId), null, false));
    return updatedDocuments.values().stream().flatMap(java.util.Collection::stream).collect(toList());
}
Also used : TaskExecutor(io.lumeer.core.task.TaskExecutor) PrintTextOperation(io.lumeer.core.task.executor.operation.PrintTextOperation) ZonedDateTime(java.time.ZonedDateTime) Operation(io.lumeer.core.task.executor.operation.Operation) StringUtils(org.apache.commons.lang3.StringUtils) CreateLinkInstance(io.lumeer.engine.api.event.CreateLinkInstance) UserMessageOperation(io.lumeer.core.task.executor.operation.UserMessageOperation) GenericPrintRequest(io.lumeer.core.task.executor.request.GenericPrintRequest) ResourceType(io.lumeer.api.model.ResourceType) Map(java.util.Map) FunctionTask(io.lumeer.core.task.FunctionTask) FunctionFacade(io.lumeer.core.facade.FunctionFacade) DocumentOperation(io.lumeer.core.task.executor.operation.DocumentOperation) DataDocument(io.lumeer.engine.api.data.DataDocument) Document(io.lumeer.api.model.Document) CreateDocument(io.lumeer.engine.api.event.CreateDocument) Set(java.util.Set) Collectors(java.util.stream.Collectors) LinkInstance(io.lumeer.api.model.LinkInstance) PurposeChangeProcessor(io.lumeer.core.facade.detector.PurposeChangeProcessor) LinkType(io.lumeer.api.model.LinkType) PrintAttributeOperation(io.lumeer.core.task.executor.operation.PrintAttributeOperation) List(java.util.List) SendEmailOperation(io.lumeer.core.task.executor.operation.SendEmailOperation) Optional(java.util.Optional) Utils(io.lumeer.core.util.Utils) ChangesTracker(io.lumeer.core.task.executor.ChangesTracker) LinkOperation(io.lumeer.core.task.executor.operation.LinkOperation) DocumentCreationOperation(io.lumeer.core.task.executor.operation.DocumentCreationOperation) SendEmailRequest(io.lumeer.core.task.executor.request.SendEmailRequest) HashMap(java.util.HashMap) Function(java.util.function.Function) ArrayList(java.util.ArrayList) NavigationOperation(io.lumeer.core.task.executor.operation.NavigationOperation) HashSet(java.util.HashSet) TaskProcessingFacade(io.lumeer.core.facade.TaskProcessingFacade) LinkCreationOperation(io.lumeer.core.task.executor.operation.LinkCreationOperation) DocumentRemovalOperation(io.lumeer.core.task.executor.operation.DocumentRemovalOperation) LinkTypeUtils(io.lumeer.core.util.LinkTypeUtils) CollectionPurposeType(io.lumeer.api.model.CollectionPurposeType) UserMessageRequest(io.lumeer.core.task.executor.request.UserMessageRequest) NavigationRequest(io.lumeer.core.task.executor.request.NavigationRequest) LinkTypeAdapter(io.lumeer.core.adapter.LinkTypeAdapter) DocumentUtils(io.lumeer.core.util.DocumentUtils) UpdateLinkInstance(io.lumeer.engine.api.event.UpdateLinkInstance) RuleTask(io.lumeer.core.task.RuleTask) OperationExecutor(io.lumeer.core.task.executor.operation.OperationExecutor) UpdateDocument(io.lumeer.engine.api.event.UpdateDocument) AutoLinkBatchTask(io.lumeer.core.task.AutoLinkBatchTask) Collection(io.lumeer.api.model.Collection) DataDocument(io.lumeer.engine.api.data.DataDocument) Set(java.util.Set) HashSet(java.util.HashSet) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) DataDocument(io.lumeer.engine.api.data.DataDocument) Document(io.lumeer.api.model.Document) CreateDocument(io.lumeer.engine.api.event.CreateDocument) UpdateDocument(io.lumeer.engine.api.event.UpdateDocument) List(java.util.List) ArrayList(java.util.ArrayList) RuleTask(io.lumeer.core.task.RuleTask) FunctionFacade(io.lumeer.core.facade.FunctionFacade) TaskProcessingFacade(io.lumeer.core.facade.TaskProcessingFacade) HashSet(java.util.HashSet) UpdateDocument(io.lumeer.engine.api.event.UpdateDocument) FunctionTask(io.lumeer.core.task.FunctionTask) CreateDocument(io.lumeer.engine.api.event.CreateDocument) Collection(io.lumeer.api.model.Collection) PurposeChangeProcessor(io.lumeer.core.facade.detector.PurposeChangeProcessor)

Aggregations

UpdateDocument (io.lumeer.engine.api.event.UpdateDocument)14 CreateDocument (io.lumeer.engine.api.event.CreateDocument)13 Document (io.lumeer.api.model.Document)10 DataDocument (io.lumeer.engine.api.data.DataDocument)9 Collection (io.lumeer.api.model.Collection)6 HashSet (java.util.HashSet)5 ZonedDateTime (java.time.ZonedDateTime)3 Attribute (io.lumeer.api.model.Attribute)2 CollectionPurpose (io.lumeer.api.model.CollectionPurpose)2 ResourceUtils.findAttribute (io.lumeer.api.util.ResourceUtils.findAttribute)2 FunctionTask (io.lumeer.core.task.FunctionTask)2 RuleTask (io.lumeer.core.task.RuleTask)2 RemoveDocument (io.lumeer.engine.api.event.RemoveDocument)2 CollectionPurposeType (io.lumeer.api.model.CollectionPurposeType)1 LinkInstance (io.lumeer.api.model.LinkInstance)1 LinkType (io.lumeer.api.model.LinkType)1 ResourceType (io.lumeer.api.model.ResourceType)1 LinkTypeAdapter (io.lumeer.core.adapter.LinkTypeAdapter)1 FunctionFacade (io.lumeer.core.facade.FunctionFacade)1 TaskProcessingFacade (io.lumeer.core.facade.TaskProcessingFacade)1