Skip to content

Commit

Permalink
Merge pull request #55 from sanctuuary/ape-update
Browse files Browse the repository at this point in the history
Update RESTful APE: Version and Testing Enhancements
  • Loading branch information
kretep committed Feb 19, 2024
2 parents 80dec8e + 99115d4 commit 63d3221
Show file tree
Hide file tree
Showing 9 changed files with 153 additions and 79 deletions.
24 changes: 5 additions & 19 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
</parent>
<groupId>nl.esciencecenter</groupId>
<artifactId>restape</artifactId>
<version>0.3.0</version>
<version>0.3.1</version>
<name>restape</name>
<description>RESTful API for the APE library</description>
<licenses>
Expand Down Expand Up @@ -42,7 +42,7 @@
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.2.1</version>
<version>3.2.2</version>
</dependency>

<dependency>
Expand All @@ -61,28 +61,14 @@
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>3.2.1</version>
<version>3.2.2</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-jpa -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>3.2.1</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-validation -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>3.2.1</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-devtools -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<version>3.2.1</version>
<version>3.2.2</version>
</dependency>

<!-- https://mvnrepository.com/artifact/com.h2database/h2 -->
Expand All @@ -96,7 +82,7 @@
<dependency>
<groupId>io.github.sanctuuary</groupId>
<artifactId>APE</artifactId>
<version>2.2.6</version>
<version>2.3.0</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springdoc/springdoc-openapi-starter-webmvc-ui -->
Expand Down
17 changes: 5 additions & 12 deletions src/main/java/nl/esciencecenter/controller/RestApeController.java
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ public ResponseEntity<String> 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();
Expand Down Expand Up @@ -243,7 +243,7 @@ public ResponseEntity<?> getImage(
@ApiResponse(responseCode = "404", description = "Not found")

})
public ResponseEntity<String> getCwl(
public ResponseEntity<String> postCwl(
@RequestBody(required = true) CWLFileInfo cwlInfoJson) throws IOException {

Path path = cwlInfoJson.calculatePath();
Expand Down Expand Up @@ -327,7 +327,7 @@ public ResponseEntity<String> 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.
Expand All @@ -343,17 +343,10 @@ public ResponseEntity<String> getBenchmarks(
@ApiResponse(responseCode = "500", description = "Internal server error"),

})
public ResponseEntity<?> getZipCWLs(
public ResponseEntity<?> postZipCWLs(
@RequestBody(required = true) CWLZip cwlZipInfo) {
try {
List<Path> 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);
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/nl/esciencecenter/controller/dto/CWLZip.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
103 changes: 76 additions & 27 deletions src/main/java/nl/esciencecenter/restape/IOUtils.java
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -14,56 +18,101 @@

import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import nl.esciencecenter.controller.dto.CWLZip;

/**
* The {@code IOUtils} class provides static methods to read the input files.
*/
@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<Path> cwlFilePaths, Path locationDirPath) throws IOException {
Path zipPath = locationDirPath.resolve("workflows.zip");
public static Path zipFilesForLocalExecution(CWLZip cwlZipInfo) throws IOException {

List<Path> 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) {
zipOut.putNextEntry(new ZipEntry(file.getFileName().toString()));
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();
}
}
}
10 changes: 5 additions & 5 deletions src/main/java/nl/esciencecenter/restape/RestApeUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -118,15 +118,15 @@ 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
* @param extension - extension of the file
* @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);
}

/**
Expand Down
12 changes: 10 additions & 2 deletions src/test/java/nl/esciencecenter/RestApeApplicationTest.java
Original file line number Diff line number Diff line change
@@ -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");

}

Expand Down

0 comments on commit 63d3221

Please sign in to comment.