pefernan commented on code in PR #4164:
URL:
https://github.com/apache/incubator-kie-kogito-runtimes/pull/4164#discussion_r2741118676
##########
jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/lifecycle/WsHumanTaskLifeCycle.java:
##########
@@ -361,16 +379,98 @@ public Optional<UserTaskTransitionToken>
fail(UserTaskInstance userTaskInstance,
private Optional<UserTaskTransitionToken> suspend(UserTaskInstance
userTaskInstance, UserTaskTransitionToken token, IdentityProvider
identityProvider) {
if (userTaskInstance.getStatus() != null) {
+ var suspendUntil = token.data().get(PARAMETER_SUSPEND_UNTIL) !=
null ? (String) token.data().get(PARAMETER_SUSPEND_UNTIL) : (String)
userTaskInstance.getMetadata().get(SUSPEND_UNTIL);
+
+ if (suspendUntil != null) {
+ ZonedDateTime expirationTime =
parseTemporalValue(suspendUntil);
+
+ UserTaskInstanceJobDescription jobDescription =
UserTaskInstanceJobDescription.newUserTaskInstanceJobDescriptionBuilder()
+ .id(UUID.randomUUID().toString())
+ .expirationTime(ExactExpirationTime.of(expirationTime))
+ .userTaskInstanceId(userTaskInstance.getId())
+
.processId(userTaskInstance.getProcessInfo().getProcessId())
+
.processInstanceId(userTaskInstance.getProcessInfo().getProcessInstanceId())
+
.rootProcessInstanceId(userTaskInstance.getProcessInfo().getRootProcessInstanceId())
+
.rootProcessId(userTaskInstance.getProcessInfo().getRootProcessId())
+ .metadata(userTaskInstance.getMetadata())
+ .build();
+
+ LOG.debug("Suspending usertask with id {} until {}",
userTaskInstance.getId(), expirationTime);
+
+ String jobId =
userTaskInstance.getJobsService().scheduleJob(jobDescription);
+ userTaskInstance.getMetadata().put(SUSPENDED_TASK_JOB_ID,
jobId);
+ }
+
userTaskInstance.getMetadata().put("PreviousStatus",
userTaskInstance.getStatus().getName());
}
return Optional.empty();
}
+ private static ZonedDateTime parseTemporalValue(String input) {
+ if (input == null || input.isBlank()) {
Review Comment:
We are doing something simliar in Deadline helper. wouldn't it be possible
to reuse that?
##########
api/kogito-api/src/main/java/org/kie/kogito/usertask/UserTaskInstance.java:
##########
@@ -155,4 +156,6 @@ public interface UserTaskInstance {
void setPotentialUsers(Set<String> potentialUsers);
UserTaskLifeCycle getUserTaskLifeCycle();
+
+ JobsService getJobsService();
Review Comment:
I'd not add this in the public API, I think it's safe to keep the getter in
the `DefaultUserTaskInstance` and do the cast (as we do in other places) in the
lifecycle tiself.
##########
jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/lifecycle/WsHumanTaskLifeCycle.java:
##########
@@ -361,16 +379,98 @@ public Optional<UserTaskTransitionToken>
fail(UserTaskInstance userTaskInstance,
private Optional<UserTaskTransitionToken> suspend(UserTaskInstance
userTaskInstance, UserTaskTransitionToken token, IdentityProvider
identityProvider) {
if (userTaskInstance.getStatus() != null) {
+ var suspendUntil = token.data().get(PARAMETER_SUSPEND_UNTIL) !=
null ? (String) token.data().get(PARAMETER_SUSPEND_UNTIL) : (String)
userTaskInstance.getMetadata().get(SUSPEND_UNTIL);
+
+ if (suspendUntil != null) {
+ ZonedDateTime expirationTime =
parseTemporalValue(suspendUntil);
+
+ UserTaskInstanceJobDescription jobDescription =
UserTaskInstanceJobDescription.newUserTaskInstanceJobDescriptionBuilder()
+ .id(UUID.randomUUID().toString())
+ .expirationTime(ExactExpirationTime.of(expirationTime))
+ .userTaskInstanceId(userTaskInstance.getId())
+
.processId(userTaskInstance.getProcessInfo().getProcessId())
+
.processInstanceId(userTaskInstance.getProcessInfo().getProcessInstanceId())
+
.rootProcessInstanceId(userTaskInstance.getProcessInfo().getRootProcessInstanceId())
+
.rootProcessId(userTaskInstance.getProcessInfo().getRootProcessId())
+ .metadata(userTaskInstance.getMetadata())
+ .build();
+
+ LOG.debug("Suspending usertask with id {} until {}",
userTaskInstance.getId(), expirationTime);
+
+ String jobId =
userTaskInstance.getJobsService().scheduleJob(jobDescription);
+ userTaskInstance.getMetadata().put(SUSPENDED_TASK_JOB_ID,
jobId);
+ }
+
userTaskInstance.getMetadata().put("PreviousStatus",
userTaskInstance.getStatus().getName());
}
return Optional.empty();
}
+ private static ZonedDateTime parseTemporalValue(String input) {
+ if (input == null || input.isBlank()) {
+ throw new IllegalArgumentException("suspendUntil cannot be null or
empty");
+ }
+
+ try {
+ var timestamp = ZonedDateTime.parse(input);
+ var now = ZonedDateTime.now();
+ if (timestamp.isAfter(now)) {
+ return timestamp;
+ }
+
+ return parseDuration(input);
+ } catch (DateTimeParseException e) {
+ return parseDuration(input);
+ }
+ }
+
+ private static ZonedDateTime parseDuration(String input) {
+ try {
+ var duration = Duration.parse(input);
+ if (duration.isNegative() || duration.isZero()) {
+ throw new IllegalArgumentException("Invalid suspendUntil
duration: " + input);
+ }
+
+ return ZonedDateTime.now().plus(duration);
+ } catch (DateTimeParseException e) {
+ return parseSimpleDuration(input);
+ }
+ }
+
+ private static ZonedDateTime parseSimpleDuration(String input) {
+ try {
+ var millis = parseTimeString(input);
+ if (millis <= 0) {
+ throw new IllegalArgumentException("Invalid suspendUntil
duration: " + input);
+ }
+
+ return ZonedDateTime.now().plus(Duration.ofMillis(millis));
+ } catch (RuntimeException e) {
+ throw new IllegalArgumentException("Invalid suspendUntil duration:
" + input);
+ }
+ }
+
private Optional<UserTaskTransitionToken> resume(UserTaskInstance
userTaskInstance, UserTaskTransitionToken token, IdentityProvider
identityProvider) {
userTaskInstance.getMetadata().remove("PreviousStatus");
+
+ var suspendedTaskJobId = (String)
userTaskInstance.getMetadata().get("SuspendedTaskJobId");
Review Comment:
Why not using the `SUSPENDED_TASK_JOB_ID` constant?
##########
jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/DefaultUserTaskInstance.java:
##########
@@ -771,13 +775,23 @@ public void stopNotCompletedReassignments() {
public void trigger(UserTaskInstanceJobDescription jobDescription) {
LOG.trace("trigger timer in user tasks {} and job {}", this,
jobDescription);
+ resumeIfSuspensionExpired(jobDescription);
checkAndSendNotification(jobDescription, notStartedDeadlinesTimers,
this::startNotification);
checkAndSendNotification(jobDescription, notCompletedDeadlinesTimers,
this::endNotification);
checkAndReassign(jobDescription, notStartedReassignmentsTimers);
checkAndReassign(jobDescription, notCompletedReassignmentsTimers);
this.updatePersistence();
}
+ private void resumeIfSuspensionExpired(UserTaskInstanceJobDescription
jobDescription) {
+ var jobId = (String) getMetadata().get("SuspendedTaskJobId");
Review Comment:
I think that this should be handled in the lifecycle since it's been the one
that scheduled the job, otherwise we are giving the task knowledge of the
lifecycle itself.
Maybe could have method on the lifecycle to handle timers something
`lifecycle.handleTimer(jobDescription)` the interface may have a default
implementation that does nothing, and the WS implements the transition based on
what we have in the metadata.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]