Add execution rerun history groups
This commit is contained in:
parent
e12ce82406
commit
163ba9afc2
|
|
@ -35,6 +35,7 @@ import it.cnr.isti.workflow.manager.executions.ExecutionAuthorizationValueReques
|
|||
import it.cnr.isti.workflow.manager.executions.ExecutionSimulationRequest;
|
||||
import it.cnr.isti.workflow.manager.executions.ExecutionsService;
|
||||
import it.cnr.isti.workflow.manager.executions.api.ExecutionContextView;
|
||||
import it.cnr.isti.workflow.manager.executions.api.ExecutionGroupView;
|
||||
import it.cnr.isti.workflow.manager.executions.api.ExecutionView;
|
||||
import it.cnr.isti.workflow.manager.auth.repo.LoginEntity;
|
||||
import it.cnr.isti.workflow.manager.flows.repo.FlowEntity;
|
||||
|
|
@ -112,7 +113,7 @@ public class ExecutionsController {
|
|||
FlowEntity flow = flowRepository.findById(flowId)
|
||||
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Flow with id " + flowId + " not found"));
|
||||
try{
|
||||
ExecutionObject eo = executionService.createExecution(flow.getName(), flow.getFlow(),
|
||||
ExecutionObject eo = executionService.createExecutionForFlow(flow.getId(), flow.getName(), flow.getFlow(),
|
||||
userDetails == null ? null : userDetails.getUsername());
|
||||
return ExecutionView.fromExecution(eo);
|
||||
} catch (ResponseStatusException e) {
|
||||
|
|
@ -150,6 +151,18 @@ public class ExecutionsController {
|
|||
.map(ExecutionView::fromExecution);
|
||||
}
|
||||
|
||||
@Operation(summary = "Retrieves execution groups",
|
||||
description = "Retrieves all execution history groups for the authenticated user, including their executions.")
|
||||
@GetMapping(path = "groups")
|
||||
public List<ExecutionGroupView> getGroups(@AuthenticationPrincipal LoginEntity userDetails) {
|
||||
if (userDetails == null) {
|
||||
throw new ResponseStatusException(HttpStatus.FORBIDDEN, "Authentication required");
|
||||
}
|
||||
return executionService.getExecutionGroupsByOwner(userDetails.getUsername()).stream()
|
||||
.map(group -> ExecutionGroupView.fromExecutions(group.groupId(), group.executions()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -192,6 +205,18 @@ public class ExecutionsController {
|
|||
return visibleExecution(id, userDetails).getContext().getEvents();
|
||||
}
|
||||
|
||||
@GetMapping(path = "{id}/history")
|
||||
@Operation(summary = "Retrieves execution history",
|
||||
description = "Retrieves all executions belonging to the same rerun history as the selected execution.")
|
||||
public List<ExecutionView> getHistory(@PathVariable String id, @AuthenticationPrincipal LoginEntity userDetails) {
|
||||
if (userDetails == null) {
|
||||
throw new ResponseStatusException(HttpStatus.FORBIDDEN, "Authentication required");
|
||||
}
|
||||
return executionService.getExecutionHistoryByOwner(id, userDetails.getUsername()).stream()
|
||||
.map(ExecutionView::fromExecution)
|
||||
.toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an execution with the specified ID.
|
||||
*
|
||||
|
|
@ -239,6 +264,16 @@ public class ExecutionsController {
|
|||
return ExecutionView.fromExecution(executionService.cancelExecution(visibleExecution(id, userDetails).getId()));
|
||||
}
|
||||
|
||||
@PostMapping(path = "{id}/rerun")
|
||||
@Operation(summary = "Creates a rerun from a final execution",
|
||||
description = "Creates a new execution with the same workflow inputs and links it to the source execution history. The source execution must be in a final state.")
|
||||
public ExecutionView rerun(@PathVariable String id, @AuthenticationPrincipal LoginEntity userDetails) {
|
||||
if (userDetails == null) {
|
||||
throw new ResponseStatusException(HttpStatus.FORBIDDEN, "Authentication required");
|
||||
}
|
||||
return ExecutionView.fromExecution(executionService.rerunExecution(id, userDetails.getUsername()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prepares the input for an execution by associating a given input string
|
||||
|
|
|
|||
|
|
@ -51,6 +51,14 @@ public class ExecutionObject {
|
|||
|
||||
String owner;
|
||||
|
||||
String runGroupId;
|
||||
|
||||
String sourceFlowId;
|
||||
|
||||
String rerunOfExecutionId;
|
||||
|
||||
int runNumber = 1;
|
||||
|
||||
@JsonIgnore
|
||||
FlowData flow;
|
||||
|
||||
|
|
@ -71,9 +79,15 @@ public class ExecutionObject {
|
|||
|
||||
@Builder
|
||||
public ExecutionObject(String executionName, FlowData flow, List<ExecutionAuthorizationRequirement> requiredAuthorizations,
|
||||
String owner) {
|
||||
String owner, String runGroupId, String sourceFlowId, String rerunOfExecutionId, Integer runNumber) {
|
||||
this.name = executionName;
|
||||
this.owner = owner;
|
||||
this.sourceFlowId = sourceFlowId;
|
||||
this.runGroupId = runGroupId == null || runGroupId.isBlank()
|
||||
? sourceFlowId == null || sourceFlowId.isBlank() ? this.id : sourceFlowId
|
||||
: runGroupId;
|
||||
this.rerunOfExecutionId = rerunOfExecutionId;
|
||||
this.runNumber = runNumber == null || runNumber < 1 ? 1 : runNumber;
|
||||
this.flow = flow;
|
||||
this.stepConnections = flow.getConnections() == null ? List.of() : List.copyOf(flow.getConnections());
|
||||
this.stepDependencies = flow.getDependencies() == null ? List.of() : List.copyOf(flow.getDependencies());
|
||||
|
|
@ -333,6 +347,9 @@ public class ExecutionObject {
|
|||
public void restore(String executionId, long creationTime, Map<String, Object> providedAuthorizations,
|
||||
ExecutionSnapshot snapshot) {
|
||||
this.id = executionId;
|
||||
if (this.runGroupId == null || this.runGroupId.isBlank()) {
|
||||
this.runGroupId = executionId;
|
||||
}
|
||||
this.creationTime = creationTime;
|
||||
this.providedAuthorizations.clear();
|
||||
if (providedAuthorizations != null) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package it.cnr.isti.workflow.manager.executions;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
|
@ -95,6 +96,18 @@ public class ExecutionsService {
|
|||
|
||||
@Transactional
|
||||
public ExecutionObject createExecution(String executionName, FlowData flow, String owner) {
|
||||
return createExecution(executionName, flow, owner, null, null, null, 1);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public ExecutionObject createExecutionForFlow(String sourceFlowId, String executionName, FlowData flow, String owner) {
|
||||
int runNumber = nextRunNumberForFlow(sourceFlowId, owner);
|
||||
return createExecution(executionName, flow, owner, sourceFlowId, sourceFlowId, null, runNumber);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public ExecutionObject createExecution(String executionName, FlowData flow, String owner, String runGroupId,
|
||||
String sourceFlowId, String rerunOfExecutionId, Integer runNumber) {
|
||||
flowExecutionValidator.validate(flow);
|
||||
List<ExecutionAuthorizationRequirement> requiredAuthorizations = resolveRequiredAuthorizations(flow);
|
||||
ExecutionObject execObject = ExecutionObject.builder()
|
||||
|
|
@ -102,6 +115,10 @@ public class ExecutionsService {
|
|||
.flow(flow)
|
||||
.requiredAuthorizations(requiredAuthorizations)
|
||||
.owner(owner)
|
||||
.runGroupId(runGroupId)
|
||||
.sourceFlowId(sourceFlowId)
|
||||
.rerunOfExecutionId(rerunOfExecutionId)
|
||||
.runNumber(runNumber)
|
||||
.build();
|
||||
attachPersistence(execObject);
|
||||
executions.put(execObject.getId(), execObject);
|
||||
|
|
@ -197,7 +214,85 @@ public class ExecutionsService {
|
|||
return page;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Transactional(readOnly = true)
|
||||
public List<ExecutionGroup> getExecutionGroupsByOwner(String owner) {
|
||||
if (owner == null || owner.isBlank()) {
|
||||
throw new ResponseStatusException(HttpStatus.FORBIDDEN, "Authentication required");
|
||||
}
|
||||
|
||||
Map<String, List<ExecutionObject>> groups = executionRepository.findByOwner(owner).stream()
|
||||
.map(entity -> executions.computeIfAbsent(entity.getId(), ignored -> rebuildExecution(entity)))
|
||||
.sorted(Comparator
|
||||
.comparingInt(ExecutionObject::getRunNumber)
|
||||
.thenComparingLong(ExecutionObject::getCreationTime))
|
||||
.collect(Collectors.groupingBy(
|
||||
this::resolveHistoryGroupId,
|
||||
LinkedHashMap::new,
|
||||
Collectors.toList()));
|
||||
|
||||
List<ExecutionGroup> executionGroups = groups.entrySet().stream()
|
||||
.map(entry -> new ExecutionGroup(entry.getKey(), entry.getValue()))
|
||||
.sorted(Comparator
|
||||
.comparingLong((ExecutionGroup group) -> group.executions().get(group.executions().size() - 1)
|
||||
.getCreationTime())
|
||||
.reversed())
|
||||
.toList();
|
||||
executionGroups.forEach(group -> group.executions()
|
||||
.forEach(execution -> touchExecution(execution.getId())));
|
||||
evictIfNeeded();
|
||||
return executionGroups;
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public List<ExecutionObject> getExecutionHistoryByOwner(String id, String owner) {
|
||||
if (owner == null || owner.isBlank()) {
|
||||
throw new ResponseStatusException(HttpStatus.FORBIDDEN, "Authentication required");
|
||||
}
|
||||
ExecutionEntity source = executionRepository.findByIdAndOwner(id, owner)
|
||||
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND,
|
||||
"Execution with id " + id + " not found"));
|
||||
String sourceFlowId = resolveSourceFlowId(source);
|
||||
String runGroupId = resolveRunGroupId(source);
|
||||
List<ExecutionEntity> history = sourceFlowId == null
|
||||
? executionRepository.findByRunGroupIdAndOwnerOrderByRunNumberAscCreationTimeAsc(runGroupId, owner)
|
||||
: executionRepository.findBySourceFlowIdAndOwnerOrderByRunNumberAscCreationTimeAsc(sourceFlowId, owner);
|
||||
if (history.stream().noneMatch(entity -> entity.getId().equals(source.getId()))) {
|
||||
history = new java.util.ArrayList<>(history);
|
||||
history.add(source);
|
||||
history.sort(java.util.Comparator
|
||||
.comparing((ExecutionEntity entity) -> resolveRunNumber(entity))
|
||||
.thenComparingLong(ExecutionEntity::getCreationTime));
|
||||
}
|
||||
List<ExecutionObject> loaded = history.stream()
|
||||
.map(entity -> executions.computeIfAbsent(entity.getId(), ignored -> rebuildExecution(entity)))
|
||||
.toList();
|
||||
loaded.forEach(execution -> touchExecution(execution.getId()));
|
||||
evictIfNeeded();
|
||||
return loaded;
|
||||
}
|
||||
|
||||
public record ExecutionGroup(String groupId, List<ExecutionObject> executions) {
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public ExecutionObject rerunExecution(String id, String owner) {
|
||||
ExecutionObject source = getExecutionByOwner(id, owner);
|
||||
if (!source.getContext().getStatus().isFinalState()) {
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
|
||||
"Execution with id " + id + " can be rerun only from a final state");
|
||||
}
|
||||
|
||||
String runGroupId = resolveHistoryGroupId(source);
|
||||
int nextRunNumber = nextRunNumber(source.getSourceFlowId(), runGroupId, owner, source.getRunNumber());
|
||||
|
||||
ExecutionObject rerun = createExecution(source.getName(), source.getFlow(), owner, runGroupId,
|
||||
source.getSourceFlowId(), source.getId(), nextRunNumber);
|
||||
copyReusableInputs(source, rerun);
|
||||
persist(rerun);
|
||||
return rerun;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void removeExecution(String id) {
|
||||
ExecutionObject execution = executions.get(id);
|
||||
if (execution == null && executionRepository.existsById(id)) {
|
||||
|
|
@ -517,6 +612,10 @@ public class ExecutionsService {
|
|||
.owner(executionObject.getOwner())
|
||||
.creationTime(executionObject.getCreationTime())
|
||||
.lastUpdateTime(System.currentTimeMillis())
|
||||
.runGroupId(executionObject.getRunGroupId())
|
||||
.sourceFlowId(executionObject.getSourceFlowId())
|
||||
.rerunOfExecutionId(executionObject.getRerunOfExecutionId())
|
||||
.runNumber(executionObject.getRunNumber())
|
||||
.flow(executionObject.getFlow())
|
||||
.snapshot(executionObject.snapshot())
|
||||
.build());
|
||||
|
|
@ -530,6 +629,10 @@ public class ExecutionsService {
|
|||
.flow(flow)
|
||||
.requiredAuthorizations(resolveRequiredAuthorizations(flow))
|
||||
.owner(entity.getOwner())
|
||||
.runGroupId(resolveRunGroupId(entity))
|
||||
.sourceFlowId(resolveSourceFlowId(entity))
|
||||
.rerunOfExecutionId(entity.getRerunOfExecutionId())
|
||||
.runNumber(resolveRunNumber(entity))
|
||||
.build();
|
||||
ExecutionSnapshot snapshot = entity.getSnapshot();
|
||||
executionObject.restore(entity.getId(), entity.getCreationTime(),
|
||||
|
|
@ -539,6 +642,78 @@ public class ExecutionsService {
|
|||
return executionObject;
|
||||
}
|
||||
|
||||
private void copyReusableInputs(ExecutionObject source, ExecutionObject rerun) {
|
||||
source.getContext().getInputs().forEach((key, value) -> rerun.setInput(key.nodeId(), key.fieldId(), value));
|
||||
rerun.setGlobalInputDescriptors(source.getContext().getGlobalInputDescriptors());
|
||||
rerun.setExecutionVariableDescriptors(filterReusableExecutionVariableDescriptors(
|
||||
source.getContext().getExecutionVariableDescriptors()));
|
||||
source.getProvidedAuthorizations().forEach(rerun::setAuthorization);
|
||||
if (source.getInteractionSimulationDescriptor() != null) {
|
||||
rerun.setInteractionSimulationDescriptor(source.getInteractionSimulationDescriptor());
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, ExecutionVariableDescriptor> filterReusableExecutionVariableDescriptors(
|
||||
Map<String, ExecutionVariableDescriptor> descriptors) {
|
||||
if (descriptors == null || descriptors.isEmpty()) {
|
||||
return Map.of();
|
||||
}
|
||||
return descriptors.entrySet().stream()
|
||||
.filter(entry -> entry.getValue() != null)
|
||||
.filter(entry -> entry.getValue().getKind() != ExecutionVariableKind.MCP_SESSION)
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
}
|
||||
|
||||
private String resolveRunGroupId(ExecutionEntity entity) {
|
||||
if (entity.getRunGroupId() == null || entity.getRunGroupId().isBlank()) {
|
||||
String sourceFlowId = resolveSourceFlowId(entity);
|
||||
return sourceFlowId == null ? entity.getId() : sourceFlowId;
|
||||
}
|
||||
return entity.getRunGroupId();
|
||||
}
|
||||
|
||||
private String resolveHistoryGroupId(ExecutionObject execution) {
|
||||
if (execution.getSourceFlowId() != null && !execution.getSourceFlowId().isBlank()) {
|
||||
return execution.getSourceFlowId();
|
||||
}
|
||||
if (execution.getRunGroupId() != null && !execution.getRunGroupId().isBlank()) {
|
||||
return execution.getRunGroupId();
|
||||
}
|
||||
return execution.getId();
|
||||
}
|
||||
|
||||
private int resolveRunNumber(ExecutionEntity entity) {
|
||||
return entity.getRunNumber() == null || entity.getRunNumber() < 1 ? 1 : entity.getRunNumber();
|
||||
}
|
||||
|
||||
private String resolveSourceFlowId(ExecutionEntity entity) {
|
||||
if (entity.getSourceFlowId() == null || entity.getSourceFlowId().isBlank()) {
|
||||
return null;
|
||||
}
|
||||
return entity.getSourceFlowId();
|
||||
}
|
||||
|
||||
private int nextRunNumberForFlow(String sourceFlowId, String owner) {
|
||||
if (sourceFlowId == null || sourceFlowId.isBlank() || owner == null || owner.isBlank()) {
|
||||
return 1;
|
||||
}
|
||||
return executionRepository.findBySourceFlowIdAndOwnerOrderByRunNumberAscCreationTimeAsc(sourceFlowId, owner)
|
||||
.stream()
|
||||
.mapToInt(this::resolveRunNumber)
|
||||
.max()
|
||||
.orElse(0) + 1;
|
||||
}
|
||||
|
||||
private int nextRunNumber(String sourceFlowId, String runGroupId, String owner, int fallbackRunNumber) {
|
||||
List<ExecutionEntity> history = sourceFlowId == null || sourceFlowId.isBlank()
|
||||
? executionRepository.findByRunGroupIdAndOwnerOrderByRunNumberAscCreationTimeAsc(runGroupId, owner)
|
||||
: executionRepository.findBySourceFlowIdAndOwnerOrderByRunNumberAscCreationTimeAsc(sourceFlowId, owner);
|
||||
return history.stream()
|
||||
.mapToInt(this::resolveRunNumber)
|
||||
.max()
|
||||
.orElse(fallbackRunNumber) + 1;
|
||||
}
|
||||
|
||||
private void cleanupManagedResourcesIfFinal(ExecutionObject executionObject) {
|
||||
if (executionObject == null || !executionObject.getContext().getStatus().isFinalState()) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
package it.cnr.isti.workflow.manager.executions.api;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import it.cnr.isti.workflow.manager.executions.ExecutionObject;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@Builder
|
||||
public class ExecutionGroupView {
|
||||
|
||||
private String id;
|
||||
private String sourceFlowId;
|
||||
private String name;
|
||||
private String firstExecutionId;
|
||||
private String latestExecutionId;
|
||||
private long creationTime;
|
||||
private long lastExecutionTime;
|
||||
private int executionCount;
|
||||
private List<ExecutionView> executions;
|
||||
|
||||
public static ExecutionGroupView fromExecutions(String groupId, List<ExecutionObject> executions) {
|
||||
if (executions == null || executions.isEmpty()) {
|
||||
return ExecutionGroupView.builder()
|
||||
.id(groupId)
|
||||
.executionCount(0)
|
||||
.executions(List.of())
|
||||
.build();
|
||||
}
|
||||
|
||||
ExecutionObject first = executions.get(0);
|
||||
ExecutionObject latest = executions.get(executions.size() - 1);
|
||||
return ExecutionGroupView.builder()
|
||||
.id(groupId)
|
||||
.sourceFlowId(first.getSourceFlowId())
|
||||
.name(latest.getName())
|
||||
.firstExecutionId(first.getId())
|
||||
.latestExecutionId(latest.getId())
|
||||
.creationTime(first.getCreationTime())
|
||||
.lastExecutionTime(latest.getCreationTime())
|
||||
.executionCount(executions.size())
|
||||
.executions(executions.stream().map(ExecutionView::fromExecution).toList())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
@ -21,6 +21,10 @@ public class ExecutionView {
|
|||
private List<Dependency> stepDependencies;
|
||||
private long creationTime;
|
||||
private String name;
|
||||
private String runGroupId;
|
||||
private String sourceFlowId;
|
||||
private String rerunOfExecutionId;
|
||||
private int runNumber;
|
||||
private List<ExecutionAuthorizationRequirement> requiredAuthorizations;
|
||||
private Map<String, Object> providedAuthorizations;
|
||||
private boolean interactionSimulationEnabled;
|
||||
|
|
@ -37,6 +41,10 @@ public class ExecutionView {
|
|||
.stepDependencies(execution.getStepDependencies())
|
||||
.creationTime(execution.getCreationTime())
|
||||
.name(execution.getName())
|
||||
.runGroupId(execution.getRunGroupId())
|
||||
.sourceFlowId(execution.getSourceFlowId())
|
||||
.rerunOfExecutionId(execution.getRerunOfExecutionId())
|
||||
.runNumber(execution.getRunNumber())
|
||||
.requiredAuthorizations(execution.getRequiredAuthorizations())
|
||||
.providedAuthorizations(execution.getProvidedAuthorizations())
|
||||
.interactionSimulationEnabled(execution.isInteractionSimulationEnabled())
|
||||
|
|
|
|||
|
|
@ -33,6 +33,14 @@ public class ExecutionEntity {
|
|||
|
||||
private long lastUpdateTime;
|
||||
|
||||
private String runGroupId;
|
||||
|
||||
private String sourceFlowId;
|
||||
|
||||
private String rerunOfExecutionId;
|
||||
|
||||
private Integer runNumber;
|
||||
|
||||
@Column(name = "flow_data", columnDefinition = "TEXT")
|
||||
@Convert(converter = FlowConverter.class)
|
||||
private FlowData flow;
|
||||
|
|
|
|||
|
|
@ -11,5 +11,7 @@ public interface ExecutionRepository extends JpaRepository<ExecutionEntity, Stri
|
|||
List<ExecutionEntity> findByOwner(String owner);
|
||||
Page<ExecutionEntity> findByOwner(String owner, Pageable pageable);
|
||||
Optional<ExecutionEntity> findByIdAndOwner(String id, String owner);
|
||||
List<ExecutionEntity> findByRunGroupIdAndOwnerOrderByRunNumberAscCreationTimeAsc(String runGroupId, String owner);
|
||||
List<ExecutionEntity> findBySourceFlowIdAndOwnerOrderByRunNumberAscCreationTimeAsc(String sourceFlowId, String owner);
|
||||
void deleteByOwner(String owner);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import it.cnr.isti.workflow.manager.executions.ExecutionSimulationRequest;
|
|||
import it.cnr.isti.workflow.manager.executions.ExecutionStatus;
|
||||
import it.cnr.isti.workflow.manager.executions.ExecutionAuthorizationValueRequest;
|
||||
import it.cnr.isti.workflow.manager.executions.ExecutionsService;
|
||||
import it.cnr.isti.workflow.manager.executions.api.ExecutionGroupView;
|
||||
import it.cnr.isti.workflow.manager.executions.api.ExecutionView;
|
||||
import it.cnr.isti.workflow.manager.flows.model.Connection;
|
||||
import it.cnr.isti.workflow.manager.flows.model.Dependency;
|
||||
|
|
@ -347,6 +348,149 @@ public class ExecutionControllerTest {
|
|||
org.junit.jupiter.api.Assertions.assertTrue(events.stream().anyMatch(event -> event.getType() == ExecutionEventType.LLM_REQUEST));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rerunRequiresFinalStateAndCreatesHistoryEntry() {
|
||||
LLMDescriptor llmDescriptor = LLMDescriptor.builder()
|
||||
.provider("testProvider")
|
||||
.model("testModel")
|
||||
.build();
|
||||
|
||||
Block<LLMBlockType> block = blocksController.create(LLMBlockConfiguration.builder()
|
||||
.prompt("Hello, ${{name}}!")
|
||||
.name("rerun-block")
|
||||
.llmDescriptor(llmDescriptor)
|
||||
.build());
|
||||
|
||||
FlowCreateRequest request = new FlowCreateRequest(
|
||||
"Rerun Flow",
|
||||
"Execution rerun history",
|
||||
FlowData.builder().block(block).build());
|
||||
ResponseEntity<FlowView> createdFlow = flowController.createFlow(request, testUser());
|
||||
ExecutionView execution = executionsController.create(createdFlow.getBody().id(), testUser());
|
||||
|
||||
org.junit.jupiter.api.Assertions.assertEquals(1, execution.getRunNumber());
|
||||
org.junit.jupiter.api.Assertions.assertEquals(createdFlow.getBody().id(), execution.getSourceFlowId());
|
||||
org.junit.jupiter.api.Assertions.assertEquals(createdFlow.getBody().id(), execution.getRunGroupId());
|
||||
|
||||
String executionId = execution.getId();
|
||||
ResponseStatusException nonFinalRerun = org.junit.jupiter.api.Assertions.assertThrows(
|
||||
ResponseStatusException.class,
|
||||
() -> executionsController.rerun(executionId, testUser()));
|
||||
org.junit.jupiter.api.Assertions.assertEquals(HttpStatus.BAD_REQUEST, nonFinalRerun.getStatusCode());
|
||||
|
||||
execution = executionsController.prepareStringInputs(execution.getId(), block.getId(), "name", "Ada", testUser());
|
||||
execution = executionsController.start(execution.getId(), testUser());
|
||||
waitForExecutionStatus(execution, ExecutionStatus.SUCCESS);
|
||||
|
||||
ExecutionView rerun = executionsController.rerun(execution.getId(), testUser());
|
||||
|
||||
org.junit.jupiter.api.Assertions.assertNotEquals(execution.getId(), rerun.getId());
|
||||
org.junit.jupiter.api.Assertions.assertEquals(createdFlow.getBody().id(), rerun.getSourceFlowId());
|
||||
org.junit.jupiter.api.Assertions.assertEquals(createdFlow.getBody().id(), rerun.getRunGroupId());
|
||||
org.junit.jupiter.api.Assertions.assertEquals(execution.getId(), rerun.getRerunOfExecutionId());
|
||||
org.junit.jupiter.api.Assertions.assertEquals(2, rerun.getRunNumber());
|
||||
org.junit.jupiter.api.Assertions.assertEquals(ExecutionStatus.READY, rerun.getContext().getStatus());
|
||||
org.junit.jupiter.api.Assertions.assertEquals("Ada",
|
||||
rerun.getContext().getInputs().get(new it.cnr.isti.workflow.manager.executions.FieldKey(block.getId(), "name")));
|
||||
|
||||
List<ExecutionView> history = executionsController.getHistory(execution.getId(), testUser());
|
||||
org.junit.jupiter.api.Assertions.assertEquals(2, history.size());
|
||||
org.junit.jupiter.api.Assertions.assertEquals(execution.getId(), history.get(0).getId());
|
||||
org.junit.jupiter.api.Assertions.assertEquals(rerun.getId(), history.get(1).getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void executionsCreatedFromSameFlowShareHistory() {
|
||||
Block<LLMBlockType> block = blocksController.create(LLMBlockConfiguration.builder()
|
||||
.prompt("Hello, ${{name}}!")
|
||||
.name("same-flow-history")
|
||||
.llmDescriptor(LLMDescriptor.builder().provider("testProvider").model("testModel").build())
|
||||
.build());
|
||||
|
||||
FlowCreateRequest request = new FlowCreateRequest(
|
||||
"Same Flow History",
|
||||
"Executions created from the same flow id",
|
||||
FlowData.builder().block(block).build());
|
||||
FlowView flow = flowController.createFlow(request, testUser()).getBody();
|
||||
|
||||
ExecutionView first = executionsController.create(flow.id(), testUser());
|
||||
|
||||
Block<LLMBlockType> updatedBlock = blocksController.create(LLMBlockConfiguration.builder()
|
||||
.prompt("Updated hello, ${{name}}!")
|
||||
.name("same-flow-history-updated")
|
||||
.llmDescriptor(LLMDescriptor.builder().provider("testProvider").model("testModel").build())
|
||||
.build());
|
||||
flowController.updateFlow(flow.id(), new FlowCreateRequest(
|
||||
"Same Flow History",
|
||||
"Updated flow with the same id",
|
||||
FlowData.builder().block(updatedBlock).build()), testUser());
|
||||
|
||||
ExecutionView second = executionsController.create(flow.id(), testUser());
|
||||
|
||||
org.junit.jupiter.api.Assertions.assertEquals(flow.id(), first.getRunGroupId());
|
||||
org.junit.jupiter.api.Assertions.assertEquals(flow.id(), second.getRunGroupId());
|
||||
org.junit.jupiter.api.Assertions.assertEquals(1, first.getRunNumber());
|
||||
org.junit.jupiter.api.Assertions.assertEquals(2, second.getRunNumber());
|
||||
|
||||
List<ExecutionView> history = executionsController.getHistory(first.getId(), testUser());
|
||||
org.junit.jupiter.api.Assertions.assertEquals(2, history.size());
|
||||
org.junit.jupiter.api.Assertions.assertEquals(first.getId(), history.get(0).getId());
|
||||
org.junit.jupiter.api.Assertions.assertEquals(second.getId(), history.get(1).getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getExecutionGroupsReturnsHistoriesWithExecutions() {
|
||||
LoginEntity owner = new LoginEntity("execution-groups-user", "testpassword");
|
||||
LLMDescriptor descriptor = LLMDescriptor.builder()
|
||||
.provider("testProvider")
|
||||
.model("testModel")
|
||||
.build();
|
||||
|
||||
Block<LLMBlockType> firstFlowBlock = blocksController.create(LLMBlockConfiguration.builder()
|
||||
.prompt("First flow ${{name}}")
|
||||
.name("first-group-flow")
|
||||
.llmDescriptor(descriptor)
|
||||
.build());
|
||||
FlowView firstFlow = flowController.createFlow(new FlowCreateRequest(
|
||||
"First Group Flow",
|
||||
"Grouped executions",
|
||||
FlowData.builder().block(firstFlowBlock).build()), owner).getBody();
|
||||
ExecutionView firstRun = executionsController.create(firstFlow.id(), owner);
|
||||
ExecutionView secondRun = executionsController.create(firstFlow.id(), owner);
|
||||
|
||||
Block<LLMBlockType> secondFlowBlock = blocksController.create(LLMBlockConfiguration.builder()
|
||||
.prompt("Second flow ${{name}}")
|
||||
.name("second-group-flow")
|
||||
.llmDescriptor(descriptor)
|
||||
.build());
|
||||
FlowView secondFlow = flowController.createFlow(new FlowCreateRequest(
|
||||
"Second Group Flow",
|
||||
"Another grouped execution",
|
||||
FlowData.builder().block(secondFlowBlock).build()), owner).getBody();
|
||||
ExecutionView thirdRun = executionsController.create(secondFlow.id(), owner);
|
||||
|
||||
List<ExecutionGroupView> groups = executionsController.getGroups(owner);
|
||||
Map<String, ExecutionGroupView> groupsById = groups.stream()
|
||||
.collect(java.util.stream.Collectors.toMap(ExecutionGroupView::getId, group -> group));
|
||||
|
||||
org.junit.jupiter.api.Assertions.assertEquals(2, groups.size());
|
||||
org.junit.jupiter.api.Assertions.assertTrue(groupsById.containsKey(firstFlow.id()));
|
||||
org.junit.jupiter.api.Assertions.assertTrue(groupsById.containsKey(secondFlow.id()));
|
||||
|
||||
ExecutionGroupView firstGroup = groupsById.get(firstFlow.id());
|
||||
org.junit.jupiter.api.Assertions.assertEquals(firstFlow.id(), firstGroup.getSourceFlowId());
|
||||
org.junit.jupiter.api.Assertions.assertEquals(2, firstGroup.getExecutionCount());
|
||||
org.junit.jupiter.api.Assertions.assertEquals(firstRun.getId(), firstGroup.getFirstExecutionId());
|
||||
org.junit.jupiter.api.Assertions.assertEquals(secondRun.getId(), firstGroup.getLatestExecutionId());
|
||||
org.junit.jupiter.api.Assertions.assertEquals(List.of(firstRun.getId(), secondRun.getId()),
|
||||
firstGroup.getExecutions().stream().map(ExecutionView::getId).toList());
|
||||
|
||||
ExecutionGroupView secondGroup = groupsById.get(secondFlow.id());
|
||||
org.junit.jupiter.api.Assertions.assertEquals(1, secondGroup.getExecutionCount());
|
||||
org.junit.jupiter.api.Assertions.assertEquals(List.of(thirdRun.getId()),
|
||||
secondGroup.getExecutions().stream().map(ExecutionView::getId).toList());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getMissingExecutionReturnsNotFound() {
|
||||
ResponseStatusException exception = org.junit.jupiter.api.Assertions.assertThrows(
|
||||
|
|
|
|||
Loading…
Reference in New Issue