diff --git a/pom.xml b/pom.xml
index 59254f1..e6294f7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -10,7 +10,7 @@
nl.esciencecenter
restape
- 0.3.0
+ 0.3.1
restape
RESTful API for the APE library
@@ -42,7 +42,7 @@
org.springframework.boot
spring-boot-starter-web
- 3.2.1
+ 3.2.2
@@ -61,28 +61,14 @@
org.springframework.boot
spring-boot-starter-actuator
- 3.2.1
+ 3.2.2
org.springframework.boot
spring-boot-starter-data-jpa
- 3.2.1
-
-
-
-
- org.springframework.boot
- spring-boot-starter-validation
- 3.2.1
-
-
-
-
- org.springframework.boot
- spring-boot-devtools
- 3.2.1
+ 3.2.2
@@ -96,7 +82,7 @@
io.github.sanctuuary
APE
- 2.2.6
+ 2.3.0
diff --git a/src/main/java/nl/esciencecenter/controller/RestApeController.java b/src/main/java/nl/esciencecenter/controller/RestApeController.java
index bf660ca..e8a4d12 100644
--- a/src/main/java/nl/esciencecenter/controller/RestApeController.java
+++ b/src/main/java/nl/esciencecenter/controller/RestApeController.java
@@ -211,7 +211,7 @@ public ResponseEntity runSynthesisAndBench(
@ApiResponse(responseCode = "400", description = "Invalid input"),
@ApiResponse(responseCode = "404", description = "Not found")
})
- public ResponseEntity> getImage(
+ public ResponseEntity> postImage(
@RequestBody(required = true) ImgFileInfo imgFileInfo) throws IOException {
Path path = imgFileInfo.calculatePath();
@@ -243,7 +243,7 @@ public ResponseEntity> getImage(
@ApiResponse(responseCode = "404", description = "Not found")
})
- public ResponseEntity getCwl(
+ public ResponseEntity postCwl(
@RequestBody(required = true) CWLFileInfo cwlInfoJson) throws IOException {
Path path = cwlInfoJson.calculatePath();
@@ -327,7 +327,7 @@ public ResponseEntity getBenchmarks(
* Retrieve the CWL solution files based on the provided run ID and CWL file
* names.
* TODO: Exeptions don't handle all cases or illegal arguments (e.g. invalid
- * workflow name that ends with an open quotation`candidate_solution_1.cwl"`).
+ * workflow name that ends with an open quotation`candidate_workflow_1.cwl"`).
*
* @param cwlFilesJson JSON object containing the run_id and the list of CWL
* files.
@@ -343,17 +343,10 @@ public ResponseEntity getBenchmarks(
@ApiResponse(responseCode = "500", description = "Internal server error"),
})
- public ResponseEntity> getZipCWLs(
+ public ResponseEntity> postZipCWLs(
@RequestBody(required = true) CWLZip cwlZipInfo) {
try {
- List cwlFilePaths = cwlZipInfo.getCWLPaths();
-
- // Add the CWL input file to the zip
- Path cwlInputPath = RestApeUtils.calculatePath(cwlZipInfo.getRunID(), "CWL", "input.yml");
- cwlFilePaths.add(cwlInputPath);
-
- // Zip the CWL files
- Path zipPath = IOUtils.zipFiles(cwlFilePaths, cwlInputPath.getParent());
+ Path zipPath = IOUtils.zipFilesForLocalExecution(cwlZipInfo);
Resource zipResource = new UrlResource(zipPath.toUri());
String zipContentType = Files.probeContentType(zipPath);
diff --git a/src/main/java/nl/esciencecenter/controller/dto/CWLZip.java b/src/main/java/nl/esciencecenter/controller/dto/CWLZip.java
index 1b184fd..5d3af30 100644
--- a/src/main/java/nl/esciencecenter/controller/dto/CWLZip.java
+++ b/src/main/java/nl/esciencecenter/controller/dto/CWLZip.java
@@ -11,6 +11,10 @@
import lombok.Setter;
import nl.esciencecenter.restape.RestApeUtils;
+/**
+ * The {@code CWLZip} class represents the structure of the request to zip CWL files. It contains the runID and the list of workflow file names.
+
+ */
@Getter
@Setter
@NoArgsConstructor
diff --git a/src/main/java/nl/esciencecenter/restape/IOUtils.java b/src/main/java/nl/esciencecenter/restape/IOUtils.java
index a7371b4..8eaaa80 100644
--- a/src/main/java/nl/esciencecenter/restape/IOUtils.java
+++ b/src/main/java/nl/esciencecenter/restape/IOUtils.java
@@ -1,7 +1,11 @@
package nl.esciencecenter.restape;
+import java.io.BufferedInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -14,6 +18,7 @@
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
+import nl.esciencecenter.controller.dto.CWLZip;
/**
* The {@code IOUtils} class provides static methods to read the input files.
@@ -21,40 +26,53 @@
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class IOUtils {
- /**
- * Get the CWL content of the file at the given path.
- *
- * @param filePath - path to the CWL file
- * @return CWL content of the file representing a workflow
- * @throws IOException - if the file cannot be read
- */
- public static String getLocalCwlFile(Path filePath) throws IOException, NoSuchFileException {
- return FileUtils.readFileToString(filePath.toFile(), StandardCharsets.UTF_8);
- }
+ /**
+ * URL to the README file containing instructions on how to run the workflows.
+ */
+ private static final String README_URL = "https://raw.githubusercontent.com/Workflomics/containers/add_instructions/instructions.txt";
- /**
- * Get the JSON content of the file at the given path.
- *
- * @param filePath - path to the benchmarking JSON file
- * @return CWL content of the file representing a workflow
- * @throws IOException - if the file cannot be read
- */
- public static String getLocalBenchmarkFile(Path filePath) throws IOException {
- return FileUtils.readFileToString(filePath.toFile(), StandardCharsets.UTF_8);
- }
+ /**
+ * Get the CWL content of the file at the given path.
+ *
+ * @param filePath - path to the CWL file
+ * @return CWL content of the file representing a workflow
+ * @throws IOException - if the file cannot be read
+ */
+ public static String getLocalCwlFile(Path filePath) throws IOException, NoSuchFileException {
+ return FileUtils.readFileToString(filePath.toFile(), StandardCharsets.UTF_8);
+ }
- /**
- * Zip the provided CWL files as well as the CWL input file (`inputs.yml`).
+ /**
+ * Get the JSON content of the file at the given path.
+ *
+ * @param filePath - path to the benchmarking JSON file
+ * @return CWL content of the file representing a workflow
+ * @throws IOException - if the file cannot be read
+ */
+ public static String getLocalBenchmarkFile(Path filePath) throws IOException {
+ return FileUtils.readFileToString(filePath.toFile(), StandardCharsets.UTF_8);
+ }
+
+ /**
+ * Zip the provided CWL files as well as the CWL input file (`inputs.yml`) into
+ * a single zip file. In addition, a `readme.txt` file with instructions on how
+ * to run the workflows is added to the zip.
+ *
+ * @param cwlZipInfo - the CWL zip information, containing the runID and the list of workflow file names.
*
- * @param cwlFilePaths List of CWL file names (with extensions).
- * @param locationDirPath Path to the directory where the zip file will be
- * created.
* @return Path to the created zip file.
* @throws IOException Error is thrown if the zip file cannot be created or
* written to.
*/
- public static Path zipFiles(List cwlFilePaths, Path locationDirPath) throws IOException {
- Path zipPath = locationDirPath.resolve("workflows.zip");
+ public static Path zipFilesForLocalExecution(CWLZip cwlZipInfo) throws IOException {
+
+ List cwlFilePaths = cwlZipInfo.getCWLPaths();
+
+ // Add the CWL input file to the zip
+ Path cwlInputPath = RestApeUtils.calculatePath(cwlZipInfo.getRunID(), "CWL", "input.yml");
+ cwlFilePaths.add(cwlInputPath);
+
+ Path zipPath = cwlInputPath.getParent().resolve("workflows.zip");
try (FileOutputStream fos = new FileOutputStream(zipPath.toFile());
ZipOutputStream zipOut = new ZipOutputStream(fos)) {
for (Path file : cwlFilePaths) {
@@ -62,8 +80,39 @@ public static Path zipFiles(List cwlFilePaths, Path locationDirPath) throw
Files.copy(file, zipOut);
zipOut.closeEntry();
}
+ addReadmeToZip(zipOut);
}
return zipPath;
}
+ /**
+ * Add the `readme.txt` file to the zip from the given URL. The file contains
+ * instructions on how to run the workflows.
+ *
+ * @param zipOut - the zip output stream
+ * @throws IOException - if the README file cannot be read
+ */
+ private static void addReadmeToZip(ZipOutputStream zipOut) throws IOException {
+ // Download readme.txt and add to the zip
+ URL readmeUrl = new URL(README_URL);
+ HttpURLConnection httpURLConnection = (HttpURLConnection) readmeUrl.openConnection();
+ httpURLConnection.setRequestMethod("GET");
+ // Ensure the connection timeout is set to a reasonable value
+ httpURLConnection.setConnectTimeout(5000); // 5 seconds
+ httpURLConnection.setReadTimeout(5000); // 5 seconds
+
+ try (InputStream in = new BufferedInputStream(httpURLConnection.getInputStream())) {
+ zipOut.putNextEntry(new ZipEntry("readme.txt"));
+
+ byte[] buffer = new byte[1024];
+ int count;
+ while ((count = in.read(buffer)) != -1) {
+ zipOut.write(buffer, 0, count);
+ }
+
+ zipOut.closeEntry();
+ } finally {
+ httpURLConnection.disconnect();
+ }
+ }
}
diff --git a/src/main/java/nl/esciencecenter/restape/RestApeUtils.java b/src/main/java/nl/esciencecenter/restape/RestApeUtils.java
index f29dc03..39f876f 100644
--- a/src/main/java/nl/esciencecenter/restape/RestApeUtils.java
+++ b/src/main/java/nl/esciencecenter/restape/RestApeUtils.java
@@ -94,19 +94,19 @@ public static boolean isValidRunID(String runID) {
/**
* Checks whether the file name is valid, by checking its format. The name
- * should start with `candidate_solution_` followed by a number and without an
+ * should start with `candidate_workflow_` followed by a number and without an
* extension.
*
* @param fileName - file name to be verified
* @return true if the file name is valid, false otherwise.
*/
public static boolean isValidAPEFileNameNoExtension(String fileName) {
- return fileName != null && fileName.matches("candidate_solution_\\d+");
+ return fileName != null && fileName.matches("candidate_workflow_\\d+");
}
/**
* Checks whether the file name is valid, by checking its format. The name
- * should start with `candidate_solution_` followed by a number and with an
+ * should start with `candidate_workflow_` followed by a number and with an
* extension.
*
* @param fileName - file name to be verified
@@ -118,7 +118,7 @@ public static boolean isValidFileNameWithExtension(String fileName) {
/**
* Checks whether the file name is valid, by checking its extension and format.
- * The name should start with `candidate_solution_` followed by a number and end
+ * The name should start with `candidate_workflow_` followed by a number and end
* with the specified extension.
*
* @param fileName - file name to be verified
@@ -126,7 +126,7 @@ public static boolean isValidFileNameWithExtension(String fileName) {
* @return true if the file name is valid, false otherwise.
*/
public static boolean isValidAPEFileName(String fileName, String extension) {
- return fileName != null && fileName.matches("candidate_solution_\\d+\\." + extension);
+ return fileName != null && fileName.matches("candidate_workflow_\\d+\\." + extension);
}
/**
diff --git a/src/test/java/nl/esciencecenter/RestApeApplicationTest.java b/src/test/java/nl/esciencecenter/RestApeApplicationTest.java
index 48a4747..ede3ea1 100644
--- a/src/test/java/nl/esciencecenter/RestApeApplicationTest.java
+++ b/src/test/java/nl/esciencecenter/RestApeApplicationTest.java
@@ -1,13 +1,21 @@
package nl.esciencecenter;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.ApplicationContext;
@SpringBootTest
class RestApeApplicationTest {
- @Test
- void contextLoads() {
+ @Autowired
+ private ApplicationContext context;
+
+ @Test
+ void contextLoads() {
+ assertNotNull(context, "Application context should not be null");
}
diff --git a/src/test/java/nl/esciencecenter/controller/RestApeControllerTest.java b/src/test/java/nl/esciencecenter/controller/RestApeControllerTest.java
index ab7f49e..f7a4c60 100644
--- a/src/test/java/nl/esciencecenter/controller/RestApeControllerTest.java
+++ b/src/test/java/nl/esciencecenter/controller/RestApeControllerTest.java
@@ -35,7 +35,7 @@ class RestApeControllerTest {
* @throws Exception
*/
@Test
- void getGreetings() throws Exception {
+ void testGetGreetings() throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().string(equalTo("Welcome to the RESTful APE API!")));
@@ -47,7 +47,7 @@ void getGreetings() throws Exception {
* @throws Exception
*/
@Test
- void getDataFail() throws Exception {
+ void testGetDataFail() throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/data_taxonomy").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isBadRequest());
}
@@ -58,7 +58,7 @@ void getDataFail() throws Exception {
* @throws Exception
*/
@Test
- void getDataTest() throws Exception {
+ void testGetData() throws Exception {
String path = "https://raw.githubusercontent.com/Workflomics/domain-annotations/main/WombatP_tools/config.json";
mvc.perform(MockMvcRequestBuilders.get("/data_taxonomy?config_path=" + path).accept(MediaType.APPLICATION_JSON))
@@ -72,7 +72,7 @@ void getDataTest() throws Exception {
* @throws Exception
*/
@Test
- void getToolsFail() throws Exception {
+ void testGetToolsFail() throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/tools_taxonomy").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isBadRequest());
}
@@ -83,7 +83,7 @@ void getToolsFail() throws Exception {
* @throws Exception
*/
@Test
- void getToolsTest() throws Exception {
+ void testGetTools() throws Exception {
String path = "https://raw.githubusercontent.com/Workflomics/domain-annotations/main/WombatP_tools/config.json";
mvc.perform(
@@ -98,7 +98,7 @@ void getToolsTest() throws Exception {
* @throws Exception
*/
@Test
- void runSynthesisGetFail() throws Exception {
+ void testRunSynthesisGestFail() throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/run_synthesis").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isMethodNotAllowed());
}
@@ -109,7 +109,7 @@ void runSynthesisGetFail() throws Exception {
* @throws Exception
*/
@Test
- void runSynthesisPostFail() throws Exception {
+ void testRunSynthesisFail() throws Exception {
mvc.perform(MockMvcRequestBuilders.post("/run_synthesis").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isBadRequest());
}
@@ -120,7 +120,7 @@ void runSynthesisPostFail() throws Exception {
* @throws Exception
*/
@Test
- void runSynthesisPostPass() throws Exception {
+ void testRunSynthesisPass() throws Exception {
String configPath = "https://raw.githubusercontent.com/Workflomics/domain-annotations/main/WombatP_tools/config.json";
String jsonContent = FileUtils.readFileToString(APEFiles.readPathToFile(configPath),
@@ -133,7 +133,7 @@ void runSynthesisPostPass() throws Exception {
}
@Test
- void getZipCWLs() throws Exception {
+ void testPostZipCWLs() throws Exception {
String path = "https://raw.githubusercontent.com/Workflomics/domain-annotations/main/WombatP_tools/config.json";
String content = FileUtils.readFileToString(APEFiles.readPathToFile(path),
diff --git a/src/test/java/nl/esciencecenter/restape/IOUtilsTest.java b/src/test/java/nl/esciencecenter/restape/IOUtilsTest.java
new file mode 100644
index 0000000..2f35899
--- /dev/null
+++ b/src/test/java/nl/esciencecenter/restape/IOUtilsTest.java
@@ -0,0 +1,39 @@
+package nl.esciencecenter.restape;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+import org.apache.commons.io.FileUtils;
+import org.json.JSONArray;
+import org.json.JSONObject;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+import nl.esciencecenter.controller.dto.CWLZip;
+import nl.uu.cs.ape.utils.APEFiles;
+
+@SpringBootTest
+class IOUtilsTest {
+
+
+ @Test
+ void testZipFilesForLocalExecution() throws Exception {
+
+ String path = "https://raw.githubusercontent.com/Workflomics/domain-annotations/main/WombatP_tools/config.json";
+ String content = FileUtils.readFileToString(APEFiles.readPathToFile(path),
+ StandardCharsets.UTF_8);
+ JSONObject jsonObject = new JSONObject(content);
+ jsonObject.put("solutions", "1");
+ JSONArray result = ApeAPI.runSynthesis(jsonObject, false);
+ assertFalse(result.isEmpty(), "The encoding should be SAT.");
+ String runID = result.getJSONObject(0).getString("run_id");
+ String cwlFile = result.getJSONObject(0).getString("cwl_name");
+
+ CWLZip cwlZip = new CWLZip();
+ cwlZip.setRunID(runID);
+ cwlZip.setWorkflows(List.of(cwlFile));
+
+ IOUtils.zipFilesForLocalExecution(cwlZip);
+ }
+}
diff --git a/src/test/java/nl/esciencecenter/restape/ToolBenchmarkingAPIsTest.java b/src/test/java/nl/esciencecenter/restape/ToolBenchmarkingAPIsTest.java
deleted file mode 100644
index 8da87a2..0000000
--- a/src/test/java/nl/esciencecenter/restape/ToolBenchmarkingAPIsTest.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package nl.esciencecenter.restape;
-
-public class ToolBenchmarkingAPIsTest {
-
-}