diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..2539c7b --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,46 @@ +name: nf-ga4gh CI +on: + push: + branches: + - '*' + tags-ignore: + - '*' + pull_request: + branches: + - '*' +jobs: + build: + name: Build nf-ga4gh + if: "!contains(github.event.head_commit.message, '[ci skip]')" + runs-on: ubuntu-latest + timeout-minutes: 10 + strategy: + fail-fast: false + matrix: + java_version: [11, 21] + + steps: + - name: Environment + run: env | sort + + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 1 + submodules: true + + - name: Setup Java ${{ matrix.java_version }} + uses: actions/setup-java@v3 + with: + java-version: ${{matrix.java_version}} + architecture: x64 + distribution: 'temurin' + + - name: Compile + run: ./gradlew assemble + + - name: Tests + run: ./gradlew check + env: + GRADLE_OPTS: '-Dorg.gradle.daemon=false' + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a47b503 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +# Ignore Gradle project-specific cache directory +.gradle +.idea +.nextflow* + +# Ignore Gradle build output directory +build +work +out diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..68c771a --- /dev/null +++ b/COPYING @@ -0,0 +1,176 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4d140c6 --- /dev/null +++ b/Makefile @@ -0,0 +1,71 @@ +config ?= compileClasspath + +ifdef module +mm = :${module}: +else +mm = +endif + +clean: + rm -rf .nextflow* + rm -rf work + rm -rf build + rm -rf plugins/*/build + ./gradlew clean + +compile: + ./gradlew compileGroovy + @echo "DONE `date`" + + +check: + ./gradlew check + + +# +# Show dependencies try `make deps config=runtime`, `make deps config=google` +# +deps: + ./gradlew -q ${mm}dependencies --configuration ${config} + +deps-all: + ./gradlew -q dependencyInsight --configuration ${config} --dependency ${module} + +# +# Refresh SNAPSHOTs dependencies +# +refresh: + ./gradlew --refresh-dependencies + +# +# Run all tests or selected ones +# +test: +ifndef class + ./gradlew ${mm}test +else + ./gradlew ${mm}test --tests ${class} +endif + +assemble: + ./gradlew assemble + +# +# generate build zips under build/plugins +# you can install the plugin copying manually these files to $HOME/.nextflow/plugins +# +buildPlugins: + ./gradlew copyPluginZip + +# +# Upload JAR artifacts to Maven Central +# +upload: + ./gradlew upload + + +upload-plugins: + ./gradlew plugins:upload + +publish-index: + ./gradlew plugins:publishIndex diff --git a/README.md b/README.md index 596c44a..4249614 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,147 @@ -# nf-ga4gh -GA4GH plugin for Nextflow +# nf-ga4gh plugin + +This plugin implements the support for GA4GH APIs for Nextflow. Currently only supports the [Task Execution Service (TES) API](https://github.com/ga4gh/task-execution-schemas). + +See the [Nextflow documentation](https://nextflow.io/docs/latest/plugins.html) for more information about developing plugins. + +## Configuration + +The [Task Execution Schema](https://github.com/ga4gh/task-execution-schemas) (TES) project by the [GA4GH](https://www.ga4gh.org) standardization initiative is an effort to define a standardized schema and API for describing batch execution tasks in a portable manner. + +Nextflow supports the TES API via the `tes` executor, which allows the submission of workflow tasks to a remote execution backend exposing a TES API endpoint. + +The pipeline processes must specify the Docker image to use by defining the `container` directive, either in the pipeline script or the `nextflow.config` file. Additionally, the pipeline work directory must be accessible to the TES backend. + +To enable this executor, add the following settings to your Nextflow configuration: + +```groovy +plugins { + id 'nf-ga4gh' +} + +process.executor = 'tes' +tes.endpoint = '' +``` + +The default endpoint is `http://localhost:8000`. It is important that the endpoint is specified without the trailing slash; otherwise, the resulting URLs will not be normalized and the requests to TES will fail. + +The TES API supports multiple forms of authentication: + +```groovy +// basic +tes.basicUsername = '' +tes.basicPassword = '' + +// API key +tes.apiKeyParamMode = '' // 'query' or 'header' +tes.apiKeyParamName = '' +tes.apiKey = '' + +// OAuth +tes.oauthToken = '' +``` + +:::{tip} +You can deploy a local [Funnel](https://ohsu-comp-bio.github.io/funnel/) server using the following command: + +```bash +./funnel server --Server.HTTPPort 8000 --LocalStorage.AllowedDirs $HOME run +``` +::: + +:::{note} +While the TES API is designed to abstract workflow managers from direct storage access, Nextflow still needs to access the shared work directory used by your TES endpoint. For example, if your TES endpoint is located in Azure and uses Azure Blob storage to store the work directory, you still need to provide the necessary Azure credentials for Nextflow to access the Blob storage. +::: + +## Plugin structure + +- `settings.gradle` + + Gradle project settings. + +- `plugins/nf-ga4gh` + + The plugin implementation base directory. + +- `plugins/nf-ga4gh/build.gradle` + + Plugin Gradle build file. Project dependencies should be added here. + +- `plugins/nf-ga4gh/src/resources/META-INF/MANIFEST.MF` + + Manifest file defining the plugin attributes e.g. name, version, etc. The attribute `Plugin-Class` declares the plugin main class. This class should extend the base class `nextflow.plugin.BasePlugin` e.g. `nextflow.hello.HelloPlugin`. + +- `plugins/nf-ga4gh/src/resources/META-INF/extensions.idx` + + This file declares one or more extension classes provided by the plugin. Each line should contain the fully qualified name of a Java class that implements the `org.pf4j.ExtensionPoint` interface (or a sub-interface). + +- `plugins/nf-ga4gh/src/main` + + The plugin implementation sources. + +- `plugins/nf-ga4gh/src/test` + + The plugin unit tests. + + +## Unit testing + +To run your unit tests, run the following command in the project root directory (ie. where the file `settings.gradle` is located): + +```bash +./gradlew check +``` + +## Testing and debugging + +To build and test the plugin during development, configure a local Nextflow build with the following steps: + +1. Clone the Nextflow repository in your computer into a sibling directory: + ```bash + git clone --depth 1 https://github.com/nextflow-io/nextflow ../nextflow + ``` + +2. Configure the plugin build to use the local Nextflow code: + ```bash + echo "includeBuild('../nextflow')" >> settings.gradle + ``` + + (Make sure to not add it more than once!) + +3. Compile the plugin alongside the Nextflow code: + ```bash + make assemble + ``` + +4. Run Nextflow with the plugin, using `./launch.sh` as a drop-in replacement for the `nextflow` command, and adding the option `-plugins nf-ga4gh` to load the plugin: + ```bash + ./launch.sh run nextflow-io/hello -plugins nf-ga4gh + ``` + +## Testing without Nextflow build + +The plugin can be tested without using a local Nextflow build using the following steps: + +1. Build the plugin: `make buildPlugins` +2. Copy `build/plugins/` to `$HOME/.nextflow/plugins` +3. Create a pipeline that uses your plugin and run it: `nextflow run ./my-pipeline-script.nf` + +## Package, upload, and publish + +The project should be hosted in a GitHub repository whose name matches the name of the plugin, that is the name of the directory in the `plugins` folder (e.g. `nf-ga4gh`). + +Follow these steps to package, upload and publish the plugin: + +1. Create a file named `gradle.properties` in the project root containing the following attributes (this file should not be committed to Git): + + * `github_organization`: the GitHub organisation where the plugin repository is hosted. + * `github_username`: The GitHub username granting access to the plugin repository. + * `github_access_token`: The GitHub access token required to upload and commit changes to the plugin repository. + * `github_commit_email`: The email address associated with your GitHub account. + +2. Use the following command to package and create a release for your plugin on GitHub: + ```bash + ./gradlew :plugins:nf-ga4gh:upload + ``` + +3. Create a pull request against [nextflow-io/plugins](https://github.com/nextflow-io/plugins/blob/main/plugins.json) to make the plugin accessible to Nextflow. diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle new file mode 100644 index 0000000..66a0569 --- /dev/null +++ b/buildSrc/build.gradle @@ -0,0 +1,14 @@ +/* + * This file was generated by the Gradle 'init' task. + */ + +plugins { + // Support convention plugins written in Groovy. Convention plugins are build scripts in 'src/main' that automatically become available as plugins in the main build. + id 'groovy-gradle-plugin' +} + +repositories { + // Use the plugin portal to apply community plugins in convention plugins. + gradlePluginPortal() +} + diff --git a/buildSrc/src/main/groovy/io.nextflow.groovy-application-conventions.gradle b/buildSrc/src/main/groovy/io.nextflow.groovy-application-conventions.gradle new file mode 100644 index 0000000..ecc029b --- /dev/null +++ b/buildSrc/src/main/groovy/io.nextflow.groovy-application-conventions.gradle @@ -0,0 +1,11 @@ +/* + * This file was generated by the Gradle 'init' task. + */ + +plugins { + // Apply the common convention plugin for shared build configuration between library and application projects. + id 'io.nextflow.groovy-common-conventions' + + // Apply the application plugin to add support for building a CLI application in Java. + id 'application' +} diff --git a/buildSrc/src/main/groovy/io.nextflow.groovy-common-conventions.gradle b/buildSrc/src/main/groovy/io.nextflow.groovy-common-conventions.gradle new file mode 100644 index 0000000..657b71e --- /dev/null +++ b/buildSrc/src/main/groovy/io.nextflow.groovy-common-conventions.gradle @@ -0,0 +1,48 @@ +/* + * This file was generated by the Gradle 'init' task. + */ + +plugins { + // Apply the groovy Plugin to add support for Groovy. + id 'groovy' +} + +repositories { + // Use Maven Central for resolving dependencies. + mavenCentral() +} + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } +} + +compileJava { + options.release.set(11) +} + +tasks.withType(GroovyCompile) { + sourceCompatibility = '11' + targetCompatibility = '11' +} + +tasks.withType(Test) { + jvmArgs ([ + '--add-opens=java.base/java.lang=ALL-UNNAMED', + '--add-opens=java.base/java.io=ALL-UNNAMED', + '--add-opens=java.base/java.nio=ALL-UNNAMED', + '--add-opens=java.base/java.nio.file.spi=ALL-UNNAMED', + '--add-opens=java.base/java.net=ALL-UNNAMED', + '--add-opens=java.base/java.util=ALL-UNNAMED', + '--add-opens=java.base/java.util.concurrent.locks=ALL-UNNAMED', + '--add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED', + '--add-opens=java.base/sun.nio.ch=ALL-UNNAMED', + '--add-opens=java.base/sun.nio.fs=ALL-UNNAMED', + '--add-opens=java.base/sun.net.www.protocol.http=ALL-UNNAMED', + '--add-opens=java.base/sun.net.www.protocol.https=ALL-UNNAMED', + '--add-opens=java.base/sun.net.www.protocol.ftp=ALL-UNNAMED', + '--add-opens=java.base/sun.net.www.protocol.file=ALL-UNNAMED', + '--add-opens=java.base/jdk.internal.misc=ALL-UNNAMED', + ]) +} diff --git a/buildSrc/src/main/groovy/io.nextflow.groovy-library-conventions.gradle b/buildSrc/src/main/groovy/io.nextflow.groovy-library-conventions.gradle new file mode 100644 index 0000000..0183e2c --- /dev/null +++ b/buildSrc/src/main/groovy/io.nextflow.groovy-library-conventions.gradle @@ -0,0 +1,11 @@ +/* + * This file was generated by the Gradle 'init' task. + */ + +plugins { + // Apply the common convention plugin for shared build configuration between library and application projects. + id 'io.nextflow.groovy-common-conventions' + // Apply the java-library plugin for API and implementation separation. + id 'java-library' +} + diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..033e24c Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..a441313 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..fcb6fca --- /dev/null +++ b/gradlew @@ -0,0 +1,248 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..6689b85 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/launch.sh b/launch.sh new file mode 100755 index 0000000..7acbacc --- /dev/null +++ b/launch.sh @@ -0,0 +1,2 @@ +export NXF_PLUGINS_DEV=$PWD/plugins +../nextflow/launch.sh "$@" diff --git a/plugins/build.gradle b/plugins/build.gradle new file mode 100644 index 0000000..ed5a617 --- /dev/null +++ b/plugins/build.gradle @@ -0,0 +1,178 @@ +/* + * Copyright 2021-2022, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + id "java" + id "io.nextflow.nf-build-plugin" version "1.0.1" +} + +ext.github_organization = 'nextflow-io' +ext.github_username = project.findProperty('github_username') ?: 'pditommaso' +ext.github_access_token = project.findProperty('github_access_token') ?: System.getenv('GITHUB_TOKEN') +ext.github_commit_email = project.findProperty('github_commit_email') ?: 'paolo.ditommaso@gmail.com' + +jar.enabled = false + +String computeSha512(File file) { + if( !file.exists() ) + throw new GradleException("Missing file: $file -- cannot compute SHA-512") + return org.apache.commons.codec.digest.DigestUtils.sha512Hex(file.bytes) +} + +String now() { + "${java.time.OffsetDateTime.now().format(java.time.format.DateTimeFormatter.ISO_DATE_TIME)}" +} + +List allPlugins() { + def plugins = [] + new File(rootProject.rootDir, 'plugins') .eachDir { if(it.name.startsWith('nf-')) plugins.add(it.name) } + return plugins +} + +String metaFromManifest(String meta, File file) { + def str = file.text + def regex = ~/(?m)^$meta:\s*([\w-\.<>=]+)$/ + def m = regex.matcher(str) + if( m.find() ) { + def ver = m.group(1) + //println "Set plugin '${file.parentFile.parentFile.parentFile.parentFile.name}' version=${ver}" + return ver + } + throw new GradleException("Cannot find '$meta' for plugin: $file") +} + +def timestamp = now() + +subprojects { + apply plugin: 'java' + apply plugin: 'groovy' + apply plugin: 'io.nextflow.nf-build-plugin' + + repositories { + mavenLocal() + mavenCentral() + } + + version = metaFromManifest('Plugin-Version',file('src/resources/META-INF/MANIFEST.MF')) + + tasks.withType(Jar) { + duplicatesStrategy = DuplicatesStrategy.INCLUDE + } + + /* + * Creates plugin zip and json meta file in plugin `build/libs` directory + */ + task makeZip(type: Jar) { + into('classes') { with jar } + into('lib') { from configurations.runtimeClasspath } + manifest.from file('src/resources/META-INF/MANIFEST.MF') + archiveExtension = 'zip' + preserveFileTimestamps = false + reproducibleFileOrder = true + + doLast { + // create the meta file + final zip = new File("$buildDir/libs/${project.name}-${project.version}.zip") + final json = new File("$buildDir/libs/${project.name}-${project.version}-meta.json") + json.text = """\ + { + "version": "${project.version}", + "date": "${timestamp}", + "url": "https://github.com/${github_organization}/${project.name}/releases/download/${project.version}/${project.name}-${project.version}.zip", + "requires": "${metaFromManifest('Plugin-Requires',file('src/resources/META-INF/MANIFEST.MF'))}", + "sha512sum": "${computeSha512(zip)}" + } + """.stripIndent() + // cleanup tmp dir + file("$buildDir/tmp/makeZip").deleteDir() + } + outputs.file("$buildDir/libs/${project.name}-${project.version}.zip") + } + + /* + * Copy the plugin dependencies in the subproject `build/target/libs` directory + */ + task copyPluginLibs(type: Sync) { + from configurations.runtimeClasspath + into 'build/target/libs' + } + + /* + * Copy the plugin in the project root build/plugins directory + */ + task copyPluginZip(type: Copy, dependsOn: project.tasks.findByName('makeZip')) { + from makeZip + into "$rootProject.buildDir/plugins" + outputs.file("$rootProject.buildDir/plugins/${project.name}-${project.version}.zip") + doLast { + ant.unzip( + src: "$rootProject.buildDir/plugins/${project.name}-${project.version}.zip", + dest: "$rootProject.buildDir/plugins/${project.name}-${project.version}" + ) + } + } + + /* + * "install" the plugin the project root build/plugins directory + */ + project.parent.tasks.getByName("assemble").dependsOn << copyPluginZip + + task uploadPlugin(type: io.nextflow.gradle.tasks.GithubUploader, dependsOn: makeZip) { + assets = providers.provider {["$buildDir/libs/${project.name}-${project.version}.zip", + "$buildDir/libs/${project.name}-${project.version}-meta.json" ]} + release = providers.provider { project.version } + repo = providers.provider { project.name } + owner = github_organization + userName = github_username + authToken = github_access_token + skipExisting = true + } + + jar { + from sourceSets.main.allSource + doLast { + file("$buildDir/tmp/jar").deleteDir() + } + } + + tasks.withType(GenerateModuleMetadata) { + enabled = false + } + + task upload(dependsOn: [uploadPlugin] ) { } +} + +/* + * Upload all plugins to the corresponding GitHub repos + */ +task upload(dependsOn: [subprojects.uploadPlugin]) { } + +/* + * Copies the plugins required dependencies in the corresponding lib directory + */ +classes.dependsOn subprojects.copyPluginLibs + +/* + * Merge and publish the plugins index file + */ +task publishIndex( type: io.nextflow.gradle.tasks.GithubRepositoryPublisher ) { + indexUrl = 'https://github.com/nextflow-io/plugins/main/plugins.json' + repos = allPlugins() + owner = github_organization + githubUser = github_username + githubEmail = github_commit_email + githubToken = github_access_token +} diff --git a/plugins/nf-ga4gh/build.gradle b/plugins/nf-ga4gh/build.gradle new file mode 100644 index 0000000..0a70f75 --- /dev/null +++ b/plugins/nf-ga4gh/build.gradle @@ -0,0 +1,94 @@ +/* + * Copyright 2021-2022, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + // Apply the groovy plugin to add support for Groovy + id 'io.nextflow.groovy-library-conventions' + id 'idea' +} + +group = 'io.nextflow' +// DO NOT SET THE VERSION HERE +// THE VERSION FOR PLUGINS IS DEFINED IN THE `/resources/META-INF/MANIFEST.NF` file + +idea { + module.inheritOutputDirs = true +} + +repositories { + mavenCentral() + maven { url = 'https://jitpack.io' } + maven { url = 'https://s3-eu-west-1.amazonaws.com/maven.seqera.io/releases' } + maven { url = 'https://s3-eu-west-1.amazonaws.com/maven.seqera.io/snapshots' } +} + +configurations { + // see https://docs.gradle.org/4.1/userguide/dependency_management.html#sub:exclude_transitive_dependencies + runtimeClasspath.exclude group: 'org.slf4j', module: 'slf4j-api' +} + +sourceSets { + main.java.srcDirs = [] + main.groovy.srcDirs = ['src/main'] + main.resources.srcDirs = ['src/resources'] + test.groovy.srcDirs = ['src/test'] + test.java.srcDirs = [] + test.resources.srcDirs = ['src/testResources'] +} + +ext{ + nextflowVersion = '24.04.0-edge' +} + +dependencies { + // This dependency is exported to consumers, that is to say found on their compile classpath. + compileOnly "io.nextflow:nextflow:$nextflowVersion" + compileOnly 'org.slf4j:slf4j-api:2.0.7' + compileOnly 'org.pf4j:pf4j:3.10.0' + // add here plugins depepencies + api 'javax.annotation:javax.annotation-api:1.3.2' + api 'io.swagger.core.v3:swagger-annotations:2.0.0' + api 'com.squareup.okhttp:okhttp:2.7.5' + api 'com.squareup.okhttp:logging-interceptor:2.7.5' + api 'com.google.code.gson:gson:2.10.1' + api 'io.gsonfire:gson-fire:1.8.3' + api 'org.threeten:threetenbp:1.3.5' + + // test configuration + testImplementation "org.apache.groovy:groovy:4.0.21" + testImplementation "org.apache.groovy:groovy-nio:4.0.21" + testImplementation "io.nextflow:nextflow:$nextflowVersion" + testImplementation ("org.apache.groovy:groovy-test:4.0.21") { exclude group: 'org.apache.groovy' } + testImplementation ("cglib:cglib-nodep:3.3.0") + testImplementation ("org.objenesis:objenesis:3.1") + testImplementation ("org.spockframework:spock-core:2.3-groovy-4.0") { exclude group: 'org.apache.groovy'; exclude group: 'net.bytebuddy' } + testImplementation ('org.spockframework:spock-junit4:2.3-groovy-4.0') { exclude group: 'org.apache.groovy'; exclude group: 'net.bytebuddy' } + testImplementation ('com.google.jimfs:jimfs:1.1') + + testImplementation(testFixtures("io.nextflow:nextflow:$nextflowVersion")) + testImplementation(testFixtures("io.nextflow:nf-commons:$nextflowVersion")) + + // see https://docs.gradle.org/4.1/userguide/dependency_management.html#sec:module_replacement + modules { + module("commons-logging:commons-logging") { replacedBy("org.slf4j:jcl-over-slf4j") } + } +} + +// use JUnit 5 platform +test { + useJUnitPlatform() +} + diff --git a/plugins/nf-ga4gh/src/main/nextflow/ga4gh/Ga4ghPlugin.groovy b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/Ga4ghPlugin.groovy new file mode 100644 index 0000000..4d03dae --- /dev/null +++ b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/Ga4ghPlugin.groovy @@ -0,0 +1,18 @@ +package nextflow.ga4gh + +import groovy.transform.CompileStatic +import nextflow.plugin.BasePlugin +import org.pf4j.PluginWrapper +/** + * Nextflow plugin for GA4GH extensions + * + * @author Paolo Di Tommaso + */ +@CompileStatic +class Ga4ghPlugin extends BasePlugin { + + Ga4ghPlugin(PluginWrapper wrapper) { + super(wrapper) + } + +} diff --git a/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/ApiCallback.java b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/ApiCallback.java new file mode 100644 index 0000000..c4b4dba --- /dev/null +++ b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/ApiCallback.java @@ -0,0 +1,76 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Task Execution Service + * + * OpenAPI spec version: 1.1.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +package nextflow.ga4gh.tes.client; + +import java.io.IOException; + +import java.util.Map; +import java.util.List; + +/** + * Callback for asynchronous API call. + * + * @param The return type + */ +public interface ApiCallback { + /** + * This is called when the API call fails. + * + * @param e The exception causing the failure + * @param statusCode Status code of the response if available, otherwise it would be 0 + * @param responseHeaders Headers of the response if available, otherwise it would be null + */ + void onFailure(ApiException e, int statusCode, Map> responseHeaders); + + /** + * This is called when the API call succeeded. + * + * @param result The result deserialized from response + * @param statusCode Status code of the response + * @param responseHeaders Headers of the response + */ + void onSuccess(T result, int statusCode, Map> responseHeaders); + + /** + * This is called when the API upload processing. + * + * @param bytesWritten bytes Written + * @param contentLength content length of request body + * @param done write end + */ + void onUploadProgress(long bytesWritten, long contentLength, boolean done); + + /** + * This is called when the API download processing. + * + * @param bytesRead bytes Read + * @param contentLength content length of the response + * @param done Read end + */ + void onDownloadProgress(long bytesRead, long contentLength, boolean done); +} diff --git a/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/ApiClient.java b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/ApiClient.java new file mode 100644 index 0000000..989a30d --- /dev/null +++ b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/ApiClient.java @@ -0,0 +1,1225 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Task Execution Service + * + * OpenAPI spec version: 1.1.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +package nextflow.ga4gh.tes.client; + +import com.squareup.okhttp.*; +import com.squareup.okhttp.internal.http.HttpMethod; +import com.squareup.okhttp.logging.HttpLoggingInterceptor; +import com.squareup.okhttp.logging.HttpLoggingInterceptor.Level; +import okio.BufferedSink; +import okio.Okio; +import org.threeten.bp.LocalDate; +import org.threeten.bp.OffsetDateTime; +import org.threeten.bp.format.DateTimeFormatter; + +import javax.net.ssl.*; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.lang.reflect.Type; +import java.net.URLConnection; +import java.net.URLEncoder; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.security.SecureRandom; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.text.DateFormat; +import java.util.*; +import java.util.Map.Entry; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import nextflow.ga4gh.tes.client.auth.Authentication; +import nextflow.ga4gh.tes.client.auth.HttpBasicAuth; +import nextflow.ga4gh.tes.client.auth.ApiKeyAuth; +import nextflow.ga4gh.tes.client.auth.OAuth; + +public class ApiClient { + + private String basePath = "http://localhost"; + private boolean debugging = false; + private Map defaultHeaderMap = new HashMap(); + private String tempFolderPath = null; + + private Map authentications; + + private DateFormat dateFormat; + private DateFormat datetimeFormat; + private boolean lenientDatetimeFormat; + private int dateLength; + + private InputStream sslCaCert; + private boolean verifyingSsl; + private KeyManager[] keyManagers; + + private OkHttpClient httpClient; + private JSON json; + + private HttpLoggingInterceptor loggingInterceptor; + + /* + * Constructor for ApiClient + */ + public ApiClient() { + httpClient = new OkHttpClient(); + + + verifyingSsl = true; + + json = new JSON(); + + // Set default User-Agent. + setUserAgent("Swagger-Codegen/1.0.0/java"); + + // Setup authentications (key: authentication name, value: authentication). + authentications = new HashMap(); + // Prevent the authentications from being modified. + authentications = Collections.unmodifiableMap(authentications); + } + + /** + * Get base path + * + * @return Baes path + */ + public String getBasePath() { + return basePath; + } + + /** + * Set base path + * + * @param basePath Base path of the URL (e.g /ga4gh/tes/v1 + * @return An instance of OkHttpClient + */ + public ApiClient setBasePath(String basePath) { + this.basePath = basePath; + return this; + } + + /** + * Get HTTP client + * + * @return An instance of OkHttpClient + */ + public OkHttpClient getHttpClient() { + return httpClient; + } + + /** + * Set HTTP client + * + * @param httpClient An instance of OkHttpClient + * @return Api Client + */ + public ApiClient setHttpClient(OkHttpClient httpClient) { + this.httpClient = httpClient; + return this; + } + + /** + * Get JSON + * + * @return JSON object + */ + public JSON getJSON() { + return json; + } + + /** + * Set JSON + * + * @param json JSON object + * @return Api client + */ + public ApiClient setJSON(JSON json) { + this.json = json; + return this; + } + + /** + * True if isVerifyingSsl flag is on + * + * @return True if isVerifySsl flag is on + */ + public boolean isVerifyingSsl() { + return verifyingSsl; + } + + /** + * Configure whether to verify certificate and hostname when making https requests. + * Default to true. + * NOTE: Do NOT set to false in production code, otherwise you would face multiple types of cryptographic attacks. + * + * @param verifyingSsl True to verify TLS/SSL connection + * @return ApiClient + */ + public ApiClient setVerifyingSsl(boolean verifyingSsl) { + this.verifyingSsl = verifyingSsl; + applySslSettings(); + return this; + } + + /** + * Get SSL CA cert. + * + * @return Input stream to the SSL CA cert + */ + public InputStream getSslCaCert() { + return sslCaCert; + } + + /** + * Configure the CA certificate to be trusted when making https requests. + * Use null to reset to default. + * + * @param sslCaCert input stream for SSL CA cert + * @return ApiClient + */ + public ApiClient setSslCaCert(InputStream sslCaCert) { + this.sslCaCert = sslCaCert; + applySslSettings(); + return this; + } + + public KeyManager[] getKeyManagers() { + return keyManagers; + } + + /** + * Configure client keys to use for authorization in an SSL session. + * Use null to reset to default. + * + * @param managers The KeyManagers to use + * @return ApiClient + */ + public ApiClient setKeyManagers(KeyManager[] managers) { + this.keyManagers = managers; + applySslSettings(); + return this; + } + + public DateFormat getDateFormat() { + return dateFormat; + } + + public ApiClient setDateFormat(DateFormat dateFormat) { + this.json.setDateFormat(dateFormat); + return this; + } + + public ApiClient setSqlDateFormat(DateFormat dateFormat) { + this.json.setSqlDateFormat(dateFormat); + return this; + } + + public ApiClient setOffsetDateTimeFormat(DateTimeFormatter dateFormat) { + this.json.setOffsetDateTimeFormat(dateFormat); + return this; + } + + public ApiClient setLocalDateFormat(DateTimeFormatter dateFormat) { + this.json.setLocalDateFormat(dateFormat); + return this; + } + + public ApiClient setLenientOnJson(boolean lenientOnJson) { + this.json.setLenientOnJson(lenientOnJson); + return this; + } + + /** + * Get authentications (key: authentication name, value: authentication). + * + * @return Map of authentication objects + */ + public Map getAuthentications() { + return authentications; + } + + /** + * Get authentication for the given name. + * + * @param authName The authentication name + * @return The authentication, null if not found + */ + public Authentication getAuthentication(String authName) { + return authentications.get(authName); + } + + /** + * Helper method to set username for the first HTTP basic authentication. + * + * @param username Username + */ + public void setUsername(String username) { + for (Authentication auth : authentications.values()) { + if (auth instanceof HttpBasicAuth) { + ((HttpBasicAuth) auth).setUsername(username); + return; + } + } + throw new RuntimeException("No HTTP basic authentication configured!"); + } + + /** + * Helper method to set password for the first HTTP basic authentication. + * + * @param password Password + */ + public void setPassword(String password) { + for (Authentication auth : authentications.values()) { + if (auth instanceof HttpBasicAuth) { + ((HttpBasicAuth) auth).setPassword(password); + return; + } + } + throw new RuntimeException("No HTTP basic authentication configured!"); + } + + /** + * Helper method to set API key value for the first API key authentication. + * + * @param apiKey API key + */ + public void setApiKey(String apiKey) { + for (Authentication auth : authentications.values()) { + if (auth instanceof ApiKeyAuth) { + ((ApiKeyAuth) auth).setApiKey(apiKey); + return; + } + } + throw new RuntimeException("No API key authentication configured!"); + } + + /** + * Helper method to set API key prefix for the first API key authentication. + * + * @param apiKeyPrefix API key prefix + */ + public void setApiKeyPrefix(String apiKeyPrefix) { + for (Authentication auth : authentications.values()) { + if (auth instanceof ApiKeyAuth) { + ((ApiKeyAuth) auth).setApiKeyPrefix(apiKeyPrefix); + return; + } + } + throw new RuntimeException("No API key authentication configured!"); + } + + /** + * Helper method to set access token for the first OAuth2 authentication. + * + * @param accessToken Access token + */ + public void setAccessToken(String accessToken) { + for (Authentication auth : authentications.values()) { + if (auth instanceof OAuth) { + ((OAuth) auth).setAccessToken(accessToken); + return; + } + } + throw new RuntimeException("No OAuth2 authentication configured!"); + } + + /** + * Set the User-Agent header's value (by adding to the default header map). + * + * @param userAgent HTTP request's user agent + * @return ApiClient + */ + public ApiClient setUserAgent(String userAgent) { + addDefaultHeader("User-Agent", userAgent); + return this; + } + + /** + * Add a default header. + * + * @param key The header's key + * @param value The header's value + * @return ApiClient + */ + public ApiClient addDefaultHeader(String key, String value) { + defaultHeaderMap.put(key, value); + return this; + } + + /** + * Check that whether debugging is enabled for this API client. + * + * @return True if debugging is enabled, false otherwise. + */ + public boolean isDebugging() { + return debugging; + } + + /** + * Enable/disable debugging for this API client. + * + * @param debugging To enable (true) or disable (false) debugging + * @return ApiClient + */ + public ApiClient setDebugging(boolean debugging) { + if (debugging != this.debugging) { + if (debugging) { + loggingInterceptor = new HttpLoggingInterceptor(); + loggingInterceptor.setLevel(Level.BODY); + httpClient.interceptors().add(loggingInterceptor); + } else { + httpClient.interceptors().remove(loggingInterceptor); + loggingInterceptor = null; + } + } + this.debugging = debugging; + return this; + } + + /** + * The path of temporary folder used to store downloaded files from endpoints + * with file response. The default value is null, i.e. using + * the system's default temporary folder. + * + * @see createTempFile + * @return Temporary folder path + */ + public String getTempFolderPath() { + return tempFolderPath; + } + + /** + * Set the temporary folder path (for downloading files) + * + * @param tempFolderPath Temporary folder path + * @return ApiClient + */ + public ApiClient setTempFolderPath(String tempFolderPath) { + this.tempFolderPath = tempFolderPath; + return this; + } + + /** + * Get connection timeout (in milliseconds). + * + * @return Timeout in milliseconds + */ + public int getConnectTimeout() { + return httpClient.getConnectTimeout(); + } + + /** + * Sets the connect timeout (in milliseconds). + * A value of 0 means no timeout, otherwise values must be between 1 and + * + * @param connectionTimeout connection timeout in milliseconds + * @return Api client + */ + public ApiClient setConnectTimeout(int connectionTimeout) { + httpClient.setConnectTimeout(connectionTimeout, TimeUnit.MILLISECONDS); + return this; + } + + /** + * Get read timeout (in milliseconds). + * + * @return Timeout in milliseconds + */ + public int getReadTimeout() { + return httpClient.getReadTimeout(); + } + + /** + * Sets the read timeout (in milliseconds). + * A value of 0 means no timeout, otherwise values must be between 1 and + * {@link Integer#MAX_VALUE}. + * + * @param readTimeout read timeout in milliseconds + * @return Api client + */ + public ApiClient setReadTimeout(int readTimeout) { + httpClient.setReadTimeout(readTimeout, TimeUnit.MILLISECONDS); + return this; + } + + /** + * Get write timeout (in milliseconds). + * + * @return Timeout in milliseconds + */ + public int getWriteTimeout() { + return httpClient.getWriteTimeout(); + } + + /** + * Sets the write timeout (in milliseconds). + * A value of 0 means no timeout, otherwise values must be between 1 and + * {@link Integer#MAX_VALUE}. + * + * @param writeTimeout connection timeout in milliseconds + * @return Api client + */ + public ApiClient setWriteTimeout(int writeTimeout) { + httpClient.setWriteTimeout(writeTimeout, TimeUnit.MILLISECONDS); + return this; + } + + /** + * Format the given parameter object into string. + * + * @param param Parameter + * @return String representation of the parameter + */ + public String parameterToString(Object param) { + if (param == null) { + return ""; + } else if (param instanceof Date || param instanceof OffsetDateTime || param instanceof LocalDate) { + //Serialize to json string and remove the " enclosing characters + String jsonStr = json.serialize(param); + return jsonStr.substring(1, jsonStr.length() - 1); + } else if (param instanceof Collection) { + StringBuilder b = new StringBuilder(); + for (Object o : (Collection)param) { + if (b.length() > 0) { + b.append(","); + } + b.append(String.valueOf(o)); + } + return b.toString(); + } else { + return String.valueOf(param); + } + } + + /** + * Formats the specified query parameter to a list containing a single {@code Pair} object. + * + * Note that {@code value} must not be a collection. + * + * @param name The name of the parameter. + * @param value The value of the parameter. + * @return A list containing a single {@code Pair} object. + */ + public List parameterToPair(String name, Object value) { + List params = new ArrayList(); + + // preconditions + if (name == null || name.isEmpty() || value == null || value instanceof Collection) return params; + + params.add(new Pair(name, parameterToString(value))); + return params; + } + + /** + * Formats the specified collection query parameters to a list of {@code Pair} objects. + * + * Note that the values of each of the returned Pair objects are percent-encoded. + * + * @param collectionFormat The collection format of the parameter. + * @param name The name of the parameter. + * @param value The value of the parameter. + * @return A list of {@code Pair} objects. + */ + public List parameterToPairs(String collectionFormat, String name, Collection value) { + List params = new ArrayList(); + + // preconditions + if (name == null || name.isEmpty() || value == null || value.isEmpty()) { + return params; + } + + // create the params based on the collection format + if ("multi".equals(collectionFormat)) { + for (Object item : value) { + params.add(new Pair(name, escapeString(parameterToString(item)))); + } + return params; + } + + // collectionFormat is assumed to be "csv" by default + String delimiter = ","; + + // escape all delimiters except commas, which are URI reserved + // characters + if ("ssv".equals(collectionFormat)) { + delimiter = escapeString(" "); + } else if ("tsv".equals(collectionFormat)) { + delimiter = escapeString("\t"); + } else if ("pipes".equals(collectionFormat)) { + delimiter = escapeString("|"); + } + + StringBuilder sb = new StringBuilder() ; + for (Object item : value) { + sb.append(delimiter); + sb.append(escapeString(parameterToString(item))); + } + + params.add(new Pair(name, sb.substring(delimiter.length()))); + + return params; + } + + /** + * Sanitize filename by removing path. + * e.g. ../../sun.gif becomes sun.gif + * + * @param filename The filename to be sanitized + * @return The sanitized filename + */ + public String sanitizeFilename(String filename) { + return filename.replaceAll(".*[/\\\\]", ""); + } + + /** + * Check if the given MIME is a JSON MIME. + * JSON MIME examples: + * application/json + * application/json; charset=UTF8 + * APPLICATION/JSON + * application/vnd.company+json + * "* / *" is also default to JSON + * @param mime MIME (Multipurpose Internet Mail Extensions) + * @return True if the given MIME is JSON, false otherwise. + */ + public boolean isJsonMime(String mime) { + String jsonMime = "(?i)^(application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(;.*)?$"; + return mime != null && (mime.matches(jsonMime) || mime.equals("*/*")); + } + + /** + * Select the Accept header's value from the given accepts array: + * if JSON exists in the given array, use it; + * otherwise use all of them (joining into a string) + * + * @param accepts The accepts array to select from + * @return The Accept header to use. If the given array is empty, + * null will be returned (not to set the Accept header explicitly). + */ + public String selectHeaderAccept(String[] accepts) { + if (accepts.length == 0) { + return null; + } + for (String accept : accepts) { + if (isJsonMime(accept)) { + return accept; + } + } + return StringUtil.join(accepts, ","); + } + + /** + * Select the Content-Type header's value from the given array: + * if JSON exists in the given array, use it; + * otherwise use the first one of the array. + * + * @param contentTypes The Content-Type array to select from + * @return The Content-Type header to use. If the given array is empty, + * or matches "any", JSON will be used. + */ + public String selectHeaderContentType(String[] contentTypes) { + if (contentTypes.length == 0 || contentTypes[0].equals("*/*")) { + return "application/json"; + } + for (String contentType : contentTypes) { + if (isJsonMime(contentType)) { + return contentType; + } + } + return contentTypes[0]; + } + + /** + * Escape the given string to be used as URL query value. + * + * @param str String to be escaped + * @return Escaped string + */ + public String escapeString(String str) { + try { + return URLEncoder.encode(str, "utf8").replaceAll("\\+", "%20"); + } catch (UnsupportedEncodingException e) { + return str; + } + } + + /** + * Deserialize response body to Java object, according to the return type and + * the Content-Type response header. + * + * @param Type + * @param response HTTP response + * @param returnType The type of the Java object + * @return The deserialized Java object + * @throws ApiException If fail to deserialize response body, i.e. cannot read response body + * or the Content-Type of the response is not supported. + */ + @SuppressWarnings("unchecked") + public T deserialize(Response response, Type returnType) throws ApiException { + if (response == null || returnType == null) { + return null; + } + + if ("byte[]".equals(returnType.toString())) { + // Handle binary response (byte array). + try { + return (T) response.body().bytes(); + } catch (IOException e) { + throw new ApiException(e); + } + } else if (returnType.equals(File.class)) { + // Handle file downloading. + return (T) downloadFileFromResponse(response); + } + + String respBody; + try { + if (response.body() != null) + respBody = response.body().string(); + else + respBody = null; + } catch (IOException e) { + throw new ApiException(e); + } + + if (respBody == null || "".equals(respBody)) { + return null; + } + + String contentType = response.headers().get("Content-Type"); + if (contentType == null) { + // ensuring a default content type + contentType = "application/json"; + } + if (isJsonMime(contentType)) { + return json.deserialize(respBody, returnType); + } else if (returnType.equals(String.class)) { + // Expecting string, return the raw response body. + return (T) respBody; + } else { + throw new ApiException( + "Content type \"" + contentType + "\" is not supported for type: " + returnType, + response.code(), + response.headers().toMultimap(), + respBody); + } + } + + /** + * Serialize the given Java object into request body according to the object's + * class and the request Content-Type. + * + * @param obj The Java object + * @param contentType The request Content-Type + * @return The serialized request body + * @throws ApiException If fail to serialize the given object + */ + public RequestBody serialize(Object obj, String contentType) throws ApiException { + if (obj instanceof byte[]) { + // Binary (byte array) body parameter support. + return RequestBody.create(MediaType.parse(contentType), (byte[]) obj); + } else if (obj instanceof File) { + // File body parameter support. + return RequestBody.create(MediaType.parse(contentType), (File) obj); + } else if (isJsonMime(contentType)) { + String content; + if (obj != null) { + content = json.serialize(obj); + } else { + content = null; + } + return RequestBody.create(MediaType.parse(contentType), content); + } else { + throw new ApiException("Content type \"" + contentType + "\" is not supported"); + } + } + + /** + * Download file from the given response. + * + * @param response An instance of the Response object + * @throws ApiException If fail to read file content from response and write to disk + * @return Downloaded file + */ + public File downloadFileFromResponse(Response response) throws ApiException { + try { + File file = prepareDownloadFile(response); + BufferedSink sink = Okio.buffer(Okio.sink(file)); + sink.writeAll(response.body().source()); + sink.close(); + return file; + } catch (IOException e) { + throw new ApiException(e); + } + } + + /** + * Prepare file for download + * + * @param response An instance of the Response object + * @throws IOException If fail to prepare file for download + * @return Prepared file for the download + */ + public File prepareDownloadFile(Response response) throws IOException { + String filename = null; + String contentDisposition = response.header("Content-Disposition"); + if (contentDisposition != null && !"".equals(contentDisposition)) { + // Get filename from the Content-Disposition header. + Pattern pattern = Pattern.compile("filename=['\"]?([^'\"\\s]+)['\"]?"); + Matcher matcher = pattern.matcher(contentDisposition); + if (matcher.find()) { + filename = sanitizeFilename(matcher.group(1)); + } + } + + String prefix = null; + String suffix = null; + if (filename == null) { + prefix = "download-"; + suffix = ""; + } else { + int pos = filename.lastIndexOf("."); + if (pos == -1) { + prefix = filename + "-"; + } else { + prefix = filename.substring(0, pos) + "-"; + suffix = filename.substring(pos); + } + // File.createTempFile requires the prefix to be at least three characters long + if (prefix.length() < 3) + prefix = "download-"; + } + + if (tempFolderPath == null) + return Files.createTempFile(prefix, suffix).toFile(); + else + return Files.createTempFile(Paths.get(tempFolderPath), prefix, suffix).toFile(); + } + + /** + * {@link #execute(Call, Type)} + * + * @param Type + * @param call An instance of the Call object + * @throws ApiException If fail to execute the call + * @return ApiResponse<T> + */ + public ApiResponse execute(Call call) throws ApiException { + return execute(call, null); + } + + /** + * Execute HTTP call and deserialize the HTTP response body into the given return type. + * + * @param returnType The return type used to deserialize HTTP response body + * @param The return type corresponding to (same with) returnType + * @param call Call + * @return ApiResponse object containing response status, headers and + * data, which is a Java object deserialized from response body and would be null + * when returnType is null. + * @throws ApiException If fail to execute the call + */ + public ApiResponse execute(Call call, Type returnType) throws ApiException { + try { + Response response = call.execute(); + T data = handleResponse(response, returnType); + return new ApiResponse(response.code(), response.headers().toMultimap(), data); + } catch (IOException e) { + throw new ApiException(e); + } + } + + /** + * {@link #executeAsync(Call, Type, ApiCallback)} + * + * @param Type + * @param call An instance of the Call object + * @param callback ApiCallback<T> + */ + public void executeAsync(Call call, ApiCallback callback) { + executeAsync(call, null, callback); + } + + /** + * Execute HTTP call asynchronously. + * + * @see #execute(Call, Type) + * @param Type + * @param call The callback to be executed when the API call finishes + * @param returnType Return type + * @param callback ApiCallback + */ + @SuppressWarnings("unchecked") + public void executeAsync(Call call, final Type returnType, final ApiCallback callback) { + call.enqueue(new Callback() { + @Override + public void onFailure(Request request, IOException e) { + callback.onFailure(new ApiException(e), 0, null); + } + + @Override + public void onResponse(Response response) throws IOException { + T result; + try { + result = (T) handleResponse(response, returnType); + } catch (ApiException e) { + callback.onFailure(e, response.code(), response.headers().toMultimap()); + return; + } + callback.onSuccess(result, response.code(), response.headers().toMultimap()); + } + }); + } + + /** + * Handle the given response, return the deserialized object when the response is successful. + * + * @param Type + * @param response Response + * @param returnType Return type + * @throws ApiException If the response has a unsuccessful status code or + * fail to deserialize the response body + * @return Type + */ + public T handleResponse(Response response, Type returnType) throws ApiException { + if (response.isSuccessful()) { + if (returnType == null || response.code() == 204) { + // returning null if the returnType is not defined, + // or the status code is 204 (No Content) + if (response.body() != null) { + try { + response.body().close(); + } catch (IOException e) { + throw new ApiException(response.message(), e, response.code(), response.headers().toMultimap()); + } + } + return null; + } else { + return deserialize(response, returnType); + } + } else { + String respBody = null; + if (response.body() != null) { + try { + respBody = response.body().string(); + } catch (IOException e) { + throw new ApiException(response.message(), e, response.code(), response.headers().toMultimap()); + } + } + throw new ApiException(response.message(), response.code(), response.headers().toMultimap(), respBody); + } + } + + /** + * Build HTTP call with the given options. + * + * @param path The sub-path of the HTTP URL + * @param method The request method, one of "GET", "HEAD", "OPTIONS", "POST", "PUT", "PATCH" and "DELETE" + * @param queryParams The query parameters + * @param collectionQueryParams The collection query parameters + * @param body The request body object + * @param headerParams The header parameters + * @param formParams The form parameters + * @param authNames The authentications to apply + * @param progressRequestListener Progress request listener + * @return The HTTP call + * @throws ApiException If fail to serialize the request body object + */ + public Call buildCall(String path, String method, List queryParams, List collectionQueryParams, Object body, Map headerParams, Map formParams, String[] authNames, ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException { + Request request = buildRequest(path, method, queryParams, collectionQueryParams, body, headerParams, formParams, authNames, progressRequestListener); + + return httpClient.newCall(request); + } + + /** + * Build an HTTP request with the given options. + * + * @param path The sub-path of the HTTP URL + * @param method The request method, one of "GET", "HEAD", "OPTIONS", "POST", "PUT", "PATCH" and "DELETE" + * @param queryParams The query parameters + * @param collectionQueryParams The collection query parameters + * @param body The request body object + * @param headerParams The header parameters + * @param formParams The form parameters + * @param authNames The authentications to apply + * @param progressRequestListener Progress request listener + * @return The HTTP request + * @throws ApiException If fail to serialize the request body object + */ + public Request buildRequest(String path, String method, List queryParams, List collectionQueryParams, Object body, Map headerParams, Map formParams, String[] authNames, ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException { + // HACK: enable all authentications by default + if (authNames.length == 0) { + authNames = authentications.keySet().toArray(new String[0]); + } + + updateParamsForAuth(authNames, queryParams, headerParams); + + final String url = buildUrl(path, queryParams, collectionQueryParams); + final Request.Builder reqBuilder = new Request.Builder().url(url); + processHeaderParams(headerParams, reqBuilder); + + String contentType = (String) headerParams.get("Content-Type"); + // ensuring a default content type + if (contentType == null) { + contentType = "application/json"; + } + + RequestBody reqBody; + if (!HttpMethod.permitsRequestBody(method)) { + reqBody = null; + } else if ("application/x-www-form-urlencoded".equals(contentType)) { + reqBody = buildRequestBodyFormEncoding(formParams); + } else if ("multipart/form-data".equals(contentType)) { + reqBody = buildRequestBodyMultipart(formParams); + } else if (body == null) { + if ("DELETE".equals(method)) { + // allow calling DELETE without sending a request body + reqBody = null; + } else { + // use an empty request body (for POST, PUT and PATCH) + reqBody = RequestBody.create(MediaType.parse(contentType), ""); + } + } else { + reqBody = serialize(body, contentType); + } + + Request request = null; + + if(progressRequestListener != null && reqBody != null) { + ProgressRequestBody progressRequestBody = new ProgressRequestBody(reqBody, progressRequestListener); + request = reqBuilder.method(method, progressRequestBody).build(); + } else { + request = reqBuilder.method(method, reqBody).build(); + } + + return request; + } + + /** + * Build full URL by concatenating base path, the given sub path and query parameters. + * + * @param path The sub path + * @param queryParams The query parameters + * @param collectionQueryParams The collection query parameters + * @return The full URL + */ + public String buildUrl(String path, List queryParams, List collectionQueryParams) { + final StringBuilder url = new StringBuilder(); + url.append(basePath).append(path); + + if (queryParams != null && !queryParams.isEmpty()) { + // support (constant) query string in `path`, e.g. "/posts?draft=1" + String prefix = path.contains("?") ? "&" : "?"; + for (Pair param : queryParams) { + if (param.getValue() != null) { + if (prefix != null) { + url.append(prefix); + prefix = null; + } else { + url.append("&"); + } + String value = parameterToString(param.getValue()); + url.append(escapeString(param.getName())).append("=").append(escapeString(value)); + } + } + } + + if (collectionQueryParams != null && !collectionQueryParams.isEmpty()) { + String prefix = url.toString().contains("?") ? "&" : "?"; + for (Pair param : collectionQueryParams) { + if (param.getValue() != null) { + if (prefix != null) { + url.append(prefix); + prefix = null; + } else { + url.append("&"); + } + String value = parameterToString(param.getValue()); + // collection query parameter value already escaped as part of parameterToPairs + url.append(escapeString(param.getName())).append("=").append(value); + } + } + } + + return url.toString(); + } + + /** + * Set header parameters to the request builder, including default headers. + * + * @param headerParams Header parameters in the ofrm of Map + * @param reqBuilder Request.Builder + */ + public void processHeaderParams(Map headerParams, Request.Builder reqBuilder) { + for (Entry param : headerParams.entrySet()) { + reqBuilder.header(param.getKey(), parameterToString(param.getValue())); + } + for (Entry header : defaultHeaderMap.entrySet()) { + if (!headerParams.containsKey(header.getKey())) { + reqBuilder.header(header.getKey(), parameterToString(header.getValue())); + } + } + } + + /** + * Update query and header parameters based on authentication settings. + * + * @param authNames The authentications to apply + * @param queryParams List of query parameters + * @param headerParams Map of header parameters + */ + public void updateParamsForAuth(String[] authNames, List queryParams, Map headerParams) { + for (String authName : authNames) { + Authentication auth = authentications.get(authName); + if (auth == null) throw new RuntimeException("Authentication undefined: " + authName); + auth.applyToParams(queryParams, headerParams); + } + } + + /** + * Build a form-encoding request body with the given form parameters. + * + * @param formParams Form parameters in the form of Map + * @return RequestBody + */ + public RequestBody buildRequestBodyFormEncoding(Map formParams) { + FormEncodingBuilder formBuilder = new FormEncodingBuilder(); + for (Entry param : formParams.entrySet()) { + formBuilder.add(param.getKey(), parameterToString(param.getValue())); + } + return formBuilder.build(); + } + + /** + * Build a multipart (file uploading) request body with the given form parameters, + * which could contain text fields and file fields. + * + * @param formParams Form parameters in the form of Map + * @return RequestBody + */ + public RequestBody buildRequestBodyMultipart(Map formParams) { + MultipartBuilder mpBuilder = new MultipartBuilder().type(MultipartBuilder.FORM); + for (Entry param : formParams.entrySet()) { + if (param.getValue() instanceof File) { + File file = (File) param.getValue(); + Headers partHeaders = Headers.of("Content-Disposition", "form-data; name=\"" + param.getKey() + "\"; filename=\"" + file.getName() + "\""); + MediaType mediaType = MediaType.parse(guessContentTypeFromFile(file)); + mpBuilder.addPart(partHeaders, RequestBody.create(mediaType, file)); + } else { + Headers partHeaders = Headers.of("Content-Disposition", "form-data; name=\"" + param.getKey() + "\""); + mpBuilder.addPart(partHeaders, RequestBody.create(null, parameterToString(param.getValue()))); + } + } + return mpBuilder.build(); + } + + /** + * Guess Content-Type header from the given file (defaults to "application/octet-stream"). + * + * @param file The given file + * @return The guessed Content-Type + */ + public String guessContentTypeFromFile(File file) { + String contentType = URLConnection.guessContentTypeFromName(file.getName()); + if (contentType == null) { + return "application/octet-stream"; + } else { + return contentType; + } + } + + /** + * Apply SSL related settings to httpClient according to the current values of + * verifyingSsl and sslCaCert. + */ + private void applySslSettings() { + try { + TrustManager[] trustManagers = null; + HostnameVerifier hostnameVerifier = null; + if (!verifyingSsl) { + TrustManager trustAll = new X509TrustManager() { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {} + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {} + @Override + public X509Certificate[] getAcceptedIssuers() { return null; } + }; + SSLContext sslContext = SSLContext.getInstance("TLS"); + trustManagers = new TrustManager[]{ trustAll }; + hostnameVerifier = new HostnameVerifier() { + @Override + public boolean verify(String hostname, SSLSession session) { return true; } + }; + } else if (sslCaCert != null) { + char[] password = null; // Any password will work. + CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); + Collection certificates = certificateFactory.generateCertificates(sslCaCert); + if (certificates.isEmpty()) { + throw new IllegalArgumentException("expected non-empty set of trusted certificates"); + } + KeyStore caKeyStore = newEmptyKeyStore(password); + int index = 0; + for (Certificate certificate : certificates) { + String certificateAlias = "ca" + Integer.toString(index++); + caKeyStore.setCertificateEntry(certificateAlias, certificate); + } + TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + trustManagerFactory.init(caKeyStore); + trustManagers = trustManagerFactory.getTrustManagers(); + } + + if (keyManagers != null || trustManagers != null) { + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(keyManagers, trustManagers, new SecureRandom()); + httpClient.setSslSocketFactory(sslContext.getSocketFactory()); + } else { + httpClient.setSslSocketFactory(null); + } + httpClient.setHostnameVerifier(hostnameVerifier); + } catch (GeneralSecurityException e) { + throw new RuntimeException(e); + } + } + + private KeyStore newEmptyKeyStore(char[] password) throws GeneralSecurityException { + try { + KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + keyStore.load(null, password); + return keyStore; + } catch (IOException e) { + throw new AssertionError(e); + } + } +} diff --git a/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/ApiException.java b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/ApiException.java new file mode 100644 index 0000000..0505561 --- /dev/null +++ b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/ApiException.java @@ -0,0 +1,105 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Task Execution Service + * + * OpenAPI spec version: 1.1.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +package nextflow.ga4gh.tes.client; + +import java.util.Map; +import java.util.List; + +@javax.annotation.Generated(value = "io.swagger.codegen.v3.generators.java.JavaClientCodegen", date = "2023-08-15T14:10:09.878Z[GMT]") +public class ApiException extends Exception { + private int code = 0; + private Map> responseHeaders = null; + private String responseBody = null; + + public ApiException() {} + + public ApiException(Throwable throwable) { + super(throwable); + } + + public ApiException(String message) { + super(message); + } + + public ApiException(String message, Throwable throwable, int code, Map> responseHeaders, String responseBody) { + super(message, throwable); + this.code = code; + this.responseHeaders = responseHeaders; + this.responseBody = responseBody; + } + + public ApiException(String message, int code, Map> responseHeaders, String responseBody) { + this(message, (Throwable) null, code, responseHeaders, responseBody); + } + + public ApiException(String message, Throwable throwable, int code, Map> responseHeaders) { + this(message, throwable, code, responseHeaders, null); + } + + public ApiException(int code, Map> responseHeaders, String responseBody) { + this((String) null, (Throwable) null, code, responseHeaders, responseBody); + } + + public ApiException(int code, String message) { + super(message); + this.code = code; + } + + public ApiException(int code, String message, Map> responseHeaders, String responseBody) { + this(code, message); + this.responseHeaders = responseHeaders; + this.responseBody = responseBody; + } + + /** + * Get the HTTP status code. + * + * @return HTTP status code + */ + public int getCode() { + return code; + } + + /** + * Get the HTTP response headers. + * + * @return A map of list of string + */ + public Map> getResponseHeaders() { + return responseHeaders; + } + + /** + * Get the HTTP response body. + * + * @return Response body in the form of string + */ + public String getResponseBody() { + return responseBody; + } +} diff --git a/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/ApiResponse.java b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/ApiResponse.java new file mode 100644 index 0000000..5b825fe --- /dev/null +++ b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/ApiResponse.java @@ -0,0 +1,73 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Task Execution Service + * + * OpenAPI spec version: 1.1.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +package nextflow.ga4gh.tes.client; + +import java.util.List; +import java.util.Map; + +/** + * API response returned by API call. + * + * @param The type of data that is deserialized from response body + */ +public class ApiResponse { + final private int statusCode; + final private Map> headers; + final private T data; + + /** + * @param statusCode The status code of HTTP response + * @param headers The headers of HTTP response + */ + public ApiResponse(int statusCode, Map> headers) { + this(statusCode, headers, null); + } + + /** + * @param statusCode The status code of HTTP response + * @param headers The headers of HTTP response + * @param data The object deserialized from response bod + */ + public ApiResponse(int statusCode, Map> headers, T data) { + this.statusCode = statusCode; + this.headers = headers; + this.data = data; + } + + public int getStatusCode() { + return statusCode; + } + + public Map> getHeaders() { + return headers; + } + + public T getData() { + return data; + } +} diff --git a/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/Configuration.java b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/Configuration.java new file mode 100644 index 0000000..a20ec37 --- /dev/null +++ b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/Configuration.java @@ -0,0 +1,53 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Task Execution Service + * + * OpenAPI spec version: 1.1.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +package nextflow.ga4gh.tes.client; + +@javax.annotation.Generated(value = "io.swagger.codegen.v3.generators.java.JavaClientCodegen", date = "2023-08-15T14:10:09.878Z[GMT]") +public class Configuration { + private static ApiClient defaultApiClient = new ApiClient(); + + /** + * Get the default API client, which would be used when creating API + * instances without providing an API client. + * + * @return Default API client + */ + public static ApiClient getDefaultApiClient() { + return defaultApiClient; + } + + /** + * Set the default API client, which would be used when creating API + * instances without providing an API client. + * + * @param apiClient API client + */ + public static void setDefaultApiClient(ApiClient apiClient) { + defaultApiClient = apiClient; + } +} diff --git a/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/GzipRequestInterceptor.java b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/GzipRequestInterceptor.java new file mode 100644 index 0000000..b2ef1f1 --- /dev/null +++ b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/GzipRequestInterceptor.java @@ -0,0 +1,95 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Task Execution Service + * + * OpenAPI spec version: 1.1.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +package nextflow.ga4gh.tes.client; + +import com.squareup.okhttp.*; +import okio.Buffer; +import okio.BufferedSink; +import okio.GzipSink; +import okio.Okio; + +import java.io.IOException; + +/** + * Encodes request bodies using gzip. + * + * Taken from https://github.com/square/okhttp/issues/350 + */ +class GzipRequestInterceptor implements Interceptor { + @Override public Response intercept(Chain chain) throws IOException { + Request originalRequest = chain.request(); + if (originalRequest.body() == null || originalRequest.header("Content-Encoding") != null) { + return chain.proceed(originalRequest); + } + + Request compressedRequest = originalRequest.newBuilder() + .header("Content-Encoding", "gzip") + .method(originalRequest.method(), forceContentLength(gzip(originalRequest.body()))) + .build(); + return chain.proceed(compressedRequest); + } + + private RequestBody forceContentLength(final RequestBody requestBody) throws IOException { + final Buffer buffer = new Buffer(); + requestBody.writeTo(buffer); + return new RequestBody() { + @Override + public MediaType contentType() { + return requestBody.contentType(); + } + + @Override + public long contentLength() { + return buffer.size(); + } + + @Override + public void writeTo(BufferedSink sink) throws IOException { + sink.write(buffer.snapshot()); + } + }; + } + + private RequestBody gzip(final RequestBody body) { + return new RequestBody() { + @Override public MediaType contentType() { + return body.contentType(); + } + + @Override public long contentLength() { + return -1; // We don't know the compressed length in advance! + } + + @Override public void writeTo(BufferedSink sink) throws IOException { + BufferedSink gzipSink = Okio.buffer(new GzipSink(sink)); + body.writeTo(gzipSink); + gzipSink.close(); + } + }; + } +} \ No newline at end of file diff --git a/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/JSON.java b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/JSON.java new file mode 100644 index 0000000..208f286 --- /dev/null +++ b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/JSON.java @@ -0,0 +1,378 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Task Execution Service + * + * OpenAPI spec version: 1.1.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +package nextflow.ga4gh.tes.client; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import io.gsonfire.GsonFireBuilder; +import io.gsonfire.PostProcessor; +import io.gsonfire.TypeSelector; +import com.google.gson.JsonParseException; +import com.google.gson.TypeAdapter; +import com.google.gson.internal.bind.util.ISO8601Utils; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import org.threeten.bp.LocalDate; +import org.threeten.bp.OffsetDateTime; +import org.threeten.bp.format.DateTimeFormatter; + +import nextflow.ga4gh.tes.client.model.*; + +import java.io.IOException; +import java.io.StringReader; +import java.lang.reflect.Type; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.ParsePosition; +import java.util.Date; +import java.util.Map; +import java.util.HashMap; + +public class JSON { + private Gson gson; + private boolean isLenientOnJson = false; + private DateTypeAdapter dateTypeAdapter = new DateTypeAdapter(); + private SqlDateTypeAdapter sqlDateTypeAdapter = new SqlDateTypeAdapter(); + private OffsetDateTimeTypeAdapter offsetDateTimeTypeAdapter = new OffsetDateTimeTypeAdapter(); + private LocalDateTypeAdapter localDateTypeAdapter = new LocalDateTypeAdapter(); + + public static GsonBuilder createGson() { + GsonFireBuilder fireBuilder = new GsonFireBuilder() + ; + return fireBuilder.createGsonBuilder(); + } + + private static String getDiscriminatorValue(JsonElement readElement, String discriminatorField) { + JsonElement element = readElement.getAsJsonObject().get(discriminatorField); + if(null == element) { + throw new IllegalArgumentException("missing discriminator field: <" + discriminatorField + ">"); + } + return element.getAsString(); + } + + private static Class getClassByDiscriminator(Map> classByDiscriminatorValue, String discriminatorValue) { + Class clazz = classByDiscriminatorValue.get(discriminatorValue.toUpperCase()); + if(null == clazz) { + throw new IllegalArgumentException("cannot determine model class of name: <" + discriminatorValue + ">"); + } + return clazz; + } + + public JSON() { + gson = createGson() + .registerTypeAdapter(Date.class, dateTypeAdapter) + .registerTypeAdapter(java.sql.Date.class, sqlDateTypeAdapter) + .registerTypeAdapter(OffsetDateTime.class, offsetDateTimeTypeAdapter) + .registerTypeAdapter(LocalDate.class, localDateTypeAdapter) + .create(); + } + + /** + * Get Gson. + * + * @return Gson + */ + public Gson getGson() { + return gson; + } + + /** + * Set Gson. + * + * @param gson Gson + * @return JSON + */ + public JSON setGson(Gson gson) { + this.gson = gson; + return this; + } + + public JSON setLenientOnJson(boolean lenientOnJson) { + isLenientOnJson = lenientOnJson; + return this; + } + + /** + * Serialize the given Java object into JSON string. + * + * @param obj Object + * @return String representation of the JSON + */ + public String serialize(Object obj) { + return gson.toJson(obj); + } + + /** + * Deserialize the given JSON string to Java object. + * + * @param Type + * @param body The JSON string + * @param returnType The type to deserialize into + * @return The deserialized Java object + */ + @SuppressWarnings("unchecked") + public T deserialize(String body, Type returnType) { + try { + if (isLenientOnJson) { + JsonReader jsonReader = new JsonReader(new StringReader(body)); + // see https://google-gson.googlecode.com/svn/trunk/gson/docs/javadocs/com/google/gson/stream/JsonReader.html#setLenient(boolean) + jsonReader.setLenient(true); + return gson.fromJson(jsonReader, returnType); + } else { + return gson.fromJson(body, returnType); + } + } catch (JsonParseException e) { + // Fallback processing when failed to parse JSON form response body: + // return the response body string directly for the String return type; + if (returnType.equals(String.class)) + return (T) body; + else throw (e); + } + } + + /** + * Gson TypeAdapter for JSR310 OffsetDateTime type + */ + public static class OffsetDateTimeTypeAdapter extends TypeAdapter { + + private DateTimeFormatter formatter; + + public OffsetDateTimeTypeAdapter() { + this(DateTimeFormatter.ISO_OFFSET_DATE_TIME); + } + + public OffsetDateTimeTypeAdapter(DateTimeFormatter formatter) { + this.formatter = formatter; + } + + public void setFormat(DateTimeFormatter dateFormat) { + this.formatter = dateFormat; + } + + @Override + public void write(JsonWriter out, OffsetDateTime date) throws IOException { + if (date == null) { + out.nullValue(); + } else { + out.value(formatter.format(date)); + } + } + + @Override + public OffsetDateTime read(JsonReader in) throws IOException { + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String date = in.nextString(); + if (date.endsWith("+0000")) { + date = date.substring(0, date.length()-5) + "Z"; + } + return OffsetDateTime.parse(date, formatter); + } + } + } + + /** + * Gson TypeAdapter for JSR310 LocalDate type + */ + public class LocalDateTypeAdapter extends TypeAdapter { + + private DateTimeFormatter formatter; + + public LocalDateTypeAdapter() { + this(DateTimeFormatter.ISO_LOCAL_DATE); + } + + public LocalDateTypeAdapter(DateTimeFormatter formatter) { + this.formatter = formatter; + } + + public void setFormat(DateTimeFormatter dateFormat) { + this.formatter = dateFormat; + } + + @Override + public void write(JsonWriter out, LocalDate date) throws IOException { + if (date == null) { + out.nullValue(); + } else { + out.value(formatter.format(date)); + } + } + + @Override + public LocalDate read(JsonReader in) throws IOException { + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String date = in.nextString(); + return LocalDate.parse(date, formatter); + } + } + } + + public JSON setOffsetDateTimeFormat(DateTimeFormatter dateFormat) { + offsetDateTimeTypeAdapter.setFormat(dateFormat); + return this; + } + + public JSON setLocalDateFormat(DateTimeFormatter dateFormat) { + localDateTypeAdapter.setFormat(dateFormat); + return this; + } + + /** + * Gson TypeAdapter for java.sql.Date type + * If the dateFormat is null, a simple "yyyy-MM-dd" format will be used + * (more efficient than SimpleDateFormat). + */ + public static class SqlDateTypeAdapter extends TypeAdapter { + + private DateFormat dateFormat; + + public SqlDateTypeAdapter() { + } + + public SqlDateTypeAdapter(DateFormat dateFormat) { + this.dateFormat = dateFormat; + } + + public void setFormat(DateFormat dateFormat) { + this.dateFormat = dateFormat; + } + + @Override + public void write(JsonWriter out, java.sql.Date date) throws IOException { + if (date == null) { + out.nullValue(); + } else { + String value; + if (dateFormat != null) { + value = dateFormat.format(date); + } else { + value = date.toString(); + } + out.value(value); + } + } + + @Override + public java.sql.Date read(JsonReader in) throws IOException { + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String date = in.nextString(); + try { + if (dateFormat != null) { + return new java.sql.Date(dateFormat.parse(date).getTime()); + } + return new java.sql.Date(ISO8601Utils.parse(date, new ParsePosition(0)).getTime()); + } catch (ParseException e) { + throw new JsonParseException(e); + } + } + } + } + + /** + * Gson TypeAdapter for java.util.Date type + * If the dateFormat is null, ISO8601Utils will be used. + */ + public static class DateTypeAdapter extends TypeAdapter { + + private DateFormat dateFormat; + + public DateTypeAdapter() { + } + + public DateTypeAdapter(DateFormat dateFormat) { + this.dateFormat = dateFormat; + } + + public void setFormat(DateFormat dateFormat) { + this.dateFormat = dateFormat; + } + + @Override + public void write(JsonWriter out, Date date) throws IOException { + if (date == null) { + out.nullValue(); + } else { + String value; + if (dateFormat != null) { + value = dateFormat.format(date); + } else { + value = ISO8601Utils.format(date, true); + } + out.value(value); + } + } + + @Override + public Date read(JsonReader in) throws IOException { + try { + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String date = in.nextString(); + try { + if (dateFormat != null) { + return dateFormat.parse(date); + } + return ISO8601Utils.parse(date, new ParsePosition(0)); + } catch (ParseException e) { + throw new JsonParseException(e); + } + } + } catch (IllegalArgumentException e) { + throw new JsonParseException(e); + } + } + } + + public JSON setDateFormat(DateFormat dateFormat) { + dateTypeAdapter.setFormat(dateFormat); + return this; + } + + public JSON setSqlDateFormat(DateFormat dateFormat) { + sqlDateTypeAdapter.setFormat(dateFormat); + return this; + } + +} diff --git a/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/Pair.java b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/Pair.java new file mode 100644 index 0000000..2602dc8 --- /dev/null +++ b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/Pair.java @@ -0,0 +1,66 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Task Execution Service + * + * OpenAPI spec version: 1.1.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +package nextflow.ga4gh.tes.client; + +@javax.annotation.Generated(value = "io.swagger.codegen.v3.generators.java.JavaClientCodegen", date = "2023-08-15T14:10:09.878Z[GMT]") +public class Pair { + private String name = ""; + private String value = ""; + + public Pair (String name, String value) { + setName(name); + setValue(value); + } + + private void setName(String name) { + if (!isValidString(name)) return; + + this.name = name; + } + + private void setValue(String value) { + if (!isValidString(value)) return; + + this.value = value; + } + + public String getName() { + return this.name; + } + + public String getValue() { + return this.value; + } + + private boolean isValidString(String arg) { + if (arg == null) return false; + if (arg.trim().isEmpty()) return false; + + return true; + } +} diff --git a/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/ProgressRequestBody.java b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/ProgressRequestBody.java new file mode 100644 index 0000000..84ac74c --- /dev/null +++ b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/ProgressRequestBody.java @@ -0,0 +1,91 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Task Execution Service + * + * OpenAPI spec version: 1.1.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +package nextflow.ga4gh.tes.client; + +import com.squareup.okhttp.MediaType; +import com.squareup.okhttp.RequestBody; + +import java.io.IOException; + +import okio.Buffer; +import okio.BufferedSink; +import okio.ForwardingSink; +import okio.Okio; +import okio.Sink; + +public class ProgressRequestBody extends RequestBody { + + public interface ProgressRequestListener { + void onRequestProgress(long bytesWritten, long contentLength, boolean done); + } + + private final RequestBody requestBody; + + private final ProgressRequestListener progressListener; + + public ProgressRequestBody(RequestBody requestBody, ProgressRequestListener progressListener) { + this.requestBody = requestBody; + this.progressListener = progressListener; + } + + @Override + public MediaType contentType() { + return requestBody.contentType(); + } + + @Override + public long contentLength() throws IOException { + return requestBody.contentLength(); + } + + @Override + public void writeTo(BufferedSink sink) throws IOException { + BufferedSink bufferedSink = Okio.buffer(sink(sink)); + requestBody.writeTo(bufferedSink); + bufferedSink.flush(); + } + + private Sink sink(Sink sink) { + return new ForwardingSink(sink) { + + long bytesWritten = 0L; + long contentLength = 0L; + + @Override + public void write(Buffer source, long byteCount) throws IOException { + super.write(source, byteCount); + if (contentLength == 0) { + contentLength = contentLength(); + } + + bytesWritten += byteCount; + progressListener.onRequestProgress(bytesWritten, contentLength, bytesWritten == contentLength); + } + }; + } +} diff --git a/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/ProgressResponseBody.java b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/ProgressResponseBody.java new file mode 100644 index 0000000..856315a --- /dev/null +++ b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/ProgressResponseBody.java @@ -0,0 +1,90 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Task Execution Service + * + * OpenAPI spec version: 1.1.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +package nextflow.ga4gh.tes.client; + +import com.squareup.okhttp.MediaType; +import com.squareup.okhttp.ResponseBody; + +import java.io.IOException; + +import okio.Buffer; +import okio.BufferedSource; +import okio.ForwardingSource; +import okio.Okio; +import okio.Source; + +public class ProgressResponseBody extends ResponseBody { + + public interface ProgressListener { + void update(long bytesRead, long contentLength, boolean done); + } + + private final ResponseBody responseBody; + private final ProgressListener progressListener; + private BufferedSource bufferedSource; + + public ProgressResponseBody(ResponseBody responseBody, ProgressListener progressListener) { + this.responseBody = responseBody; + this.progressListener = progressListener; + } + + @Override + public MediaType contentType() { + return responseBody.contentType(); + } + + @Override + public long contentLength() throws IOException { + return responseBody.contentLength(); + } + + @Override + public BufferedSource source() throws IOException { + if (bufferedSource == null) { + bufferedSource = Okio.buffer(source(responseBody.source())); + } + return bufferedSource; + } + + private Source source(Source source) { + return new ForwardingSource(source) { + long totalBytesRead = 0L; + + @Override + public long read(Buffer sink, long byteCount) throws IOException { + long bytesRead = super.read(sink, byteCount); + // read() returns the number of bytes read, or -1 if this source is exhausted. + totalBytesRead += bytesRead != -1 ? bytesRead : 0; + progressListener.update(totalBytesRead, responseBody.contentLength(), bytesRead == -1); + return bytesRead; + } + }; + } +} + + diff --git a/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/StringUtil.java b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/StringUtil.java new file mode 100644 index 0000000..e3bffe7 --- /dev/null +++ b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/StringUtil.java @@ -0,0 +1,69 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Task Execution Service + * + * OpenAPI spec version: 1.1.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +package nextflow.ga4gh.tes.client; + +@javax.annotation.Generated(value = "io.swagger.codegen.v3.generators.java.JavaClientCodegen", date = "2023-08-15T14:10:09.878Z[GMT]") +public class StringUtil { + /** + * Check if the given array contains the given value (with case-insensitive comparison). + * + * @param array The array + * @param value The value to search + * @return true if the array contains the value + */ + public static boolean containsIgnoreCase(String[] array, String value) { + for (String str : array) { + if (value == null && str == null) return true; + if (value != null && value.equalsIgnoreCase(str)) return true; + } + return false; + } + + /** + * Join an array of strings with the given separator. + *

