use of io.lumeer.core.task.executor.operation.LinkOperation in project engine by Lumeer.
the class LumeerBridge method getOperationsDescription.
public String getOperationsDescription() {
final Map<String, Collection> collections = new HashMap<>();
final Map<String, LinkType> linkTypes = new HashMap<>();
final StringBuilder sb = new StringBuilder();
final LongAdder i = new LongAdder();
operations.forEach(operation -> {
if (operation instanceof DocumentCreationOperation) {
final DocumentCreationOperation documentCreationOperation = (DocumentCreationOperation) operation;
if (StringUtils.isEmpty(documentCreationOperation.getEntity().getId())) {
i.increment();
documentCreationOperation.getEntity().setId("NEW" + i.intValue());
}
}
});
operations.forEach(operation -> {
if (operation instanceof DocumentCreationOperation) {
final DocumentCreationOperation documentCreationOperation = (DocumentCreationOperation) operation;
final Collection collection = collections.computeIfAbsent(documentCreationOperation.getEntity().getCollectionId(), id -> task.getDaoContextSnapshot().getCollectionDao().getCollectionById(id));
sb.append("new Document(").append(collection.getName()).append(")\n");
} else if (operation instanceof DocumentOperation) {
final DocumentOperation documentChange = (DocumentOperation) operation;
final Collection collection = collections.computeIfAbsent(documentChange.getEntity().getCollectionId(), id -> task.getDaoContextSnapshot().getCollectionDao().getCollectionById(id));
appendOperation(sb, collection.getName(), collection.getAttributes(), documentChange);
} else if (operation instanceof LinkOperation) {
final LinkOperation linkChange = (LinkOperation) operation;
final LinkType linkType = linkTypes.computeIfAbsent(linkChange.getEntity().getId(), id -> task.getDaoContextSnapshot().getLinkTypeDao().getLinkType(id));
appendOperation(sb, linkType.getName(), linkType.getAttributes(), linkChange);
} else if (operation instanceof UserMessageOperation || operation instanceof PrintAttributeOperation || operation instanceof NavigationOperation || operation instanceof SendEmailOperation) {
sb.append(operation.toString());
} else if (operation instanceof LinkCreationOperation) {
final LinkCreationOperation linkCreationOperation = (LinkCreationOperation) operation;
final LinkType linkType = linkTypes.computeIfAbsent(linkCreationOperation.getEntity().getLinkTypeId(), id -> task.getDaoContextSnapshot().getLinkTypeDao().getLinkType(id));
sb.append("new Link(").append(linkType.getName()).append(")\n");
} else if (operation instanceof AddDocumentFileAttachmentOperation) {
final AddDocumentFileAttachmentOperation addDocumentFileAttachmentOperation = (AddDocumentFileAttachmentOperation) operation;
final Collection collection = collections.computeIfAbsent(addDocumentFileAttachmentOperation.getEntity().getCollectionId(), id -> task.getDaoContextSnapshot().getCollectionDao().getCollectionById(id));
sb.append("new record file attachment (").append(collection.getName()).append(".").append(CollectionUtil.getAttribute(collection, addDocumentFileAttachmentOperation.getAttrId())).append(": ").append(addDocumentFileAttachmentOperation.getFileAttachmentData().getFileName()).append(")\n");
} else if (operation instanceof AddLinkFileAttachmentOperation) {
final AddLinkFileAttachmentOperation addLinkFileAttachmentOperation = (AddLinkFileAttachmentOperation) operation;
final LinkType linkType = linkTypes.computeIfAbsent(addLinkFileAttachmentOperation.getEntity().getLinkTypeId(), id -> task.getDaoContextSnapshot().getLinkTypeDao().getLinkType(id));
sb.append("new link file attachment (").append(linkType.getName()).append(".").append(LinkTypeUtil.getAttribute(linkType, addLinkFileAttachmentOperation.getAttrId())).append(": ").append(addLinkFileAttachmentOperation.getFileAttachmentData().getFileName()).append(")\n");
} else if (operation instanceof SendSmtpEmailOperation) {
final SendSmtpEmailOperation sendSmtpEmailOperation = (SendSmtpEmailOperation) operation;
sb.append("send email (to: ").append(sendSmtpEmailOperation.getEntity().getEmail()).append(", subject: ").append(sendSmtpEmailOperation.getEntity().getSubject()).append(")\n");
}
});
return sb.toString();
}
use of io.lumeer.core.task.executor.operation.LinkOperation in project engine by Lumeer.
the class SingleStage method commitOperations.
public ChangesTracker commitOperations() {
if (operations.isEmpty()) {
return new ChangesTracker();
}
@SuppressWarnings("rawtypes") final List<Operation> invalidOperations = operations.stream().filter(operation -> !operation.isComplete()).collect(toList());
if (invalidOperations.size() > 0) {
final StringBuilder sb = new StringBuilder();
invalidOperations.forEach(operation -> sb.append("Invalid update request: ").append(operation.toString()).append("\n"));
throw new IllegalArgumentException(sb.toString());
}
// first create all new documents
final List<Document> createdDocuments = createDocuments(operations.stream().filter(operation -> operation instanceof DocumentCreationOperation && operation.isComplete()).map(operation -> (DocumentCreationOperation) operation).collect(toList()));
final Map<String, List<Document>> toBeRemovedDocumentsByCollection = Utils.categorize(operations.stream().filter(operation -> operation instanceof DocumentRemovalOperation && operation.isComplete()).map(operation -> ((DocumentRemovalOperation) operation).getEntity()), Document::getCollectionId);
// get data structures for efficient manipulation with the new documents
final Map<String, List<Document>> documentsByCollection = DocumentUtils.getDocumentsByCollection(createdDocuments);
final List<String> usedCollections = new ArrayList<>(documentsByCollection.keySet());
usedCollections.addAll(toBeRemovedDocumentsByCollection.keySet());
final Map<String, Collection> collectionsMap = task.getDaoContextSnapshot().getCollectionDao().getCollectionsByIds(usedCollections).stream().collect(Collectors.toMap(Collection::getId, Function.identity()));
final Map<String, String> correlationIdsToIds = createdDocuments.stream().collect(Collectors.toMap(doc -> doc.createIfAbsentMetaData().getString(Document.META_CORRELATION_ID), Document::getId));
// report new empty documents, later updates are sent separately
changesTracker.addCreatedDocuments(createdDocuments);
changesTracker.addCollections(collectionsMap.values().stream().filter(c -> documentsByCollection.containsKey(c.getId())).collect(toSet()));
// map the newly create document IDs to all other changes so that we use the correct document in updates etc.
operations.stream().filter(operation -> operation instanceof DocumentOperation).forEach(operation -> {
final Document doc = (Document) operation.getEntity();
if (StringUtils.isEmpty(doc.getId()) && StringUtils.isNotEmpty(doc.createIfAbsentMetaData().getString(Document.META_CORRELATION_ID))) {
doc.setId(correlationIdsToIds.get(doc.getMetaData().getString(Document.META_CORRELATION_ID)));
}
});
operations.stream().filter(operation -> operation instanceof LinkCreationOperation).forEach(operation -> {
final LinkInstance link = ((LinkCreationOperation) operation).getEntity();
if (StringUtils.isEmpty(link.getId()) && StringUtils.isNotEmpty(link.getTemplateId())) {
link.setDocumentIds(List.of(correlationIdsToIds.containsKey(link.getDocumentIds().get(0)) ? correlationIdsToIds.get(link.getDocumentIds().get(0)) : link.getDocumentIds().get(0), correlationIdsToIds.containsKey(link.getDocumentIds().get(1)) ? correlationIdsToIds.get(link.getDocumentIds().get(1)) : link.getDocumentIds().get(1)));
}
});
// commit document changes
final List<Document> changedDocuments = commitDocumentOperations(operations.stream().filter(operation -> operation instanceof DocumentOperation && operation.isComplete()).map(operation -> (DocumentOperation) operation).collect(toList()), createdDocuments, collectionsMap);
// remove documents
final List<Document> removedDocuments = removeDocuments(operations.stream().filter(operation -> operation instanceof DocumentRemovalOperation).map(operation -> (DocumentRemovalOperation) operation).collect(toList()));
changesTracker.addRemovedDocuments(removedDocuments);
// remove created documents that were deleted later
final Set<Document> unusedCreatedDocuments = new HashSet<>(changesTracker.getRemovedDocuments());
unusedCreatedDocuments.retainAll(changesTracker.getCreatedDocuments());
changesTracker.getCreatedDocuments().removeAll(unusedCreatedDocuments);
// create new links
final List<LinkCreationOperation> linkCreationOperations = operations.stream().filter(operation -> operation instanceof LinkCreationOperation && operation.isComplete()).map(operation -> (LinkCreationOperation) operation).collect(toList());
final List<LinkInstance> createdLinks = createLinks(linkCreationOperations);
final Map<String, List<LinkInstance>> linksByType = LinkTypeUtils.getLinksByType(createdLinks);
final Map<String, LinkType> linkTypesMap = task.getDaoContextSnapshot().getLinkTypeDao().getLinkTypesByIds(linksByType.keySet()).stream().collect(Collectors.toMap(LinkType::getId, Function.identity()));
final Map<String, String> linkCorrelationIdsToIds = createdLinks.stream().collect(Collectors.toMap(LinkInstance::getTemplateId, LinkInstance::getId));
// report new empty links, later updates are sent separately
changesTracker.addCreatedLinkInstances(createdLinks);
changesTracker.addLinkTypes(linkTypesMap.values().stream().filter(c -> linksByType.containsKey(c.getId())).collect(toSet()));
// map the newly create link IDs to all other changes so that we use the correct document in updates etc.
operations.stream().filter(operation -> operation instanceof LinkOperation).forEach(operation -> {
final LinkInstance link = (LinkInstance) operation.getEntity();
if (StringUtils.isEmpty(link.getId()) && StringUtils.isNotEmpty(link.getTemplateId())) {
link.setId(linkCorrelationIdsToIds.get(link.getTemplateId()));
}
});
// commit link changes
final List<LinkInstance> changedLinkInstances = commitLinkOperations(taskExecutor, operations.stream().filter(operation -> operation instanceof LinkOperation && operation.isComplete()).map(operation -> (LinkOperation) operation).collect(toList()), createdLinks, linkTypesMap);
// report user messages, print, navigate, and send email requests for rules triggered via an Action button
final String correlationId = task.getAppId() != null ? task.getAppId().getValue() : null;
if (StringUtils.isNotEmpty(correlationId)) {
final List<UserMessageRequest> userMessageRequests = operations.stream().filter(operation -> operation instanceof UserMessageOperation).map(operation -> ((UserMessageOperation) operation).getEntity()).collect(toList());
changesTracker.addUserMessageRequests(userMessageRequests);
List<GenericPrintRequest> printRequests = operations.stream().filter(operation -> operation instanceof PrintAttributeOperation).map(operation -> ((PrintAttributeOperation) operation).getEntity()).collect(toList());
changesTracker.addPrintRequests(printRequests);
printRequests = operations.stream().filter(operation -> operation instanceof PrintTextOperation).map(operation -> ((PrintTextOperation) operation).getEntity()).collect(toList());
changesTracker.addPrintRequests(printRequests);
final List<NavigationRequest> navigationRequests = operations.stream().filter(operation -> operation instanceof NavigationOperation).map(operation -> ((NavigationOperation) operation).getEntity()).collect(toList());
changesTracker.addNavigationRequests(navigationRequests);
final List<SendEmailRequest> sendEmailRequests = operations.stream().filter(operation -> operation instanceof SendEmailOperation).map(operation -> ((SendEmailOperation) operation).getEntity()).collect(toList());
changesTracker.addSendEmailRequests(sendEmailRequests);
}
// propagate changes in existing documents and links that has been loaded prior to calling this rule
task.propagateChanges(changedDocuments, changedLinkInstances);
return changesTracker;
}
use of io.lumeer.core.task.executor.operation.LinkOperation in project engine by Lumeer.
the class LumeerBridge method setLinkAttribute.
@SuppressWarnings("unused")
public LinkOperation setLinkAttribute(final LinkBridge l, final String attrId, final Value value) {
try {
final LinkOperation operation = new LinkOperation(l.getLink(), attrId, convertValue(value));
operations.add(operation);
return operation;
} catch (Exception e) {
cause = e;
throw e;
}
}
use of io.lumeer.core.task.executor.operation.LinkOperation in project engine by Lumeer.
the class SingleStage method commitLinkOperations.
private List<LinkInstance> commitLinkOperations(final TaskExecutor taskExecutor, final List<LinkOperation> changes, final List<LinkInstance> createdLinks, final Map<String, LinkType> linkTypeMapForCreatedLinks) {
if (changes.isEmpty() && linkTypeMapForCreatedLinks.isEmpty()) {
return List.of();
}
final FunctionFacade functionFacade = task.getFunctionFacade();
final TaskProcessingFacade taskProcessingFacade = task.getTaskProcessingFacade(taskExecutor, functionFacade);
// LinkType -> [LinkInstance]
final Map<String, List<LinkInstance>> updatedLinks = new HashMap<>();
final Map<String, LinkType> linkTypesMap = task.getDaoContextSnapshot().getLinkTypeDao().getAllLinkTypes().stream().collect(Collectors.toMap(LinkType::getId, linkType -> linkType));
Set<String> linkTypesChanged = new HashSet<>();
Map<String, LinkInstance> linksByCorrelationId = createdLinks.stream().collect(Collectors.toMap(LinkInstance::getTemplateId, Function.identity()));
// aggregate all changes to individual link instances
final Map<String, List<LinkOperation>> changesByLinkTypeId = Utils.categorize(changes.stream(), change -> change.getEntity().getId());
final Set<String> unprocessedCreatedLinks = createdLinks.stream().map(LinkInstance::getId).collect(toSet());
changesByLinkTypeId.forEach((id, changeList) -> {
unprocessedCreatedLinks.remove(id);
final LinkInstance linkInstance = changeList.get(0).getEntity();
final LinkInstance originalLinkInstance = (task instanceof RuleTask) ? ((RuleTask) task).getOldLinkInstance() : ((task instanceof FunctionTask) ? ((FunctionTask) task).getOriginalLinkInstanceOrDefault(id, changeList.get(0).getOriginalLinkInstance()) : changeList.get(0).getOriginalLinkInstance());
final LinkType linkType = linkTypesMap.get(linkInstance.getLinkTypeId());
final DataDocument aggregatedUpdate = new DataDocument();
changeList.forEach(change -> aggregatedUpdate.put(change.getAttrId(), change.getValue()));
final DataDocument newData = constraintManager.encodeDataTypes(linkType, aggregatedUpdate);
final DataDocument oldData = originalLinkInstance != null ? new DataDocument(originalLinkInstance.getData()) : new DataDocument();
Set<String> attributesIdsToAdd = new HashSet<>(newData.keySet());
attributesIdsToAdd.removeAll(oldData.keySet());
if (attributesIdsToAdd.size() > 0) {
linkType.getAttributes().stream().filter(attr -> attributesIdsToAdd.contains(attr.getId())).forEach(attr -> {
attr.setUsageCount(attr.getUsageCount() + 1);
linkTypesChanged.add(linkType.getId());
});
}
linkInstance.setUpdatedBy(task.getInitiator().getId());
linkInstance.setUpdateDate(ZonedDateTime.now());
final DataDocument beforePatch = task.getDaoContextSnapshot().getLinkDataDao().getData(linkInstance.getLinkTypeId(), linkInstance.getId());
// thanks to an auto-link rule, the link could have ceased to exist
if (task.getDaoContextSnapshot().getLinkInstanceDao().getLinkInstances(Set.of(linkInstance.getId())).size() > 0) {
DataDocument patchedData = task.getDaoContextSnapshot().getLinkDataDao().patchData(linkInstance.getLinkTypeId(), linkInstance.getId(), newData);
LinkInstance updatedLink = task.getDaoContextSnapshot().getLinkInstanceDao().updateLinkInstance(linkInstance.getId(), linkInstance);
updatedLink.setData(patchedData);
var oldDataDecoded = constraintManager.decodeDataTypes(linkType, beforePatch);
var patchedDataDecoded = constraintManager.decodeDataTypes(linkType, patchedData);
auditAdapter.registerDataChange(updatedLink.getLinkTypeId(), ResourceType.LINK, updatedLink.getId(), task.getInitiator(), automationName, null, beforePatch, oldDataDecoded, patchedData, patchedDataDecoded);
// add patched data to new links
boolean created = false;
if (StringUtils.isNotEmpty(linkInstance.getTemplateId())) {
final LinkInstance link = linksByCorrelationId.get(linkInstance.getTemplateId());
if (link != null) {
link.setData(patchedData);
created = true;
}
}
if (task instanceof RuleTask) {
if (created) {
taskProcessingFacade.onCreateLink(new CreateLinkInstance(updatedLink));
} 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 UpdateLinkInstance updateLinkInstanceEvent;
final LinkInstance orig = new LinkInstance(linkInstance);
orig.setData(beforePatch);
updateLinkInstanceEvent = new UpdateLinkInstance(updatedLink, orig);
taskProcessingFacade.onUpdateLink(updateLinkInstanceEvent, ((RuleTask) task).getRule().getName());
} else {
taskExecutor.submitTask(functionFacade.creatTaskForChangedLink(linkType, originalLinkInstance, updatedLink, aggregatedUpdate.keySet()));
}
}
}
patchedData = constraintManager.decodeDataTypes(linkType, patchedData);
updatedLink.setData(patchedData);
updatedLinks.computeIfAbsent(linkInstance.getLinkTypeId(), key -> new ArrayList<>()).add(updatedLink);
}
});
unprocessedCreatedLinks.forEach(id -> {
createdLinks.stream().filter(l -> l.getId().equals(id)).findFirst().ifPresent(link -> {
taskProcessingFacade.onCreateLink(new CreateLinkInstance(link));
});
});
linkTypeMapForCreatedLinks.forEach((id, linkType) -> linkTypesChanged.add(id));
changesTracker.addLinkTypes(linkTypesChanged.stream().map(linkTypesMap::get).collect(toSet()));
changesTracker.addUpdatedLinkInstances(updatedLinks.values().stream().flatMap(java.util.Collection::stream).collect(toSet()));
changesTracker.updateLinkTypesMap(linkTypeMapForCreatedLinks);
changesTracker.updateLinkTypesMap(linkTypesMap);
linkTypesChanged.forEach(linkTypeId -> task.getDaoContextSnapshot().getLinkTypeDao().updateLinkType(linkTypeId, linkTypesMap.get(linkTypeId), null));
return updatedLinks.values().stream().flatMap(java.util.Collection::stream).collect(toList());
}
Aggregations