Expose draft and executable status in flow views
This commit is contained in:
parent
3d02bec226
commit
cbdb5e48d0
|
|
@ -4,6 +4,7 @@ import java.time.LocalDateTime;
|
|||
|
||||
import it.cnr.isti.workflow.manager.flows.model.FlowCreateRequest;
|
||||
import it.cnr.isti.workflow.manager.flows.model.FlowView;
|
||||
import it.cnr.isti.workflow.manager.flows.model.FlowViewStatus;
|
||||
import it.cnr.isti.workflow.manager.flows.repo.FlowEntity;
|
||||
|
||||
public final class FlowMapper {
|
||||
|
|
@ -30,7 +31,7 @@ public final class FlowMapper {
|
|||
entity.setLastUpdateAt(LocalDateTime.now());
|
||||
}
|
||||
|
||||
public static FlowView toView(FlowEntity entity) {
|
||||
public static FlowView toView(FlowEntity entity, FlowViewStatus status) {
|
||||
return new FlowView(
|
||||
entity.getId(),
|
||||
entity.getName(),
|
||||
|
|
@ -38,6 +39,7 @@ public final class FlowMapper {
|
|||
entity.getCreatedAt(),
|
||||
entity.getLastUpdateAt(),
|
||||
entity.getOwner(),
|
||||
status,
|
||||
entity.isPublished(),
|
||||
entity.isFinalized(),
|
||||
entity.getFlow());
|
||||
|
|
|
|||
|
|
@ -9,8 +9,10 @@ import org.springframework.web.server.ResponseStatusException;
|
|||
|
||||
import it.cnr.isti.workflow.manager.flows.model.FlowCreateRequest;
|
||||
import it.cnr.isti.workflow.manager.flows.model.FlowView;
|
||||
import it.cnr.isti.workflow.manager.flows.model.FlowViewStatus;
|
||||
import it.cnr.isti.workflow.manager.flows.repo.FlowEntity;
|
||||
import it.cnr.isti.workflow.manager.flows.repo.FlowRepository;
|
||||
import it.cnr.isti.workflow.manager.flows.validation.FlowExecutionValidator;
|
||||
import jakarta.validation.ConstraintViolation;
|
||||
import jakarta.validation.Validator;
|
||||
|
||||
|
|
@ -28,11 +30,14 @@ public class FlowService {
|
|||
@Autowired
|
||||
Validator validator;
|
||||
|
||||
@Autowired
|
||||
FlowExecutionValidator flowExecutionValidator;
|
||||
|
||||
public FlowView createFlow(String owner, FlowCreateRequest request) {
|
||||
validateFlow(request);
|
||||
FlowEntity flowEntity = FlowMapper.toNewEntity(owner, request);
|
||||
FlowEntity savedEntity = flowRepository.save(flowEntity);
|
||||
return FlowMapper.toView(savedEntity);
|
||||
return toView(savedEntity);
|
||||
}
|
||||
|
||||
public FlowView updateFlow(String id, String owner, FlowCreateRequest request) {
|
||||
|
|
@ -47,7 +52,7 @@ public class FlowService {
|
|||
validateFlow(request);
|
||||
FlowMapper.updateEntity(entity, request);
|
||||
FlowEntity savedEntity = flowRepository.save(entity);
|
||||
return FlowMapper.toView(savedEntity);
|
||||
return toView(savedEntity);
|
||||
}
|
||||
|
||||
public void deleteFlow(String id, String owner) {
|
||||
|
|
@ -63,7 +68,7 @@ public class FlowService {
|
|||
}
|
||||
|
||||
public List<FlowView> getAllFlows() {
|
||||
return flowRepository.findAll().stream().map(FlowMapper::toView).toList();
|
||||
return flowRepository.findAll().stream().map(this::toView).toList();
|
||||
}
|
||||
|
||||
private void validateFlow(FlowCreateRequest request) {
|
||||
|
|
@ -78,4 +83,11 @@ public class FlowService {
|
|||
logger.warn("Flow validation failed: {}", message);
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, message);
|
||||
}
|
||||
|
||||
private FlowView toView(FlowEntity entity) {
|
||||
FlowViewStatus status = flowExecutionValidator.isExecutable(entity.getFlow())
|
||||
? FlowViewStatus.EXECUTABLE
|
||||
: FlowViewStatus.DRAFT;
|
||||
return FlowMapper.toView(entity, status);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ public record FlowView(
|
|||
LocalDateTime createdAt,
|
||||
LocalDateTime lastUpdateAt,
|
||||
String owner,
|
||||
FlowViewStatus status,
|
||||
boolean published,
|
||||
boolean finalized,
|
||||
FlowData flow) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
package it.cnr.isti.workflow.manager.flows.model;
|
||||
|
||||
public enum FlowViewStatus {
|
||||
DRAFT,
|
||||
EXECUTABLE
|
||||
}
|
||||
|
|
@ -21,6 +21,18 @@ public class FlowExecutionValidator {
|
|||
Validator validator;
|
||||
|
||||
public void validate(FlowData flowData) {
|
||||
List<ValidationError> errors = collectErrors(flowData);
|
||||
|
||||
if (!errors.isEmpty()) {
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, ValidationErrorCodec.encode(errors));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isExecutable(FlowData flowData) {
|
||||
return collectErrors(flowData).isEmpty();
|
||||
}
|
||||
|
||||
private List<ValidationError> collectErrors(FlowData flowData) {
|
||||
List<ValidationError> errors = new ArrayList<>();
|
||||
|
||||
errors.addAll(validator.validate(flowData).stream()
|
||||
|
|
@ -43,8 +55,6 @@ public class FlowExecutionValidator {
|
|||
}
|
||||
}
|
||||
|
||||
if (!errors.isEmpty()) {
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, ValidationErrorCodec.encode(errors));
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,11 +25,15 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
|
|||
import it.cnr.isti.workflow.manager.app.ObjectMapperHolder;
|
||||
import it.cnr.isti.workflow.manager.auth.config.JwtUtil;
|
||||
import it.cnr.isti.workflow.manager.auth.repo.LoginEntity;
|
||||
import it.cnr.isti.workflow.manager.blocks.Block;
|
||||
import it.cnr.isti.workflow.manager.blocks.configurations.LLMBlockConfiguration;
|
||||
import it.cnr.isti.workflow.manager.blocks.types.LLMBlockType;
|
||||
import it.cnr.isti.workflow.manager.flows.FlowTestCreator;
|
||||
import it.cnr.isti.workflow.manager.flows.model.Flow;
|
||||
import it.cnr.isti.workflow.manager.flows.model.FlowCreateRequest;
|
||||
import it.cnr.isti.workflow.manager.flows.model.FlowData;
|
||||
import it.cnr.isti.workflow.manager.flows.model.FlowView;
|
||||
import it.cnr.isti.workflow.manager.flows.model.FlowViewStatus;
|
||||
import it.cnr.isti.workflow.manager.llms.LLMDescriptor;
|
||||
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
|
|
@ -44,6 +48,9 @@ public class FlowControllerTest {
|
|||
@Autowired
|
||||
private FlowController flowController;
|
||||
|
||||
@Autowired
|
||||
private BlocksController blocksController;
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
|
|
@ -77,6 +84,7 @@ public class FlowControllerTest {
|
|||
assertNotNull(createResponse.getBody());
|
||||
FlowView created = createResponse.getBody();
|
||||
assertNotNull(created.id());
|
||||
assertEquals(FlowViewStatus.EXECUTABLE, created.status());
|
||||
|
||||
List<FlowView> flows = flowController.getAllFlows();
|
||||
assertNotNull(flows);
|
||||
|
|
@ -97,6 +105,7 @@ public class FlowControllerTest {
|
|||
assertEquals("testuser", retrieved.owner());
|
||||
assertEquals(flow.getName(), retrieved.name());
|
||||
assertEquals(flow.getDescription(), retrieved.description());
|
||||
assertEquals(FlowViewStatus.EXECUTABLE, retrieved.status());
|
||||
assertNotNull(retrieved.flow());
|
||||
assertEquals(flow.getBlocks().size(), retrieved.flow().getBlocks().size());
|
||||
assertEquals(flow.getConnections().size(), retrieved.flow().getConnections().size());
|
||||
|
|
@ -150,6 +159,7 @@ public class FlowControllerTest {
|
|||
|| updated.lastUpdateAt().isEqual(created.lastUpdateAt()));
|
||||
assertEquals(updatedFlowDefinition.getName(), updated.name());
|
||||
assertEquals(updatedFlowDefinition.getDescription(), updated.description());
|
||||
assertEquals(FlowViewStatus.EXECUTABLE, updated.status());
|
||||
assertNotNull(updated.flow());
|
||||
assertEquals(updatedFlowDefinition.getBlocks().size(), updated.flow().getBlocks().size());
|
||||
assertEquals(updatedFlowDefinition.getConnections().size(), updated.flow().getConnections().size());
|
||||
|
|
@ -312,4 +322,32 @@ public class FlowControllerTest {
|
|||
.andExpect(jsonPath("$.errors[0].field").value("inputs"))
|
||||
.andExpect(jsonPath("$.errors[0].message").value("Inputs do not match its configuration"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createDraftFlowReturnsDraftStatus() {
|
||||
Block<LLMBlockType> draftBlock = blocksController.create(LLMBlockConfiguration.empty());
|
||||
draftBlock.asSink();
|
||||
|
||||
Flow draftFlow = Flow.builder()
|
||||
.name("Draft Flow")
|
||||
.description("Incomplete but structurally valid flow")
|
||||
.block(draftBlock)
|
||||
.build();
|
||||
|
||||
FlowCreateRequest request = new FlowCreateRequest(
|
||||
draftFlow.getName(),
|
||||
draftFlow.getDescription(),
|
||||
FlowData.builder()
|
||||
.blocks(draftFlow.getBlocks())
|
||||
.connections(draftFlow.getConnections())
|
||||
.build());
|
||||
|
||||
ResponseEntity<FlowView> createResponse = flowController.createFlow(
|
||||
request,
|
||||
new LoginEntity("testuser", "testpassword"));
|
||||
|
||||
assertTrue(createResponse.getStatusCode().is2xxSuccessful());
|
||||
assertNotNull(createResponse.getBody());
|
||||
assertEquals(FlowViewStatus.DRAFT, createResponse.getBody().status());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue