feat: allows importFlows endpoint to be able to throw when having an invalid flow (#12995)

This commit is contained in:
YannC
2025-11-18 09:33:32 +01:00
committed by GitHub
parent 6918d5d512
commit 6e0197b542
2 changed files with 69 additions and 2 deletions

View File

@@ -875,7 +875,8 @@ public class FlowController {
@ApiResponse(responseCode = "200", description = "On success")
public HttpResponse<List<String>> importFlows(
@Parameter(description = "The file to import, can be a ZIP archive or a multi-objects YAML file")
@Part CompletedFileUpload fileUpload
@Part CompletedFileUpload fileUpload,
@Parameter(description = "If should fail on invalid flows") @QueryValue(defaultValue = "false") Boolean failOnError
) throws IOException {
String tenantId = tenantService.resolveTenant();
final List<String> wrongFiles = new ArrayList<>();
@@ -892,6 +893,9 @@ public class FlowController {
fileUpload.discard();
return HttpResponse.badRequest();
}
if (failOnError && !wrongFiles.isEmpty()) {
throw new IllegalArgumentException("Following invalids flows were not imported: " + String.join(", ", wrongFiles));
}
return HttpResponse.ok(wrongFiles);
}

View File

@@ -39,7 +39,6 @@ import io.micronaut.http.hateoas.JsonError;
import io.micronaut.http.uri.UriBuilder;
import io.micronaut.reactor.http.client.ReactorHttpClient;
import jakarta.inject.Inject;
import java.net.URI;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
@@ -48,6 +47,7 @@ import org.slf4j.event.Level;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLEncoder;
@@ -699,6 +699,55 @@ class FlowControllerTest {
temp.delete();
}
@Test
void importFlowsWithInvalidButAllowed() throws IOException {
var yaml = generateFlowAsString(TEST_NAMESPACE,"a") + "---" +
generateInvalidFlowAsString("importFlowsWithInvalidButAllowed",TEST_NAMESPACE);
var temp = File.createTempFile("flows", ".yaml");
Files.writeString(temp.toPath(), yaml);
var body = MultipartBody.builder()
.addPart("fileUpload", "flows.yaml", temp)
.build();
var response = client.toBlocking().exchange(POST("/api/v1/main/flows/import", body).contentType(MediaType.MULTIPART_FORM_DATA));
assertThat(response.getStatus().getCode()).isEqualTo(OK.getCode());
temp.delete();
}
@Test
void importFlowsWithInvalidNotAllowed() throws IOException {
var yaml = generateFlowAsString(TEST_NAMESPACE,"a") + "---" +
generateInvalidFlowAsString("importFlowsWithInvalidNotAllowed",TEST_NAMESPACE);
var temp = File.createTempFile("flows", ".yaml");
Files.writeString(temp.toPath(), yaml);
var body = MultipartBody.builder()
.addPart("fileUpload", "flows.yaml", temp)
.build();
var exception = assertThrows(HttpClientResponseException.class, () -> {
client.toBlocking().exchange(POST("/api/v1/main/flows/import?failOnError=true", body).contentType(MediaType.MULTIPART_FORM_DATA));
});
assertThat(exception.getStatus().getCode()).isEqualTo(UNPROCESSABLE_ENTITY.getCode());
temp.delete();
}
@Test
void importFlowsWithInvalidFile() throws IOException {
var temp = File.createTempFile("flows", ".txt");
Files.writeString(temp.toPath(), "this is not a valid file");
var body = MultipartBody.builder()
.addPart("fileUpload", "flows.txt", temp)
.build();
var exception = assertThrows(HttpClientResponseException.class, () -> {
client.toBlocking().exchange(POST("/api/v1/main/flows/import?failOnError=false", body).contentType(MediaType.MULTIPART_FORM_DATA));
});
assertThat(exception.getStatus().getCode()).isEqualTo(UNPROCESSABLE_ENTITY.getCode());
temp.delete();
}
@Test
void disableEnableFlowsByIds() {
List<IdWithNamespace> ids = List.of(
@@ -1112,6 +1161,20 @@ class FlowControllerTest {
}
private String generateInvalidFlowAsString(String id, String namespace) {
return """
id: %s
# Comment i added
namespace: %s
tasks:
- id: test
type: io.kestra.plugin.core.debug.Invalid
format: test
disabled: false
deleted: false
""".formatted(id, namespace);
}
private String postFlow(String friendlyId, String namespace, String format) {
return client.toBlocking().retrieve(POST("/api/v1/main/flows", generateFlow(friendlyId, namespace, format)), String.class);
}