+ * Note: This might be replaced by utility method from commons-lang or guava someday + * if one of those libraries is added as dependency. + *

+ * + * @param array The array of strings + * @param separator The separator + * @return the resulting string + */ + public static String join(String[] array, String separator) { + int len = array.length; + if (len == 0) return ""; + + StringBuilder out = new StringBuilder(); + out.append(array[0]); + for (int i = 1; i < len; i++) { + out.append(separator).append(array[i]); + } + return out.toString(); + } +} diff --git a/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/api/TaskServiceApi.java b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/api/TaskServiceApi.java new file mode 100644 index 0000000..784b84c --- /dev/null +++ b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/api/TaskServiceApi.java @@ -0,0 +1,730 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Task Execution Service + * + * OpenAPI spec version: 1.1.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +package nextflow.ga4gh.tes.client.api; + +import nextflow.ga4gh.tes.client.ApiCallback; +import nextflow.ga4gh.tes.client.ApiClient; +import nextflow.ga4gh.tes.client.ApiException; +import nextflow.ga4gh.tes.client.ApiResponse; +import nextflow.ga4gh.tes.client.Configuration; +import nextflow.ga4gh.tes.client.Pair; +import nextflow.ga4gh.tes.client.ProgressRequestBody; +import nextflow.ga4gh.tes.client.ProgressResponseBody; + +import com.google.gson.reflect.TypeToken; + +import java.io.IOException; + + +import nextflow.ga4gh.tes.client.model.TesCancelTaskResponse; +import nextflow.ga4gh.tes.client.model.TesCreateTaskResponse; +import nextflow.ga4gh.tes.client.model.TesListTasksResponse; +import nextflow.ga4gh.tes.client.model.TesServiceInfo; +import nextflow.ga4gh.tes.client.model.TesState; +import nextflow.ga4gh.tes.client.model.TesTask; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class TaskServiceApi { + private ApiClient apiClient; + + public TaskServiceApi() { + this(Configuration.getDefaultApiClient()); + } + + public TaskServiceApi(ApiClient apiClient) { + this.apiClient = apiClient; + } + + public ApiClient getApiClient() { + return apiClient; + } + + public void setApiClient(ApiClient apiClient) { + this.apiClient = apiClient; + } + + /** + * Build call for cancelTask + * @param id ID of task to be canceled. (required) + * @param progressListener Progress listener + * @param progressRequestListener Progress request listener + * @return Call to execute + * @throws ApiException If fail to serialize the request body object + */ + public com.squareup.okhttp.Call cancelTaskCall(String id, final ProgressResponseBody.ProgressListener progressListener, final ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException { + Object localVarPostBody = null; + + // create path and map variables + String localVarPath = "/v1/tasks/{id}:cancel" + .replaceAll("\\{" + "id" + "\\}", apiClient.escapeString(id.toString())); + + List localVarQueryParams = new ArrayList(); + List localVarCollectionQueryParams = new ArrayList(); + + Map localVarHeaderParams = new HashMap(); + + Map localVarFormParams = new HashMap(); + + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = apiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) localVarHeaderParams.put("Accept", localVarAccept); + + final String[] localVarContentTypes = { + + }; + final String localVarContentType = apiClient.selectHeaderContentType(localVarContentTypes); + localVarHeaderParams.put("Content-Type", localVarContentType); + + if(progressListener != null) { + apiClient.getHttpClient().networkInterceptors().add(new com.squareup.okhttp.Interceptor() { + @Override + public com.squareup.okhttp.Response intercept(com.squareup.okhttp.Interceptor.Chain chain) throws IOException { + com.squareup.okhttp.Response originalResponse = chain.proceed(chain.request()); + return originalResponse.newBuilder() + .body(new ProgressResponseBody(originalResponse.body(), progressListener)) + .build(); + } + }); + } + + String[] localVarAuthNames = new String[] { }; + return apiClient.buildCall(localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarFormParams, localVarAuthNames, progressRequestListener); + } + + @SuppressWarnings("rawtypes") + private com.squareup.okhttp.Call cancelTaskValidateBeforeCall(String id, final ProgressResponseBody.ProgressListener progressListener, final ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException { + // verify the required parameter 'id' is set + if (id == null) { + throw new ApiException("Missing the required parameter 'id' when calling cancelTask(Async)"); + } + + com.squareup.okhttp.Call call = cancelTaskCall(id, progressListener, progressRequestListener); + return call; + + + + + + } + + /** + * CancelTask + * Cancel a task based on providing an exact task ID. + * @param id ID of task to be canceled. (required) + * @return TesCancelTaskResponse + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + */ + public TesCancelTaskResponse cancelTask(String id) throws ApiException { + ApiResponse resp = cancelTaskWithHttpInfo(id); + return resp.getData(); + } + + /** + * CancelTask + * Cancel a task based on providing an exact task ID. + * @param id ID of task to be canceled. (required) + * @return ApiResponse<TesCancelTaskResponse> + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + */ + public ApiResponse cancelTaskWithHttpInfo(String id) throws ApiException { + com.squareup.okhttp.Call call = cancelTaskValidateBeforeCall(id, null, null); + Type localVarReturnType = new TypeToken(){}.getType(); + return apiClient.execute(call, localVarReturnType); + } + + /** + * CancelTask (asynchronously) + * Cancel a task based on providing an exact task ID. + * @param id ID of task to be canceled. (required) + * @param callback The callback to be executed when the API call finishes + * @return The request call + * @throws ApiException If fail to process the API call, e.g. serializing the request body object + */ + public com.squareup.okhttp.Call cancelTaskAsync(String id, final ApiCallback callback) throws ApiException { + + ProgressResponseBody.ProgressListener progressListener = null; + ProgressRequestBody.ProgressRequestListener progressRequestListener = null; + + if (callback != null) { + progressListener = new ProgressResponseBody.ProgressListener() { + @Override + public void update(long bytesRead, long contentLength, boolean done) { + callback.onDownloadProgress(bytesRead, contentLength, done); + } + }; + + progressRequestListener = new ProgressRequestBody.ProgressRequestListener() { + @Override + public void onRequestProgress(long bytesWritten, long contentLength, boolean done) { + callback.onUploadProgress(bytesWritten, contentLength, done); + } + }; + } + + com.squareup.okhttp.Call call = cancelTaskValidateBeforeCall(id, progressListener, progressRequestListener); + Type localVarReturnType = new TypeToken(){}.getType(); + apiClient.executeAsync(call, localVarReturnType, callback); + return call; + } + /** + * Build call for createTask + * @param body (required) + * @param progressListener Progress listener + * @param progressRequestListener Progress request listener + * @return Call to execute + * @throws ApiException If fail to serialize the request body object + */ + public com.squareup.okhttp.Call createTaskCall(TesTask body, final ProgressResponseBody.ProgressListener progressListener, final ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException { + Object localVarPostBody = body; + + // create path and map variables + String localVarPath = "/v1/tasks"; + + List localVarQueryParams = new ArrayList(); + List localVarCollectionQueryParams = new ArrayList(); + + Map localVarHeaderParams = new HashMap(); + + Map localVarFormParams = new HashMap(); + + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = apiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) localVarHeaderParams.put("Accept", localVarAccept); + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = apiClient.selectHeaderContentType(localVarContentTypes); + localVarHeaderParams.put("Content-Type", localVarContentType); + + if(progressListener != null) { + apiClient.getHttpClient().networkInterceptors().add(new com.squareup.okhttp.Interceptor() { + @Override + public com.squareup.okhttp.Response intercept(com.squareup.okhttp.Interceptor.Chain chain) throws IOException { + com.squareup.okhttp.Response originalResponse = chain.proceed(chain.request()); + return originalResponse.newBuilder() + .body(new ProgressResponseBody(originalResponse.body(), progressListener)) + .build(); + } + }); + } + + String[] localVarAuthNames = new String[] { }; + return apiClient.buildCall(localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarFormParams, localVarAuthNames, progressRequestListener); + } + + @SuppressWarnings("rawtypes") + private com.squareup.okhttp.Call createTaskValidateBeforeCall(TesTask body, final ProgressResponseBody.ProgressListener progressListener, final ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException { + // verify the required parameter 'body' is set + if (body == null) { + throw new ApiException("Missing the required parameter 'body' when calling createTask(Async)"); + } + + com.squareup.okhttp.Call call = createTaskCall(body, progressListener, progressRequestListener); + return call; + + + + + + } + + /** + * CreateTask + * Create a new task. The user provides a Task document, which the server uses as a basis and adds additional fields. + * @param body (required) + * @return TesCreateTaskResponse + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + */ + public TesCreateTaskResponse createTask(TesTask body) throws ApiException { + ApiResponse resp = createTaskWithHttpInfo(body); + return resp.getData(); + } + + /** + * CreateTask + * Create a new task. The user provides a Task document, which the server uses as a basis and adds additional fields. + * @param body (required) + * @return ApiResponse<TesCreateTaskResponse> + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + */ + public ApiResponse createTaskWithHttpInfo(TesTask body) throws ApiException { + com.squareup.okhttp.Call call = createTaskValidateBeforeCall(body, null, null); + Type localVarReturnType = new TypeToken(){}.getType(); + return apiClient.execute(call, localVarReturnType); + } + + /** + * CreateTask (asynchronously) + * Create a new task. The user provides a Task document, which the server uses as a basis and adds additional fields. + * @param body (required) + * @param callback The callback to be executed when the API call finishes + * @return The request call + * @throws ApiException If fail to process the API call, e.g. serializing the request body object + */ + public com.squareup.okhttp.Call createTaskAsync(TesTask body, final ApiCallback callback) throws ApiException { + + ProgressResponseBody.ProgressListener progressListener = null; + ProgressRequestBody.ProgressRequestListener progressRequestListener = null; + + if (callback != null) { + progressListener = new ProgressResponseBody.ProgressListener() { + @Override + public void update(long bytesRead, long contentLength, boolean done) { + callback.onDownloadProgress(bytesRead, contentLength, done); + } + }; + + progressRequestListener = new ProgressRequestBody.ProgressRequestListener() { + @Override + public void onRequestProgress(long bytesWritten, long contentLength, boolean done) { + callback.onUploadProgress(bytesWritten, contentLength, done); + } + }; + } + + com.squareup.okhttp.Call call = createTaskValidateBeforeCall(body, progressListener, progressRequestListener); + Type localVarReturnType = new TypeToken(){}.getType(); + apiClient.executeAsync(call, localVarReturnType, callback); + return call; + } + /** + * Build call for getServiceInfo + * @param progressListener Progress listener + * @param progressRequestListener Progress request listener + * @return Call to execute + * @throws ApiException If fail to serialize the request body object + */ + public com.squareup.okhttp.Call getServiceInfoCall(final ProgressResponseBody.ProgressListener progressListener, final ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException { + Object localVarPostBody = null; + + // create path and map variables + String localVarPath = "/service-info"; + + List localVarQueryParams = new ArrayList(); + List localVarCollectionQueryParams = new ArrayList(); + + Map localVarHeaderParams = new HashMap(); + + Map localVarFormParams = new HashMap(); + + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = apiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) localVarHeaderParams.put("Accept", localVarAccept); + + final String[] localVarContentTypes = { + + }; + final String localVarContentType = apiClient.selectHeaderContentType(localVarContentTypes); + localVarHeaderParams.put("Content-Type", localVarContentType); + + if(progressListener != null) { + apiClient.getHttpClient().networkInterceptors().add(new com.squareup.okhttp.Interceptor() { + @Override + public com.squareup.okhttp.Response intercept(com.squareup.okhttp.Interceptor.Chain chain) throws IOException { + com.squareup.okhttp.Response originalResponse = chain.proceed(chain.request()); + return originalResponse.newBuilder() + .body(new ProgressResponseBody(originalResponse.body(), progressListener)) + .build(); + } + }); + } + + String[] localVarAuthNames = new String[] { }; + return apiClient.buildCall(localVarPath, "GET", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarFormParams, localVarAuthNames, progressRequestListener); + } + + @SuppressWarnings("rawtypes") + private com.squareup.okhttp.Call getServiceInfoValidateBeforeCall(final ProgressResponseBody.ProgressListener progressListener, final ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException { + + com.squareup.okhttp.Call call = getServiceInfoCall(progressListener, progressRequestListener); + return call; + + + + + + } + + /** + * GetServiceInfo + * Provides information about the service, this structure is based on the standardized GA4GH service info structure. In addition, this endpoint will also provide information about customized storage endpoints offered by the TES server. + * @return TesServiceInfo + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + */ + public TesServiceInfo getServiceInfo() throws ApiException { + ApiResponse resp = getServiceInfoWithHttpInfo(); + return resp.getData(); + } + + /** + * GetServiceInfo + * Provides information about the service, this structure is based on the standardized GA4GH service info structure. In addition, this endpoint will also provide information about customized storage endpoints offered by the TES server. + * @return ApiResponse<TesServiceInfo> + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + */ + public ApiResponse getServiceInfoWithHttpInfo() throws ApiException { + com.squareup.okhttp.Call call = getServiceInfoValidateBeforeCall(null, null); + Type localVarReturnType = new TypeToken(){}.getType(); + return apiClient.execute(call, localVarReturnType); + } + + /** + * GetServiceInfo (asynchronously) + * Provides information about the service, this structure is based on the standardized GA4GH service info structure. In addition, this endpoint will also provide information about customized storage endpoints offered by the TES server. + * @param callback The callback to be executed when the API call finishes + * @return The request call + * @throws ApiException If fail to process the API call, e.g. serializing the request body object + */ + public com.squareup.okhttp.Call getServiceInfoAsync(final ApiCallback callback) throws ApiException { + + ProgressResponseBody.ProgressListener progressListener = null; + ProgressRequestBody.ProgressRequestListener progressRequestListener = null; + + if (callback != null) { + progressListener = new ProgressResponseBody.ProgressListener() { + @Override + public void update(long bytesRead, long contentLength, boolean done) { + callback.onDownloadProgress(bytesRead, contentLength, done); + } + }; + + progressRequestListener = new ProgressRequestBody.ProgressRequestListener() { + @Override + public void onRequestProgress(long bytesWritten, long contentLength, boolean done) { + callback.onUploadProgress(bytesWritten, contentLength, done); + } + }; + } + + com.squareup.okhttp.Call call = getServiceInfoValidateBeforeCall(progressListener, progressRequestListener); + Type localVarReturnType = new TypeToken(){}.getType(); + apiClient.executeAsync(call, localVarReturnType, callback); + return call; + } + /** + * Build call for getTask + * @param id ID of task to retrieve. (required) + * @param view OPTIONAL. Affects the fields included in the returned Task messages. `MINIMAL`: Task message will include ONLY the fields: - `tesTask.Id` - `tesTask.State` `BASIC`: Task message will include all fields EXCEPT: - `tesTask.ExecutorLog.stdout` - `tesTask.ExecutorLog.stderr` - `tesInput.content` - `tesTaskLog.system_logs` `FULL`: Task message includes all fields. (optional, default to MINIMAL) + * @param progressListener Progress listener + * @param progressRequestListener Progress request listener + * @return Call to execute + * @throws ApiException If fail to serialize the request body object + */ + public com.squareup.okhttp.Call getTaskCall(String id, String view, final ProgressResponseBody.ProgressListener progressListener, final ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException { + Object localVarPostBody = null; + + // create path and map variables + String localVarPath = "/v1/tasks/{id}" + .replaceAll("\\{" + "id" + "\\}", apiClient.escapeString(id.toString())); + + List localVarQueryParams = new ArrayList(); + List localVarCollectionQueryParams = new ArrayList(); + if (view != null) + localVarQueryParams.addAll(apiClient.parameterToPair("view", view)); + + Map localVarHeaderParams = new HashMap(); + + Map localVarFormParams = new HashMap(); + + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = apiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) localVarHeaderParams.put("Accept", localVarAccept); + + final String[] localVarContentTypes = { + + }; + final String localVarContentType = apiClient.selectHeaderContentType(localVarContentTypes); + localVarHeaderParams.put("Content-Type", localVarContentType); + + if(progressListener != null) { + apiClient.getHttpClient().networkInterceptors().add(new com.squareup.okhttp.Interceptor() { + @Override + public com.squareup.okhttp.Response intercept(com.squareup.okhttp.Interceptor.Chain chain) throws IOException { + com.squareup.okhttp.Response originalResponse = chain.proceed(chain.request()); + return originalResponse.newBuilder() + .body(new ProgressResponseBody(originalResponse.body(), progressListener)) + .build(); + } + }); + } + + String[] localVarAuthNames = new String[] { }; + return apiClient.buildCall(localVarPath, "GET", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarFormParams, localVarAuthNames, progressRequestListener); + } + + @SuppressWarnings("rawtypes") + private com.squareup.okhttp.Call getTaskValidateBeforeCall(String id, String view, final ProgressResponseBody.ProgressListener progressListener, final ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException { + // verify the required parameter 'id' is set + if (id == null) { + throw new ApiException("Missing the required parameter 'id' when calling getTask(Async)"); + } + + com.squareup.okhttp.Call call = getTaskCall(id, view, progressListener, progressRequestListener); + return call; + + + + + + } + + /** + * GetTask + * Get a single task, based on providing the exact task ID string. + * @param id ID of task to retrieve. (required) + * @param view OPTIONAL. Affects the fields included in the returned Task messages. `MINIMAL`: Task message will include ONLY the fields: - `tesTask.Id` - `tesTask.State` `BASIC`: Task message will include all fields EXCEPT: - `tesTask.ExecutorLog.stdout` - `tesTask.ExecutorLog.stderr` - `tesInput.content` - `tesTaskLog.system_logs` `FULL`: Task message includes all fields. (optional, default to MINIMAL) + * @return TesTask + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + */ + public TesTask getTask(String id, String view) throws ApiException { + ApiResponse resp = getTaskWithHttpInfo(id, view); + return resp.getData(); + } + + /** + * GetTask + * Get a single task, based on providing the exact task ID string. + * @param id ID of task to retrieve. (required) + * @param view OPTIONAL. Affects the fields included in the returned Task messages. `MINIMAL`: Task message will include ONLY the fields: - `tesTask.Id` - `tesTask.State` `BASIC`: Task message will include all fields EXCEPT: - `tesTask.ExecutorLog.stdout` - `tesTask.ExecutorLog.stderr` - `tesInput.content` - `tesTaskLog.system_logs` `FULL`: Task message includes all fields. (optional, default to MINIMAL) + * @return ApiResponse<TesTask> + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + */ + public ApiResponse getTaskWithHttpInfo(String id, String view) throws ApiException { + com.squareup.okhttp.Call call = getTaskValidateBeforeCall(id, view, null, null); + Type localVarReturnType = new TypeToken(){}.getType(); + return apiClient.execute(call, localVarReturnType); + } + + /** + * GetTask (asynchronously) + * Get a single task, based on providing the exact task ID string. + * @param id ID of task to retrieve. (required) + * @param view OPTIONAL. Affects the fields included in the returned Task messages. `MINIMAL`: Task message will include ONLY the fields: - `tesTask.Id` - `tesTask.State` `BASIC`: Task message will include all fields EXCEPT: - `tesTask.ExecutorLog.stdout` - `tesTask.ExecutorLog.stderr` - `tesInput.content` - `tesTaskLog.system_logs` `FULL`: Task message includes all fields. (optional, default to MINIMAL) + * @param callback The callback to be executed when the API call finishes + * @return The request call + * @throws ApiException If fail to process the API call, e.g. serializing the request body object + */ + public com.squareup.okhttp.Call getTaskAsync(String id, String view, final ApiCallback callback) throws ApiException { + + ProgressResponseBody.ProgressListener progressListener = null; + ProgressRequestBody.ProgressRequestListener progressRequestListener = null; + + if (callback != null) { + progressListener = new ProgressResponseBody.ProgressListener() { + @Override + public void update(long bytesRead, long contentLength, boolean done) { + callback.onDownloadProgress(bytesRead, contentLength, done); + } + }; + + progressRequestListener = new ProgressRequestBody.ProgressRequestListener() { + @Override + public void onRequestProgress(long bytesWritten, long contentLength, boolean done) { + callback.onUploadProgress(bytesWritten, contentLength, done); + } + }; + } + + com.squareup.okhttp.Call call = getTaskValidateBeforeCall(id, view, progressListener, progressRequestListener); + Type localVarReturnType = new TypeToken(){}.getType(); + apiClient.executeAsync(call, localVarReturnType, callback); + return call; + } + /** + * Build call for listTasks + * @param namePrefix OPTIONAL. Filter the list to include tasks where the name matches this prefix. If unspecified, no task name filtering is done. (optional) + * @param state OPTIONAL. Filter tasks by state. If unspecified, no task state filtering is done. (optional) + * @param tagKey OPTIONAL. Provide key tag to filter. The field tag_key is an array of key values, and will be zipped with an optional tag_value array. So the query: ``` ?tag_key=foo1&tag_value=bar1&tag_key=foo2&tag_value=bar2 ``` Should be constructed into the structure { \"foo1\" : \"bar1\", \"foo2\" : \"bar2\"} ``` ?tag_key=foo1 ``` Should be constructed into the structure {\"foo1\" : \"\"} If the tag_value is empty, it will be treated as matching any possible value. If a tag value is provided, both the tag's key and value must be exact matches for a task to be returned. Filter Tags Match? ---------------------------------------------------------------------- {\"foo\": \"bar\"} {\"foo\": \"bar\"} Yes {\"foo\": \"bar\"} {\"foo\": \"bat\"} No {\"foo\": \"\"} {\"foo\": \"\"} Yes {\"foo\": \"bar\", \"baz\": \"bat\"} {\"foo\": \"bar\", \"baz\": \"bat\"} Yes {\"foo\": \"bar\"} {\"foo\": \"bar\", \"baz\": \"bat\"} Yes {\"foo\": \"bar\", \"baz\": \"bat\"} {\"foo\": \"bar\"} No {\"foo\": \"\"} {\"foo\": \"bar\"} Yes {\"foo\": \"\"} {} No (optional) + * @param tagValue OPTIONAL. The companion value field for tag_key (optional) + * @param pageSize Optional number of tasks to return in one page. Must be less than 2048. Defaults to 256. (optional) + * @param pageToken OPTIONAL. Page token is used to retrieve the next page of results. If unspecified, returns the first page of results. The value can be found in the `next_page_token` field of the last returned result of ListTasks (optional) + * @param view OPTIONAL. Affects the fields included in the returned Task messages. `MINIMAL`: Task message will include ONLY the fields: - `tesTask.Id` - `tesTask.State` `BASIC`: Task message will include all fields EXCEPT: - `tesTask.ExecutorLog.stdout` - `tesTask.ExecutorLog.stderr` - `tesInput.content` - `tesTaskLog.system_logs` `FULL`: Task message includes all fields. (optional, default to MINIMAL) + * @param progressListener Progress listener + * @param progressRequestListener Progress request listener + * @return Call to execute + * @throws ApiException If fail to serialize the request body object + */ + public com.squareup.okhttp.Call listTasksCall(String namePrefix, TesState state, List tagKey, List tagValue, Integer pageSize, String pageToken, String view, final ProgressResponseBody.ProgressListener progressListener, final ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException { + Object localVarPostBody = null; + + // create path and map variables + String localVarPath = "/v1/tasks"; + + List localVarQueryParams = new ArrayList(); + List localVarCollectionQueryParams = new ArrayList(); + if (namePrefix != null) + localVarQueryParams.addAll(apiClient.parameterToPair("name_prefix", namePrefix)); + if (state != null) + localVarQueryParams.addAll(apiClient.parameterToPair("state", state)); + if (tagKey != null) + localVarCollectionQueryParams.addAll(apiClient.parameterToPairs("multi", "tag_key", tagKey)); + if (tagValue != null) + localVarCollectionQueryParams.addAll(apiClient.parameterToPairs("multi", "tag_value", tagValue)); + if (pageSize != null) + localVarQueryParams.addAll(apiClient.parameterToPair("page_size", pageSize)); + if (pageToken != null) + localVarQueryParams.addAll(apiClient.parameterToPair("page_token", pageToken)); + if (view != null) + localVarQueryParams.addAll(apiClient.parameterToPair("view", view)); + + Map localVarHeaderParams = new HashMap(); + + Map localVarFormParams = new HashMap(); + + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = apiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) localVarHeaderParams.put("Accept", localVarAccept); + + final String[] localVarContentTypes = { + + }; + final String localVarContentType = apiClient.selectHeaderContentType(localVarContentTypes); + localVarHeaderParams.put("Content-Type", localVarContentType); + + if(progressListener != null) { + apiClient.getHttpClient().networkInterceptors().add(new com.squareup.okhttp.Interceptor() { + @Override + public com.squareup.okhttp.Response intercept(com.squareup.okhttp.Interceptor.Chain chain) throws IOException { + com.squareup.okhttp.Response originalResponse = chain.proceed(chain.request()); + return originalResponse.newBuilder() + .body(new ProgressResponseBody(originalResponse.body(), progressListener)) + .build(); + } + }); + } + + String[] localVarAuthNames = new String[] { }; + return apiClient.buildCall(localVarPath, "GET", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarFormParams, localVarAuthNames, progressRequestListener); + } + + @SuppressWarnings("rawtypes") + private com.squareup.okhttp.Call listTasksValidateBeforeCall(String namePrefix, TesState state, List tagKey, List tagValue, Integer pageSize, String pageToken, String view, final ProgressResponseBody.ProgressListener progressListener, final ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException { + + com.squareup.okhttp.Call call = listTasksCall(namePrefix, state, tagKey, tagValue, pageSize, pageToken, view, progressListener, progressRequestListener); + return call; + + + + + + } + + /** + * ListTasks + * List tasks tracked by the TES server. This includes queued, active and completed tasks. How long completed tasks are stored by the system may be dependent on the underlying implementation. + * @param namePrefix OPTIONAL. Filter the list to include tasks where the name matches this prefix. If unspecified, no task name filtering is done. (optional) + * @param state OPTIONAL. Filter tasks by state. If unspecified, no task state filtering is done. (optional) + * @param tagKey OPTIONAL. Provide key tag to filter. The field tag_key is an array of key values, and will be zipped with an optional tag_value array. So the query: ``` ?tag_key=foo1&tag_value=bar1&tag_key=foo2&tag_value=bar2 ``` Should be constructed into the structure { \"foo1\" : \"bar1\", \"foo2\" : \"bar2\"} ``` ?tag_key=foo1 ``` Should be constructed into the structure {\"foo1\" : \"\"} If the tag_value is empty, it will be treated as matching any possible value. If a tag value is provided, both the tag's key and value must be exact matches for a task to be returned. Filter Tags Match? ---------------------------------------------------------------------- {\"foo\": \"bar\"} {\"foo\": \"bar\"} Yes {\"foo\": \"bar\"} {\"foo\": \"bat\"} No {\"foo\": \"\"} {\"foo\": \"\"} Yes {\"foo\": \"bar\", \"baz\": \"bat\"} {\"foo\": \"bar\", \"baz\": \"bat\"} Yes {\"foo\": \"bar\"} {\"foo\": \"bar\", \"baz\": \"bat\"} Yes {\"foo\": \"bar\", \"baz\": \"bat\"} {\"foo\": \"bar\"} No {\"foo\": \"\"} {\"foo\": \"bar\"} Yes {\"foo\": \"\"} {} No (optional) + * @param tagValue OPTIONAL. The companion value field for tag_key (optional) + * @param pageSize Optional number of tasks to return in one page. Must be less than 2048. Defaults to 256. (optional) + * @param pageToken OPTIONAL. Page token is used to retrieve the next page of results. If unspecified, returns the first page of results. The value can be found in the `next_page_token` field of the last returned result of ListTasks (optional) + * @param view OPTIONAL. Affects the fields included in the returned Task messages. `MINIMAL`: Task message will include ONLY the fields: - `tesTask.Id` - `tesTask.State` `BASIC`: Task message will include all fields EXCEPT: - `tesTask.ExecutorLog.stdout` - `tesTask.ExecutorLog.stderr` - `tesInput.content` - `tesTaskLog.system_logs` `FULL`: Task message includes all fields. (optional, default to MINIMAL) + * @return TesListTasksResponse + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + */ + public TesListTasksResponse listTasks(String namePrefix, TesState state, List tagKey, List tagValue, Integer pageSize, String pageToken, String view) throws ApiException { + ApiResponse resp = listTasksWithHttpInfo(namePrefix, state, tagKey, tagValue, pageSize, pageToken, view); + return resp.getData(); + } + + /** + * ListTasks + * List tasks tracked by the TES server. This includes queued, active and completed tasks. How long completed tasks are stored by the system may be dependent on the underlying implementation. + * @param namePrefix OPTIONAL. Filter the list to include tasks where the name matches this prefix. If unspecified, no task name filtering is done. (optional) + * @param state OPTIONAL. Filter tasks by state. If unspecified, no task state filtering is done. (optional) + * @param tagKey OPTIONAL. Provide key tag to filter. The field tag_key is an array of key values, and will be zipped with an optional tag_value array. So the query: ``` ?tag_key=foo1&tag_value=bar1&tag_key=foo2&tag_value=bar2 ``` Should be constructed into the structure { \"foo1\" : \"bar1\", \"foo2\" : \"bar2\"} ``` ?tag_key=foo1 ``` Should be constructed into the structure {\"foo1\" : \"\"} If the tag_value is empty, it will be treated as matching any possible value. If a tag value is provided, both the tag's key and value must be exact matches for a task to be returned. Filter Tags Match? ---------------------------------------------------------------------- {\"foo\": \"bar\"} {\"foo\": \"bar\"} Yes {\"foo\": \"bar\"} {\"foo\": \"bat\"} No {\"foo\": \"\"} {\"foo\": \"\"} Yes {\"foo\": \"bar\", \"baz\": \"bat\"} {\"foo\": \"bar\", \"baz\": \"bat\"} Yes {\"foo\": \"bar\"} {\"foo\": \"bar\", \"baz\": \"bat\"} Yes {\"foo\": \"bar\", \"baz\": \"bat\"} {\"foo\": \"bar\"} No {\"foo\": \"\"} {\"foo\": \"bar\"} Yes {\"foo\": \"\"} {} No (optional) + * @param tagValue OPTIONAL. The companion value field for tag_key (optional) + * @param pageSize Optional number of tasks to return in one page. Must be less than 2048. Defaults to 256. (optional) + * @param pageToken OPTIONAL. Page token is used to retrieve the next page of results. If unspecified, returns the first page of results. The value can be found in the `next_page_token` field of the last returned result of ListTasks (optional) + * @param view OPTIONAL. Affects the fields included in the returned Task messages. `MINIMAL`: Task message will include ONLY the fields: - `tesTask.Id` - `tesTask.State` `BASIC`: Task message will include all fields EXCEPT: - `tesTask.ExecutorLog.stdout` - `tesTask.ExecutorLog.stderr` - `tesInput.content` - `tesTaskLog.system_logs` `FULL`: Task message includes all fields. (optional, default to MINIMAL) + * @return ApiResponse<TesListTasksResponse> + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + */ + public ApiResponse listTasksWithHttpInfo(String namePrefix, TesState state, List tagKey, List tagValue, Integer pageSize, String pageToken, String view) throws ApiException { + com.squareup.okhttp.Call call = listTasksValidateBeforeCall(namePrefix, state, tagKey, tagValue, pageSize, pageToken, view, null, null); + Type localVarReturnType = new TypeToken(){}.getType(); + return apiClient.execute(call, localVarReturnType); + } + + /** + * ListTasks (asynchronously) + * List tasks tracked by the TES server. This includes queued, active and completed tasks. How long completed tasks are stored by the system may be dependent on the underlying implementation. + * @param namePrefix OPTIONAL. Filter the list to include tasks where the name matches this prefix. If unspecified, no task name filtering is done. (optional) + * @param state OPTIONAL. Filter tasks by state. If unspecified, no task state filtering is done. (optional) + * @param tagKey OPTIONAL. Provide key tag to filter. The field tag_key is an array of key values, and will be zipped with an optional tag_value array. So the query: ``` ?tag_key=foo1&tag_value=bar1&tag_key=foo2&tag_value=bar2 ``` Should be constructed into the structure { \"foo1\" : \"bar1\", \"foo2\" : \"bar2\"} ``` ?tag_key=foo1 ``` Should be constructed into the structure {\"foo1\" : \"\"} If the tag_value is empty, it will be treated as matching any possible value. If a tag value is provided, both the tag's key and value must be exact matches for a task to be returned. Filter Tags Match? ---------------------------------------------------------------------- {\"foo\": \"bar\"} {\"foo\": \"bar\"} Yes {\"foo\": \"bar\"} {\"foo\": \"bat\"} No {\"foo\": \"\"} {\"foo\": \"\"} Yes {\"foo\": \"bar\", \"baz\": \"bat\"} {\"foo\": \"bar\", \"baz\": \"bat\"} Yes {\"foo\": \"bar\"} {\"foo\": \"bar\", \"baz\": \"bat\"} Yes {\"foo\": \"bar\", \"baz\": \"bat\"} {\"foo\": \"bar\"} No {\"foo\": \"\"} {\"foo\": \"bar\"} Yes {\"foo\": \"\"} {} No (optional) + * @param tagValue OPTIONAL. The companion value field for tag_key (optional) + * @param pageSize Optional number of tasks to return in one page. Must be less than 2048. Defaults to 256. (optional) + * @param pageToken OPTIONAL. Page token is used to retrieve the next page of results. If unspecified, returns the first page of results. The value can be found in the `next_page_token` field of the last returned result of ListTasks (optional) + * @param view OPTIONAL. Affects the fields included in the returned Task messages. `MINIMAL`: Task message will include ONLY the fields: - `tesTask.Id` - `tesTask.State` `BASIC`: Task message will include all fields EXCEPT: - `tesTask.ExecutorLog.stdout` - `tesTask.ExecutorLog.stderr` - `tesInput.content` - `tesTaskLog.system_logs` `FULL`: Task message includes all fields. (optional, default to MINIMAL) + * @param callback The callback to be executed when the API call finishes + * @return The request call + * @throws ApiException If fail to process the API call, e.g. serializing the request body object + */ + public com.squareup.okhttp.Call listTasksAsync(String namePrefix, TesState state, List tagKey, List tagValue, Integer pageSize, String pageToken, String view, final ApiCallback callback) throws ApiException { + + ProgressResponseBody.ProgressListener progressListener = null; + ProgressRequestBody.ProgressRequestListener progressRequestListener = null; + + if (callback != null) { + progressListener = new ProgressResponseBody.ProgressListener() { + @Override + public void update(long bytesRead, long contentLength, boolean done) { + callback.onDownloadProgress(bytesRead, contentLength, done); + } + }; + + progressRequestListener = new ProgressRequestBody.ProgressRequestListener() { + @Override + public void onRequestProgress(long bytesWritten, long contentLength, boolean done) { + callback.onUploadProgress(bytesWritten, contentLength, done); + } + }; + } + + com.squareup.okhttp.Call call = listTasksValidateBeforeCall(namePrefix, state, tagKey, tagValue, pageSize, pageToken, view, progressListener, progressRequestListener); + Type localVarReturnType = new TypeToken(){}.getType(); + apiClient.executeAsync(call, localVarReturnType, callback); + return call; + } +} diff --git a/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/auth/ApiKeyAuth.java b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/auth/ApiKeyAuth.java new file mode 100644 index 0000000..e4b4c58 --- /dev/null +++ b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/auth/ApiKeyAuth.java @@ -0,0 +1,89 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Task Execution Service + * + * OpenAPI spec version: 1.1.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +package nextflow.ga4gh.tes.client.auth; + +import nextflow.ga4gh.tes.client.Pair; + +import java.util.Map; +import java.util.List; + +@javax.annotation.Generated(value = "io.swagger.codegen.v3.generators.java.JavaClientCodegen", date = "2023-08-15T14:10:09.878Z[GMT]") +public class ApiKeyAuth implements Authentication { + private final String location; + private final String paramName; + + private String apiKey; + private String apiKeyPrefix; + + public ApiKeyAuth(String location, String paramName) { + this.location = location; + this.paramName = paramName; + } + + public String getLocation() { + return location; + } + + public String getParamName() { + return paramName; + } + + public String getApiKey() { + return apiKey; + } + + public void setApiKey(String apiKey) { + this.apiKey = apiKey; + } + + public String getApiKeyPrefix() { + return apiKeyPrefix; + } + + public void setApiKeyPrefix(String apiKeyPrefix) { + this.apiKeyPrefix = apiKeyPrefix; + } + + @Override + public void applyToParams(List queryParams, Map headerParams) { + if (apiKey == null) { + return; + } + String value; + if (apiKeyPrefix != null) { + value = apiKeyPrefix + " " + apiKey; + } else { + value = apiKey; + } + if ("query".equals(location)) { + queryParams.add(new Pair(paramName, value)); + } else if ("header".equals(location)) { + headerParams.put(paramName, value); + } + } +} diff --git a/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/auth/Authentication.java b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/auth/Authentication.java new file mode 100644 index 0000000..422f772 --- /dev/null +++ b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/auth/Authentication.java @@ -0,0 +1,43 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Task Execution Service + * + * OpenAPI spec version: 1.1.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +package nextflow.ga4gh.tes.client.auth; + +import nextflow.ga4gh.tes.client.Pair; + +import java.util.Map; +import java.util.List; + +public interface Authentication { + /** + * Apply authentication settings to header and query params. + * + * @param queryParams List of query parameters + * @param headerParams Map of header parameters + */ + void applyToParams(List queryParams, Map headerParams); +} diff --git a/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/auth/HttpBasicAuth.java b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/auth/HttpBasicAuth.java new file mode 100644 index 0000000..22c3a6c --- /dev/null +++ b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/auth/HttpBasicAuth.java @@ -0,0 +1,68 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Task Execution Service + * + * OpenAPI spec version: 1.1.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +package nextflow.ga4gh.tes.client.auth; + +import nextflow.ga4gh.tes.client.Pair; + +import com.squareup.okhttp.Credentials; + +import java.util.Map; +import java.util.List; + +import java.io.UnsupportedEncodingException; + +public class HttpBasicAuth implements Authentication { + private String username; + private String password; + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + @Override + public void applyToParams(List queryParams, Map headerParams) { + if (username == null && password == null) { + return; + } + headerParams.put("Authorization", Credentials.basic( + username == null ? "" : username, + password == null ? "" : password)); + } +} diff --git a/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/auth/OAuth.java b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/auth/OAuth.java new file mode 100644 index 0000000..84ecc22 --- /dev/null +++ b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/auth/OAuth.java @@ -0,0 +1,53 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Task Execution Service + * + * OpenAPI spec version: 1.1.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +package nextflow.ga4gh.tes.client.auth; + +import nextflow.ga4gh.tes.client.Pair; + +import java.util.Map; +import java.util.List; + +@javax.annotation.Generated(value = "io.swagger.codegen.v3.generators.java.JavaClientCodegen", date = "2023-08-15T14:10:09.878Z[GMT]") +public class OAuth implements Authentication { + private String accessToken; + + public String getAccessToken() { + return accessToken; + } + + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } + + @Override + public void applyToParams(List queryParams, Map headerParams) { + if (accessToken != null) { + headerParams.put("Authorization", "Bearer " + accessToken); + } + } +} diff --git a/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/auth/OAuthFlow.java b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/auth/OAuthFlow.java new file mode 100644 index 0000000..fff74fb --- /dev/null +++ b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/auth/OAuthFlow.java @@ -0,0 +1,32 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Task Execution Service + * + * OpenAPI spec version: 1.1.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +package nextflow.ga4gh.tes.client.auth; + +public enum OAuthFlow { + accessCode, implicit, password, application +} diff --git a/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/Service.java b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/Service.java new file mode 100644 index 0000000..a510c1d --- /dev/null +++ b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/Service.java @@ -0,0 +1,340 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Task Execution Service + * + * OpenAPI spec version: 1.1.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +package nextflow.ga4gh.tes.client.model; + +import java.util.Objects; +import java.util.Arrays; +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import nextflow.ga4gh.tes.client.model.ServiceOrganization; +import nextflow.ga4gh.tes.client.model.ServiceType; +import io.swagger.v3.oas.annotations.media.Schema; +import java.io.IOException; +import org.threeten.bp.OffsetDateTime; +/** + * GA4GH service + */ +@Schema(description = "GA4GH service") +@javax.annotation.Generated(value = "io.swagger.codegen.v3.generators.java.JavaClientCodegen", date = "2023-08-15T14:10:09.878Z[GMT]") + +public class Service { + @SerializedName("id") + private String id = null; + + @SerializedName("name") + private String name = null; + + @SerializedName("type") + private ServiceType type = null; + + @SerializedName("description") + private String description = null; + + @SerializedName("organization") + private ServiceOrganization organization = null; + + @SerializedName("contactUrl") + private String contactUrl = null; + + @SerializedName("documentationUrl") + private String documentationUrl = null; + + @SerializedName("createdAt") + private OffsetDateTime createdAt = null; + + @SerializedName("updatedAt") + private OffsetDateTime updatedAt = null; + + @SerializedName("environment") + private String environment = null; + + @SerializedName("version") + private String version = null; + + public Service id(String id) { + this.id = id; + return this; + } + + /** + * Unique ID of this service. Reverse domain name notation is recommended, though not required. The identifier should attempt to be globally unique so it can be used in downstream aggregator services e.g. Service Registry. + * @return id + **/ + @Schema(example = "org.ga4gh.myservice", required = true, description = "Unique ID of this service. Reverse domain name notation is recommended, though not required. The identifier should attempt to be globally unique so it can be used in downstream aggregator services e.g. Service Registry.") + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public Service name(String name) { + this.name = name; + return this; + } + + /** + * Name of this service. Should be human readable. + * @return name + **/ + @Schema(example = "My project", required = true, description = "Name of this service. Should be human readable.") + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Service type(ServiceType type) { + this.type = type; + return this; + } + + /** + * Get type + * @return type + **/ + @Schema(required = true, description = "") + public ServiceType getType() { + return type; + } + + public void setType(ServiceType type) { + this.type = type; + } + + public Service description(String description) { + this.description = description; + return this; + } + + /** + * Description of the service. Should be human readable and provide information about the service. + * @return description + **/ + @Schema(example = "This service provides...", description = "Description of the service. Should be human readable and provide information about the service.") + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Service organization(ServiceOrganization organization) { + this.organization = organization; + return this; + } + + /** + * Get organization + * @return organization + **/ + @Schema(required = true, description = "") + public ServiceOrganization getOrganization() { + return organization; + } + + public void setOrganization(ServiceOrganization organization) { + this.organization = organization; + } + + public Service contactUrl(String contactUrl) { + this.contactUrl = contactUrl; + return this; + } + + /** + * URL of the contact for the provider of this service, e.g. a link to a contact form (RFC 3986 format), or an email (RFC 2368 format). + * @return contactUrl + **/ + @Schema(example = "mailto:support@example.com", description = "URL of the contact for the provider of this service, e.g. a link to a contact form (RFC 3986 format), or an email (RFC 2368 format).") + public String getContactUrl() { + return contactUrl; + } + + public void setContactUrl(String contactUrl) { + this.contactUrl = contactUrl; + } + + public Service documentationUrl(String documentationUrl) { + this.documentationUrl = documentationUrl; + return this; + } + + /** + * URL of the documentation of this service (RFC 3986 format). This should help someone learn how to use your service, including any specifics required to access data, e.g. authentication. + * @return documentationUrl + **/ + @Schema(example = "https://docs.myservice.example.com", description = "URL of the documentation of this service (RFC 3986 format). This should help someone learn how to use your service, including any specifics required to access data, e.g. authentication.") + public String getDocumentationUrl() { + return documentationUrl; + } + + public void setDocumentationUrl(String documentationUrl) { + this.documentationUrl = documentationUrl; + } + + public Service createdAt(OffsetDateTime createdAt) { + this.createdAt = createdAt; + return this; + } + + /** + * Timestamp describing when the service was first deployed and available (RFC 3339 format) + * @return createdAt + **/ + @Schema(example = "2019-06-04T12:58:19Z", description = "Timestamp describing when the service was first deployed and available (RFC 3339 format)") + public OffsetDateTime getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(OffsetDateTime createdAt) { + this.createdAt = createdAt; + } + + public Service updatedAt(OffsetDateTime updatedAt) { + this.updatedAt = updatedAt; + return this; + } + + /** + * Timestamp describing when the service was last updated (RFC 3339 format) + * @return updatedAt + **/ + @Schema(example = "2019-06-04T12:58:19Z", description = "Timestamp describing when the service was last updated (RFC 3339 format)") + public OffsetDateTime getUpdatedAt() { + return updatedAt; + } + + public void setUpdatedAt(OffsetDateTime updatedAt) { + this.updatedAt = updatedAt; + } + + public Service environment(String environment) { + this.environment = environment; + return this; + } + + /** + * Environment the service is running in. Use this to distinguish between production, development and testing/staging deployments. Suggested values are prod, test, dev, staging. However this is advised and not enforced. + * @return environment + **/ + @Schema(example = "test", description = "Environment the service is running in. Use this to distinguish between production, development and testing/staging deployments. Suggested values are prod, test, dev, staging. However this is advised and not enforced.") + public String getEnvironment() { + return environment; + } + + public void setEnvironment(String environment) { + this.environment = environment; + } + + public Service version(String version) { + this.version = version; + return this; + } + + /** + * Version of the service being described. Semantic versioning is recommended, but other identifiers, such as dates or commit hashes, are also allowed. The version should be changed whenever the service is updated. + * @return version + **/ + @Schema(example = "1.0.0", required = true, description = "Version of the service being described. Semantic versioning is recommended, but other identifiers, such as dates or commit hashes, are also allowed. The version should be changed whenever the service is updated.") + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Service service = (Service) o; + return Objects.equals(this.id, service.id) && + Objects.equals(this.name, service.name) && + Objects.equals(this.type, service.type) && + Objects.equals(this.description, service.description) && + Objects.equals(this.organization, service.organization) && + Objects.equals(this.contactUrl, service.contactUrl) && + Objects.equals(this.documentationUrl, service.documentationUrl) && + Objects.equals(this.createdAt, service.createdAt) && + Objects.equals(this.updatedAt, service.updatedAt) && + Objects.equals(this.environment, service.environment) && + Objects.equals(this.version, service.version); + } + + @Override + public int hashCode() { + return Objects.hash(id, name, type, description, organization, contactUrl, documentationUrl, createdAt, updatedAt, environment, version); + } + + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class Service {\n"); + + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append(" type: ").append(toIndentedString(type)).append("\n"); + sb.append(" description: ").append(toIndentedString(description)).append("\n"); + sb.append(" organization: ").append(toIndentedString(organization)).append("\n"); + sb.append(" contactUrl: ").append(toIndentedString(contactUrl)).append("\n"); + sb.append(" documentationUrl: ").append(toIndentedString(documentationUrl)).append("\n"); + sb.append(" createdAt: ").append(toIndentedString(createdAt)).append("\n"); + sb.append(" updatedAt: ").append(toIndentedString(updatedAt)).append("\n"); + sb.append(" environment: ").append(toIndentedString(environment)).append("\n"); + sb.append(" version: ").append(toIndentedString(version)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} diff --git a/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/ServiceOrganization.java b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/ServiceOrganization.java new file mode 100644 index 0000000..5298078 --- /dev/null +++ b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/ServiceOrganization.java @@ -0,0 +1,130 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Task Execution Service + * + * OpenAPI spec version: 1.1.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +package nextflow.ga4gh.tes.client.model; + +import java.util.Objects; +import java.util.Arrays; +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import io.swagger.v3.oas.annotations.media.Schema; +import java.io.IOException; +/** + * Organization providing the service + */ +@Schema(description = "Organization providing the service") +@javax.annotation.Generated(value = "io.swagger.codegen.v3.generators.java.JavaClientCodegen", date = "2023-08-15T14:10:09.878Z[GMT]") + +public class ServiceOrganization { + @SerializedName("name") + private String name = null; + + @SerializedName("url") + private String url = null; + + public ServiceOrganization name(String name) { + this.name = name; + return this; + } + + /** + * Name of the organization responsible for the service + * @return name + **/ + @Schema(example = "My organization", required = true, description = "Name of the organization responsible for the service") + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public ServiceOrganization url(String url) { + this.url = url; + return this; + } + + /** + * URL of the website of the organization (RFC 3986 format) + * @return url + **/ + @Schema(example = "https://example.com", required = true, description = "URL of the website of the organization (RFC 3986 format)") + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ServiceOrganization serviceOrganization = (ServiceOrganization) o; + return Objects.equals(this.name, serviceOrganization.name) && + Objects.equals(this.url, serviceOrganization.url); + } + + @Override + public int hashCode() { + return Objects.hash(name, url); + } + + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class ServiceOrganization {\n"); + + sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append(" url: ").append(toIndentedString(url)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} diff --git a/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/ServiceType.java b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/ServiceType.java new file mode 100644 index 0000000..07062ce --- /dev/null +++ b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/ServiceType.java @@ -0,0 +1,153 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Task Execution Service + * + * OpenAPI spec version: 1.1.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +package nextflow.ga4gh.tes.client.model; + +import java.util.Objects; +import java.util.Arrays; +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import io.swagger.v3.oas.annotations.media.Schema; +import java.io.IOException; +/** + * Type of a GA4GH service + */ +@Schema(description = "Type of a GA4GH service") +@javax.annotation.Generated(value = "io.swagger.codegen.v3.generators.java.JavaClientCodegen", date = "2023-08-15T14:10:09.878Z[GMT]") + +public class ServiceType { + @SerializedName("group") + private String group = null; + + @SerializedName("artifact") + private String artifact = null; + + @SerializedName("version") + private String version = null; + + public ServiceType group(String group) { + this.group = group; + return this; + } + + /** + * Namespace in reverse domain name format. Use `org.ga4gh` for implementations compliant with official GA4GH specifications. For services with custom APIs not standardized by GA4GH, or implementations diverging from official GA4GH specifications, use a different namespace (e.g. your organization's reverse domain name). + * @return group + **/ + @Schema(example = "org.ga4gh", required = true, description = "Namespace in reverse domain name format. Use `org.ga4gh` for implementations compliant with official GA4GH specifications. For services with custom APIs not standardized by GA4GH, or implementations diverging from official GA4GH specifications, use a different namespace (e.g. your organization's reverse domain name).") + public String getGroup() { + return group; + } + + public void setGroup(String group) { + this.group = group; + } + + public ServiceType artifact(String artifact) { + this.artifact = artifact; + return this; + } + + /** + * Name of the API or GA4GH specification implemented. Official GA4GH types should be assigned as part of standards approval process. Custom artifacts are supported. + * @return artifact + **/ + @Schema(example = "beacon", required = true, description = "Name of the API or GA4GH specification implemented. Official GA4GH types should be assigned as part of standards approval process. Custom artifacts are supported.") + public String getArtifact() { + return artifact; + } + + public void setArtifact(String artifact) { + this.artifact = artifact; + } + + public ServiceType version(String version) { + this.version = version; + return this; + } + + /** + * Version of the API or specification. GA4GH specifications use semantic versioning. + * @return version + **/ + @Schema(example = "1.0.0", required = true, description = "Version of the API or specification. GA4GH specifications use semantic versioning.") + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ServiceType serviceType = (ServiceType) o; + return Objects.equals(this.group, serviceType.group) && + Objects.equals(this.artifact, serviceType.artifact) && + Objects.equals(this.version, serviceType.version); + } + + @Override + public int hashCode() { + return Objects.hash(group, artifact, version); + } + + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class ServiceType {\n"); + + sb.append(" group: ").append(toIndentedString(group)).append("\n"); + sb.append(" artifact: ").append(toIndentedString(artifact)).append("\n"); + sb.append(" version: ").append(toIndentedString(version)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} diff --git a/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesCancelTaskResponse.java b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesCancelTaskResponse.java new file mode 100644 index 0000000..68ba7cb --- /dev/null +++ b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesCancelTaskResponse.java @@ -0,0 +1,78 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Task Execution Service + * + * OpenAPI spec version: 1.1.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +package nextflow.ga4gh.tes.client.model; + +import java.util.Objects; +import java.util.Arrays; +import io.swagger.v3.oas.annotations.media.Schema; +/** + * CancelTaskResponse describes a response from the CancelTask endpoint. + */ +@Schema(description = "CancelTaskResponse describes a response from the CancelTask endpoint.") +@javax.annotation.Generated(value = "io.swagger.codegen.v3.generators.java.JavaClientCodegen", date = "2023-08-15T14:10:09.878Z[GMT]") + +public class TesCancelTaskResponse { + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + return true; + } + + @Override + public int hashCode() { + return Objects.hash(); + } + + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class TesCancelTaskResponse {\n"); + + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} diff --git a/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesCreateTaskResponse.java b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesCreateTaskResponse.java new file mode 100644 index 0000000..b27260b --- /dev/null +++ b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesCreateTaskResponse.java @@ -0,0 +1,107 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Task Execution Service + * + * OpenAPI spec version: 1.1.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +package nextflow.ga4gh.tes.client.model; + +import java.util.Objects; +import java.util.Arrays; +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import io.swagger.v3.oas.annotations.media.Schema; +import java.io.IOException; +/** + * CreateTaskResponse describes a response from the CreateTask endpoint. It will include the task ID that can be used to look up the status of the job. + */ +@Schema(description = "CreateTaskResponse describes a response from the CreateTask endpoint. It will include the task ID that can be used to look up the status of the job.") +@javax.annotation.Generated(value = "io.swagger.codegen.v3.generators.java.JavaClientCodegen", date = "2023-08-15T14:10:09.878Z[GMT]") + +public class TesCreateTaskResponse { + @SerializedName("id") + private String id = null; + + public TesCreateTaskResponse id(String id) { + this.id = id; + return this; + } + + /** + * Task identifier assigned by the server. + * @return id + **/ + @Schema(required = true, description = "Task identifier assigned by the server.") + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TesCreateTaskResponse tesCreateTaskResponse = (TesCreateTaskResponse) o; + return Objects.equals(this.id, tesCreateTaskResponse.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } + + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class TesCreateTaskResponse {\n"); + + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} diff --git a/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesExecutor.java b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesExecutor.java new file mode 100644 index 0000000..5d8a84f --- /dev/null +++ b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesExecutor.java @@ -0,0 +1,285 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Task Execution Service + * + * OpenAPI spec version: 1.1.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +package nextflow.ga4gh.tes.client.model; + +import java.util.Objects; +import java.util.Arrays; +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import io.swagger.v3.oas.annotations.media.Schema; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +/** + * Executor describes a command to be executed, and its environment. + */ +@Schema(description = "Executor describes a command to be executed, and its environment.") +@javax.annotation.Generated(value = "io.swagger.codegen.v3.generators.java.JavaClientCodegen", date = "2023-08-15T14:10:09.878Z[GMT]") + +public class TesExecutor { + @SerializedName("image") + private String image = null; + + @SerializedName("command") + private List command = new ArrayList(); + + @SerializedName("workdir") + private String workdir = null; + + @SerializedName("stdin") + private String stdin = null; + + @SerializedName("stdout") + private String stdout = null; + + @SerializedName("stderr") + private String stderr = null; + + @SerializedName("env") + private Map env = null; + + @SerializedName("ignore_error") + private Boolean ignoreError = null; + + public TesExecutor image(String image) { + this.image = image; + return this; + } + + /** + * Name of the container image. The string will be passed as the image argument to the containerization run command. Examples: - `ubuntu` - `quay.io/aptible/ubuntu` - `gcr.io/my-org/my-image` - `myregistryhost:5000/fedora/httpd:version1.0` + * @return image + **/ + @Schema(example = "ubuntu:20.04", required = true, description = "Name of the container image. The string will be passed as the image argument to the containerization run command. Examples: - `ubuntu` - `quay.io/aptible/ubuntu` - `gcr.io/my-org/my-image` - `myregistryhost:5000/fedora/httpd:version1.0`") + public String getImage() { + return image; + } + + public void setImage(String image) { + this.image = image; + } + + public TesExecutor command(List command) { + this.command = command; + return this; + } + + public TesExecutor addCommandItem(String commandItem) { + this.command.add(commandItem); + return this; + } + + /** + * A sequence of program arguments to execute, where the first argument is the program to execute (i.e. argv). Example: ``` { \"command\" : [\"/bin/md5\", \"/data/file1\"] } ``` + * @return command + **/ + @Schema(example = "[\"/bin/md5\",\"/data/file1\"]", required = true, description = "A sequence of program arguments to execute, where the first argument is the program to execute (i.e. argv). Example: ``` { \"command\" : [\"/bin/md5\", \"/data/file1\"] } ```") + public List getCommand() { + return command; + } + + public void setCommand(List command) { + this.command = command; + } + + public TesExecutor workdir(String workdir) { + this.workdir = workdir; + return this; + } + + /** + * The working directory that the command will be executed in. If not defined, the system will default to the directory set by the container image. + * @return workdir + **/ + @Schema(example = "/data/", description = "The working directory that the command will be executed in. If not defined, the system will default to the directory set by the container image.") + public String getWorkdir() { + return workdir; + } + + public void setWorkdir(String workdir) { + this.workdir = workdir; + } + + public TesExecutor stdin(String stdin) { + this.stdin = stdin; + return this; + } + + /** + * Path inside the container to a file which will be piped to the executor's stdin. This must be an absolute path. This mechanism could be used in conjunction with the input declaration to process a data file using a tool that expects STDIN. For example, to get the MD5 sum of a file by reading it into the STDIN ``` { \"command\" : [\"/bin/md5\"], \"stdin\" : \"/data/file1\" } ``` + * @return stdin + **/ + @Schema(example = "/data/file1", description = "Path inside the container to a file which will be piped to the executor's stdin. This must be an absolute path. This mechanism could be used in conjunction with the input declaration to process a data file using a tool that expects STDIN. For example, to get the MD5 sum of a file by reading it into the STDIN ``` { \"command\" : [\"/bin/md5\"], \"stdin\" : \"/data/file1\" } ```") + public String getStdin() { + return stdin; + } + + public void setStdin(String stdin) { + this.stdin = stdin; + } + + public TesExecutor stdout(String stdout) { + this.stdout = stdout; + return this; + } + + /** + * Path inside the container to a file where the executor's stdout will be written to. Must be an absolute path. Example: ``` { \"stdout\" : \"/tmp/stdout.log\" } ``` + * @return stdout + **/ + @Schema(example = "/tmp/stdout.log", description = "Path inside the container to a file where the executor's stdout will be written to. Must be an absolute path. Example: ``` { \"stdout\" : \"/tmp/stdout.log\" } ```") + public String getStdout() { + return stdout; + } + + public void setStdout(String stdout) { + this.stdout = stdout; + } + + public TesExecutor stderr(String stderr) { + this.stderr = stderr; + return this; + } + + /** + * Path inside the container to a file where the executor's stderr will be written to. Must be an absolute path. Example: ``` { \"stderr\" : \"/tmp/stderr.log\" } ``` + * @return stderr + **/ + @Schema(example = "/tmp/stderr.log", description = "Path inside the container to a file where the executor's stderr will be written to. Must be an absolute path. Example: ``` { \"stderr\" : \"/tmp/stderr.log\" } ```") + public String getStderr() { + return stderr; + } + + public void setStderr(String stderr) { + this.stderr = stderr; + } + + public TesExecutor env(Map env) { + this.env = env; + return this; + } + + public TesExecutor putEnvItem(String key, String envItem) { + if (this.env == null) { + this.env = new HashMap(); + } + this.env.put(key, envItem); + return this; + } + + /** + * Environmental variables to set within the container. Example: ``` { \"env\" : { \"ENV_CONFIG_PATH\" : \"/data/config.file\", \"BLASTDB\" : \"/data/GRC38\", \"HMMERDB\" : \"/data/hmmer\" } } ``` + * @return env + **/ + @Schema(example = "{\"BLASTDB\":\"/data/GRC38\",\"HMMERDB\":\"/data/hmmer\"}", description = "Environmental variables to set within the container. Example: ``` { \"env\" : { \"ENV_CONFIG_PATH\" : \"/data/config.file\", \"BLASTDB\" : \"/data/GRC38\", \"HMMERDB\" : \"/data/hmmer\" } } ```") + public Map getEnv() { + return env; + } + + public void setEnv(Map env) { + this.env = env; + } + + public TesExecutor ignoreError(Boolean ignoreError) { + this.ignoreError = ignoreError; + return this; + } + + /** + * Default behavior of running an array of executors is that execution stops on the first error. If `ignore_error` is `True`, then the runner will record error exit codes, but will continue on to the next tesExecutor. + * @return ignoreError + **/ + @Schema(description = "Default behavior of running an array of executors is that execution stops on the first error. If `ignore_error` is `True`, then the runner will record error exit codes, but will continue on to the next tesExecutor.") + public Boolean isIgnoreError() { + return ignoreError; + } + + public void setIgnoreError(Boolean ignoreError) { + this.ignoreError = ignoreError; + } + + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TesExecutor tesExecutor = (TesExecutor) o; + return Objects.equals(this.image, tesExecutor.image) && + Objects.equals(this.command, tesExecutor.command) && + Objects.equals(this.workdir, tesExecutor.workdir) && + Objects.equals(this.stdin, tesExecutor.stdin) && + Objects.equals(this.stdout, tesExecutor.stdout) && + Objects.equals(this.stderr, tesExecutor.stderr) && + Objects.equals(this.env, tesExecutor.env) && + Objects.equals(this.ignoreError, tesExecutor.ignoreError); + } + + @Override + public int hashCode() { + return Objects.hash(image, command, workdir, stdin, stdout, stderr, env, ignoreError); + } + + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class TesExecutor {\n"); + + sb.append(" image: ").append(toIndentedString(image)).append("\n"); + sb.append(" command: ").append(toIndentedString(command)).append("\n"); + sb.append(" workdir: ").append(toIndentedString(workdir)).append("\n"); + sb.append(" stdin: ").append(toIndentedString(stdin)).append("\n"); + sb.append(" stdout: ").append(toIndentedString(stdout)).append("\n"); + sb.append(" stderr: ").append(toIndentedString(stderr)).append("\n"); + sb.append(" env: ").append(toIndentedString(env)).append("\n"); + sb.append(" ignoreError: ").append(toIndentedString(ignoreError)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} diff --git a/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesExecutorLog.java b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesExecutorLog.java new file mode 100644 index 0000000..a6e7bc5 --- /dev/null +++ b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesExecutorLog.java @@ -0,0 +1,199 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Task Execution Service + * + * OpenAPI spec version: 1.1.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +package nextflow.ga4gh.tes.client.model; + +import java.util.Objects; +import java.util.Arrays; +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import io.swagger.v3.oas.annotations.media.Schema; +import java.io.IOException; +/** + * ExecutorLog describes logging information related to an Executor. + */ +@Schema(description = "ExecutorLog describes logging information related to an Executor.") +@javax.annotation.Generated(value = "io.swagger.codegen.v3.generators.java.JavaClientCodegen", date = "2023-08-15T14:10:09.878Z[GMT]") + +public class TesExecutorLog { + @SerializedName("start_time") + private String startTime = null; + + @SerializedName("end_time") + private String endTime = null; + + @SerializedName("stdout") + private String stdout = null; + + @SerializedName("stderr") + private String stderr = null; + + @SerializedName("exit_code") + private Integer exitCode = null; + + public TesExecutorLog startTime(String startTime) { + this.startTime = startTime; + return this; + } + + /** + * Time the executor started, in RFC 3339 format. + * @return startTime + **/ + @Schema(example = "2020-10-02 10:00:00-05:00", description = "Time the executor started, in RFC 3339 format.") + public String getStartTime() { + return startTime; + } + + public void setStartTime(String startTime) { + this.startTime = startTime; + } + + public TesExecutorLog endTime(String endTime) { + this.endTime = endTime; + return this; + } + + /** + * Time the executor ended, in RFC 3339 format. + * @return endTime + **/ + @Schema(example = "2020-10-02 11:00:00-05:00", description = "Time the executor ended, in RFC 3339 format.") + public String getEndTime() { + return endTime; + } + + public void setEndTime(String endTime) { + this.endTime = endTime; + } + + public TesExecutorLog stdout(String stdout) { + this.stdout = stdout; + return this; + } + + /** + * Stdout content. This is meant for convenience. No guarantees are made about the content. Implementations may chose different approaches: only the head, only the tail, a URL reference only, etc. In order to capture the full stdout client should set Executor.stdout to a container file path, and use Task.outputs to upload that file to permanent storage. + * @return stdout + **/ + @Schema(description = "Stdout content. This is meant for convenience. No guarantees are made about the content. Implementations may chose different approaches: only the head, only the tail, a URL reference only, etc. In order to capture the full stdout client should set Executor.stdout to a container file path, and use Task.outputs to upload that file to permanent storage.") + public String getStdout() { + return stdout; + } + + public void setStdout(String stdout) { + this.stdout = stdout; + } + + public TesExecutorLog stderr(String stderr) { + this.stderr = stderr; + return this; + } + + /** + * Stderr content. This is meant for convenience. No guarantees are made about the content. Implementations may chose different approaches: only the head, only the tail, a URL reference only, etc. In order to capture the full stderr client should set Executor.stderr to a container file path, and use Task.outputs to upload that file to permanent storage. + * @return stderr + **/ + @Schema(description = "Stderr content. This is meant for convenience. No guarantees are made about the content. Implementations may chose different approaches: only the head, only the tail, a URL reference only, etc. In order to capture the full stderr client should set Executor.stderr to a container file path, and use Task.outputs to upload that file to permanent storage.") + public String getStderr() { + return stderr; + } + + public void setStderr(String stderr) { + this.stderr = stderr; + } + + public TesExecutorLog exitCode(Integer exitCode) { + this.exitCode = exitCode; + return this; + } + + /** + * Exit code. + * @return exitCode + **/ + @Schema(required = true, description = "Exit code.") + public Integer getExitCode() { + return exitCode; + } + + public void setExitCode(Integer exitCode) { + this.exitCode = exitCode; + } + + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TesExecutorLog tesExecutorLog = (TesExecutorLog) o; + return Objects.equals(this.startTime, tesExecutorLog.startTime) && + Objects.equals(this.endTime, tesExecutorLog.endTime) && + Objects.equals(this.stdout, tesExecutorLog.stdout) && + Objects.equals(this.stderr, tesExecutorLog.stderr) && + Objects.equals(this.exitCode, tesExecutorLog.exitCode); + } + + @Override + public int hashCode() { + return Objects.hash(startTime, endTime, stdout, stderr, exitCode); + } + + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class TesExecutorLog {\n"); + + sb.append(" startTime: ").append(toIndentedString(startTime)).append("\n"); + sb.append(" endTime: ").append(toIndentedString(endTime)).append("\n"); + sb.append(" stdout: ").append(toIndentedString(stdout)).append("\n"); + sb.append(" stderr: ").append(toIndentedString(stderr)).append("\n"); + sb.append(" exitCode: ").append(toIndentedString(exitCode)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} diff --git a/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesFileType.java b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesFileType.java new file mode 100644 index 0000000..7831e64 --- /dev/null +++ b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesFileType.java @@ -0,0 +1,86 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Task Execution Service + * + * OpenAPI spec version: 1.1.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +package nextflow.ga4gh.tes.client.model; + +import java.util.Objects; +import java.util.Arrays; +import io.swagger.v3.oas.annotations.media.Schema; +import com.google.gson.annotations.SerializedName; +import java.io.IOException; +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; + +/** + * Define if input/output element is a file or a directory. It is not required that the user provide this value, but it is required that the server fill in the value once the information is available at run time. + */ +@JsonAdapter(TesFileType.Adapter.class) +public enum TesFileType { + @SerializedName("FILE") + FILE("FILE"), + @SerializedName("DIRECTORY") + DIRECTORY("DIRECTORY"); + + private String value; + + TesFileType(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + public static TesFileType fromValue(String input) { + for (TesFileType b : TesFileType.values()) { + if (b.value.equals(input)) { + return b; + } + } + return null; + } + + public static class Adapter extends TypeAdapter { + @Override + public void write(final JsonWriter jsonWriter, final TesFileType enumeration) throws IOException { + jsonWriter.value(String.valueOf(enumeration.getValue())); + } + + @Override + public TesFileType read(final JsonReader jsonReader) throws IOException { + Object value = jsonReader.nextString(); + return TesFileType.fromValue((String)(value)); + } + } +} diff --git a/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesInput.java b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesInput.java new file mode 100644 index 0000000..8d4331c --- /dev/null +++ b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesInput.java @@ -0,0 +1,246 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Task Execution Service + * + * OpenAPI spec version: 1.1.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +package nextflow.ga4gh.tes.client.model; + +import java.util.Objects; +import java.util.Arrays; +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import nextflow.ga4gh.tes.client.model.TesFileType; +import io.swagger.v3.oas.annotations.media.Schema; +import java.io.IOException; +/** + * Input describes Task input files. + */ +@Schema(description = "Input describes Task input files.") +@javax.annotation.Generated(value = "io.swagger.codegen.v3.generators.java.JavaClientCodegen", date = "2023-08-15T14:10:09.878Z[GMT]") + +public class TesInput { + @SerializedName("name") + private String name = null; + + @SerializedName("description") + private String description = null; + + @SerializedName("url") + private String url = null; + + @SerializedName("path") + private String path = null; + + @SerializedName("type") + private TesFileType type = null; + + @SerializedName("content") + private String content = null; + + @SerializedName("streamable") + private Boolean streamable = null; + + public TesInput name(String name) { + this.name = name; + return this; + } + + /** + * Get name + * @return name + **/ + @Schema(description = "") + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public TesInput description(String description) { + this.description = description; + return this; + } + + /** + * Get description + * @return description + **/ + @Schema(description = "") + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public TesInput url(String url) { + this.url = url; + return this; + } + + /** + * REQUIRED, unless \"content\" is set. URL in long term storage, for example: - s3://my-object-store/file1 - gs://my-bucket/file2 - file:///path/to/my/file - /path/to/my/file + * @return url + **/ + @Schema(example = "s3://my-object-store/file1", description = "REQUIRED, unless \"content\" is set. URL in long term storage, for example: - s3://my-object-store/file1 - gs://my-bucket/file2 - file:///path/to/my/file - /path/to/my/file") + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public TesInput path(String path) { + this.path = path; + return this; + } + + /** + * Path of the file inside the container. Must be an absolute path. + * @return path + **/ + @Schema(example = "/data/file1", required = true, description = "Path of the file inside the container. Must be an absolute path.") + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public TesInput type(TesFileType type) { + this.type = type; + return this; + } + + /** + * Get type + * @return type + **/ + @Schema(description = "") + public TesFileType getType() { + return type; + } + + public void setType(TesFileType type) { + this.type = type; + } + + public TesInput content(String content) { + this.content = content; + return this; + } + + /** + * File content literal. Implementations should support a minimum of 128 KiB in this field and may define their own maximum. UTF-8 encoded If content is not empty, \"url\" must be ignored. + * @return content + **/ + @Schema(description = "File content literal. Implementations should support a minimum of 128 KiB in this field and may define their own maximum. UTF-8 encoded If content is not empty, \"url\" must be ignored.") + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public TesInput streamable(Boolean streamable) { + this.streamable = streamable; + return this; + } + + /** + * Indicate that a file resource could be accessed using a streaming interface, ie a FUSE mounted s3 object. This flag indicates that using a streaming mount, as opposed to downloading the whole file to the local scratch space, may be faster despite the latency and overhead. This does not mean that the backend will use a streaming interface, as it may not be provided by the vendor, but if the capacity is available it can be used without degrading the performance of the underlying program. + * @return streamable + **/ + @Schema(description = "Indicate that a file resource could be accessed using a streaming interface, ie a FUSE mounted s3 object. This flag indicates that using a streaming mount, as opposed to downloading the whole file to the local scratch space, may be faster despite the latency and overhead. This does not mean that the backend will use a streaming interface, as it may not be provided by the vendor, but if the capacity is available it can be used without degrading the performance of the underlying program.") + public Boolean isStreamable() { + return streamable; + } + + public void setStreamable(Boolean streamable) { + this.streamable = streamable; + } + + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TesInput tesInput = (TesInput) o; + return Objects.equals(this.name, tesInput.name) && + Objects.equals(this.description, tesInput.description) && + Objects.equals(this.url, tesInput.url) && + Objects.equals(this.path, tesInput.path) && + Objects.equals(this.type, tesInput.type) && + Objects.equals(this.content, tesInput.content) && + Objects.equals(this.streamable, tesInput.streamable); + } + + @Override + public int hashCode() { + return Objects.hash(name, description, url, path, type, content, streamable); + } + + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class TesInput {\n"); + + sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append(" description: ").append(toIndentedString(description)).append("\n"); + sb.append(" url: ").append(toIndentedString(url)).append("\n"); + sb.append(" path: ").append(toIndentedString(path)).append("\n"); + sb.append(" type: ").append(toIndentedString(type)).append("\n"); + sb.append(" content: ").append(toIndentedString(content)).append("\n"); + sb.append(" streamable: ").append(toIndentedString(streamable)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} diff --git a/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesListTasksResponse.java b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesListTasksResponse.java new file mode 100644 index 0000000..454737f --- /dev/null +++ b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesListTasksResponse.java @@ -0,0 +1,138 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Task Execution Service + * + * OpenAPI spec version: 1.1.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +package nextflow.ga4gh.tes.client.model; + +import java.util.Objects; +import java.util.Arrays; +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import nextflow.ga4gh.tes.client.model.TesTask; +import io.swagger.v3.oas.annotations.media.Schema; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +/** + * ListTasksResponse describes a response from the ListTasks endpoint. + */ +@Schema(description = "ListTasksResponse describes a response from the ListTasks endpoint.") +@javax.annotation.Generated(value = "io.swagger.codegen.v3.generators.java.JavaClientCodegen", date = "2023-08-15T14:10:09.878Z[GMT]") + +public class TesListTasksResponse { + @SerializedName("tasks") + private List tasks = new ArrayList(); + + @SerializedName("next_page_token") + private String nextPageToken = null; + + public TesListTasksResponse tasks(List tasks) { + this.tasks = tasks; + return this; + } + + public TesListTasksResponse addTasksItem(TesTask tasksItem) { + this.tasks.add(tasksItem); + return this; + } + + /** + * List of tasks. These tasks will be based on the original submitted task document, but with other fields, such as the job state and logging info, added/changed as the job progresses. + * @return tasks + **/ + @Schema(required = true, description = "List of tasks. These tasks will be based on the original submitted task document, but with other fields, such as the job state and logging info, added/changed as the job progresses.") + public List getTasks() { + return tasks; + } + + public void setTasks(List tasks) { + this.tasks = tasks; + } + + public TesListTasksResponse nextPageToken(String nextPageToken) { + this.nextPageToken = nextPageToken; + return this; + } + + /** + * Token used to return the next page of results. This value can be used in the `page_token` field of the next ListTasks request. + * @return nextPageToken + **/ + @Schema(description = "Token used to return the next page of results. This value can be used in the `page_token` field of the next ListTasks request.") + public String getNextPageToken() { + return nextPageToken; + } + + public void setNextPageToken(String nextPageToken) { + this.nextPageToken = nextPageToken; + } + + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TesListTasksResponse tesListTasksResponse = (TesListTasksResponse) o; + return Objects.equals(this.tasks, tesListTasksResponse.tasks) && + Objects.equals(this.nextPageToken, tesListTasksResponse.nextPageToken); + } + + @Override + public int hashCode() { + return Objects.hash(tasks, nextPageToken); + } + + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class TesListTasksResponse {\n"); + + sb.append(" tasks: ").append(toIndentedString(tasks)).append("\n"); + sb.append(" nextPageToken: ").append(toIndentedString(nextPageToken)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} diff --git a/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesOutput.java b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesOutput.java new file mode 100644 index 0000000..d415b5a --- /dev/null +++ b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesOutput.java @@ -0,0 +1,223 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Task Execution Service + * + * OpenAPI spec version: 1.1.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +package nextflow.ga4gh.tes.client.model; + +import java.util.Objects; +import java.util.Arrays; +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import nextflow.ga4gh.tes.client.model.TesFileType; +import io.swagger.v3.oas.annotations.media.Schema; +import java.io.IOException; +/** + * Output describes Task output files. + */ +@Schema(description = "Output describes Task output files.") +@javax.annotation.Generated(value = "io.swagger.codegen.v3.generators.java.JavaClientCodegen", date = "2023-08-15T14:10:09.878Z[GMT]") + +public class TesOutput { + @SerializedName("name") + private String name = null; + + @SerializedName("description") + private String description = null; + + @SerializedName("url") + private String url = null; + + @SerializedName("path") + private String path = null; + + @SerializedName("path_prefix") + private String pathPrefix = null; + + @SerializedName("type") + private TesFileType type = null; + + public TesOutput name(String name) { + this.name = name; + return this; + } + + /** + * User-provided name of output file + * @return name + **/ + @Schema(description = "User-provided name of output file") + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public TesOutput description(String description) { + this.description = description; + return this; + } + + /** + * Optional users provided description field, can be used for documentation. + * @return description + **/ + @Schema(description = "Optional users provided description field, can be used for documentation.") + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public TesOutput url(String url) { + this.url = url; + return this; + } + + /** + * URL at which the TES server makes the output accessible after the task is complete. When tesOutput.path contains wildcards, it must be a directory; see `tesOutput.path_prefix` for details on how output URLs are constructed in this case. For Example: - `s3://my-object-store/file1` - `gs://my-bucket/file2` - `file:///path/to/my/file` + * @return url + **/ + @Schema(required = true, description = "URL at which the TES server makes the output accessible after the task is complete. When tesOutput.path contains wildcards, it must be a directory; see `tesOutput.path_prefix` for details on how output URLs are constructed in this case. For Example: - `s3://my-object-store/file1` - `gs://my-bucket/file2` - `file:///path/to/my/file`") + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public TesOutput path(String path) { + this.path = path; + return this; + } + + /** + * Absolute path of the file inside the container. May contain pattern matching wildcards to select multiple outputs at once, but mind implications for `tesOutput.url` and `tesOutput.path_prefix`. Only wildcards defined in IEEE Std 1003.1-2017 (POSIX), 12.3 are supported; see https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_13 + * @return path + **/ + @Schema(required = true, description = "Absolute path of the file inside the container. May contain pattern matching wildcards to select multiple outputs at once, but mind implications for `tesOutput.url` and `tesOutput.path_prefix`. Only wildcards defined in IEEE Std 1003.1-2017 (POSIX), 12.3 are supported; see https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_13") + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public TesOutput pathPrefix(String pathPrefix) { + this.pathPrefix = pathPrefix; + return this; + } + + /** + * Prefix to be removed from matching outputs if `tesOutput.path` contains wildcards; output URLs are constructed by appending pruned paths to the directory specified in `tesOutput.url`. Required if `tesOutput.path` contains wildcards, ignored otherwise. + * @return pathPrefix + **/ + @Schema(description = "Prefix to be removed from matching outputs if `tesOutput.path` contains wildcards; output URLs are constructed by appending pruned paths to the directory specified in `tesOutput.url`. Required if `tesOutput.path` contains wildcards, ignored otherwise.") + public String getPathPrefix() { + return pathPrefix; + } + + public void setPathPrefix(String pathPrefix) { + this.pathPrefix = pathPrefix; + } + + public TesOutput type(TesFileType type) { + this.type = type; + return this; + } + + /** + * Get type + * @return type + **/ + @Schema(description = "") + public TesFileType getType() { + return type; + } + + public void setType(TesFileType type) { + this.type = type; + } + + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TesOutput tesOutput = (TesOutput) o; + return Objects.equals(this.name, tesOutput.name) && + Objects.equals(this.description, tesOutput.description) && + Objects.equals(this.url, tesOutput.url) && + Objects.equals(this.path, tesOutput.path) && + Objects.equals(this.pathPrefix, tesOutput.pathPrefix) && + Objects.equals(this.type, tesOutput.type); + } + + @Override + public int hashCode() { + return Objects.hash(name, description, url, path, pathPrefix, type); + } + + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class TesOutput {\n"); + + sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append(" description: ").append(toIndentedString(description)).append("\n"); + sb.append(" url: ").append(toIndentedString(url)).append("\n"); + sb.append(" path: ").append(toIndentedString(path)).append("\n"); + sb.append(" pathPrefix: ").append(toIndentedString(pathPrefix)).append("\n"); + sb.append(" type: ").append(toIndentedString(type)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} diff --git a/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesOutputFileLog.java b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesOutputFileLog.java new file mode 100644 index 0000000..da2d64c --- /dev/null +++ b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesOutputFileLog.java @@ -0,0 +1,153 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Task Execution Service + * + * OpenAPI spec version: 1.1.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +package nextflow.ga4gh.tes.client.model; + +import java.util.Objects; +import java.util.Arrays; +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import io.swagger.v3.oas.annotations.media.Schema; +import java.io.IOException; +/** + * OutputFileLog describes a single output file. This describes file details after the task has completed successfully, for logging purposes. + */ +@Schema(description = "OutputFileLog describes a single output file. This describes file details after the task has completed successfully, for logging purposes.") +@javax.annotation.Generated(value = "io.swagger.codegen.v3.generators.java.JavaClientCodegen", date = "2023-08-15T14:10:09.878Z[GMT]") + +public class TesOutputFileLog { + @SerializedName("url") + private String url = null; + + @SerializedName("path") + private String path = null; + + @SerializedName("size_bytes") + private String sizeBytes = null; + + public TesOutputFileLog url(String url) { + this.url = url; + return this; + } + + /** + * URL of the file in storage, e.g. s3://bucket/file.txt + * @return url + **/ + @Schema(required = true, description = "URL of the file in storage, e.g. s3://bucket/file.txt") + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public TesOutputFileLog path(String path) { + this.path = path; + return this; + } + + /** + * Path of the file inside the container. Must be an absolute path. + * @return path + **/ + @Schema(required = true, description = "Path of the file inside the container. Must be an absolute path.") + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public TesOutputFileLog sizeBytes(String sizeBytes) { + this.sizeBytes = sizeBytes; + return this; + } + + /** + * Size of the file in bytes. Note, this is currently coded as a string because official JSON doesn't support int64 numbers. + * @return sizeBytes + **/ + @Schema(example = "[\"1024\"]", required = true, description = "Size of the file in bytes. Note, this is currently coded as a string because official JSON doesn't support int64 numbers.") + public String getSizeBytes() { + return sizeBytes; + } + + public void setSizeBytes(String sizeBytes) { + this.sizeBytes = sizeBytes; + } + + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TesOutputFileLog tesOutputFileLog = (TesOutputFileLog) o; + return Objects.equals(this.url, tesOutputFileLog.url) && + Objects.equals(this.path, tesOutputFileLog.path) && + Objects.equals(this.sizeBytes, tesOutputFileLog.sizeBytes); + } + + @Override + public int hashCode() { + return Objects.hash(url, path, sizeBytes); + } + + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class TesOutputFileLog {\n"); + + sb.append(" url: ").append(toIndentedString(url)).append("\n"); + sb.append(" path: ").append(toIndentedString(path)).append("\n"); + sb.append(" sizeBytes: ").append(toIndentedString(sizeBytes)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} diff --git a/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesResources.java b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesResources.java new file mode 100644 index 0000000..551581e --- /dev/null +++ b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesResources.java @@ -0,0 +1,265 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Task Execution Service + * + * OpenAPI spec version: 1.1.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +package nextflow.ga4gh.tes.client.model; + +import java.util.Objects; +import java.util.Arrays; +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import io.swagger.v3.oas.annotations.media.Schema; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +/** + * Resources describes the resources requested by a task. + */ +@Schema(description = "Resources describes the resources requested by a task.") +@javax.annotation.Generated(value = "io.swagger.codegen.v3.generators.java.JavaClientCodegen", date = "2023-08-15T14:10:09.878Z[GMT]") + +public class TesResources { + @SerializedName("cpu_cores") + private Integer cpuCores = null; + + @SerializedName("preemptible") + private Boolean preemptible = null; + + @SerializedName("ram_gb") + private Double ramGb = null; + + @SerializedName("disk_gb") + private Double diskGb = null; + + @SerializedName("zones") + private List zones = null; + + @SerializedName("backend_parameters") + private Map backendParameters = null; + + @SerializedName("backend_parameters_strict") + private Boolean backendParametersStrict = false; + + public TesResources cpuCores(Integer cpuCores) { + this.cpuCores = cpuCores; + return this; + } + + /** + * Requested number of CPUs + * @return cpuCores + **/ + @Schema(example = "4", description = "Requested number of CPUs") + public Integer getCpuCores() { + return cpuCores; + } + + public void setCpuCores(Integer cpuCores) { + this.cpuCores = cpuCores; + } + + public TesResources preemptible(Boolean preemptible) { + this.preemptible = preemptible; + return this; + } + + /** + * Define if the task is allowed to run on preemptible compute instances, for example, AWS Spot. This option may have no effect when utilized on some backends that don't have the concept of preemptible jobs. + * @return preemptible + **/ + @Schema(example = "false", description = "Define if the task is allowed to run on preemptible compute instances, for example, AWS Spot. This option may have no effect when utilized on some backends that don't have the concept of preemptible jobs.") + public Boolean isPreemptible() { + return preemptible; + } + + public void setPreemptible(Boolean preemptible) { + this.preemptible = preemptible; + } + + public TesResources ramGb(Double ramGb) { + this.ramGb = ramGb; + return this; + } + + /** + * Requested RAM required in gigabytes (GB) + * @return ramGb + **/ + @Schema(example = "8", description = "Requested RAM required in gigabytes (GB)") + public Double getRamGb() { + return ramGb; + } + + public void setRamGb(Double ramGb) { + this.ramGb = ramGb; + } + + public TesResources diskGb(Double diskGb) { + this.diskGb = diskGb; + return this; + } + + /** + * Requested disk size in gigabytes (GB) + * @return diskGb + **/ + @Schema(example = "40", description = "Requested disk size in gigabytes (GB)") + public Double getDiskGb() { + return diskGb; + } + + public void setDiskGb(Double diskGb) { + this.diskGb = diskGb; + } + + public TesResources zones(List zones) { + this.zones = zones; + return this; + } + + public TesResources addZonesItem(String zonesItem) { + if (this.zones == null) { + this.zones = new ArrayList(); + } + this.zones.add(zonesItem); + return this; + } + + /** + * Request that the task be run in these compute zones. How this string is utilized will be dependent on the backend system. For example, a system based on a cluster queueing system may use this string to define priority queue to which the job is assigned. + * @return zones + **/ + @Schema(example = "us-west-1", description = "Request that the task be run in these compute zones. How this string is utilized will be dependent on the backend system. For example, a system based on a cluster queueing system may use this string to define priority queue to which the job is assigned.") + public List getZones() { + return zones; + } + + public void setZones(List zones) { + this.zones = zones; + } + + public TesResources backendParameters(Map backendParameters) { + this.backendParameters = backendParameters; + return this; + } + + public TesResources putBackendParametersItem(String key, String backendParametersItem) { + if (this.backendParameters == null) { + this.backendParameters = new HashMap(); + } + this.backendParameters.put(key, backendParametersItem); + return this; + } + + /** + * Key/value pairs for backend configuration. ServiceInfo shall return a list of keys that a backend supports. Keys are case insensitive. It is expected that clients pass all runtime or hardware requirement key/values that are not mapped to existing tesResources properties to backend_parameters. Backends shall log system warnings if a key is passed that is unsupported. Backends shall not store or return unsupported keys if included in a task. If backend_parameters_strict equals true, backends should fail the task if any key/values are unsupported, otherwise, backends should attempt to run the task Intended uses include VM size selection, coprocessor configuration, etc. Example: ``` { \"backend_parameters\" : { \"VmSize\" : \"Standard_D64_v3\" } } ``` + * @return backendParameters + **/ + @Schema(example = "{\"VmSize\":\"Standard_D64_v3\"}", description = "Key/value pairs for backend configuration. ServiceInfo shall return a list of keys that a backend supports. Keys are case insensitive. It is expected that clients pass all runtime or hardware requirement key/values that are not mapped to existing tesResources properties to backend_parameters. Backends shall log system warnings if a key is passed that is unsupported. Backends shall not store or return unsupported keys if included in a task. If backend_parameters_strict equals true, backends should fail the task if any key/values are unsupported, otherwise, backends should attempt to run the task Intended uses include VM size selection, coprocessor configuration, etc. Example: ``` { \"backend_parameters\" : { \"VmSize\" : \"Standard_D64_v3\" } } ```") + public Map getBackendParameters() { + return backendParameters; + } + + public void setBackendParameters(Map backendParameters) { + this.backendParameters = backendParameters; + } + + public TesResources backendParametersStrict(Boolean backendParametersStrict) { + this.backendParametersStrict = backendParametersStrict; + return this; + } + + /** + * If set to true, backends should fail the task if any backend_parameters key/values are unsupported, otherwise, backends should attempt to run the task + * @return backendParametersStrict + **/ + @Schema(example = "false", description = "If set to true, backends should fail the task if any backend_parameters key/values are unsupported, otherwise, backends should attempt to run the task") + public Boolean isBackendParametersStrict() { + return backendParametersStrict; + } + + public void setBackendParametersStrict(Boolean backendParametersStrict) { + this.backendParametersStrict = backendParametersStrict; + } + + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TesResources tesResources = (TesResources) o; + return Objects.equals(this.cpuCores, tesResources.cpuCores) && + Objects.equals(this.preemptible, tesResources.preemptible) && + Objects.equals(this.ramGb, tesResources.ramGb) && + Objects.equals(this.diskGb, tesResources.diskGb) && + Objects.equals(this.zones, tesResources.zones) && + Objects.equals(this.backendParameters, tesResources.backendParameters) && + Objects.equals(this.backendParametersStrict, tesResources.backendParametersStrict); + } + + @Override + public int hashCode() { + return Objects.hash(cpuCores, preemptible, ramGb, diskGb, zones, backendParameters, backendParametersStrict); + } + + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class TesResources {\n"); + + sb.append(" cpuCores: ").append(toIndentedString(cpuCores)).append("\n"); + sb.append(" preemptible: ").append(toIndentedString(preemptible)).append("\n"); + sb.append(" ramGb: ").append(toIndentedString(ramGb)).append("\n"); + sb.append(" diskGb: ").append(toIndentedString(diskGb)).append("\n"); + sb.append(" zones: ").append(toIndentedString(zones)).append("\n"); + sb.append(" backendParameters: ").append(toIndentedString(backendParameters)).append("\n"); + sb.append(" backendParametersStrict: ").append(toIndentedString(backendParametersStrict)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} diff --git a/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesServiceInfo.java b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesServiceInfo.java new file mode 100644 index 0000000..fac937f --- /dev/null +++ b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesServiceInfo.java @@ -0,0 +1,176 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Task Execution Service + * + * OpenAPI spec version: 1.1.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +package nextflow.ga4gh.tes.client.model; + +import java.util.Objects; +import java.util.Arrays; +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import nextflow.ga4gh.tes.client.model.Service; +import nextflow.ga4gh.tes.client.model.ServiceOrganization; +import nextflow.ga4gh.tes.client.model.TesServiceType; +import io.swagger.v3.oas.annotations.media.Schema; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import org.threeten.bp.OffsetDateTime; +/** + * TesServiceInfo + */ + +@javax.annotation.Generated(value = "io.swagger.codegen.v3.generators.java.JavaClientCodegen", date = "2023-08-15T14:10:09.878Z[GMT]") + +public class TesServiceInfo extends Service { + @SerializedName("storage") + private List storage = null; + + @SerializedName("tesResources_backend_parameters") + private List tesResourcesBackendParameters = null; + + @SerializedName("type") + private TesServiceType tesServiceInfoType = null; + + public TesServiceInfo storage(List storage) { + this.storage = storage; + return this; + } + + public TesServiceInfo addStorageItem(String storageItem) { + if (this.storage == null) { + this.storage = new ArrayList(); + } + this.storage.add(storageItem); + return this; + } + + /** + * Lists some, but not necessarily all, storage locations supported by the service. + * @return storage + **/ + @Schema(example = "[\"file:///path/to/local/funnel-storage\",\"s3://ohsu-compbio-funnel/storage\"]", description = "Lists some, but not necessarily all, storage locations supported by the service.") + public List getStorage() { + return storage; + } + + public void setStorage(List storage) { + this.storage = storage; + } + + public TesServiceInfo tesResourcesBackendParameters(List tesResourcesBackendParameters) { + this.tesResourcesBackendParameters = tesResourcesBackendParameters; + return this; + } + + public TesServiceInfo addTesResourcesBackendParametersItem(String tesResourcesBackendParametersItem) { + if (this.tesResourcesBackendParameters == null) { + this.tesResourcesBackendParameters = new ArrayList(); + } + this.tesResourcesBackendParameters.add(tesResourcesBackendParametersItem); + return this; + } + + /** + * Lists all tesResources.backend_parameters keys supported by the service + * @return tesResourcesBackendParameters + **/ + @Schema(example = "[\"VmSize\"]", description = "Lists all tesResources.backend_parameters keys supported by the service") + public List getTesResourcesBackendParameters() { + return tesResourcesBackendParameters; + } + + public void setTesResourcesBackendParameters(List tesResourcesBackendParameters) { + this.tesResourcesBackendParameters = tesResourcesBackendParameters; + } + + public TesServiceInfo tesServiceInfoType(TesServiceType tesServiceInfoType) { + this.tesServiceInfoType = tesServiceInfoType; + return this; + } + + /** + * Get tesServiceInfoType + * @return tesServiceInfoType + **/ + @Schema(description = "") + public TesServiceType getTesServiceInfoType() { + return tesServiceInfoType; + } + + public void setTesServiceInfoType(TesServiceType tesServiceInfoType) { + this.tesServiceInfoType = tesServiceInfoType; + } + + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TesServiceInfo tesServiceInfo = (TesServiceInfo) o; + return Objects.equals(this.storage, tesServiceInfo.storage) && + Objects.equals(this.tesResourcesBackendParameters, tesServiceInfo.tesResourcesBackendParameters) && + Objects.equals(this.tesServiceInfoType, tesServiceInfo.tesServiceInfoType) && + super.equals(o); + } + + @Override + public int hashCode() { + return Objects.hash(storage, tesResourcesBackendParameters, tesServiceInfoType, super.hashCode()); + } + + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class TesServiceInfo {\n"); + sb.append(" ").append(toIndentedString(super.toString())).append("\n"); + sb.append(" storage: ").append(toIndentedString(storage)).append("\n"); + sb.append(" tesResourcesBackendParameters: ").append(toIndentedString(tesResourcesBackendParameters)).append("\n"); + sb.append(" tesServiceInfoType: ").append(toIndentedString(tesServiceInfoType)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} diff --git a/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesServiceType.java b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesServiceType.java new file mode 100644 index 0000000..5757d89 --- /dev/null +++ b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesServiceType.java @@ -0,0 +1,150 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Task Execution Service + * + * OpenAPI spec version: 1.1.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +package nextflow.ga4gh.tes.client.model; + +import java.util.Objects; +import java.util.Arrays; +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import nextflow.ga4gh.tes.client.model.ServiceType; +import io.swagger.v3.oas.annotations.media.Schema; +import java.io.IOException; +/** + * TesServiceType + */ + +@javax.annotation.Generated(value = "io.swagger.codegen.v3.generators.java.JavaClientCodegen", date = "2023-08-15T14:10:09.878Z[GMT]") + +public class TesServiceType extends ServiceType { + /** + * Gets or Sets tesServiceTypeArtifact + */ + @JsonAdapter(ArtifactEnum.Adapter.class) + public enum ArtifactEnum { + @SerializedName("tes") + TES("tes"); + + private String value; + + ArtifactEnum(String value) { + this.value = value; + } + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + public static ArtifactEnum fromValue(String input) { + for (ArtifactEnum b : ArtifactEnum.values()) { + if (b.value.equals(input)) { + return b; + } + } + return null; + } + public static class Adapter extends TypeAdapter { + @Override + public void write(final JsonWriter jsonWriter, final ArtifactEnum enumeration) throws IOException { + jsonWriter.value(String.valueOf(enumeration.getValue())); + } + + @Override + public ArtifactEnum read(final JsonReader jsonReader) throws IOException { + Object value = jsonReader.nextString(); + return ArtifactEnum.fromValue((String)(value)); + } + } + } @SerializedName("artifact") + private ArtifactEnum tesServiceTypeArtifact = null; + + public TesServiceType tesServiceTypeArtifact(ArtifactEnum tesServiceTypeArtifact) { + this.tesServiceTypeArtifact = tesServiceTypeArtifact; + return this; + } + + /** + * Get tesServiceTypeArtifact + * @return tesServiceTypeArtifact + **/ + @Schema(example = "tes", required = true, description = "") + public ArtifactEnum getTesServiceTypeArtifact() { + return tesServiceTypeArtifact; + } + + public void setTesServiceTypeArtifact(ArtifactEnum tesServiceTypeArtifact) { + this.tesServiceTypeArtifact = tesServiceTypeArtifact; + } + + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TesServiceType tesServiceType = (TesServiceType) o; + return Objects.equals(this.tesServiceTypeArtifact, tesServiceType.tesServiceTypeArtifact) && + super.equals(o); + } + + @Override + public int hashCode() { + return Objects.hash(tesServiceTypeArtifact, super.hashCode()); + } + + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class TesServiceType {\n"); + sb.append(" ").append(toIndentedString(super.toString())).append("\n"); + sb.append(" tesServiceTypeArtifact: ").append(toIndentedString(tesServiceTypeArtifact)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} diff --git a/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesState.java b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesState.java new file mode 100644 index 0000000..1eeee3a --- /dev/null +++ b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesState.java @@ -0,0 +1,104 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Task Execution Service + * + * OpenAPI spec version: 1.1.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +package nextflow.ga4gh.tes.client.model; + +import java.util.Objects; +import java.util.Arrays; +import io.swagger.v3.oas.annotations.media.Schema; +import com.google.gson.annotations.SerializedName; +import java.io.IOException; +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; + +/** + * Task state as defined by the server. - `UNKNOWN`: The state of the task is unknown. The cause for this status message may be dependent on the underlying system. The `UNKNOWN` states provides a safe default for messages where this field is missing so that a missing field does not accidentally imply that the state is QUEUED. - `QUEUED`: The task is queued and awaiting resources to begin computing. - `INITIALIZING`: The task has been assigned to a worker and is currently preparing to run. For example, the worker may be turning on, downloading input files, etc. - `RUNNING`: The task is running. Input files are downloaded and the first Executor has been started. - `PAUSED`: The task is paused. The reasons for this would be tied to the specific system running the job. An implementation may have the ability to pause a task, but this is not required. - `COMPLETE`: The task has completed running. Executors have exited without error and output files have been successfully uploaded. - `EXECUTOR_ERROR`: The task encountered an error in one of the Executor processes. Generally, this means that an Executor exited with a non-zero exit code. - `SYSTEM_ERROR`: The task was stopped due to a system error, but not from an Executor, for example an upload failed due to network issues, the worker's ran out of disk space, etc. - `CANCELED`: The task was canceled by the user, and downstream resources have been deleted. - `CANCELING`: The task was canceled by the user, but the downstream resources are still awaiting deletion. - `PREEMPTED`: The task is stopped (preempted) by the system. The reasons for this would be tied to the specific system running the job. Generally, this means that the system reclaimed the compute capacity for reallocation. + */ +@JsonAdapter(TesState.Adapter.class) +public enum TesState { + @SerializedName("UNKNOWN") + UNKNOWN("UNKNOWN"), + @SerializedName("QUEUED") + QUEUED("QUEUED"), + @SerializedName("INITIALIZING") + INITIALIZING("INITIALIZING"), + @SerializedName("RUNNING") + RUNNING("RUNNING"), + @SerializedName("PAUSED") + PAUSED("PAUSED"), + @SerializedName("COMPLETE") + COMPLETE("COMPLETE"), + @SerializedName("EXECUTOR_ERROR") + EXECUTOR_ERROR("EXECUTOR_ERROR"), + @SerializedName("SYSTEM_ERROR") + SYSTEM_ERROR("SYSTEM_ERROR"), + @SerializedName("CANCELED") + CANCELED("CANCELED"), + @SerializedName("PREEMPTED") + PREEMPTED("PREEMPTED"), + @SerializedName("CANCELING") + CANCELING("CANCELING"); + + private String value; + + TesState(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + public static TesState fromValue(String input) { + for (TesState b : TesState.values()) { + if (b.value.equals(input)) { + return b; + } + } + return null; + } + + public static class Adapter extends TypeAdapter { + @Override + public void write(final JsonWriter jsonWriter, final TesState enumeration) throws IOException { + jsonWriter.value(String.valueOf(enumeration.getValue())); + } + + @Override + public TesState read(final JsonReader jsonReader) throws IOException { + Object value = jsonReader.nextString(); + return TesState.fromValue((String)(value)); + } + } +} diff --git a/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesTask.java b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesTask.java new file mode 100644 index 0000000..f693ea9 --- /dev/null +++ b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesTask.java @@ -0,0 +1,380 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Task Execution Service + * + * OpenAPI spec version: 1.1.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +package nextflow.ga4gh.tes.client.model; + +import java.util.Objects; +import java.util.Arrays; +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import nextflow.ga4gh.tes.client.model.TesExecutor; +import nextflow.ga4gh.tes.client.model.TesInput; +import nextflow.ga4gh.tes.client.model.TesOutput; +import nextflow.ga4gh.tes.client.model.TesResources; +import nextflow.ga4gh.tes.client.model.TesState; +import nextflow.ga4gh.tes.client.model.TesTaskLog; +import io.swagger.v3.oas.annotations.media.Schema; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +/** + * Task describes an instance of a task. + */ +@Schema(description = "Task describes an instance of a task.") +@javax.annotation.Generated(value = "io.swagger.codegen.v3.generators.java.JavaClientCodegen", date = "2023-08-15T14:10:09.878Z[GMT]") + +public class TesTask { + @SerializedName("id") + private String id = null; + + @SerializedName("state") + private TesState state = null; + + @SerializedName("name") + private String name = null; + + @SerializedName("description") + private String description = null; + + @SerializedName("inputs") + private List inputs = null; + + @SerializedName("outputs") + private List outputs = null; + + @SerializedName("resources") + private TesResources resources = null; + + @SerializedName("executors") + private List executors = new ArrayList(); + + @SerializedName("volumes") + private List volumes = null; + + @SerializedName("tags") + private Map tags = null; + + @SerializedName("logs") + private List logs = null; + + @SerializedName("creation_time") + private String creationTime = null; + + /** + * Task identifier assigned by the server. + * @return id + **/ + @Schema(example = "job-0012345", description = "Task identifier assigned by the server.") + public String getId() { + return id; + } + + public TesTask state(TesState state) { + this.state = state; + return this; + } + + /** + * Get state + * @return state + **/ + @Schema(description = "") + public TesState getState() { + return state; + } + + public void setState(TesState state) { + this.state = state; + } + + public TesTask name(String name) { + this.name = name; + return this; + } + + /** + * User-provided task name. + * @return name + **/ + @Schema(description = "User-provided task name.") + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public TesTask description(String description) { + this.description = description; + return this; + } + + /** + * Optional user-provided description of task for documentation purposes. + * @return description + **/ + @Schema(description = "Optional user-provided description of task for documentation purposes.") + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public TesTask inputs(List inputs) { + this.inputs = inputs; + return this; + } + + public TesTask addInputsItem(TesInput inputsItem) { + if (this.inputs == null) { + this.inputs = new ArrayList(); + } + this.inputs.add(inputsItem); + return this; + } + + /** + * Input files that will be used by the task. Inputs will be downloaded and mounted into the executor container as defined by the task request document. + * @return inputs + **/ + @Schema(example = "[{\"url\":\"s3://my-object-store/file1\",\"path\":\"/data/file1\"}]", description = "Input files that will be used by the task. Inputs will be downloaded and mounted into the executor container as defined by the task request document.") + public List getInputs() { + return inputs; + } + + public void setInputs(List inputs) { + this.inputs = inputs; + } + + public TesTask outputs(List outputs) { + this.outputs = outputs; + return this; + } + + public TesTask addOutputsItem(TesOutput outputsItem) { + if (this.outputs == null) { + this.outputs = new ArrayList(); + } + this.outputs.add(outputsItem); + return this; + } + + /** + * Output files. Outputs will be uploaded from the executor container to long-term storage. + * @return outputs + **/ + @Schema(example = "[{\"path\":\"/data/outfile\",\"url\":\"s3://my-object-store/outfile-1\",\"type\":\"FILE\"}]", description = "Output files. Outputs will be uploaded from the executor container to long-term storage.") + public List getOutputs() { + return outputs; + } + + public void setOutputs(List outputs) { + this.outputs = outputs; + } + + public TesTask resources(TesResources resources) { + this.resources = resources; + return this; + } + + /** + * Get resources + * @return resources + **/ + @Schema(description = "") + public TesResources getResources() { + return resources; + } + + public void setResources(TesResources resources) { + this.resources = resources; + } + + public TesTask executors(List executors) { + this.executors = executors; + return this; + } + + public TesTask addExecutorsItem(TesExecutor executorsItem) { + this.executors.add(executorsItem); + return this; + } + + /** + * An array of executors to be run. Each of the executors will run one at a time sequentially. Each executor is a different command that will be run, and each can utilize a different docker image. But each of the executors will see the same mapped inputs and volumes that are declared in the parent CreateTask message. Execution stops on the first error. + * @return executors + **/ + @Schema(required = true, description = "An array of executors to be run. Each of the executors will run one at a time sequentially. Each executor is a different command that will be run, and each can utilize a different docker image. But each of the executors will see the same mapped inputs and volumes that are declared in the parent CreateTask message. Execution stops on the first error.") + public List getExecutors() { + return executors; + } + + public void setExecutors(List executors) { + this.executors = executors; + } + + public TesTask volumes(List volumes) { + this.volumes = volumes; + return this; + } + + public TesTask addVolumesItem(String volumesItem) { + if (this.volumes == null) { + this.volumes = new ArrayList(); + } + this.volumes.add(volumesItem); + return this; + } + + /** + * Volumes are directories which may be used to share data between Executors. Volumes are initialized as empty directories by the system when the task starts and are mounted at the same path in each Executor. For example, given a volume defined at `/vol/A`, executor 1 may write a file to `/vol/A/exec1.out.txt`, then executor 2 may read from that file. (Essentially, this translates to a `docker run -v` flag where the container path is the same for each executor). + * @return volumes + **/ + @Schema(example = "[\"/vol/A/\"]", description = "Volumes are directories which may be used to share data between Executors. Volumes are initialized as empty directories by the system when the task starts and are mounted at the same path in each Executor. For example, given a volume defined at `/vol/A`, executor 1 may write a file to `/vol/A/exec1.out.txt`, then executor 2 may read from that file. (Essentially, this translates to a `docker run -v` flag where the container path is the same for each executor).") + public List getVolumes() { + return volumes; + } + + public void setVolumes(List volumes) { + this.volumes = volumes; + } + + public TesTask tags(Map tags) { + this.tags = tags; + return this; + } + + public TesTask putTagsItem(String key, String tagsItem) { + if (this.tags == null) { + this.tags = new HashMap(); + } + this.tags.put(key, tagsItem); + return this; + } + + /** + * A key-value map of arbitrary tags. These can be used to store meta-data and annotations about a task. Example: ``` { \"tags\" : { \"WORKFLOW_ID\" : \"cwl-01234\", \"PROJECT_GROUP\" : \"alice-lab\" } } ``` + * @return tags + **/ + @Schema(example = "{\"WORKFLOW_ID\":\"cwl-01234\",\"PROJECT_GROUP\":\"alice-lab\"}", description = "A key-value map of arbitrary tags. These can be used to store meta-data and annotations about a task. Example: ``` { \"tags\" : { \"WORKFLOW_ID\" : \"cwl-01234\", \"PROJECT_GROUP\" : \"alice-lab\" } } ```") + public Map getTags() { + return tags; + } + + public void setTags(Map tags) { + this.tags = tags; + } + + /** + * Task logging information. Normally, this will contain only one entry, but in the case where a task fails and is retried, an entry will be appended to this list. + * @return logs + **/ + @Schema(description = "Task logging information. Normally, this will contain only one entry, but in the case where a task fails and is retried, an entry will be appended to this list.") + public List getLogs() { + return logs; + } + + /** + * Date + time the task was created, in RFC 3339 format. This is set by the system, not the client. + * @return creationTime + **/ + @Schema(example = "2020-10-02 10:00:00-05:00", description = "Date + time the task was created, in RFC 3339 format. This is set by the system, not the client.") + public String getCreationTime() { + return creationTime; + } + + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TesTask tesTask = (TesTask) o; + return Objects.equals(this.id, tesTask.id) && + Objects.equals(this.state, tesTask.state) && + Objects.equals(this.name, tesTask.name) && + Objects.equals(this.description, tesTask.description) && + Objects.equals(this.inputs, tesTask.inputs) && + Objects.equals(this.outputs, tesTask.outputs) && + Objects.equals(this.resources, tesTask.resources) && + Objects.equals(this.executors, tesTask.executors) && + Objects.equals(this.volumes, tesTask.volumes) && + Objects.equals(this.tags, tesTask.tags) && + Objects.equals(this.logs, tesTask.logs) && + Objects.equals(this.creationTime, tesTask.creationTime); + } + + @Override + public int hashCode() { + return Objects.hash(id, state, name, description, inputs, outputs, resources, executors, volumes, tags, logs, creationTime); + } + + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class TesTask {\n"); + + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append(" state: ").append(toIndentedString(state)).append("\n"); + sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append(" description: ").append(toIndentedString(description)).append("\n"); + sb.append(" inputs: ").append(toIndentedString(inputs)).append("\n"); + sb.append(" outputs: ").append(toIndentedString(outputs)).append("\n"); + sb.append(" resources: ").append(toIndentedString(resources)).append("\n"); + sb.append(" executors: ").append(toIndentedString(executors)).append("\n"); + sb.append(" volumes: ").append(toIndentedString(volumes)).append("\n"); + sb.append(" tags: ").append(toIndentedString(tags)).append("\n"); + sb.append(" logs: ").append(toIndentedString(logs)).append("\n"); + sb.append(" creationTime: ").append(toIndentedString(creationTime)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} diff --git a/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesTaskLog.java b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesTaskLog.java new file mode 100644 index 0000000..8001474 --- /dev/null +++ b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/client/model/TesTaskLog.java @@ -0,0 +1,254 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Task Execution Service + * + * OpenAPI spec version: 1.1.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +package nextflow.ga4gh.tes.client.model; + +import java.util.Objects; +import java.util.Arrays; +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import nextflow.ga4gh.tes.client.model.TesExecutorLog; +import nextflow.ga4gh.tes.client.model.TesOutputFileLog; +import io.swagger.v3.oas.annotations.media.Schema; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +/** + * TaskLog describes logging information related to a Task. + */ +@Schema(description = "TaskLog describes logging information related to a Task.") +@javax.annotation.Generated(value = "io.swagger.codegen.v3.generators.java.JavaClientCodegen", date = "2023-08-15T14:10:09.878Z[GMT]") + +public class TesTaskLog { + @SerializedName("logs") + private List logs = new ArrayList(); + + @SerializedName("metadata") + private Map metadata = null; + + @SerializedName("start_time") + private String startTime = null; + + @SerializedName("end_time") + private String endTime = null; + + @SerializedName("outputs") + private List outputs = new ArrayList(); + + @SerializedName("system_logs") + private List systemLogs = null; + + public TesTaskLog logs(List logs) { + this.logs = logs; + return this; + } + + public TesTaskLog addLogsItem(TesExecutorLog logsItem) { + this.logs.add(logsItem); + return this; + } + + /** + * Logs for each executor + * @return logs + **/ + @Schema(required = true, description = "Logs for each executor") + public List getLogs() { + return logs; + } + + public void setLogs(List logs) { + this.logs = logs; + } + + public TesTaskLog metadata(Map metadata) { + this.metadata = metadata; + return this; + } + + public TesTaskLog putMetadataItem(String key, String metadataItem) { + if (this.metadata == null) { + this.metadata = new HashMap(); + } + this.metadata.put(key, metadataItem); + return this; + } + + /** + * Arbitrary logging metadata included by the implementation. + * @return metadata + **/ + @Schema(example = "{\"host\":\"worker-001\",\"slurmm_id\":123456}", description = "Arbitrary logging metadata included by the implementation.") + public Map getMetadata() { + return metadata; + } + + public void setMetadata(Map metadata) { + this.metadata = metadata; + } + + public TesTaskLog startTime(String startTime) { + this.startTime = startTime; + return this; + } + + /** + * When the task started, in RFC 3339 format. + * @return startTime + **/ + @Schema(example = "2020-10-02 10:00:00-05:00", description = "When the task started, in RFC 3339 format.") + public String getStartTime() { + return startTime; + } + + public void setStartTime(String startTime) { + this.startTime = startTime; + } + + public TesTaskLog endTime(String endTime) { + this.endTime = endTime; + return this; + } + + /** + * When the task ended, in RFC 3339 format. + * @return endTime + **/ + @Schema(example = "2020-10-02 11:00:00-05:00", description = "When the task ended, in RFC 3339 format.") + public String getEndTime() { + return endTime; + } + + public void setEndTime(String endTime) { + this.endTime = endTime; + } + + public TesTaskLog outputs(List outputs) { + this.outputs = outputs; + return this; + } + + public TesTaskLog addOutputsItem(TesOutputFileLog outputsItem) { + this.outputs.add(outputsItem); + return this; + } + + /** + * Information about all output files. Directory outputs are flattened into separate items. + * @return outputs + **/ + @Schema(required = true, description = "Information about all output files. Directory outputs are flattened into separate items.") + public List getOutputs() { + return outputs; + } + + public void setOutputs(List outputs) { + this.outputs = outputs; + } + + public TesTaskLog systemLogs(List systemLogs) { + this.systemLogs = systemLogs; + return this; + } + + public TesTaskLog addSystemLogsItem(String systemLogsItem) { + if (this.systemLogs == null) { + this.systemLogs = new ArrayList(); + } + this.systemLogs.add(systemLogsItem); + return this; + } + + /** + * System logs are any logs the system decides are relevant, which are not tied directly to an Executor process. Content is implementation specific: format, size, etc. System logs may be collected here to provide convenient access. For example, the system may include the name of the host where the task is executing, an error message that caused a SYSTEM_ERROR state (e.g. disk is full), etc. System logs are only included in the FULL task view. + * @return systemLogs + **/ + @Schema(description = "System logs are any logs the system decides are relevant, which are not tied directly to an Executor process. Content is implementation specific: format, size, etc. System logs may be collected here to provide convenient access. For example, the system may include the name of the host where the task is executing, an error message that caused a SYSTEM_ERROR state (e.g. disk is full), etc. System logs are only included in the FULL task view.") + public List getSystemLogs() { + return systemLogs; + } + + public void setSystemLogs(List systemLogs) { + this.systemLogs = systemLogs; + } + + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TesTaskLog tesTaskLog = (TesTaskLog) o; + return Objects.equals(this.logs, tesTaskLog.logs) && + Objects.equals(this.metadata, tesTaskLog.metadata) && + Objects.equals(this.startTime, tesTaskLog.startTime) && + Objects.equals(this.endTime, tesTaskLog.endTime) && + Objects.equals(this.outputs, tesTaskLog.outputs) && + Objects.equals(this.systemLogs, tesTaskLog.systemLogs); + } + + @Override + public int hashCode() { + return Objects.hash(logs, metadata, startTime, endTime, outputs, systemLogs); + } + + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class TesTaskLog {\n"); + + sb.append(" logs: ").append(toIndentedString(logs)).append("\n"); + sb.append(" metadata: ").append(toIndentedString(metadata)).append("\n"); + sb.append(" startTime: ").append(toIndentedString(startTime)).append("\n"); + sb.append(" endTime: ").append(toIndentedString(endTime)).append("\n"); + sb.append(" outputs: ").append(toIndentedString(outputs)).append("\n"); + sb.append(" systemLogs: ").append(toIndentedString(systemLogs)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} diff --git a/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/executor/TesBashBuilder.groovy b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/executor/TesBashBuilder.groovy new file mode 100644 index 0000000..2f67da0 --- /dev/null +++ b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/executor/TesBashBuilder.groovy @@ -0,0 +1,40 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package nextflow.ga4gh.tes.executor + +import groovy.transform.CompileStatic +import nextflow.executor.BashWrapperBuilder +import nextflow.processor.TaskBean +import nextflow.processor.TaskRun + +/** + * Bash builder adapter to manage TES specific tasks + * + * @author Paolo Di Tommaso + */ +@CompileStatic +class TesBashBuilder extends BashWrapperBuilder { + + TesBashBuilder(TaskRun task, String remoteBinDir) { + super(new TaskBean(task), new TesFileCopyStrategy(remoteBinDir)) + } + + TesBashBuilder(TaskBean task, String remoteBinDir) { + super(task, new TesFileCopyStrategy(remoteBinDir)) + } + +} diff --git a/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/executor/TesExecutor.groovy b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/executor/TesExecutor.groovy new file mode 100644 index 0000000..115eaba --- /dev/null +++ b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/executor/TesExecutor.groovy @@ -0,0 +1,184 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package nextflow.ga4gh.tes.executor + +import groovy.transform.CompileStatic +import groovy.transform.PackageScope +import groovy.util.logging.Slf4j +import nextflow.exception.AbortOperationException +import nextflow.executor.Executor +import nextflow.extension.FilesEx +import nextflow.ga4gh.tes.client.ApiClient +import nextflow.ga4gh.tes.client.api.TaskServiceApi +import nextflow.ga4gh.tes.client.auth.ApiKeyAuth +import nextflow.ga4gh.tes.client.auth.Authentication +import nextflow.ga4gh.tes.client.auth.HttpBasicAuth +import nextflow.ga4gh.tes.client.auth.OAuth +import nextflow.processor.TaskHandler +import nextflow.processor.TaskMonitor +import nextflow.processor.TaskPollingMonitor +import nextflow.processor.TaskRun +import nextflow.util.Duration +import nextflow.util.ServiceName +import org.pf4j.ExtensionPoint + +import java.nio.file.Path + +/** + * Experimental TES executor + * + * See https://github.com/ga4gh/task-execution-schemas/ + * + * @author Paolo Di Tommaso + */ +@Slf4j +@CompileStatic +@ServiceName('tes') +class TesExecutor extends Executor implements ExtensionPoint { + + private TaskServiceApi client + + /** + * A path accessible to TES where executable scripts need to be uploaded + */ + private Path remoteBinDir + + private List remoteBinFiles = [] + + @Override + protected void register() { + super.register() + uploadBinDir() + + client = new TaskServiceApi( new ApiClient( + basePath: getEndpoint(), + debugging: log.isTraceEnabled(), + authentications: getAuthentications()) ) + } + + protected String getDisplayName() { + return "$name [${getEndpoint()}]" + } + + TaskServiceApi getClient() { + client + } + + @PackageScope + Path getRemoteBinDir() { + remoteBinDir + } + + @PackageScope + List getRemoteBinFiles() { + remoteBinFiles + } + + protected void uploadBinDir() { + /* + * upload local binaries + */ + if( session.binDir && !session.binDir.empty() && !session.disableRemoteBinDir ) { + final tempBin = getTempDir() + log.info "Uploading local `bin` scripts folder to ${tempBin.toUriString()}/bin" + remoteBinDir = FilesEx.copyTo(session.binDir, tempBin) + + remoteBinFiles = [] + session.binDir.eachFileRecurse { file -> + if( file.isDirectory() ) + return + remoteBinFiles << tempBin.resolve('bin').resolve(session.binDir.relativize(file).toString()) + } + } + } + + protected String getEndpoint() { + def result = session.getConfigAttribute('executor.tes.endpoint', null) + if( result ) + log.warn 'Config option `executor.tes.endpoint` is deprecated, use `tes.endpoint` instead' + else + result = session.config.navigate('tes.endpoint', 'http://localhost:8000') + + log.debug "[TES] endpoint=$result" + return result + } + + protected Map getAuthentications() { + final Map result = [:] + + // basic + final username = session.config.navigate('tes.basicUsername') + final password = session.config.navigate('tes.basicPassword') + if( username && password ) + result['basic'] = new HttpBasicAuth(username: username, password: password) + + // API key + final apiKeyParamMode = session.config.navigate('tes.apiKeyParamMode', 'query') as String + final apiKeyParamName = session.config.navigate('tes.apiKeyParamName') as String + final apiKey = session.config.navigate('tes.apiKey') as String + if( apiKeyParamName && apiKey ) { + final auth = new ApiKeyAuth(apiKeyParamMode, apiKeyParamName) + auth.setApiKey(apiKey) + result['apikey'] = auth + } + + // OAuth + final oauthToken = session.config.navigate('tes.oauthToken') + if( oauthToken ) + result['oauth'] = new OAuth(accessToken: oauthToken) + + log.debug "[TES] Authentication methods: ${result.keySet()}" + return result + } + + protected String getAzureStorageAccount() { + final storageAccount = session.config.navigate('azure.storage.accountName') + log.debug "[TES] Azure storage account = ${storageAccount}" + return storageAccount + } + + /** + * @return {@code true} whenever the containerization is managed by the executor itself + */ + boolean isContainerNative() { + return true + } + + /** + * Create a a queue holder for this executor + * + * @return + */ + TaskMonitor createTaskMonitor() { + return TaskPollingMonitor.create(session, name, 100, Duration.of('1 sec')) + } + + + /* + * Prepare and launch the task in the underlying execution platform + */ + @Override + TaskHandler createTaskHandler(TaskRun task) { + assert task + assert task.workDir + log.debug "[TES] Launching process > ${task.name} -- work folder: ${task.workDir}" + new TesTaskHandler(task, this) + } +} + + + diff --git a/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/executor/TesFileCopyStrategy.groovy b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/executor/TesFileCopyStrategy.groovy new file mode 100644 index 0000000..4a46abb --- /dev/null +++ b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/executor/TesFileCopyStrategy.groovy @@ -0,0 +1,127 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package nextflow.ga4gh.tes.executor + +import java.nio.file.Path + +import groovy.transform.CompileStatic +import nextflow.executor.ScriptFileCopyStrategy +import nextflow.processor.TaskProcessor +import nextflow.util.Escape +/** + * Implements file inputs/outputs staging strategy for tasks executed + * by TES executor + * + * @author Paolo Di Tommaso + */ +@CompileStatic +class TesFileCopyStrategy implements ScriptFileCopyStrategy { + private String remoteBinDir + + TesFileCopyStrategy(String remoteBinDir) { + this.remoteBinDir = remoteBinDir + } + + /** + * {@inheritDoc} + */ + @Override + String getBeforeStartScript() { + return null + } + + /** + * {@inheritDoc} + */ + @Override + String getStageInputFilesScript(Map inputFiles) { + return null + } + + /** + * {@inheritDoc} + */ + @Override + String getUnstageOutputFilesScript(List outputFiles, Path targetDir) { + return null + } + + @Override + String touchFile(Path file) { + return '' + } + + /** + * {@inheritDoc} + */ + @Override + String fileStr(Path file) { + Escape.path(file.getFileName()) + } + + /** + * {@inheritDoc} + */ + @Override + String copyFile(String name, Path target) { + return 'true' + } + + /** + * {@inheritDoc} + */ + @Override + String exitFile(Path file) { + "> ${Escape.path(file.getName())}" + } + + /** + * {@inheritDoc} + */ + @Override + String pipeInputFile(Path file) { + " < ${Escape.path(file.getName())}" + } + + /** + * {@inheritDoc} + */ + @Override + String getEnvScript(Map env, boolean container) { + if( container ) + throw new UnsupportedOperationException("Parameter `container` not supported by ${this.class.simpleName}") + + final result = new StringBuilder() + final copy = env ? new HashMap(env) : Collections.emptyMap() + final path = copy.containsKey('PATH') + // remove any external PATH + if( path ) + copy.remove('PATH') + // when a remote bin directory is provide managed it properly + if( remoteBinDir ) { + result << "mkdir \$PWD/nextflow-bin/\n" + result << "cp -r ${remoteBinDir}/* \$PWD/nextflow-bin/\n" + result << "chmod +x \$PWD/nextflow-bin/* || true\n" + result << "export PATH=\$PWD/nextflow-bin:\$PATH\n" + } + // finally render the environment + final envSnippet = TaskProcessor.bashEnvironmentScript(copy,false) + if( envSnippet ) + result << envSnippet + return result.toString() + } +} diff --git a/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/executor/TesTaskHandler.groovy b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/executor/TesTaskHandler.groovy new file mode 100644 index 0000000..4e1ea4f --- /dev/null +++ b/plugins/nf-ga4gh/src/main/nextflow/ga4gh/tes/executor/TesTaskHandler.groovy @@ -0,0 +1,317 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package nextflow.ga4gh.tes.executor + +import nextflow.ga4gh.tes.client.model.TesFileType +import nextflow.processor.TaskBean + +import static nextflow.processor.TaskStatus.COMPLETED +import static nextflow.processor.TaskStatus.RUNNING + +import java.nio.file.Path + +import groovy.transform.CompileStatic +import groovy.util.logging.Slf4j +import nextflow.exception.ProcessUnrecoverableException +import nextflow.executor.BashWrapperBuilder +import nextflow.ga4gh.tes.client.api.TaskServiceApi +import nextflow.ga4gh.tes.client.model.TesExecutor as TesExecutorModel +import nextflow.ga4gh.tes.client.model.TesInput +import nextflow.ga4gh.tes.client.model.TesOutput +import nextflow.ga4gh.tes.client.model.TesResources +import nextflow.ga4gh.tes.client.model.TesState +import nextflow.ga4gh.tes.client.model.TesTask +import nextflow.processor.TaskConfig +import nextflow.processor.TaskHandler +import nextflow.processor.TaskRun +import nextflow.processor.TaskStatus +import nextflow.script.params.FileOutParam +import nextflow.util.Escape +import nextflow.util.MemoryUnit +/** + * Handle execution phases for a task executed by a TES executor + * + * @author Paolo Di Tommaso + */ +@Slf4j +@CompileStatic +class TesTaskHandler extends TaskHandler { + + static final private String WORK_DIR = '/work' + + final List COMPLETE_STATUSES = [TesState.COMPLETE, TesState.EXECUTOR_ERROR, TesState.SYSTEM_ERROR, TesState.CANCELED] + + final List STARTED_STATUSES = [TesState.INITIALIZING, TesState.RUNNING, TesState.PAUSED] + COMPLETE_STATUSES + + private TesExecutor executor + + private final Path exitFile + + private final Path wrapperFile + + private final Path outputFile + + private final Path errorFile + + private final Path logFile + + private final Path scriptFile + + private final Path inputFile + + private final Path traceFile + + private TaskServiceApi client + + private String requestId + + /** only for testing purpose -- do not use */ + protected TesTaskHandler() {} + + TesTaskHandler(TaskRun task, TesExecutor executor) { + super(task) + this.executor = executor + this.client = executor.getClient() + + this.logFile = task.workDir.resolve(TaskRun.CMD_LOG) + this.scriptFile = task.workDir.resolve(TaskRun.CMD_SCRIPT) + this.inputFile = task.workDir.resolve(TaskRun.CMD_INFILE) + this.outputFile = task.workDir.resolve(TaskRun.CMD_OUTFILE) + this.errorFile = task.workDir.resolve(TaskRun.CMD_ERRFILE) + this.exitFile = task.workDir.resolve(TaskRun.CMD_EXIT) + this.wrapperFile = task.workDir.resolve(TaskRun.CMD_RUN) + this.traceFile = task.workDir.resolve(TaskRun.CMD_TRACE) + } + + protected String getRequestId() { requestId } + + @Override + boolean checkIfRunning() { + + if( requestId && isSubmitted() ) { + final response = client.getTask(requestId, null) + final started = response.state in STARTED_STATUSES + if( started ) { + log.trace "[TES] Task started > $task.name" + status = RUNNING + return true + } + } + + return false + } + + @Override + boolean checkIfCompleted() { + if( !isRunning() ) { + return false + } + + final response = client.getTask(requestId, null) + if( response.state in COMPLETE_STATUSES ) { + // finalize the task + log.trace "[TES] Task completed > $task.name" + task.exitStatus = readExitStatus() + task.stdout = outputFile + task.stderr = errorFile + status = COMPLETED + return true + } + + return false + } + + private int readExitStatus() { + try { + return client + .getTask(requestId, 'FULL') + .logs[0] + .logs[0] + .exitCode + } + catch( Exception e ) { + log.trace "[TES] Cannot read exitstatus for task: `$task.name` | ${e.message}" + return Integer.MAX_VALUE + } + } + + @Override + void kill() { + if( requestId ) + client.cancelTask(requestId) + else + log.trace "[TES] Invalid kill request -- missing requestId" + } + + @Override + void submit() { + + // create task wrapper + String remoteBinDir = executor.getRemoteBinDir() + final bash = newTesBashBuilder(task, remoteBinDir) + bash.build() + + final body = newTesTask() + + // submit the task + final task = client.createTask(body) + requestId = task.id + status = TaskStatus.SUBMITTED + } + + protected TesBashBuilder newTesBashBuilder(TaskRun task, String remoteBinDir) { + final builder = new TesBashBuilder(task, remoteBinDir) + builder.headerScript = "NXF_CHDIR=${Escape.path(WORK_DIR)}" + return builder + } + + protected TesTask newTesTask() { + if( !task.container ) + throw new ProcessUnrecoverableException("Process `${task.lazyName()}` failed because the container image was not specified") + + final exec = new TesExecutorModel() + exec.command = List.of('bash', '-o', 'pipefail', '-c', "bash ${WORK_DIR}/${TaskRun.CMD_RUN} 2>&1 | tee ${WORK_DIR}/${TaskRun.CMD_LOG}".toString()) + exec.image = task.container + exec.workdir = WORK_DIR + + final body = new TesTask() + + // add task control files + body.addInputsItem(inItem(scriptFile)) + body.addInputsItem(inItem(wrapperFile)) + + for( Path path : executor.getRemoteBinFiles() ) + body.addInputsItem(inItemFromBin(path)) + + // add task input files + if( inputFile.exists() ) + body.addInputsItem(inItem(inputFile)) + + task.getInputFilesMap().each { String name, Path path -> + body.addInputsItem(inItem(path, name)) + } + + // add the task output files + body.addOutputsItem(outItem(outputFile.name)) + body.addOutputsItem(outItem(errorFile.name)) + body.addOutputsItem(outItem(logFile.name)) + body.addOutputsItem(outItem(exitFile.name)) + + // set requested resources + body.setResources(getResources(task.config)) + + for( final param : task.getOutputsByType(FileOutParam).keySet() ) { + final type = param.type == 'dir' + ? 'DIRECTORY' + : 'FILE' + for( final pattern : param.getFilePatterns(task.context, task.workDir) ) + body.addOutputsItem(outItem(pattern, type)) + } + + body.setName(task.getName()) + + // add the executor + body.executors = List.of(exec) + + return body + } + + private TesResources getResources(TaskConfig config) { + final res = new TesResources() + .cpuCores(config.getCpus()) + .ramGb(toGiga(config.getMemory())) + .diskGb(config.getDisk()?.toGiga()) + log.trace("[TES] Adding resource request: $res") + // @TODO preemptible + // @TODO zones + return res + } + + private Double toGiga(MemoryUnit size) { + // 1073741824 = 1GB + return size != null ? ((double)size.bytes)/1073741824 : null + } + + private TesInput inItem(Path realPath, String fileName = null) { + final result = new TesInput() + result.url = realPath.toUriString() + result.path = fileName ? "$WORK_DIR/$fileName" : "$WORK_DIR/${realPath.getName()}" + result.type = realPath.isDirectory() ? 'DIRECTORY' : 'FILE' + + // fix local path for TES Azure + result.url = fixTesAzureLocalPath(result.url) + + log.trace "[TES] Adding INPUT file: $result" + return result + } + + private TesInput inItemFromBin(Path realPath) { + final result = new TesInput() + result.url = realPath.toUriString() + result.path = realPath.toString() + result.type = 'FILE' + + // fix local path for TES Azure + result.url = fixTesAzureLocalPath(result.url) + + log.trace "[TES] Adding INPUT file: $result" + return result + } + + private TesOutput outItem( String fileName, String type = null ) { + final result = new TesOutput() + if( fileName.contains('*') || fileName.contains('?') ) { + result.path = "$WORK_DIR/$fileName" + result.pathPrefix = WORK_DIR + result.url = task.workDir.toUriString() + } + else { + result.path = "$WORK_DIR/$fileName" + result.url = task.workDir.resolve(fileName).toUriString() + } + if( type != null ) + result.type = type + + // fix local path for TES Azure + result.url = fixTesAzureLocalPath(result.url) + + log.trace "[TES] Adding OUTPUT file: $result" + return result + } + + /** + * Fix local paths when using TES Azure, which requires the + * following AZ path: + * + * az:/// + * + * to be formatted as: + * + * /// + * + * @param url + */ + private String fixTesAzureLocalPath(String url) { + if( !url.startsWith('az://') ) + return url + final storageAccount = executor.getAzureStorageAccount() + if( !executor.getEndpoint().contains('azure.com') || !storageAccount ) + return url + return url.replaceAll('az://', "/${storageAccount}/") + } + +} diff --git a/plugins/nf-ga4gh/src/resources/META-INF/MANIFEST.MF b/plugins/nf-ga4gh/src/resources/META-INF/MANIFEST.MF new file mode 100644 index 0000000..e7fe6df --- /dev/null +++ b/plugins/nf-ga4gh/src/resources/META-INF/MANIFEST.MF @@ -0,0 +1,6 @@ +Manifest-Version: 1.0 +Plugin-Class: nextflow.ga4gh.Ga4ghPlugin +Plugin-Id: nf-ga4gh +Plugin-Version: 1.3.0 +Plugin-Provider: Seqera Labs +Plugin-Requires: >=24.01.0-edge diff --git a/plugins/nf-ga4gh/src/resources/META-INF/extensions.idx b/plugins/nf-ga4gh/src/resources/META-INF/extensions.idx new file mode 100644 index 0000000..45231d7 --- /dev/null +++ b/plugins/nf-ga4gh/src/resources/META-INF/extensions.idx @@ -0,0 +1,17 @@ +# +# Copyright 2013-2024, Seqera Labs +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +nextflow.ga4gh.tes.executor.TesExecutor diff --git a/plugins/nf-ga4gh/src/test/nextflow/ga4gh/tes/executor/TesBashBuilderTest.groovy b/plugins/nf-ga4gh/src/test/nextflow/ga4gh/tes/executor/TesBashBuilderTest.groovy new file mode 100644 index 0000000..b13b010 --- /dev/null +++ b/plugins/nf-ga4gh/src/test/nextflow/ga4gh/tes/executor/TesBashBuilderTest.groovy @@ -0,0 +1,66 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package nextflow.ga4gh.tes.executor + +import java.nio.file.Files + +import nextflow.processor.TaskBean +import spock.lang.Specification + +/** + * + * @author Paolo Di Tommaso + */ +class TesBashBuilderTest extends Specification { + + + def 'test bash wrapper' () { + + given: + def folder = Files.createTempDirectory('test') + + /* + * simple bash run + */ + when: + def bash = new TesBashBuilder([ + name: 'Hello 1', + workDir: folder, + script: 'echo Hello world!', + ] as TaskBean, null) + bash.build() + + then: + Files.exists(folder.resolve('.command.sh')) + Files.exists(folder.resolve('.command.run')) + + folder.resolve('.command.sh').text == + ''' + #!/bin/bash -ue + echo Hello world! + ''' + .stripIndent().leftTrim() + + + folder.resolve('.command.run').text.contains('nxf_main') + + cleanup: + folder?.deleteDir() + } + + +} diff --git a/plugins/nf-ga4gh/src/test/nextflow/ga4gh/tes/executor/TesExecutorTest.groovy b/plugins/nf-ga4gh/src/test/nextflow/ga4gh/tes/executor/TesExecutorTest.groovy new file mode 100644 index 0000000..b75b09e --- /dev/null +++ b/plugins/nf-ga4gh/src/test/nextflow/ga4gh/tes/executor/TesExecutorTest.groovy @@ -0,0 +1,58 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package nextflow.ga4gh.tes.executor + +import nextflow.Session +import spock.lang.Specification + +/** + * + * @author Paolo Di Tommaso + */ +class TesExecutorTest extends Specification { + + def 'should get endpoint' () { + given: + def config = [ + tes: [endpoint: 'http://foo.com'] + ] + def session = new Session(config) + def exec = new TesExecutor(session: session) + + when: + def result = exec.getEndpoint() + then: + result == 'http://foo.com' + } + + + def 'should resolve endpoint from env'() { + given: + def ENV = [NXF_EXECUTOR_TES_ENDPOINT: 'http://back.end.com' ] + def session = Spy(Session) + def exec = Spy(TesExecutor) + + when: + def result = exec.getEndpoint() + then: + 1 * exec.getSession() >> session + 1 * session.getSystemEnv() >> ENV + result == 'http://back.end.com' + + } + +} diff --git a/plugins/nf-ga4gh/src/test/nextflow/ga4gh/tes/executor/TesFileCopyStrategyTest.groovy b/plugins/nf-ga4gh/src/test/nextflow/ga4gh/tes/executor/TesFileCopyStrategyTest.groovy new file mode 100644 index 0000000..4983424 --- /dev/null +++ b/plugins/nf-ga4gh/src/test/nextflow/ga4gh/tes/executor/TesFileCopyStrategyTest.groovy @@ -0,0 +1,45 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package nextflow.ga4gh.tes.executor + +import spock.lang.Specification + +/** + * + * @author Paolo Di Tommaso + */ +class TesFileCopyStrategyTest extends Specification { + + def 'should return task env' () { + + given: + def strategy = new TesFileCopyStrategy() + + when: + def script = strategy.getEnvScript([ALPHA:'xx', BETA:'yy'],false) + then: + script == '''\ + export ALPHA="xx" + export BETA="yy" + '''.stripIndent() + + when: + script = strategy.getEnvScript([ALPHA:'xx', BETA:'yy'], true) + then: + thrown(UnsupportedOperationException) + } +} diff --git a/plugins/nf-ga4gh/src/test/nextflow/ga4gh/tes/executor/TesTaskHandlerTest.groovy b/plugins/nf-ga4gh/src/test/nextflow/ga4gh/tes/executor/TesTaskHandlerTest.groovy new file mode 100644 index 0000000..ef56493 --- /dev/null +++ b/plugins/nf-ga4gh/src/test/nextflow/ga4gh/tes/executor/TesTaskHandlerTest.groovy @@ -0,0 +1,92 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package nextflow.ga4gh.tes.executor + +import nextflow.ga4gh.tes.client.api.TaskServiceApi +import nextflow.ga4gh.tes.client.model.TesCreateTaskResponse +import nextflow.ga4gh.tes.client.model.TesTask +import nextflow.processor.TaskStatus + +import java.nio.file.Path +import java.nio.file.Paths + +import nextflow.processor.TaskConfig +import nextflow.processor.TaskRun +import spock.lang.Specification +/** + * @author Emilio Palumbo + */ +class TesTaskHandlerTest extends Specification { + + def 'should set resources' () { + + given: + def executor = Mock(TesExecutor) + def task = Mock(TaskRun) { + getInputFilesMap() >> [:] + getOutputFilesNames() >> [] + getOutputsByType(_) >> [:] + } + task.getName() >> 'tes-task' + task.getWorkDir() >> Paths.get(".") + task.getConfig() >> new TaskConfig(memory: '2GB', cpus: 4, disk: '10GB') + task.getContainer() >> 'foo' + def handler = new TesTaskHandler(task, executor) + + + when: + def t = handler.newTesTask() + + then: + t.getResources().cpuCores == 4 + t.getResources().ramGb == 2 + t.getResources().diskGb == 10 + t.getResources().preemptible == null + t.getResources().zones == null + + } + + def 'should submit job' () { + + given: + def executor = Mock(TesExecutor) + def task = Mock(TaskRun) + def bashBuilder = Mock(TesBashBuilder) + def client = Mock(TaskServiceApi) + def handler = Spy(TesTaskHandler) + handler.@client = client + handler.@executor = executor + handler.task = task + + def req = Mock(TesTask) + def resp = Mock(TesCreateTaskResponse) + + when: + handler.submit() + then: + 1 * executor.getRemoteBinDir() >> Path.of("/work/bin") + 1 * handler.newTesBashBuilder(task, "/work/bin") >> bashBuilder + 1 * bashBuilder.build() >> null + 1 * handler.newTesTask() >> req + 1 * client.createTask(req) >> resp + 1 * resp.getId() >> '12345' + + handler.status == TaskStatus.SUBMITTED + handler.requestId == '12345' + } + +} diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..777230e --- /dev/null +++ b/settings.gradle @@ -0,0 +1,26 @@ +/* + * Copyright 2021, Seqera Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + // required to download the toolchain (jdk) from a remote repository + // https://github.com/gradle/foojay-toolchains + // https://docs.gradle.org/current/userguide/toolchains.html#sub:download_repositories + id("org.gradle.toolchains.foojay-resolver-convention") version "0.7.0" +} + +rootProject.name = 'nf-ga4gh' +include('plugins') +include('plugins:nf-ga4gh')