I'm creating Process with User Task with following code:
public void transformToNode(
final RuleFlowProcessFactory factory) {
factory.humanTaskNode(
getId())
.taskName(getType())
.name(getName())
.actorId(getActors())
.done();
}
But I want to change direct user assignment to group assignment, so it would be easier to manage resources in the future.
So how can I assign HumanTask to a group and add users to that group?
I see a method under that factory for 'swimlane' which should correspond to the task group.
Related
My work starts from Create Order.This task will be triggered from some GUI screen.
I have written a RestController which will be exposed to GUI.How can i map CreateOrderController to the Camunda CreateOrder task .And I need to pass OrderInfo object to the next task i.e."Place Order"
#RestController
public class CreateOrderController {
#PostMapping("/rest/create/order")
public String createOrder(#RequestBody OrderInfo orderInfo) {
System.out.println(" Order created with Order id " + orderInfo.getOrderId());
return "Order id created with " + orderInfo.getOrderId() ;
}
From within you controller, you can use the Java API
https://docs.camunda.org/javadoc/camunda-bpm-platform/7.18/org/camunda/bpm/engine/TaskService.html#complete(java.lang.String,java.util.Map)
if the controller is running in the same JVM. If the controller is running on a different environment you need to use the REST API:https://docs.camunda.org/manual/7.18/reference/rest/task/post-complete/
...but is the instance already running when you perform the user task?
It seems like you may want to remove the user task and instead start the process from your rest controller. For this you should look at:
https://docs.camunda.org/javadoc/camunda-bpm-platform/7.18/org/camunda/bpm/engine/RuntimeService.html#startProcessInstanceById(java.lang.String)
or
https://docs.camunda.org/manual/7.18/reference/rest/process-definition/post-start-process-instance/
You may also find the free training on Camunda Academy helpful:
https://academy.camunda.com/page/camunda-7
I am trying to implement a pure event sourced service to see where I will get problems. Now I found a problem that I can not solve so far, so I would like to open a discussion about it.
Given the following aggregate:
class User
{
public Guid Id { get; private set; }
public string Name { get; private set; }
public void Apply(UserNameChangedEvent domainEvent)
{
Name = domainEvent.NewName;
}
public void Apply(UserCreatedEvent domainEvent)
{
Name = domainEvent.Name;
Id = domainEvent.Id;
}
}
and those Domain Events
class UserCreatedEvent
{
public string NewName { get; }
public Guid Id { get; }
public UserCreatedEvent(string newName, Guid id)
{
NewName = newName;
Id = id;
}
}
class UserNameChangedEvent
{
public string NewName { get; }
public UserNameChangedEvent(string newName)
{
NewName = newName;
}
}
Lets say I create a user and change its name to "Peter" afterwards, then I have a UserCretedEvent and a UserChangedNameEvent persisted in my EventStore. Now the business says that changing a name is no longer possible and therefore I would remove the class UserChangedNameEvent and the function that handles it. But now I have the problem that I can not recreate the aggregate in its correct state, which would be with the name "Peter".
Of course I could start hacking around and mark the function and class as deprecated, so I could keep using it, but I might end up with a lot of event classes afterwards and this will be a nightmare to keep track of. I also heard you might create a new event that persists the change in the domain, but that also seems very hacky to me and not a very good style, as this is no domain event in my point of view.
So the question is, how do I deal the best with changes like this?
edit: just to clarify: I do not want to delete any event, just the class and the function where I use it, as the requirement is different now.
THE resource for questions related to changes in event schemas is Versioning in an Event Sourced System, by Greg Young.
So the question is, how do I deal the best with changes like this?
It depends on the real problem that you are trying to solve.
If the requirement is that users aren't allowed to change their names in the future, then you take away the logic in the domain model that creates new UserNameChangedEvents, but leave behind the correct processing where the events do appear.
If the requirement is that changes to user names should be ignored, then you also take the Apply(UserNameChanged) handler and turn it into a NoOp, just as you would for any other unrecognized event.
If the requirement is that information about name changes should be destroyed, then you migrate your event store to a new schema, that no longer includes the UserNameChanged event.
It may help to think through how you would solve the problem if you were storing your state in an RDBMS: is it enough to ignore the User Name column? do you need to delete the column? Do you need to (somehow) restore value in a column to a previously written value?
Knowing the problem in a traditional database that is analogous to the problem you want to solve in the event store should help identify the appropriate solution.
Also: pay attention to whether or not your domain model is the system of record for the data that needs changed, or if instead you are caching a representation of information published by a different authority.
Events captured facts about the system. If User name was changed at some point, it is a fact. Future business rule changes cannot affect past facts.
So you should not remove UserNameChanged events, and all associated handlers, events are there, and you should not rewrite past history.
In CQRS app, events are generated by command handlers. So this is a place where you specify business requirements. "Now the business says that changing a name is no longer possible" means that ChangeName command is no longer available: you can simply remove it, or just throw an error saying that you cannot change names anymore.
I am using activiti for my application.Here,when i'm assigning a task to the particular user sometimes the identity link type is as 'PARTICIPANT' and sometimes it is like 'CANDIDATE".In the modeler,for the user task i'm assigning the variable to assignee as
assignee = ${user} //In my case user = "kermit" or customUser
If the identity link type is 'participant' it has the process instance id.With the process instance id i'm getting the task as null.Is there any way to get the task.
val processEngine: ProcessEngine = ProcessEngineConfiguration.createStandaloneProcessEngineConfiguration.buildProcessEngine()
val taskService: TaskService = processEngine.getTaskService
val task = taskService.createTaskQuery().processInstanceId(idl.getProcessInstanceId).singleResult()
Can anyone please tell me how the identity link type will be generated during runtime as 'candidate' or 'participant'.
'candidate' is added when the user task has the 'candidate groups' or 'candidate users' set.
'participant' is set for a user that is the assignee, the task owner or the person who has completed the task (not necessarily the same as assignee/owner for the Activiti API).
It is also possible to add a custom user/group with these identityLink types to a task, using the taskService.addUserIdentityLink and taskService.addGroupIdentityLink method. The types are in the org.activiti.engine.task.IdentityLinkType class.
I am designing a workflow in activiti so far i have been able to design it like above.
My problem is
i start a work flow.
present user option to execute one of the two possible action(Archive and complete in diagram)
i also need to have authorization on whether he can archive or complete or both.
user can take one of these options.
based on one of the action taken workflow proceeds.
So far to achieve this i introduced user task new before complete and archive and added two form variables named archive and complete as boolean.
Depending on which form variable he chooses to fill i proceed further.
But in this case i can't restrict user based on whether it has permission of archive and complete and all users will be shown both options.
is there any other way to achieve this i am very new to activiti and workflow and bpmn in general.
Any help will be appreciated thanks in advance
1. How to presents possible transitions to user:
Set transitions directly to task and set transition id according this pattern:
<task_id>_<transition_id> that means in this case: newTask_archive and newTask_complete. Then you can read all transitions from task definition and parse the postfix from id and send to user list of possible transitions (complete, archive). Your bussines layer can remove any transition according user permissions.
// Source: http://forums.activiti.org/content/how-get-all-possible-flows-current-activity
public List<String> getPossibleTransitionIds(long processInstanceId, String taskId) {
RepositoryServiceImpl repoServiceImpl = (RepositoryServiceImpl) repositoryService;
List<String> possibleTransitionIds = new ArrayList<String>();
ReadOnlyProcessDefinition processDef = repoServiceImpl.getDeployedProcessDefinition(processInstance.getProcessDefinitionId());
PvmActivity activity = processDef.findActivity(taskId);
for (PvmTransition pvmTransition : activity.getOutgoingTransitions()) {
String transitionId = extractTransitionId(pvmTransition);
if (transitionId != null) {
possibleTransitionIds.add(transitionId);
}
}
return possibleTransitionIds;
}
2. How to move process by selected transition:
User selects one of presented transition ids. Bussines layer checks user's permissions and move process. Set selected transition to process variables and resolve task.
Map<String, Object> variableMap = new HashMap<String, Object>();
variableMap.put("selectedTransition", selectedTransition);
taskService.resolveTask(taskId, variableMap);
In every transition has to be set a condition expression ${selectedTransition == '<transition_id>'}. In this case ${selectedTransition == 'complete'} and ${selectedTransition == 'archive'}
<sequenceFlow id="newTask_complete" name="Complete" sourceRef="newTask" targetRef="completeTask">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${selectedTransition == 'complete'}]]></conditionExpression>
</sequenceFlow>
i got a question regarding sharepoint workflows and event receivers. i got an event receiver that is setting metadata on an element. after that, i use a workflow to copy item metadata to a list. unfortunately the workflow does not copy the metadata set by the event receiver. i think because it is executed before the event receiver. is there a possibility to change the order, so that the workflow will execute after the event receiver? the receiver ist bound to the ItemAdded and ItemUpdated Events i a syncrounous manner.
Thank you for your help!
Patrick
You can use SPWorkFlowAssociation to run workflow that associate with List or Content Type .
Example ( run workflow after adding item)
public override void ItemAdded(SPItemEventProperties properties)
{
SPList parentList = properties.ListItem.ParentList;
SPWorkflowAssociation associationTemplate =
parentList.WorkflowAssociations.GetAssociationByName("Your Workflow Name",
new CultureInfo
(Convert.ToInt32(parentList.ParentWeb.RegionalSettings.LocaleId)));
SPSite siteCollection = properties.ListItem.ParentList.ParentWeb.Site;
siteCollection.WorkflowManager.StartWorkflow(properties.ListItem,
associationTemplate, String.Empty);
}
More information about SPWorkflowAssociation Check the below link
http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.workflow.spworkflowassociation.aspx
SPListItem:
The "Synchronous" events (-ing ending like ItemAdd*ing*), are always executed before the workflow.
The "Asynchronous" events (-ed ending like ItemAdd*ed*), are always executed after the execution of the workflow.
So, you have to set the "Synchronization" property of the Elements.xml file equal to "Synchronous" and the workflow will always be executed after the event receiver.
ATTENTION: Events Added and Updated run asynchronously by default, so you have to do the change in the Elements.xml .