diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 515c5e7..d87598f 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -49,11 +49,13 @@ jobs: sam build --use-container sam local invoke WDLParsingFunction -e events/event.json &> output.txt grep "statusCode\": 200" output.txt - - name: Test Nextflow parsing with SAM CLI build (no invoke) + - name: Test Nextflow parsing with SAM CLI build and invoke working-directory: ./nextflow-parsing # SAM build also runs the Java tests run: | sam build --use-container + sam local invoke HelloWorldFunction -e events/sarek.json &> output.txt + grep "statusCode\": 200" output.txt - name: Build CWL packing with SAM CLI build (no invoke, no test) working-directory: ./cwlpack run: sam build --use-container diff --git a/CLAUDE.md b/CLAUDE.md index 35df70d..3cb57ba 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -11,7 +11,7 @@ A monorepo of small, independent AWS Lambda functions used by Dockstore ("backup | `checkUrlExists/` | Node 22 (`lambda/index.js`) | Checks a URL (http/https/ftp/sftp) is reachable; always returns 200, body is true/false | | `cwlpack/` | Python 3.12 (`cwl_pack_function/app.py`) | Clones a git repo with pygit2 and packs a CWL descriptor with sbpack | | `wdl-parsing/` | Java 21 (`WDLParsingFunction`, Maven) | Parses WDL descriptors | -| `nextflow-parsing/` | Java 11 template (`NextflowParsingFunction`, Maven) | Parses Nextflow descriptors | +| `nextflow-parsing/` | Java 21 (`NextflowParsingFunction`, Maven) | Parses Nextflow descriptors | | `cloud-watch-to-slack-testing/`, `upsertGitHubTag/` | Node (`deployment/index.js`) | Not SAM; zipped and uploaded to S3 by CircleCI. `upsertGitHubTag` relays API Gateway -> SQS -> Lambda -> Dockstore webservice | ## Commands @@ -59,7 +59,10 @@ When creating a PR, always create it in draft mode. A human developer must be th review/move it out of draft state — Claude Code should not do this itself. Always check with the user before pushing changes to GitHub, even to a branch/PR already being worked on in -the conversation — a push can kick off a long CI build or interrupt one that's already running. +the conversation — a push can kick off a long CI build or interrupt one that's already running. Before asking, +show the user what would be pushed (e.g. `git log` and `git diff` against the remote branch) so they can review +it first. An earlier "push" request does not cover later commits, and a force-push (e.g. after a rebase) +always needs explicit approval. When a GitHub MCP server or `gh` is available, diff the current work against `develop` (or whatever branch the PR targets) and try to minimize stylistic or otherwise-minor changes that inflate the diff and make it harder diff --git a/nextflow-parsing/NextflowParsingFunction/ignore.xml b/nextflow-parsing/NextflowParsingFunction/ignore.xml index d9492e9..242c9a5 100644 --- a/nextflow-parsing/NextflowParsingFunction/ignore.xml +++ b/nextflow-parsing/NextflowParsingFunction/ignore.xml @@ -13,4 +13,9 @@ + + + + + diff --git a/nextflow-parsing/NextflowParsingFunction/pom.xml b/nextflow-parsing/NextflowParsingFunction/pom.xml index 6106ef1..3077fbc 100644 --- a/nextflow-parsing/NextflowParsingFunction/pom.xml +++ b/nextflow-parsing/NextflowParsingFunction/pom.xml @@ -8,11 +8,11 @@ jar Nextflow parser created for SAM CLI. - 3.0.0-M5 - 11 - 11 - 5.7.2 - 2.34 + 3.5.3 + 21 + 21 + 5.9.1 + 3.1.11 2.22.3 @@ -43,7 +43,7 @@ org.eclipse.jgit org.eclipse.jgit - 5.13.4.202507202350-r + 7.8.0.202609011348-r @@ -56,17 +56,17 @@ jakarta.ws.rs jakarta.ws.rs-api - 2.1.6 + 3.1.0 aws-lambda-java-core com.amazonaws - 1.2.1 + 1.4.0 aws-lambda-java-events com.amazonaws - 3.6.0 + 3.16.1 org.junit.jupiter @@ -83,28 +83,35 @@ org.slf4j slf4j-api - 1.7.31 + 2.0.18 com.google.guava guava - 32.0.0-jre + 33.6.0-jre - org.codehaus.groovy + org.apache.groovy groovy - 3.0.8 + 4.0.26 io.dockstore openapi-java-client - 1.12.0 + 1.20.0 compile io.nextflow nextflow - 21.04.3 + 25.04.4 + + + + io.seqera + lib-trace + + @@ -135,7 +142,7 @@ org.apache.maven.plugins - 3.2.0 + 3.6.1 spotbugs-maven-plugin @@ -150,13 +157,14 @@ com.github.spotbugs - 4.0.4 + 4.8.3.1 maven-checkstyle-plugin - - https://raw.githubusercontent.com/dockstore/lambda/a6ae4d98592b0d057c495bdb7b40c54e062ff998/wdl-parsing/WDLParsingFunction/checkstyle.xml + + https://raw.githubusercontent.com/dockstore/dockstore/1.20.0/checkstyle.xml + https://raw.githubusercontent.com/dockstore/dockstore/1.20.0/checkstyle-suppressions.xml true true true @@ -165,7 +173,7 @@ checkstyle com.puppycrawl.tools - 8.34 + 10.0 @@ -182,6 +190,11 @@ fmt-maven-plugin + + + + true + @@ -189,8 +202,8 @@ - com.coveo - 2.10 + com.spotify.fmt + 2.29 maven-shade-plugin @@ -210,10 +223,10 @@ maven-compiler-plugin org.apache.maven.plugins - 3.8.1 + 3.12.1 - 11 - 11 + 21 + 21 diff --git a/nextflow-parsing/NextflowParsingFunction/src/main/java/io/dockstore/nextflowparsing/App.java b/nextflow-parsing/NextflowParsingFunction/src/main/java/io/dockstore/nextflowparsing/App.java index e525c17..3ae2f40 100644 --- a/nextflow-parsing/NextflowParsingFunction/src/main/java/io/dockstore/nextflowparsing/App.java +++ b/nextflow-parsing/NextflowParsingFunction/src/main/java/io/dockstore/nextflowparsing/App.java @@ -24,6 +24,7 @@ import io.dockstore.openapi.client.model.LanguageParsingRequest; import io.dockstore.openapi.client.model.LanguageParsingResponse; import io.dockstore.openapi.client.model.VersionTypeValidation; +import jakarta.ws.rs.core.MediaType; import java.io.File; import java.io.IOException; import java.io.PrintWriter; @@ -35,7 +36,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import javax.ws.rs.core.MediaType; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.errors.GitAPIException; import org.slf4j.Logger; @@ -43,122 +43,122 @@ /** Handler for requests to Lambda function. */ public class App - implements RequestHandler { + implements RequestHandler { - private static final Logger LOGGER = LoggerFactory.getLogger(App.class); - ObjectMapper mapper = new ObjectMapper(); + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + ObjectMapper mapper = new ObjectMapper(); - /** - * Get a language parsing response from NextflowHandler. - * - * @param nextflowHandler NextflowHandler that contains all relevant Nextflow information - * @return LanguageParsingResponse constructed after getting information from NextflowHandler - */ - public static LanguageParsingResponse getResponse(NextflowHandler nextflowHandler) { - LanguageParsingResponse response = new LanguageParsingResponse(); - String descriptorAbsolutePathString = nextflowHandler.getDescriptorTempAbsolutePath(); - response.setClonedRepositoryAbsolutePath(descriptorAbsolutePathString); - VersionTypeValidation versionTypeValidation = nextflowHandler.getVersionTypeValidation(); - if (nextflowHandler.getDescriptorContents() == null) { - versionTypeValidation.setValid(false); - Map messageMap = new HashMap<>(); - // TODO: Don't use temp absolute path - messageMap.put(nextflowHandler.getDescriptorTempAbsolutePath(), "File not found"); - versionTypeValidation.setMessage(messageMap); - } - if (nextflowHandler.getConfiguration() == null) { - versionTypeValidation.setValid(false); + /** + * Get a language parsing response from NextflowHandler. + * + * @param nextflowHandler NextflowHandler that contains all relevant Nextflow information + * @return LanguageParsingResponse constructed after getting information from NextflowHandler + */ + public static LanguageParsingResponse getResponse(NextflowHandler nextflowHandler) { + LanguageParsingResponse response = new LanguageParsingResponse(); + String descriptorAbsolutePathString = nextflowHandler.getDescriptorTempAbsolutePath(); + response.setClonedRepositoryAbsolutePath(descriptorAbsolutePathString); + VersionTypeValidation versionTypeValidation = nextflowHandler.getVersionTypeValidation(); + if (nextflowHandler.getDescriptorContents() == null) { + versionTypeValidation.setValid(false); + Map messageMap = new HashMap<>(); + // TODO: Don't use temp absolute path + messageMap.put(nextflowHandler.getDescriptorTempAbsolutePath(), "File not found"); + versionTypeValidation.setMessage(messageMap); + } + if (nextflowHandler.getConfiguration() == null) { + versionTypeValidation.setValid(false); + } + // TODO: This should be an array, don't join + response.setAuthor( + String.join( + ", ", NextflowUtilities.getAuthors(nextflowHandler.getConfiguration()))); + response.setDescription( + NextflowUtilities.getDescription(nextflowHandler.getConfiguration())); + versionTypeValidation.setValid(true); + response.setVersionTypeValidation(versionTypeValidation); + response.setSecondaryFilePaths(nextflowHandler.getSecondaryDescriptorPaths()); + return response; } - // TODO: This should be an array, don't join - response.setAuthor( - String.join(", ", NextflowUtilities.getAuthors(nextflowHandler.getConfiguration()))); - response.setDescription(NextflowUtilities.getDescription(nextflowHandler.getConfiguration())); - versionTypeValidation.setValid(true); - response.setVersionTypeValidation(versionTypeValidation); - response.setSecondaryFilePaths(nextflowHandler.getSecondaryDescriptorPaths()); - return response; - } - @Override - public APIGatewayProxyResponseEvent handleRequest( - final APIGatewayProxyRequestEvent input, final Context context) { + @Override + public APIGatewayProxyResponseEvent handleRequest( + final APIGatewayProxyRequestEvent input, final Context context) { - Map headers = new HashMap<>(); - headers.put("Content-Type", MediaType.APPLICATION_JSON); + Map headers = new HashMap<>(); + headers.put("Content-Type", MediaType.APPLICATION_JSON); - APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent().withHeaders(headers); - if (input != null && input.getBody() != null) { - try { - LanguageParsingRequest request = - mapper.readValue(input.getBody(), LanguageParsingRequest.class); - try { - String s = parseFile(request); - return response.withStatusCode(HttpURLConnection.HTTP_OK).withBody(s); - } catch (IOException e) { - String errorMessage = "Could not clone repository to temporary directory"; - LOGGER.error(errorMessage, e); - return response - .withBody(errorMessage) - .withStatusCode(HttpURLConnection.HTTP_INTERNAL_ERROR); - } catch (GitAPIException e) { - StringWriter sw = new StringWriter(); - e.printStackTrace(new PrintWriter(sw)); - String exceptionAsString = sw.toString(); - return response - .withBody(exceptionAsString) - .withStatusCode(HttpURLConnection.HTTP_INTERNAL_ERROR); + APIGatewayProxyResponseEvent response = + new APIGatewayProxyResponseEvent().withHeaders(headers); + if (input != null && input.getBody() != null) { + try { + LanguageParsingRequest request = + mapper.readValue(input.getBody(), LanguageParsingRequest.class); + try { + String s = parseFile(request); + return response.withStatusCode(HttpURLConnection.HTTP_OK).withBody(s); + } catch (IOException e) { + String errorMessage = "Could not clone repository to temporary directory"; + LOGGER.error(errorMessage, e); + return response.withBody(errorMessage) + .withStatusCode(HttpURLConnection.HTTP_INTERNAL_ERROR); + } catch (GitAPIException e) { + StringWriter sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw)); + String exceptionAsString = sw.toString(); + return response.withBody(exceptionAsString) + .withStatusCode(HttpURLConnection.HTTP_INTERNAL_ERROR); + } + } catch (IOException e) { + String errorMessage = "Could not process request"; + LOGGER.error(errorMessage, e); + return response.withBody(errorMessage) + .withStatusCode(HttpURLConnection.HTTP_BAD_REQUEST); + } + } else { + return response.withBody("No body in request") + .withStatusCode(HttpURLConnection.HTTP_BAD_REQUEST); } - } catch (IOException e) { - String errorMessage = "Could not process request"; - LOGGER.error(errorMessage, e); - return response.withBody(errorMessage).withStatusCode(HttpURLConnection.HTTP_BAD_REQUEST); - } - } else { - return response - .withBody("No body in request") - .withStatusCode(HttpURLConnection.HTTP_BAD_REQUEST); } - } - private String parseFile(LanguageParsingRequest languageParsingRequest) - throws IOException, GitAPIException { - Path tempDirWithPrefix = Files.createTempDirectory("clonedRepository"); - Git.cloneRepository() - .setCloneAllBranches(false) - .setBranch(languageParsingRequest.getBranch()) - .setURI(languageParsingRequest.getUri()) - .setDirectory(tempDirWithPrefix.toFile()) - .call(); - Path descriptorAbsolutePath = - tempDirWithPrefix.resolve(languageParsingRequest.getDescriptorRelativePathInGit()); - String descriptorAbsolutePathString = descriptorAbsolutePath.toString(); - NextflowHandler nextflowHandler = new NextflowHandler(); - nextflowHandler.setDescriptorTempAbsolutePath(descriptorAbsolutePathString); - nextflowHandler.setConfiguration( - NextflowUtilities.getConfig(new File(descriptorAbsolutePathString))); - try { - String s = Files.readString(Path.of(descriptorAbsolutePathString)); + private String parseFile(LanguageParsingRequest languageParsingRequest) + throws IOException, GitAPIException { + Path tempDirWithPrefix = Files.createTempDirectory("clonedRepository"); + Git.cloneRepository() + .setCloneAllBranches(false) + .setBranch(languageParsingRequest.getBranch()) + .setURI(languageParsingRequest.getUri()) + .setDirectory(tempDirWithPrefix.toFile()) + .call(); + Path descriptorAbsolutePath = + tempDirWithPrefix.resolve(languageParsingRequest.getDescriptorRelativePathInGit()); + String descriptorAbsolutePathString = descriptorAbsolutePath.toString(); + NextflowHandler nextflowHandler = new NextflowHandler(); + nextflowHandler.setDescriptorTempAbsolutePath(descriptorAbsolutePathString); + nextflowHandler.setConfiguration( + NextflowUtilities.getConfig(new File(descriptorAbsolutePathString))); + try { + String s = Files.readString(Path.of(descriptorAbsolutePathString)); - nextflowHandler.setDescriptorContents(s); - List strings = - nextflowHandler.processImports(nextflowHandler.getDescriptorContents()); - nextflowHandler.setSecondaryDescriptorPaths(strings); - } catch (IOException e) { - LOGGER.error(e.getMessage()); - nextflowHandler.setDescriptorContents(null); - } - LanguageParsingResponse response = getResponse(nextflowHandler); - response.setLanguageParsingRequest(languageParsingRequest); - // Deleting a directory without Common IO - Files.walk(tempDirWithPrefix) - .sorted(Comparator.reverseOrder()) - .map(Path::toFile) - .forEach(File::delete); - if (response.getSecondaryFilePaths() != null) { - response - .getSecondaryFilePaths() - .replaceAll(s -> s.replaceFirst(tempDirWithPrefix.toString(), "")); + nextflowHandler.setDescriptorContents(s); + List strings = + nextflowHandler.processImports(nextflowHandler.getDescriptorContents()); + nextflowHandler.setSecondaryDescriptorPaths(strings); + } catch (IOException e) { + LOGGER.error(e.getMessage()); + nextflowHandler.setDescriptorContents(null); + } + LanguageParsingResponse response = getResponse(nextflowHandler); + response.setLanguageParsingRequest(languageParsingRequest); + // Deleting a directory without Common IO + Files.walk(tempDirWithPrefix) + .sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); + if (response.getSecondaryFilePaths() != null) { + response.getSecondaryFilePaths() + .replaceAll(s -> s.replaceFirst(tempDirWithPrefix.toString(), "")); + } + return mapper.writeValueAsString(response); } - return mapper.writeValueAsString(response); - } } diff --git a/nextflow-parsing/NextflowParsingFunction/src/main/java/io/dockstore/nextflowparsing/NextflowHandler.java b/nextflow-parsing/NextflowParsingFunction/src/main/java/io/dockstore/nextflowparsing/NextflowHandler.java index 1e895a9..8e601dd 100644 --- a/nextflow-parsing/NextflowParsingFunction/src/main/java/io/dockstore/nextflowparsing/NextflowHandler.java +++ b/nextflow-parsing/NextflowParsingFunction/src/main/java/io/dockstore/nextflowparsing/NextflowHandler.java @@ -32,113 +32,115 @@ public class NextflowHandler { - private String descriptorContents; - private String descriptorTempAbsolutePath; - private ConfigObject configuration; - private VersionTypeValidation versionTypeValidation = new VersionTypeValidation(); - private List secondaryDescriptorPaths; - protected static final Pattern IMPORT_PATTERN = - Pattern.compile("^\\s*include.+?from.+?'.+?'", Pattern.DOTALL | Pattern.MULTILINE); - private static final Pattern INCLUDE_CONFIG_PATTERN = - Pattern.compile("(?i)(?m)^[ \t]*includeConfig(.*)"); - - public String getDescriptorTempAbsolutePath() { - return descriptorTempAbsolutePath; - } - - public void setDescriptorTempAbsolutePath(String descriptorTempAbsolutePath) { - this.descriptorTempAbsolutePath = descriptorTempAbsolutePath; - } - - public String getDescriptorContents() { - return descriptorContents; - } - - public void setDescriptorContents(String descriptorContents) { - this.descriptorContents = descriptorContents; - } - - /** - * Get the relative path imports of the workflow. - * - * @param content The contents of the main descriptor file - * @return - */ - public List processImports(String content) { - // FIXME: see{@link NextflowUtilities#grabConfig(String) grabConfig} method for comments on why - // we have to look at imports in this crummy way - final Matcher matcher = INCLUDE_CONFIG_PATTERN.matcher(content); - Set suspectedConfigImports = new HashSet<>(); - while (matcher.find()) { - suspectedConfigImports.add(CharMatcher.is('\'').trimFrom(matcher.group(1).trim())); + private String descriptorContents; + private String descriptorTempAbsolutePath; + private ConfigObject configuration; + private VersionTypeValidation versionTypeValidation = new VersionTypeValidation(); + private List secondaryDescriptorPaths; + protected static final Pattern IMPORT_PATTERN = + Pattern.compile("^\\s*include.+?from.+?'.+?'", Pattern.DOTALL | Pattern.MULTILINE); + private static final Pattern INCLUDE_CONFIG_PATTERN = + Pattern.compile("(?i)(?m)^[ \t]*includeConfig(.*)"); + + public String getDescriptorTempAbsolutePath() { + return descriptorTempAbsolutePath; } - List imports = new ArrayList<>(); - // add the Nextflow scripts - String mainScriptPath = "main.nf"; - String manifest = getManifest(configuration); - if (manifest != null) { - mainScriptPath = manifest; + public void setDescriptorTempAbsolutePath(String descriptorTempAbsolutePath) { + this.descriptorTempAbsolutePath = descriptorTempAbsolutePath; } - suspectedConfigImports.add(mainScriptPath); - - imports.addAll(handleNextflowImports("bin")); - imports.addAll(handleNextflowImports("lib")); - imports.addAll(suspectedConfigImports); - return imports; - } - - private String getManifest(ConfigObject configObject) { - try { - Map property = (Map) configObject.get("manifest"); - return (String) property.get("mainScript"); - } catch (Exception e) { - return null; + + public String getDescriptorContents() { + return descriptorContents; + } + + public void setDescriptorContents(String descriptorContents) { + this.descriptorContents = descriptorContents; + } + + /** + * Get the relative path imports of the workflow. + * + * @param content The contents of the main descriptor file + * @return + */ + public List processImports(String content) { + // FIXME: see{@link NextflowUtilities#grabConfig(String) grabConfig} method for comments on + // why + // we have to look at imports in this crummy way + final Matcher matcher = INCLUDE_CONFIG_PATTERN.matcher(content); + Set suspectedConfigImports = new HashSet<>(); + while (matcher.find()) { + suspectedConfigImports.add(CharMatcher.is('\'').trimFrom(matcher.group(1).trim())); + } + List imports = new ArrayList<>(); + + // add the Nextflow scripts + String mainScriptPath = "main.nf"; + String manifest = getManifest(configuration); + if (manifest != null) { + mainScriptPath = manifest; + } + suspectedConfigImports.add(mainScriptPath); + + imports.addAll(handleNextflowImports("bin")); + imports.addAll(handleNextflowImports("lib")); + imports.addAll(suspectedConfigImports); + return imports; + } + + private String getManifest(ConfigObject configObject) { + try { + Map property = (Map) configObject.get("manifest"); + return (String) property.get("mainScript"); + } catch (Exception e) { + return null; + } } - } - - /** - * Get relative file paths from directory. - * - * @param directory The directory to get files from - * @return List of files relative to the main descriptor file - */ - private List handleNextflowImports(String directory) { - File binDirectory = Paths.get(descriptorTempAbsolutePath).resolveSibling(directory).toFile(); - List binFiles = new ArrayList<>(); - String[] binFileNames = - binDirectory.list((current, name) -> !(new File(current, name).isDirectory())); - if (binFileNames != null) { - List binFileNamesList = List.of(binFileNames); - binFiles = - binFileNamesList.stream() - .map(string -> directory + "/" + string) - .collect(Collectors.toList()); + + /** + * Get relative file paths from directory. + * + * @param directory The directory to get files from + * @return List of files relative to the main descriptor file + */ + private List handleNextflowImports(String directory) { + File binDirectory = + Paths.get(descriptorTempAbsolutePath).resolveSibling(directory).toFile(); + List binFiles = new ArrayList<>(); + String[] binFileNames = + binDirectory.list((current, name) -> !(new File(current, name).isDirectory())); + if (binFileNames != null) { + List binFileNamesList = List.of(binFileNames); + binFiles = + binFileNamesList.stream() + .map(string -> directory + "/" + string) + .collect(Collectors.toList()); + } + return binFiles; } - return binFiles; - } - public ConfigObject getConfiguration() { - return configuration; - } + public ConfigObject getConfiguration() { + return configuration; + } - public void setConfiguration(ConfigObject configuration) { - this.configuration = configuration; - } + public void setConfiguration(ConfigObject configuration) { + this.configuration = configuration; + } - public VersionTypeValidation getVersionTypeValidation() { - return this.versionTypeValidation; - } + public VersionTypeValidation getVersionTypeValidation() { + return this.versionTypeValidation; + } - public void setVersionTypeValidation(VersionTypeValidation versionTypeValidation) { - this.versionTypeValidation = versionTypeValidation; - } + public void setVersionTypeValidation(VersionTypeValidation versionTypeValidation) { + this.versionTypeValidation = versionTypeValidation; + } - public List getSecondaryDescriptorPaths() { - return secondaryDescriptorPaths; - } + public List getSecondaryDescriptorPaths() { + return secondaryDescriptorPaths; + } - public void setSecondaryDescriptorPaths(List secondaryDescriptorPaths) { - this.secondaryDescriptorPaths = secondaryDescriptorPaths; - } + public void setSecondaryDescriptorPaths(List secondaryDescriptorPaths) { + this.secondaryDescriptorPaths = secondaryDescriptorPaths; + } } diff --git a/nextflow-parsing/NextflowParsingFunction/src/main/java/io/dockstore/nextflowparsing/NextflowUtilities.java b/nextflow-parsing/NextflowParsingFunction/src/main/java/io/dockstore/nextflowparsing/NextflowUtilities.java index fbed9ba..c57d8df 100644 --- a/nextflow-parsing/NextflowParsingFunction/src/main/java/io/dockstore/nextflowparsing/NextflowUtilities.java +++ b/nextflow-parsing/NextflowParsingFunction/src/main/java/io/dockstore/nextflowparsing/NextflowUtilities.java @@ -22,51 +22,53 @@ import java.util.List; import java.util.Map; import nextflow.config.ConfigParser; +import nextflow.config.ConfigParserFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public final class NextflowUtilities { - private static final Logger LOG = LoggerFactory.getLogger(NextflowUtilities.class); + private static final Logger LOG = LoggerFactory.getLogger(NextflowUtilities.class); - private NextflowUtilities() { - // hide the default constructor for a utility class - } + private NextflowUtilities() { + // hide the default constructor for a utility class + } - /** - * Get authors of the workflow. - * - * @param configuration The Nextflow configuration - * @return - */ - public static List getAuthors(ConfigObject configuration) { - try { - Map manifest = (Map) configuration.get("manifest"); - String author = (String) manifest.get("author"); - String[] authors = Arrays.stream(author.split(",")).map(String::trim).toArray(String[]::new); - return Arrays.asList(authors); - } catch (Exception e) { - return null; + /** + * Get authors of the workflow. + * + * @param configuration The Nextflow configuration + * @return + */ + public static List getAuthors(ConfigObject configuration) { + try { + Map manifest = (Map) configuration.get("manifest"); + String author = (String) manifest.get("author"); + String[] authors = + Arrays.stream(author.split(",")).map(String::trim).toArray(String[]::new); + return Arrays.asList(authors); + } catch (Exception e) { + return null; + } } - } - /** - * Get the description of the workflow. - * - * @param configuration The Nextflow configuration - * @return - */ - public static String getDescription(ConfigObject configuration) { - try { - Map manifest = (Map) configuration.get("manifest"); - return (String) manifest.get("description"); - } catch (Exception e) { - return null; + /** + * Get the description of the workflow. + * + * @param configuration The Nextflow configuration + * @return + */ + public static String getDescription(ConfigObject configuration) { + try { + Map manifest = (Map) configuration.get("manifest"); + return (String) manifest.get("description"); + } catch (Exception e) { + return null; + } } - } - public static ConfigObject getConfig(File tempMainDescriptor) { - ConfigParser configParser = new ConfigParser(); - return configParser.parse(tempMainDescriptor); - } + public static ConfigObject getConfig(File tempMainDescriptor) { + ConfigParser configParser = ConfigParserFactory.create(); + return configParser.parse(tempMainDescriptor); + } } diff --git a/nextflow-parsing/NextflowParsingFunction/src/test/java/io/dockstore/nextflowparsing/AppTest.java b/nextflow-parsing/NextflowParsingFunction/src/test/java/io/dockstore/nextflowparsing/AppTest.java index 85c5ea8..aa22301 100644 --- a/nextflow-parsing/NextflowParsingFunction/src/test/java/io/dockstore/nextflowparsing/AppTest.java +++ b/nextflow-parsing/NextflowParsingFunction/src/test/java/io/dockstore/nextflowparsing/AppTest.java @@ -25,44 +25,45 @@ import com.fasterxml.jackson.databind.ObjectMapper; import io.dockstore.openapi.client.model.LanguageParsingRequest; import io.dockstore.openapi.client.model.LanguageParsingResponse; +import jakarta.ws.rs.core.MediaType; import java.net.HttpURLConnection; -import javax.ws.rs.core.MediaType; import org.junit.jupiter.api.Test; public class AppTest { - @Test - public void successfulResponse() throws Exception { - LanguageParsingRequest request = new LanguageParsingRequest(); - request.setBranch("2.3"); - // Archived repository with tag that is unlikely to change - request.setUri("https://github.com/SciLifeLab/Sarek.git"); - request.setDescriptorRelativePathInGit("nextflow.config"); - App app = new App(); - APIGatewayProxyRequestEvent requestEvent = new APIGatewayProxyRequestEvent(); - ObjectMapper objectMapper = new ObjectMapper(); - requestEvent.setBody(objectMapper.writeValueAsString(request)); - APIGatewayProxyResponseEvent result = app.handleRequest(requestEvent, null); - System.out.println(result.getBody()); - assertEquals(HttpURLConnection.HTTP_OK, result.getStatusCode().intValue()); - assertEquals(MediaType.APPLICATION_JSON, result.getHeaders().get("Content-Type")); - String content = result.getBody(); - assertNotNull(content); - LanguageParsingResponse response = - objectMapper.readValue(content, LanguageParsingResponse.class); - assertNotNull(response.getVersionTypeValidation()); - assertNotNull(response.getVersionTypeValidation().isValid()); - assertTrue(response.getVersionTypeValidation().isValid()); - assertNotNull(response.getClonedRepositoryAbsolutePath()); - assertTrue(response.getClonedRepositoryAbsolutePath().contains("clonedRepository")); - assertNotNull(response.getSecondaryFilePaths()); - final int knownSecondaryFilesCount = 23; - assertEquals( - knownSecondaryFilesCount, - response.getSecondaryFilePaths().size(), - "Should be 4 bin files, 16 config files, 2 lib files, and main.nf"); - assertEquals("Sarek - Workflow For Somatic And Germline Variations", response.getDescription()); - System.out.println(response.getClonedRepositoryAbsolutePath()); - assertEquals("Szilvester Juhos, Maxime Garcia", response.getAuthor()); - } + @Test + public void successfulResponse() throws Exception { + LanguageParsingRequest request = new LanguageParsingRequest(); + request.setBranch("2.3"); + // Archived repository with tag that is unlikely to change + request.setUri("https://github.com/SciLifeLab/Sarek.git"); + request.setDescriptorRelativePathInGit("nextflow.config"); + App app = new App(); + APIGatewayProxyRequestEvent requestEvent = new APIGatewayProxyRequestEvent(); + ObjectMapper objectMapper = new ObjectMapper(); + requestEvent.setBody(objectMapper.writeValueAsString(request)); + APIGatewayProxyResponseEvent result = app.handleRequest(requestEvent, null); + System.out.println(result.getBody()); + assertEquals(HttpURLConnection.HTTP_OK, result.getStatusCode().intValue()); + assertEquals(MediaType.APPLICATION_JSON, result.getHeaders().get("Content-Type")); + String content = result.getBody(); + assertNotNull(content); + LanguageParsingResponse response = + objectMapper.readValue(content, LanguageParsingResponse.class); + assertNotNull(response.getVersionTypeValidation()); + assertNotNull(response.getVersionTypeValidation().isValid()); + assertTrue(response.getVersionTypeValidation().isValid()); + assertNotNull(response.getClonedRepositoryAbsolutePath()); + assertTrue(response.getClonedRepositoryAbsolutePath().contains("clonedRepository")); + assertNotNull(response.getSecondaryFilePaths()); + final int knownSecondaryFilesCount = 23; + assertEquals( + knownSecondaryFilesCount, + response.getSecondaryFilePaths().size(), + "Should be 4 bin files, 16 config files, 2 lib files, and main.nf"); + assertEquals( + "Sarek - Workflow For Somatic And Germline Variations", response.getDescription()); + System.out.println(response.getClonedRepositoryAbsolutePath()); + assertEquals("Szilvester Juhos, Maxime Garcia", response.getAuthor()); + } } diff --git a/nextflow-parsing/events/sarek.json b/nextflow-parsing/events/sarek.json new file mode 100644 index 0000000..aa94998 --- /dev/null +++ b/nextflow-parsing/events/sarek.json @@ -0,0 +1,5 @@ +{ + "httpMethod": "POST", + "path": "/parse", + "body": "{\"uri\":\"https://github.com/SciLifeLab/Sarek.git\",\"branch\":\"2.3\",\"descriptorRelativePathInGit\":\"nextflow.config\"}" +} diff --git a/nextflow-parsing/template.yaml b/nextflow-parsing/template.yaml index fa81242..eb6ea42 100644 --- a/nextflow-parsing/template.yaml +++ b/nextflow-parsing/template.yaml @@ -15,8 +15,8 @@ Resources: Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction Properties: CodeUri: NextflowParsingFunction - Handler: helloworld.App::handleRequest - Runtime: java11 + Handler: io.dockstore.nextflowparsing.App::handleRequest + Runtime: java21 MemorySize: 512 Environment: # More info about Env Vars: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#environment-object Variables: