@ExecuteOn(TaskExecutors.IO)
    @Post(uri = "/export/by-ids", produces = MediaType.APPLICATION_OCTET_STREAM)
    @Operation(
        tags = {"Templates"},
        summary = "Export templates as a ZIP archive of yaml sources."
    )
    public HttpResponse<byte[]> exportByIds(
        @Parameter(description = "A list of tuple flow ID and namespace as template identifiers") @Body List<IdWithNamespace> ids
    ) throws IOException {
        var templates = ids.stream()
            .map(id -> templateRepository.findById(tenantService.resolveTenant(), id.getNamespace(), id.getId()).orElseThrow())
            .toList();
        var bytes = zipTemplates(templates);
        return HttpResponse.ok(bytes).header("Content-Disposition", "attachment; filename=\"templates.zip\"");
    }
@Test
    void exportByIds() throws IOException {
        // create 3 templates, so we can retrieve them by id
        var template1 = client.toBlocking().retrieve(POST("/api/v1/templates", createTemplate()), Template.class);
        var template2 = client.toBlocking().retrieve(POST("/api/v1/templates", createTemplate()), Template.class);
        var template3 = client.toBlocking().retrieve(POST("/api/v1/templates", createTemplate()), Template.class);

        List<IdWithNamespace> ids = List.of(
            new IdWithNamespace("kestra.test", template1.getId()),
            new IdWithNamespace("kestra.test", template2.getId()),
            new IdWithNamespace("kestra.test", template3.getId()));
        byte[] zip = client.toBlocking().retrieve(HttpRequest.POST("/api/v1/templates/export/by-ids?namespace=kestra.test", ids),
            Argument.of(byte[].class));
        File file = File.createTempFile("templates", ".zip");
        Files.write(file.toPath(), zip);

        try(ZipFile zipFile = new ZipFile(file)) {
            assertThat(zipFile.stream().count(), is(3L));
        }

        file.delete();
    }