diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..4b65ef4 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,140 @@ +name: Standard Tests +on: [push, pull_request, workflow_dispatch] #workflow_dispatch works only if its active in the main branch +jobs: + Unit-Test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Setting up JDK 12 + uses: actions/setup-java@v2 + with: + java-version: '12' + distribution: 'adopt' #using a specific distribution of jdk12 (AdoptOpenJDK) + + - name: Setting up Gradle + uses: gradle/gradle-build-action@v2 + + - name: Setting up Gradle Wrapper + run: "gradle wrapper" + + - name: Validating Wrapper + uses: gradle/wrapper-validation-action@v1 + + - name: Setting up the SQLite database and Make + run: | + sqlite3 -version + make -version + make clean-all + make sqlite-db-build + make sqlite-db-populate-dev-dataset + + - name: Running Tests + run: "./gradlew test --tests=org.*" + + - name: After Success Submitting Code Coverage + run: | #jacocoTestReport is for testing code coverage, submits the last report to the link + ./gradlew jacocoTestReport + bash <(curl -s https://codecov.io/bash) + + Docker-Integration-Test: + needs: Unit-Test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Setting up Gradle + uses: gradle/gradle-build-action@v2 + + - name: Setting Up Docker Buildx #used for caching image layers, improves performance + id: buildx + uses: docker/setup-buildx-action@v1 + + - name: Login to Docker Hub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKER_HUB_USERNAME }} + password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} + + - name: Get and Set Version + run: | + source ci/set-docker-image-version.sh + echo "version=${DOCKER_IMG_VER}" >> $GITHUB_ENV + + - name: Build and push + uses: docker/build-push-action@v2 + with: + context: . + builder: ${{ steps.buildx.outputs.name }} + file: ./Dockerfile + push: true + tags: ga4gh/ga4gh-starter-kit-drs:test + build-args: VERSION=${{ env.version }} + cache-from: type=gha #GitHub Actions Cache Exporter + cache-to: type=gha,mode=max + + - name: Running Tests + run: | + docker run --rm -d --name starter-kit-drs-test-default -p 4500:4500 -p 4501:4501 ga4gh/ga4gh-starter-kit-drs:test + docker run --rm -d --name starter-kit-drs-test-custom -p 7000:7000 -p 7001:7001 ga4gh/ga4gh-starter-kit-drs:test --config ./src/test/resources/config/test-config-01.yml + + - name: Gradle Integration Test + run: ./gradlew test --tests=integration.* + + Docker-Release: + needs: Docker-Integration-Test + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' && github.event_name == 'push' #Only runs if pushing to main + steps: + - uses: actions/checkout@v2 + + - name: Setting Up Docker Buildx #used for caching image layers, improves performance + id: buildx + uses: docker/setup-buildx-action@v1 + + - name: Login to Docker Hub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKER_HUB_USERNAME }} + password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} + + - name: Get and Set Version + run: | + source ci/set-docker-image-version.sh + echo "version=${DOCKER_IMG_VER}" >> $GITHUB_ENV + - name: Build and push + uses: docker/build-push-action@v2 + with: + context: . + builder: ${{ steps.buildx.outputs.name }} + file: ./Dockerfile + push: true + tags: ga4gh/ga4gh-starter-kit-drs:${{ env.version }} #Using the correct version as the tag + build-args: VERSION=${{ env.version }} + cache-from: type=gha #GitHub Actions Cache Exporter + cache-to: type=gha,mode=max + + build: + name: Create GitHub Release + needs: Docker-Integration-Test + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' && github.event_name == 'push' #Only runs if pushing to main + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Get and Set Version + run: | + source ci/set-docker-image-version.sh + echo "version=${DOCKER_IMG_VER}" >> $GITHUB_ENV + + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: v${{ env.version }} #will be v0.2.2 for example + release_name: GA4GH Starter Kit - DRS v${{ env.version }} + draft: false + prerelease: true diff --git a/.gitignore b/.gitignore index 47d904d..986f050 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,6 @@ bin *.db gradle.properties + +# Mac OS files +**/.DS_Store diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index b45c970..0000000 --- a/.travis.yml +++ /dev/null @@ -1,22 +0,0 @@ -dist: xenial -addons: - apt: - sources: - - travis-ci/sqlite3 - packages: - - sqlite3 - update: true -git: - depth: false -language: java -jdk: oraclejdk12 -before_script: - - sqlite3 -version - - make -version - - make clean-all - - make sqlite-db-build - - make sqlite-db-populate-dev-dataset -script: "./gradlew test" -after_success: - - "./gradlew jacocoTestReport" - - bash <(curl -s https://codecov.io/bash) diff --git a/Dockerfile b/Dockerfile index 5f6e5db..f42690e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,15 +2,15 @@ # BUILDER CONTAINER ################################################## -FROM openjdk:11.0.10 as builder +FROM openjdk:11.0.12 as builder USER root WORKDIR /usr/src/dependencies # INSTALL MAKE -RUN apt update -RUN apt install build-essential -y +RUN apt update \ + && apt install build-essential -y # INSTALL SQLITE3 RUN wget https://www.sqlite.org/2021/sqlite-autoconf-3340100.tar.gz \ @@ -22,11 +22,26 @@ RUN wget https://www.sqlite.org/2021/sqlite-autoconf-3340100.tar.gz \ # USER 'make' and 'sqlite3' to create the dev database COPY Makefile Makefile -COPY settings.gradle settings.gradle -COPY build.gradle build.gradle COPY database/sqlite database/sqlite RUN make sqlite-db-refresh +################################################## +# GRADLE CONTAINER +################################################## + +FROM gradle:7.3.3-jdk11 as gradleimage + +WORKDIR /home/gradle/source + +COPY build.gradle build.gradle +COPY gradlew gradlew +COPY settings.gradle settings.gradle +COPY src src + +RUN gradle wrapper + +RUN ./gradlew bootJar + ################################################## # FINAL CONTAINER ################################################## @@ -40,7 +55,7 @@ ARG VERSION WORKDIR /usr/src/app # copy jar, dev db, and dev resource files -COPY build/libs/ga4gh-starter-kit-drs-${VERSION}.jar ga4gh-starter-kit-drs.jar +COPY --from=gradleimage /home/gradle/source/build/libs/ga4gh-starter-kit-drs-${VERSION}.jar ga4gh-starter-kit-drs.jar COPY --from=builder /usr/src/dependencies/ga4gh-starter-kit.dev.db ga4gh-starter-kit.dev.db COPY src/test/resources/ src/test/resources/ diff --git a/README.md b/README.md index 7c8c547..8db5743 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg?style=flat-square)](https://opensource.org/licenses/Apache-2.0) [![Java 11+](https://img.shields.io/badge/java-11+-blue.svg?style=flat-square)](https://www.java.com) -[![Gradle 6.1+](https://img.shields.io/badge/gradle-6.1+-blue.svg?style=flat-square)](https://gradle.org/) -[![Travis (.org) branch](https://img.shields.io/travis/ga4gh/ga4gh-starter-kit-drs/master.svg?style=flat-square)](https://travis-ci.org/ga4gh/ga4gh-starter-kit-drs) -![Codecov](https://img.shields.io/codecov/c/github/ga4gh/ga4gh-starter-kit-drs?style=flat-square) +[![Gradle 7.3.2+](https://img.shields.io/badge/gradle-7.3.2+-blue.svg?style=flat-square)](https://gradle.org/) +[![GitHub Actions](https://img.shields.io/github/workflow/status/ga4gh/ga4gh-starter-kit-drs/Standard%20Tests/main)](https://github.com/ga4gh/ga4gh-starter-kit-drs/actions) +[![Codecov](https://img.shields.io/codecov/c/github/ga4gh/ga4gh-starter-kit-drs?style=flat-square)](https://app.codecov.io/gh/ga4gh/ga4gh-starter-kit-drs) # GA4GH Starter Kit DRS @@ -35,7 +35,7 @@ docker run -p 4500:4500 ga4gh/ga4gh-starter-kit-drs:latest java -jar ga4gh-start The service can also be installed locally in cases where docker deployments are not possible, or for development of the codebase. Native installations require: * Java 11+ -* Gradle 6.1.1+ +* Gradle 7.3.2+ * SQLite (for creating the dev database) First, clone the repository from Github: @@ -186,5 +186,10 @@ Multiple datasets are currently contained in this repo for development and testi ## Changelog +### v0.3.0 +* DRS object batch requests +* Passport support - Passport mediated auth to DRS objects (using Starter Kit implementation of Passports) +* Auth info - Discover Passport broker(s) and visa(s) for requested controlled access DRS Objects (single object and bulk request) + ### v0.2.2 * patched log4j dependencies to v2.16.0 to avoid [Log4j Vulnerability](https://www.cisa.gov/uscert/apache-log4j-vulnerability-guidance) \ No newline at end of file diff --git a/build.gradle b/build.gradle index 50c0c02..108e01d 100644 --- a/build.gradle +++ b/build.gradle @@ -36,7 +36,7 @@ configurations.all { archivesBaseName = 'ga4gh-starter-kit-drs' group 'org.ga4gh' -version '0.2.2' +version '0.3.0' repositories { // Use jcenter for resolving dependencies. @@ -46,6 +46,9 @@ repositories { } dependencies { + compileOnly 'org.projectlombok:lombok:1.18.22' + annotationProcessor 'org.projectlombok:lombok:1.18.22' + implementation 'com.google.guava:guava:28.1-jre' implementation 'org.springframework.boot:spring-boot-starter-web:2.5.0' implementation 'commons-cli:commons-cli:1.4' @@ -62,8 +65,12 @@ dependencies { implementation 'javax.xml.bind:jaxb-api:2.2.8' implementation 'org.springdoc:springdoc-openapi-ui:1.2.33' implementation 'org.xerial:sqlite-jdbc:3.8.11.2' + implementation 'com.auth0:java-jwt:3.19.1' implementation 'org.ga4gh:ga4gh-starter-kit-common:0.5.6' + testCompileOnly 'org.projectlombok:lombok:1.18.22' + testAnnotationProcessor 'org.projectlombok:lombok:1.18.22' + testImplementation 'org.testng:testng:7.0.0' testImplementation 'org.springframework.boot:spring-boot-starter-test:2.5.0' testImplementation 'org.springframework.security:spring-security-test' diff --git a/ci/set-docker-image-version.sh b/ci/set-docker-image-version.sh new file mode 100755 index 0000000..9b0c324 --- /dev/null +++ b/ci/set-docker-image-version.sh @@ -0,0 +1 @@ +export DOCKER_IMG_VER=`cat build.gradle | grep "^version" | cut -f 2 -d ' ' | sed "s/'//g"` diff --git a/database/sqlite/add-dev-dataset.sql b/database/sqlite/add-dev-dataset.sql index 1110eb7..b4239ce 100644 --- a/database/sqlite/add-dev-dataset.sql +++ b/database/sqlite/add-dev-dataset.sql @@ -538,3 +538,113 @@ INSERT INTO drs_object_bundle VALUES ("355a74bd-6571-4d4a-8602-a9989936717f", "8f40acc0-0c54-45c5-8c85-a6f5fb32a1a7"), ("355a74bd-6571-4d4a-8602-a9989936717f", "41898242-62a9-4129-9a2c-5a4e8f5f0afb"), ("355a74bd-6571-4d4a-8602-a9989936717f", "6b994f18-6189-4233-bb83-139686490d68"); + +/* MINI DATASET FOR PASSPORTS AUTH */ + +INSERT INTO passport_broker VALUES ( + 'https://ga4gh.org/', + 'insecureSecretPleaseOverride' +); + +INSERT INTO passport_visa VALUES +( + 0, + 'StarterKitDatasetsControlledAccessGrants', + 'https://datasets.starterkit.ga4gh.org/', + '87A3B5D68FD88197254D9889B4AAB', + 'https://ga4gh.org/' +); + +INSERT INTO drs_object ( + id, + description, + created_time, + mime_type, + name, + size, + updated_time, + version, + is_bundle +) VALUES + ( + "e26f8428-1a5d-4465-b389-10998ba7b894", + "PASSPORT AUTH - Phenopackets, Cao Patient 1", + "2022-05-16 20:00:00.000", + "application/json", + "passport.phenopackets.cao.1", + 4257, + "2022-05-16 20:00:00.000", + "1.0.0", + 0 + ), + ( + "996ead07-8dfc-4c5d-b319-5b59bebb507e", + "PASSPORT AUTH - Phenopackets, Cao Patient 2", + "2022-05-16 20:00:00.000", + "application/json", + "passport.phenopackets.cao.2", + 7401, + "2022-05-16 20:00:00.000", + "1.0.0", + 0 + ), + ( + "81e8b499-1054-4846-8df4-d079ea32a44f", + "PASSPORT AUTH - Phenopackets, Cao Patient 3", + "2022-05-16 20:00:00.000", + "application/json", + "passport.phenopackets.cao.3", + 4251, + "2022-05-16 20:00:00.000", + "1.0.0", + 0 + ), + ( + "672d0425-137c-437f-ab12-29a3cce1ffc0", + "PASSPORT AUTH - Phenopackets, Cao Patient 4", + "2022-05-16 20:00:00.000", + "application/json", + "passport.phenopackets.cao.4", + 9264, + "2022-05-16 20:00:00.000", + "1.0.0", + 0 + ); + +INSERT INTO drs_object_alias VALUES + ("e26f8428-1a5d-4465-b389-10998ba7b894", "Passport-Cao-FBN1-1"), + ("996ead07-8dfc-4c5d-b319-5b59bebb507e", "Passport-Cao-FBN1-2"), + ("81e8b499-1054-4846-8df4-d079ea32a44f", "Passport-Cao-FBN1-3"), + ("672d0425-137c-437f-ab12-29a3cce1ffc0", "Passport-Cao-TGFBR2-4"); + +INSERT INTO drs_object_checksum (drs_object_id, checksum, type) VALUES + ("e26f8428-1a5d-4465-b389-10998ba7b894", "f81ea43c74824cc72c77a39a92bf7b71", "md5"), + ("e26f8428-1a5d-4465-b389-10998ba7b894", "34880a6b8aa517a6999da912614753ffb0a837a8", "sha1"), + ("e26f8428-1a5d-4465-b389-10998ba7b894", "ec44e2ad7ec84c7c42ba57b205e67c7c7416ae1932029d8364cc053cef7abe58", "sha256"), + ("996ead07-8dfc-4c5d-b319-5b59bebb507e", "1cbab050aa20410dc14ce6906f0312fa", "md5"), + ("996ead07-8dfc-4c5d-b319-5b59bebb507e", "3f2f2133054faf71ca9d678fa1fd8918a521faec", "sha1"), + ("996ead07-8dfc-4c5d-b319-5b59bebb507e", "2709878797b4e8c6a7db824fa596f42885551cef730d1408b2b620c9eee43089", "sha256"), + ("81e8b499-1054-4846-8df4-d079ea32a44f", "45b61bbe53b13463cd602081613ad855", "md5"), + ("81e8b499-1054-4846-8df4-d079ea32a44f", "d3192e17ffd97f7255ffbe2c6f9b447568107612", "sha1"), + ("81e8b499-1054-4846-8df4-d079ea32a44f", "f60583aad0e25fc4805668663bcc3bef271f4f93ee98be4f57f6e8c3e26d9dba", "sha256"), + ("672d0425-137c-437f-ab12-29a3cce1ffc0", "7d3b0a967215523c29de61baff26bfa2", "md5"), + ("672d0425-137c-437f-ab12-29a3cce1ffc0", "ae6c744487ddf785976893d1bdce2f81c017921e", "sha1"), + ("672d0425-137c-437f-ab12-29a3cce1ffc0", "69dd919e14d162ebd0132a395cf1967346fd203210428287c354b11eebdda8b3", "sha256"); + +INSERT INTO file_access_object (drs_object_id, path) VALUES + ("e26f8428-1a5d-4465-b389-10998ba7b894", "./src/test/resources/data/phenopackets/Cao-2018-FBN1-Patient_1.json"), + ("996ead07-8dfc-4c5d-b319-5b59bebb507e", "./src/test/resources/data/phenopackets/Cao-2018-FBN1-Patient_2.json"), + ("81e8b499-1054-4846-8df4-d079ea32a44f", "./src/test/resources/data/phenopackets/Cao-2018-FBN1-Patient_3.json"), + ("672d0425-137c-437f-ab12-29a3cce1ffc0", "./src/test/resources/data/phenopackets/Cao-2018-TGFBR2-Patient_4.json"); + +INSERT INTO aws_s3_access_object (drs_object_id, region, bucket, key) VALUES + ("e26f8428-1a5d-4465-b389-10998ba7b894", "us-east-2", "ga4gh-demo-data", "/phenopackets/Cao-2018-FBN1-Patient_1.json"), + ("996ead07-8dfc-4c5d-b319-5b59bebb507e", "us-east-2", "ga4gh-demo-data", "/phenopackets/Cao-2018-FBN1-Patient_2.json"), + ("81e8b499-1054-4846-8df4-d079ea32a44f", "us-east-2", "ga4gh-demo-data", "/phenopackets/Cao-2018-FBN1-Patient_3.json"), + ("672d0425-137c-437f-ab12-29a3cce1ffc0", "us-east-2", "ga4gh-demo-data", "/phenopackets/Cao-2018-TGFBR2-Patient_4.json"); + +INSERT INTO drs_object_visa VALUES + ("e26f8428-1a5d-4465-b389-10998ba7b894", 0), + ("996ead07-8dfc-4c5d-b319-5b59bebb507e", 0), + ("81e8b499-1054-4846-8df4-d079ea32a44f", 0), + ("672d0425-137c-437f-ab12-29a3cce1ffc0", 0); \ No newline at end of file diff --git a/database/sqlite/create-tables.sql b/database/sqlite/create-tables.sql index 00f4d13..ac7a31b 100644 --- a/database/sqlite/create-tables.sql +++ b/database/sqlite/create-tables.sql @@ -48,3 +48,24 @@ CREATE TABLE drs_object_bundle ( FOREIGN KEY(parent_id) REFERENCES drs_object(id), FOREIGN KEY(child_id) REFERENCES drs_object(id) ); + +CREATE TABLE passport_broker ( + url TEXT PRIMARY KEY, + secret TEXT +); + +CREATE TABLE passport_visa ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT, + issuer TEXT, + secret TEXT, + passport_broker_url TEXT, + FOREIGN KEY (passport_broker_url) REFERENCES passport_broker(url) +); + +CREATE TABLE drs_object_visa ( + drs_object_id TEXT, + visa_id TEXT, + FOREIGN KEY(drs_object_id) REFERENCES drs_object(id), + FOREIGN KEY(visa_id) REFERENCES passport_visa(id) +); diff --git a/src/main/java/org/ga4gh/starterkit/drs/app/DrsServerSpringConfig.java b/src/main/java/org/ga4gh/starterkit/drs/app/DrsServerSpringConfig.java index 48f54d9..bcc7bc8 100644 --- a/src/main/java/org/ga4gh/starterkit/drs/app/DrsServerSpringConfig.java +++ b/src/main/java/org/ga4gh/starterkit/drs/app/DrsServerSpringConfig.java @@ -22,9 +22,13 @@ import org.ga4gh.starterkit.drs.model.DrsObject; import org.ga4gh.starterkit.drs.model.DrsServiceInfo; import org.ga4gh.starterkit.drs.model.FileAccessObject; +import org.ga4gh.starterkit.drs.model.PassportBroker; +import org.ga4gh.starterkit.drs.model.PassportVisa; import org.ga4gh.starterkit.drs.utils.cache.AccessCache; import org.ga4gh.starterkit.drs.utils.hibernate.DrsHibernateUtil; +import org.ga4gh.starterkit.drs.utils.passport.UserPassportMapVerifier; import org.ga4gh.starterkit.drs.utils.requesthandler.AccessRequestHandler; +import org.ga4gh.starterkit.drs.utils.requesthandler.AuthInfoRequestHandler; import org.ga4gh.starterkit.drs.utils.requesthandler.FileStreamRequestHandler; import org.ga4gh.starterkit.drs.utils.requesthandler.ObjectRequestHandler; import org.springframework.beans.factory.annotation.Autowired; @@ -232,6 +236,8 @@ public List>> getAnnotat annotatedClasses.add(Checksum.class); annotatedClasses.add(FileAccessObject.class); annotatedClasses.add(AwsS3AccessObject.class); + annotatedClasses.add(PassportBroker.class); + annotatedClasses.add(PassportVisa.class); return annotatedClasses; } @@ -261,7 +267,7 @@ public DrsHibernateUtil getDrsHibernateUtil( * @return drs object request handler */ @Bean - @RequestScope + @Scope(DrsServerConstants.PROTOTYPE) public ObjectRequestHandler objectRequestHandler() { return new ObjectRequestHandler(); } @@ -277,6 +283,12 @@ public AccessRequestHandler accessRequestHandler() { return new AccessRequestHandler(); } + @Bean + @RequestScope + public AuthInfoRequestHandler authInfoRequestHandler() { + return new AuthInfoRequestHandler(); + } + /** * Get new request handler facilitating streaming of a local file over http(s) * @return streaming request handler @@ -299,4 +311,9 @@ public FileStreamRequestHandler fileStreamRequestHandler() { public AccessCache accessCache() { return new AccessCache(); } + + @Bean + public UserPassportMapVerifier userPAssportMapVerifier() { + return new UserPassportMapVerifier(); + } } diff --git a/src/main/java/org/ga4gh/starterkit/drs/constant/DrsServiceInfoDefaults.java b/src/main/java/org/ga4gh/starterkit/drs/constant/DrsServiceInfoDefaults.java index a1af528..ab906ae 100644 --- a/src/main/java/org/ga4gh/starterkit/drs/constant/DrsServiceInfoDefaults.java +++ b/src/main/java/org/ga4gh/starterkit/drs/constant/DrsServiceInfoDefaults.java @@ -54,7 +54,7 @@ public class DrsServiceInfoDefaults { /** * Default service version */ - public static final String VERSION = "0.2.2"; + public static final String VERSION = "0.3.0"; /** * Default service organization name diff --git a/src/main/java/org/ga4gh/starterkit/drs/controller/DrsAdmin.java b/src/main/java/org/ga4gh/starterkit/drs/controller/DrsAdmin.java index 4888e99..25801dd 100644 --- a/src/main/java/org/ga4gh/starterkit/drs/controller/DrsAdmin.java +++ b/src/main/java/org/ga4gh/starterkit/drs/controller/DrsAdmin.java @@ -29,7 +29,7 @@ * entities */ @RestController -@RequestMapping(ADMIN_DRS_API_V1 + "/objects") +@RequestMapping(ADMIN_DRS_API_V1 + "/objects") // /admin/ga4gh/drs/v1 public class DrsAdmin { @Autowired @@ -177,5 +177,6 @@ private void breakInterminableFetch(DrsObject drsObject) { drsObject.setAwsS3AccessObjects(null); drsObject.setDrsObjectChildren(null); drsObject.setDrsObjectParents(null); + drsObject.setPassportVisas(null); } } diff --git a/src/main/java/org/ga4gh/starterkit/drs/controller/Objects.java b/src/main/java/org/ga4gh/starterkit/drs/controller/Objects.java index e53378f..96e8a59 100644 --- a/src/main/java/org/ga4gh/starterkit/drs/controller/Objects.java +++ b/src/main/java/org/ga4gh/starterkit/drs/controller/Objects.java @@ -2,18 +2,38 @@ import static org.ga4gh.starterkit.drs.constant.DrsApiConstants.DRS_API_V1; +import java.util.ArrayList; +import java.util.List; +import org.ga4gh.starterkit.common.exception.CustomException; import org.ga4gh.starterkit.common.util.logging.LoggingUtil; import org.ga4gh.starterkit.drs.model.AccessURL; +import org.ga4gh.starterkit.drs.model.AuthInfo; +import org.ga4gh.starterkit.drs.model.BulkAccessRequest; +import org.ga4gh.starterkit.drs.model.BulkAuthInfoRequest; +import org.ga4gh.starterkit.drs.model.BulkAuthInfoResponse; +import org.ga4gh.starterkit.drs.model.BulkObjectAccessId; +import org.ga4gh.starterkit.drs.model.BulkRequest; +import org.ga4gh.starterkit.drs.model.BulkResponse; import org.ga4gh.starterkit.drs.model.DrsObject; +import org.ga4gh.starterkit.drs.model.PostSingleObjectRequestBody; import org.ga4gh.starterkit.drs.utils.SerializeView; +import org.ga4gh.starterkit.drs.utils.passport.UserPassportMap; +import org.ga4gh.starterkit.drs.utils.passport.UserPassportMapVerifier; import org.ga4gh.starterkit.drs.utils.requesthandler.AccessRequestHandler; +import org.ga4gh.starterkit.drs.utils.requesthandler.AuthInfoRequestHandler; import org.ga4gh.starterkit.drs.utils.requesthandler.ObjectRequestHandler; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseStatus; import javax.annotation.Resource; import com.fasterxml.jackson.annotation.JsonView; @@ -22,7 +42,7 @@ */ @RestController @RequestMapping(DRS_API_V1 + "/objects") -public class Objects { +public class Objects implements ApplicationContextAware { @Resource(name = "objectRequestHandler") private ObjectRequestHandler objectRequestHandler; @@ -30,9 +50,17 @@ public class Objects { @Resource(name = "accessRequestHandler") private AccessRequestHandler accessRequestHandler; + @Resource(name = "authInfoRequestHandler") + private AuthInfoRequestHandler authInfoRequestHandler; + @Autowired private LoggingUtil loggingUtil; + @Autowired + private UserPassportMapVerifier passportVerifier; + + private ApplicationContext context; + // Standard endpoints /** @@ -48,7 +76,29 @@ public DrsObject getObjectById( @RequestParam(name = "expand", required = false) boolean expand ) { loggingUtil.debug("Public API request: DrsObject with id '" + objectId + "', expand=" + expand); - return objectRequestHandler.prepare(objectId, expand).handleRequest(); + return objectRequestHandler.prepare(objectId, expand, null).handleRequest(); + } + + @PostMapping(path = "/{object_id:.+}") + @JsonView(SerializeView.Public.class) + public DrsObject getObjectByIdViaPost( + @PathVariable(name = "object_id") String objectId, + @RequestBody PostSingleObjectRequestBody requestBody + ) { + UserPassportMap userPassportMap = null; + if (requestBody.getPassports() != null) { + userPassportMap = new UserPassportMap(requestBody.getPassports()); + passportVerifier.verifyAll(userPassportMap); + } + return objectRequestHandler.prepare(objectId, requestBody.isExpand(), userPassportMap).handleRequest(); + } + + @RequestMapping(value = "/{object_id:.+}", method = RequestMethod.OPTIONS) + @JsonView(SerializeView.Public.class) + public AuthInfo singleObjectAuthInfo( + @PathVariable(name = "object_id") String objectId + ) { + return authInfoRequestHandler.prepare(objectId).handleRequest(); } /** @@ -65,4 +115,93 @@ public AccessURL getAccessURLById( loggingUtil.debug("Public API request: AccessURL for DRS id '" + objectId + "', access id '" + accessId + "'"); return accessRequestHandler.prepare(objectId, accessId).handleRequest(); } + + @PostMapping + @JsonView(SerializeView.Public.class) + public BulkResponse getBulkObjects( + @RequestBody BulkRequest bulkRequest + ) { + // parse user passport + UserPassportMap userPassportMap = null; + if (bulkRequest.getPassports() != null) { + userPassportMap = new UserPassportMap(bulkRequest.getPassports()); + passportVerifier.verifyAll(userPassportMap); + } + + BulkResponse bulkResponse = new BulkResponse(); + int requested = 0; + int resolved = 0; + int unresolved = 0; + + for (String drsObjectId : bulkRequest.getSelection()) { + requested++; + try { + ObjectRequestHandler handler = context.getBean(ObjectRequestHandler.class); + DrsObject drsObject = handler.prepare(drsObjectId, false, userPassportMap).handleRequest(); + bulkResponse.getResolvedDrsObject().add(drsObject); + resolved++; + } catch (CustomException ex) { + int httpStatus = ex.getClass().getAnnotation(ResponseStatus.class).value().value(); + bulkResponse.getUnresolvedDrsObject().put(drsObjectId, httpStatus); + unresolved++; + } + } + bulkResponse.getSummary().setRequested(requested); + bulkResponse.getSummary().setResolved(resolved); + bulkResponse.getSummary().setUnresolved(unresolved); + + return bulkResponse; + } + + @RequestMapping(method = RequestMethod.OPTIONS) + @JsonView(SerializeView.Public.class) + public BulkAuthInfoResponse getBulkAuthInfo( + @RequestBody BulkAuthInfoRequest request + ) { + BulkAuthInfoResponse response = new BulkAuthInfoResponse(); + int requested = 0; + int resolved = 0; + int unresolved = 0; + + for (String drsObjectId : request.getSelection()) { + requested++; + try { + AuthInfoRequestHandler handler = context.getBean(AuthInfoRequestHandler.class); + AuthInfo authInfo = handler.prepare(drsObjectId).handleRequest(); + response.getResolvedDrsObjectAuthInfo().put(drsObjectId, authInfo); + resolved++; + } catch (CustomException ex) { + int httpStatus = ex.getClass().getAnnotation(ResponseStatus.class).value().value(); + response.getUnresolvedDrsObjectAuthInfo().put(drsObjectId, httpStatus); + unresolved++; + } + } + response.getSummary().setRequested(requested); + response.getSummary().setResolved(resolved); + response.getSummary().setUnresolved(unresolved); + return response; + } + + @PostMapping(path = "/access") + @JsonView(SerializeView.Public.class) + public List getBulkAccessURLs( + @RequestBody BulkAccessRequest bulkAccessRequest + ) { + List accessURLs = new ArrayList<>(); + for (BulkObjectAccessId idPair : bulkAccessRequest.getSelection()) { + try { + AccessRequestHandler handler = context.getBean(AccessRequestHandler.class); + AccessURL accessURL = handler.prepare(idPair.getObjectId(), idPair.getAccessId()).handleRequest(); + accessURLs.add(accessURL); + } catch (CustomException ex) { + + } + } + + return accessURLs; + } + + public void setApplicationContext(ApplicationContext context) { + this.context = context; + } } diff --git a/src/main/java/org/ga4gh/starterkit/drs/exception/ForbiddenException.java b/src/main/java/org/ga4gh/starterkit/drs/exception/ForbiddenException.java new file mode 100644 index 0000000..ab6185b --- /dev/null +++ b/src/main/java/org/ga4gh/starterkit/drs/exception/ForbiddenException.java @@ -0,0 +1,23 @@ +package org.ga4gh.starterkit.drs.exception; + +import org.ga4gh.starterkit.common.exception.CustomException; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(value = HttpStatus.FORBIDDEN) +public class ForbiddenException extends CustomException { + + private static final long serialVersionUID = 1L; + + public ForbiddenException() { + super(); + } + + public ForbiddenException(String message) { + super(message); + } + + public ForbiddenException(Throwable cause) { + super(cause); + } +} diff --git a/src/main/java/org/ga4gh/starterkit/drs/exception/UnauthorizedException.java b/src/main/java/org/ga4gh/starterkit/drs/exception/UnauthorizedException.java new file mode 100644 index 0000000..6c62430 --- /dev/null +++ b/src/main/java/org/ga4gh/starterkit/drs/exception/UnauthorizedException.java @@ -0,0 +1,23 @@ +package org.ga4gh.starterkit.drs.exception; + +import org.ga4gh.starterkit.common.exception.CustomException; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(value = HttpStatus.UNAUTHORIZED) +public class UnauthorizedException extends CustomException { + + private static final long serialVersionUID = 1L; + + public UnauthorizedException() { + super(); + } + + public UnauthorizedException(String message) { + super(message); + } + + public UnauthorizedException(Throwable cause) { + super(cause); + } +} diff --git a/src/main/java/org/ga4gh/starterkit/drs/model/AuthInfo.java b/src/main/java/org/ga4gh/starterkit/drs/model/AuthInfo.java new file mode 100644 index 0000000..920bbe2 --- /dev/null +++ b/src/main/java/org/ga4gh/starterkit/drs/model/AuthInfo.java @@ -0,0 +1,33 @@ +package org.ga4gh.starterkit.drs.model; + +import java.util.ArrayList; +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonView; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; + +import org.ga4gh.starterkit.drs.utils.SerializeView; + +import lombok.Getter; +import lombok.Setter; + +// Authorization information for a single DRS object +@Setter +@Getter +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class AuthInfo { + + @JsonView(SerializeView.Always.class) + private List supportedTypes; + + @JsonView(SerializeView.Always.class) + private List passportAuthIssuers; + + public AuthInfo() { + supportedTypes = new ArrayList<>(); + passportAuthIssuers = new ArrayList<>(); + } +} diff --git a/src/main/java/org/ga4gh/starterkit/drs/model/AuthIssuer.java b/src/main/java/org/ga4gh/starterkit/drs/model/AuthIssuer.java new file mode 100644 index 0000000..139d452 --- /dev/null +++ b/src/main/java/org/ga4gh/starterkit/drs/model/AuthIssuer.java @@ -0,0 +1,27 @@ +package org.ga4gh.starterkit.drs.model; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonView; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; + +import org.ga4gh.starterkit.drs.utils.SerializeView; + +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class AuthIssuer { + + @JsonView(SerializeView.Always.class) + private String brokerUrl; + + @JsonView(SerializeView.Always.class) + private String visaName; + + @JsonView(SerializeView.Always.class) + private String visaIssuer; +} diff --git a/src/main/java/org/ga4gh/starterkit/drs/model/AuthType.java b/src/main/java/org/ga4gh/starterkit/drs/model/AuthType.java new file mode 100644 index 0000000..e8dcdae --- /dev/null +++ b/src/main/java/org/ga4gh/starterkit/drs/model/AuthType.java @@ -0,0 +1,8 @@ +package org.ga4gh.starterkit.drs.model; + +public enum AuthType { + None, + BasicAuth, + BearerAuth, + PassportAuth +} diff --git a/src/main/java/org/ga4gh/starterkit/drs/model/BulkAccessRequest.java b/src/main/java/org/ga4gh/starterkit/drs/model/BulkAccessRequest.java new file mode 100644 index 0000000..84c083d --- /dev/null +++ b/src/main/java/org/ga4gh/starterkit/drs/model/BulkAccessRequest.java @@ -0,0 +1,25 @@ +package org.ga4gh.starterkit.drs.model; + +import java.util.ArrayList; +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonView; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import org.ga4gh.starterkit.drs.utils.SerializeView; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +@JsonView(SerializeView.Public.class) +public class BulkAccessRequest { + private List selection; + + public BulkAccessRequest() { + selection = new ArrayList<>(); + } +} diff --git a/src/main/java/org/ga4gh/starterkit/drs/model/BulkAuthInfoRequest.java b/src/main/java/org/ga4gh/starterkit/drs/model/BulkAuthInfoRequest.java new file mode 100644 index 0000000..f576ffa --- /dev/null +++ b/src/main/java/org/ga4gh/starterkit/drs/model/BulkAuthInfoRequest.java @@ -0,0 +1,24 @@ +package org.ga4gh.starterkit.drs.model; + +import java.util.ArrayList; +import java.util.List; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonView; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import org.ga4gh.starterkit.drs.utils.SerializeView; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +@JsonView(SerializeView.Public.class) +public class BulkAuthInfoRequest { + private List selection; + + public BulkAuthInfoRequest() { + selection = new ArrayList<>(); + } +} diff --git a/src/main/java/org/ga4gh/starterkit/drs/model/BulkAuthInfoResponse.java b/src/main/java/org/ga4gh/starterkit/drs/model/BulkAuthInfoResponse.java new file mode 100644 index 0000000..fcba244 --- /dev/null +++ b/src/main/java/org/ga4gh/starterkit/drs/model/BulkAuthInfoResponse.java @@ -0,0 +1,29 @@ +package org.ga4gh.starterkit.drs.model; + +import java.util.HashMap; +import java.util.Map; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonView; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import org.ga4gh.starterkit.drs.utils.SerializeView; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +@JsonView(SerializeView.Public.class) +public class BulkAuthInfoResponse { + + private BulkSummary summary; + private Map resolvedDrsObjectAuthInfo; + private Map unresolvedDrsObjectAuthInfo; + + public BulkAuthInfoResponse() { + summary = new BulkSummary(); + resolvedDrsObjectAuthInfo = new HashMap<>(); + unresolvedDrsObjectAuthInfo = new HashMap<>(); + } +} diff --git a/src/main/java/org/ga4gh/starterkit/drs/model/BulkObjectAccessId.java b/src/main/java/org/ga4gh/starterkit/drs/model/BulkObjectAccessId.java new file mode 100644 index 0000000..ffdf077 --- /dev/null +++ b/src/main/java/org/ga4gh/starterkit/drs/model/BulkObjectAccessId.java @@ -0,0 +1,21 @@ +package org.ga4gh.starterkit.drs.model; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonView; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import org.ga4gh.starterkit.drs.utils.SerializeView; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Setter +@Getter +@NoArgsConstructor +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +@JsonView(SerializeView.Public.class) +public class BulkObjectAccessId { + private String objectId; + private String accessId; +} diff --git a/src/main/java/org/ga4gh/starterkit/drs/model/BulkRequest.java b/src/main/java/org/ga4gh/starterkit/drs/model/BulkRequest.java new file mode 100644 index 0000000..1fadca5 --- /dev/null +++ b/src/main/java/org/ga4gh/starterkit/drs/model/BulkRequest.java @@ -0,0 +1,27 @@ +package org.ga4gh.starterkit.drs.model; + +import java.util.ArrayList; +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonView; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import org.ga4gh.starterkit.drs.utils.SerializeView; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +@JsonView(SerializeView.Public.class) +public class BulkRequest { + private List selection; + private List passports; + + public BulkRequest() { + selection = new ArrayList<>(); + passports = new ArrayList<>(); + } +} diff --git a/src/main/java/org/ga4gh/starterkit/drs/model/BulkResponse.java b/src/main/java/org/ga4gh/starterkit/drs/model/BulkResponse.java new file mode 100644 index 0000000..35b11a0 --- /dev/null +++ b/src/main/java/org/ga4gh/starterkit/drs/model/BulkResponse.java @@ -0,0 +1,32 @@ +package org.ga4gh.starterkit.drs.model; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonView; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import org.ga4gh.starterkit.drs.utils.SerializeView; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +@JsonView(SerializeView.Public.class) +public class BulkResponse { + + private BulkSummary summary; + private List resolvedDrsObject; + private Map unresolvedDrsObject; + + public BulkResponse() { + summary = new BulkSummary(); + resolvedDrsObject = new ArrayList<>(); + unresolvedDrsObject = new HashMap<>(); + } +} diff --git a/src/main/java/org/ga4gh/starterkit/drs/model/BulkSummary.java b/src/main/java/org/ga4gh/starterkit/drs/model/BulkSummary.java new file mode 100644 index 0000000..7706682 --- /dev/null +++ b/src/main/java/org/ga4gh/starterkit/drs/model/BulkSummary.java @@ -0,0 +1,22 @@ +package org.ga4gh.starterkit.drs.model; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonView; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import org.ga4gh.starterkit.drs.utils.SerializeView; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Setter +@Getter +@NoArgsConstructor +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +@JsonView(SerializeView.Public.class) +public class BulkSummary { + private int requested; + private int resolved; + private int unresolved; +} diff --git a/src/main/java/org/ga4gh/starterkit/drs/model/DrsObject.java b/src/main/java/org/ga4gh/starterkit/drs/model/DrsObject.java index 252759b..3cb72eb 100644 --- a/src/main/java/org/ga4gh/starterkit/drs/model/DrsObject.java +++ b/src/main/java/org/ga4gh/starterkit/drs/model/DrsObject.java @@ -17,6 +17,10 @@ import org.hibernate.annotations.Cascade; import org.hibernate.annotations.CascadeType; import org.springframework.lang.NonNull; + +import lombok.Getter; +import lombok.Setter; + import java.net.URI; import java.time.LocalDateTime; import java.util.ArrayList; @@ -43,6 +47,8 @@ */ @Entity @Table(name = "drs_object") +@Setter +@Getter @JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) public class DrsObject implements HibernateEntity { @@ -176,6 +182,15 @@ public class DrsObject implements HibernateEntity { @JsonManagedReference private List awsS3AccessObjects; + @ManyToMany + @JoinTable( + name = "drs_object_visa", + joinColumns = {@JoinColumn(name = "drs_object_id")}, + inverseJoinColumns = {@JoinColumn(name = "visa_id")} + ) + @JsonView(SerializeView.Admin.class) + private List passportVisas; + /* Transient attributes produced from transforming database records. They are needed to conform to the DRS spec: selfURI, accessMethods, contents @@ -211,25 +226,7 @@ public DrsObject() { checksums = new ArrayList<>(); fileAccessObjects = new ArrayList<>(); awsS3AccessObjects = new ArrayList<>(); - } - - /* Constructors */ - - /** - * Instantiates a new DrsObject with preconfigured attributes - * @param id DRSObject identifier - * @param selfURI self URI - * @param checksums checksum object list - * @param createdTime timestamp of DRSObject creation - * @param size size of file in bytes - */ - public DrsObject(String id, URI selfURI, List checksums, LocalDateTime createdTime, Long size) { - super(); - this.setId(id); - this.setSelfURI(selfURI); - this.setChecksums(checksums); - this.setCreatedTime(createdTime); - this.setSize(size); + passportVisas = new ArrayList<>(); } /* Custom API methods */ @@ -244,287 +241,6 @@ public void loadRelations() { Hibernate.initialize(getDrsObjectParents()); Hibernate.initialize(getFileAccessObjects()); Hibernate.initialize(getAwsS3AccessObjects()); - } - - /* Setters and Getters */ - - /** - * Assign id - * @param id DRSObject identifier - */ - public void setId(String id) { - this.id = id; - } - - /** - * Retrieve id - * @return DRSObject identifier - */ - public String getId() { - return id; - } - - /** - * Assign description - * @param description DRSObject description - */ - public void setDescription(String description) { - this.description = description; - } - - /** - * Retrieve description - * @return DRSObject description - */ - public String getDescription() { - return description; - } - - /** - * Assign created time - * @param createdTime timestamp of object creation time - */ - public void setCreatedTime(LocalDateTime createdTime) { - this.createdTime = createdTime; - } - - /** - * Retrieve created time - * @return timestamp of object creation time - */ - public LocalDateTime getCreatedTime() { - return createdTime; - } - - /** - * Assign mimeType - * @param mimeType valid media type - */ - public void setMimeType(String mimeType) { - this.mimeType = mimeType; - } - - /** - * Retrieve mimeType - * @return valid media type - */ - public String getMimeType() { - return mimeType; - } - - /** - * Assign name - * @param name DRSObject name - */ - public void setName(String name) { - this.name = name; - } - - /** - * Retrieve name - * @return DRSObject name - */ - public String getName() { - return name; - } - - /** - * Assign size - * @param size DRSObject size - */ - public void setSize(Long size) { - this.size = size; - } - - /** - * Retrieve size - * @return DRSObject size - */ - public Long getSize() { - return size; - } - - /** - * Assign updatedTime - * @param updatedTime timestamp of object last updated time - */ - public void setUpdatedTime(LocalDateTime updatedTime) { - this.updatedTime = updatedTime; - } - - /** - * Retrieve updatedTime - * @return timestamp of object last updated time - */ - public LocalDateTime getUpdatedTime() { - return updatedTime; - } - - /** - * Assign version - * @param version DRSObject version - */ - public void setVersion(String version) { - this.version = version; - } - - /** - * Retrieve version - * @return DRSObject version - */ - public String getVersion() { - return version; - } - - /** - * Assign aliases - * @param aliases DRSObject aliases - */ - public void setAliases(List aliases) { - this.aliases = aliases; - } - - /** - * Retrieve aliases - * @return DRSObject aliases - */ - public List getAliases() { - return aliases; - } - - /** - * Assign checksums - * @param checksums checksum list - */ - public void setChecksums(List checksums) { - this.checksums = checksums; - } - - /** - * Retrieve checksums - * @return checksum list - */ - public List getChecksums() { - return checksums; - } - - public void setIsBundle(Boolean isBundle) { - this.isBundle = isBundle; - } - - public Boolean getIsBundle() { - return isBundle; - } - - /** - * Assign drsObjectChildren - * @param drsObjectChildren list of child DRS Objects - */ - public void setDrsObjectChildren(List drsObjectChildren) { - this.drsObjectChildren = drsObjectChildren; - } - - /** - * Retrieve drsObjectChildren - * @return list of child DRS Objects - */ - public List getDrsObjectChildren() { - return drsObjectChildren; - } - - /** - * Assign drsObjectParents - * @param drsObjectParents list of parent DRS Objects - */ - public void setDrsObjectParents(List drsObjectParents) { - this.drsObjectParents = drsObjectParents; - } - - /** - * Retrieve drsObjectParents - * @return list of parent DRS Objects - */ - public List getDrsObjectParents() { - return drsObjectParents; - } - - /** - * Assign fileAccessObjects - * @param fileAccessObjects list of file-based access objects - */ - public void setFileAccessObjects(List fileAccessObjects) { - this.fileAccessObjects = fileAccessObjects; - } - - /** - * Retrieve fileAccessObjects - * @return list of file-based access objects - */ - public List getFileAccessObjects() { - return fileAccessObjects; - } - - /** - * Assign awsS3AccessObjects - * @param awsS3AccessObjects list of s3-based access objects - */ - public void setAwsS3AccessObjects(List awsS3AccessObjects) { - this.awsS3AccessObjects = awsS3AccessObjects; - } - - /** - * Retrieve awsS3AccessObjects - * @return list of s3-based access objects - */ - public List getAwsS3AccessObjects() { - return awsS3AccessObjects; - } - - /** - * Assign selfURI - * @param selfURI self-referencing DRS URI - */ - public void setSelfURI(URI selfURI) { - this.selfURI = selfURI; - } - - /** - * Retrieve selfURI - * @return self-referencing DRS URI - */ - public URI getSelfURI() { - return selfURI; - } - - /** - * Assign access methods - * @param accessMethods list of all valid access methods - */ - public void setAccessMethods(List accessMethods) { - this.accessMethods = accessMethods; - } - - /** - * Retrieve access methods - * @return list of all valid access methods - */ - public List getAccessMethods() { - return accessMethods; - } - - /** - * Assign contents - * @param contents list of contents objects representing sub/child DRS Objects - */ - public void setContents(List contents) { - this.contents = contents; - } - - /** - * Retrieve contents - * @return list of contents objects representing child DRS Objects - */ - public List getContents() { - return contents; + Hibernate.initialize(getPassportVisas()); } } diff --git a/src/main/java/org/ga4gh/starterkit/drs/model/PassportBroker.java b/src/main/java/org/ga4gh/starterkit/drs/model/PassportBroker.java new file mode 100644 index 0000000..dd942f9 --- /dev/null +++ b/src/main/java/org/ga4gh/starterkit/drs/model/PassportBroker.java @@ -0,0 +1,68 @@ +package org.ga4gh.starterkit.drs.model; + +import java.util.ArrayList; +import java.util.List; + +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import javax.persistence.Table; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonView; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; + +import org.ga4gh.starterkit.common.hibernate.HibernateEntity; +import org.ga4gh.starterkit.drs.utils.SerializeView; +import org.hibernate.Hibernate; +import org.springframework.lang.NonNull; +import lombok.Getter; +import lombok.Setter; + +@Entity +@Table(name = "passport_broker") +@Setter +@Getter +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class PassportBroker implements HibernateEntity { + + @Id + @Column(name = "url", updatable = false, nullable = false) + @NonNull + @JsonView(SerializeView.Admin.class) + private String url; + + @Column(name = "secret") + @JsonView(SerializeView.Admin.class) + private String secret; + + @OneToMany( + mappedBy = "passportBroker", + fetch = FetchType.LAZY, + cascade = {CascadeType.ALL}, + orphanRemoval = true + ) + @JsonView(SerializeView.Admin.class) + private List passportVisas; + + public PassportBroker() { + passportVisas = new ArrayList<>(); + } + + public void setId(String url) { + this.url = url; + } + + public String getId() { + return url; + } + + public void loadRelations() { + Hibernate.initialize(getPassportVisas()); + } +} diff --git a/src/main/java/org/ga4gh/starterkit/drs/model/PassportVisa.java b/src/main/java/org/ga4gh/starterkit/drs/model/PassportVisa.java new file mode 100644 index 0000000..893c36c --- /dev/null +++ b/src/main/java/org/ga4gh/starterkit/drs/model/PassportVisa.java @@ -0,0 +1,80 @@ +package org.ga4gh.starterkit.drs.model; + +import java.util.ArrayList; +import java.util.List; + +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.JoinTable; +import javax.persistence.ManyToMany; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonView; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; + +import org.ga4gh.starterkit.common.hibernate.HibernateEntity; +import org.ga4gh.starterkit.drs.utils.SerializeView; +import org.hibernate.Hibernate; + +import lombok.Getter; +import lombok.NonNull; +import lombok.Setter; + +@Entity +@Table(name = "passport_visa") +@Setter +@Getter +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class PassportVisa implements HibernateEntity { + + @Id + @Column(name = "id", updatable = false, nullable = false) + @NonNull + @JsonView(SerializeView.Admin.class) + private Integer id; + + @Column(name = "name") + @JsonView(SerializeView.Admin.class) + private String name; + + @Column(name = "issuer") + @JsonView(SerializeView.Admin.class) + private String issuer; + + @Column(name = "secret") + @JsonView(SerializeView.Admin.class) + private String secret; + + @ManyToOne( + fetch = FetchType.EAGER, + cascade = {CascadeType.PERSIST, CascadeType.MERGE, + CascadeType.DETACH, CascadeType.REFRESH} + ) + @JoinColumn(name = "passport_broker_url") + private PassportBroker passportBroker; + + @ManyToMany + @JoinTable( + name = "drs_object_visa", + joinColumns = {@JoinColumn(name = "visa_id")}, + inverseJoinColumns = {@JoinColumn(name = "drs_object_id")} + ) + @JsonView(SerializeView.Admin.class) + private List drsObjects; + + public PassportVisa() { + drsObjects = new ArrayList<>(); + } + + public void loadRelations() { + Hibernate.initialize(getDrsObjects()); + } +} diff --git a/src/main/java/org/ga4gh/starterkit/drs/model/PostSingleObjectRequestBody.java b/src/main/java/org/ga4gh/starterkit/drs/model/PostSingleObjectRequestBody.java new file mode 100644 index 0000000..4e3ce09 --- /dev/null +++ b/src/main/java/org/ga4gh/starterkit/drs/model/PostSingleObjectRequestBody.java @@ -0,0 +1,26 @@ +package org.ga4gh.starterkit.drs.model; + +import java.util.ArrayList; +import java.util.List; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonView; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import org.ga4gh.starterkit.drs.utils.SerializeView; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +@JsonView(SerializeView.Public.class) +public class PostSingleObjectRequestBody { + private boolean expand; + private List passports; + + public PostSingleObjectRequestBody() { + expand = false; + passports = new ArrayList<>(); + } +} diff --git a/src/main/java/org/ga4gh/starterkit/drs/utils/hibernate/DrsHibernateUtil.java b/src/main/java/org/ga4gh/starterkit/drs/utils/hibernate/DrsHibernateUtil.java index 4b6e458..067d8b7 100644 --- a/src/main/java/org/ga4gh/starterkit/drs/utils/hibernate/DrsHibernateUtil.java +++ b/src/main/java/org/ga4gh/starterkit/drs/utils/hibernate/DrsHibernateUtil.java @@ -5,12 +5,17 @@ import javax.persistence.PersistenceException; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; + import org.ga4gh.starterkit.drs.model.DrsObject; +import org.ga4gh.starterkit.drs.model.PassportVisa; import org.ga4gh.starterkit.drs.utils.BundleRecursiveChecksumCalculator; import org.ga4gh.starterkit.common.hibernate.HibernateEntity; import org.ga4gh.starterkit.common.hibernate.HibernateUtil; import org.hibernate.HibernateException; import org.hibernate.Session; +import org.hibernate.query.Query; /** * Provides access to DRS entities/tables in the database, enabling access, creation, @@ -108,4 +113,29 @@ public > List getEntityList } return entities; } + + public PassportVisa findPassportVisa(String visaName, String visaIssuer) { + Session session = newTransaction(); + PassportVisa visa = null; + try { + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(PassportVisa.class); + Root root = cq.from(PassportVisa.class); + Predicate[] predicates = new Predicate[2]; + predicates[0] = cb.equal(root.get("name"), visaName); + predicates[1] = cb.equal(root.get("issuer"), visaIssuer); + cq.select(root).where(predicates); + Query query = session.createQuery(cq); + List results = query.getResultList(); + if (results.size() != 1) { + throw new Exception("no unique visa found"); + } + visa = results.get(0); + } catch (Exception ex) { + + } finally { + endTransaction(session); + } + return visa; + } } diff --git a/src/main/java/org/ga4gh/starterkit/drs/utils/passport/UserPassport.java b/src/main/java/org/ga4gh/starterkit/drs/utils/passport/UserPassport.java new file mode 100644 index 0000000..167025a --- /dev/null +++ b/src/main/java/org/ga4gh/starterkit/drs/utils/passport/UserPassport.java @@ -0,0 +1,19 @@ +package org.ga4gh.starterkit.drs.utils.passport; + +import java.util.HashMap; +import java.util.Map; + +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +public class UserPassport { + + private String passportJwt; + private Map visaJwtMap; + + public UserPassport() { + visaJwtMap = new HashMap<>(); + } +} diff --git a/src/main/java/org/ga4gh/starterkit/drs/utils/passport/UserPassportMap.java b/src/main/java/org/ga4gh/starterkit/drs/utils/passport/UserPassportMap.java new file mode 100644 index 0000000..e6b6d28 --- /dev/null +++ b/src/main/java/org/ga4gh/starterkit/drs/utils/passport/UserPassportMap.java @@ -0,0 +1,41 @@ +package org.ga4gh.starterkit.drs.utils.passport; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import com.auth0.jwt.JWT; +import com.auth0.jwt.interfaces.DecodedJWT; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +public class UserPassportMap { + + private Map map; + + public UserPassportMap(List passports) { + map = new HashMap<>(); + + for (String rawPassportJwt : passports) { + UserPassport userPassport = new UserPassport(); + userPassport.setPassportJwt(rawPassportJwt); + + DecodedJWT decodedPassportJwt = JWT.decode(rawPassportJwt); + String passportIss = decodedPassportJwt.getClaim("iss").asString(); + + String[] containedVisas = decodedPassportJwt.getClaim("contained_visas").asArray(String.class); + String[] rawVisaJwts = decodedPassportJwt.getClaim("ga4gh_passport_v1").asArray(String.class); + for (int i = 0; i < containedVisas.length; i++) { + String containedVisa = containedVisas[i]; + String rawVisaJwt = rawVisaJwts[i]; + userPassport.getVisaJwtMap().put(containedVisa, rawVisaJwt); + } + map.put(passportIss, userPassport); + } + } + + public void verifyAllJwts() { + + } +} diff --git a/src/main/java/org/ga4gh/starterkit/drs/utils/passport/UserPassportMapVerifier.java b/src/main/java/org/ga4gh/starterkit/drs/utils/passport/UserPassportMapVerifier.java new file mode 100644 index 0000000..ae70214 --- /dev/null +++ b/src/main/java/org/ga4gh/starterkit/drs/utils/passport/UserPassportMapVerifier.java @@ -0,0 +1,58 @@ +package org.ga4gh.starterkit.drs.utils.passport; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.JWTVerifier; +import com.auth0.jwt.algorithms.Algorithm; + +import org.ga4gh.starterkit.common.exception.BadRequestException; +import org.ga4gh.starterkit.drs.exception.UnauthorizedException; +import org.ga4gh.starterkit.drs.model.PassportBroker; +import org.ga4gh.starterkit.drs.model.PassportVisa; +import org.ga4gh.starterkit.drs.utils.hibernate.DrsHibernateUtil; +import org.springframework.beans.factory.annotation.Autowired; + +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +public class UserPassportMapVerifier { + + @Autowired + private DrsHibernateUtil hibernateUtil; + + public void verifyAll(UserPassportMap userPassportMap) { + try { + for (String passportIssKey : userPassportMap.getMap().keySet()) { + // verify the passport-level JWT + // first find the secret from the DB + // then use it to verify + UserPassport userPassport = userPassportMap.getMap().get(passportIssKey); + String rawPassportJwt = userPassport.getPassportJwt(); + PassportBroker passportBroker = hibernateUtil.readEntityObject(PassportBroker.class, passportIssKey, false); + if (passportBroker == null) { + throw new Exception("Passports from issuer: '" + passportIssKey + "' are not accepted here."); + } + String passportBrokerSecret = passportBroker.getSecret(); + JWTVerifier passportVerifier = JWT.require(Algorithm.HMAC256(passportBrokerSecret)).build(); + passportVerifier.verify(rawPassportJwt); + + for (String visaKey : userPassport.getVisaJwtMap().keySet()) { + String rawVisaJwt = userPassport.getVisaJwtMap().get(visaKey); + String visaName = visaKey.split("@")[0]; + String visaIssuer = visaKey.split("@")[1]; + PassportVisa registeredVisa = hibernateUtil.findPassportVisa(visaName, visaIssuer); + if (registeredVisa == null) { + throw new Exception("The Visa you provided: '" + visaKey + "' is not accepted here."); + } + String visaSecret = registeredVisa.getSecret(); + JWTVerifier visaVerifier = JWT.require(Algorithm.HMAC256(visaSecret)).build(); + visaVerifier.verify(rawVisaJwt); + } + } + } catch (Exception ex) { + String message = "Invalid Passport(s), message: " + ex.getMessage(); + throw new UnauthorizedException(message); + } + } +} diff --git a/src/main/java/org/ga4gh/starterkit/drs/utils/requesthandler/AuthInfoRequestHandler.java b/src/main/java/org/ga4gh/starterkit/drs/utils/requesthandler/AuthInfoRequestHandler.java new file mode 100644 index 0000000..867ffe8 --- /dev/null +++ b/src/main/java/org/ga4gh/starterkit/drs/utils/requesthandler/AuthInfoRequestHandler.java @@ -0,0 +1,58 @@ +package org.ga4gh.starterkit.drs.utils.requesthandler; + +import org.ga4gh.starterkit.common.exception.ResourceNotFoundException; +import org.ga4gh.starterkit.common.requesthandler.RequestHandler; +import org.ga4gh.starterkit.drs.model.AuthInfo; +import org.ga4gh.starterkit.drs.model.AuthIssuer; +import org.ga4gh.starterkit.drs.model.AuthType; +import org.ga4gh.starterkit.drs.model.DrsObject; +import org.ga4gh.starterkit.drs.model.PassportVisa; +import org.ga4gh.starterkit.drs.utils.hibernate.DrsHibernateUtil; +import org.springframework.beans.factory.annotation.Autowired; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Setter +@Getter +@NoArgsConstructor +public class AuthInfoRequestHandler implements RequestHandler { + + @Autowired + private DrsHibernateUtil hibernateUtil; + + private String objectId; + + public AuthInfoRequestHandler prepare(String objectId) { + this.objectId = objectId; + return this; + } + + public AuthInfo handleRequest() { + AuthInfo authInfo = new AuthInfo(); + + DrsObject drsObject = hibernateUtil.loadDrsObject(objectId, false); + if (drsObject == null) { + throw new ResourceNotFoundException("No DrsObject found by id: " + objectId); + } + + if (drsObject.getPassportVisas().size() == 0) { + authInfo.getSupportedTypes().add(AuthType.None); + } else { + authInfo.getSupportedTypes().add(AuthType.PassportAuth); + + for (PassportVisa visa : drsObject.getPassportVisas()) { + AuthIssuer issuer = new AuthIssuer(); + issuer.setBrokerUrl(visa.getPassportBroker().getId()); + issuer.setVisaName(visa.getName()); + issuer.setVisaIssuer(visa.getIssuer()); + authInfo.getPassportAuthIssuers().add(issuer); + } + } + System.out.println("what is auth info??"); + System.out.println(authInfo.getSupportedTypes().size()); + System.out.println(authInfo.getSupportedTypes().get(0)); + return authInfo; + } +} diff --git a/src/main/java/org/ga4gh/starterkit/drs/utils/requesthandler/ObjectRequestHandler.java b/src/main/java/org/ga4gh/starterkit/drs/utils/requesthandler/ObjectRequestHandler.java index 43d3b1e..b411d55 100644 --- a/src/main/java/org/ga4gh/starterkit/drs/utils/requesthandler/ObjectRequestHandler.java +++ b/src/main/java/org/ga4gh/starterkit/drs/utils/requesthandler/ObjectRequestHandler.java @@ -8,6 +8,8 @@ import org.ga4gh.starterkit.common.exception.ResourceNotFoundException; import org.ga4gh.starterkit.common.requesthandler.RequestHandler; import org.ga4gh.starterkit.drs.config.DrsServiceProps; +import org.ga4gh.starterkit.drs.exception.ForbiddenException; +import org.ga4gh.starterkit.drs.exception.UnauthorizedException; import org.ga4gh.starterkit.drs.model.AccessMethod; import org.ga4gh.starterkit.drs.model.AccessType; import org.ga4gh.starterkit.drs.model.AccessURL; @@ -15,9 +17,12 @@ import org.ga4gh.starterkit.drs.model.ContentsObject; import org.ga4gh.starterkit.drs.model.DrsObject; import org.ga4gh.starterkit.drs.model.FileAccessObject; +import org.ga4gh.starterkit.drs.model.PassportVisa; import org.ga4gh.starterkit.drs.utils.cache.AccessCache; import org.ga4gh.starterkit.drs.utils.cache.AccessCacheItem; import org.ga4gh.starterkit.drs.utils.hibernate.DrsHibernateUtil; +import org.ga4gh.starterkit.drs.utils.passport.UserPassport; +import org.ga4gh.starterkit.drs.utils.passport.UserPassportMap; import org.springframework.beans.factory.annotation.Autowired; /** @@ -42,6 +47,8 @@ public class ObjectRequestHandler implements RequestHandler { private boolean expand; + private UserPassportMap userPassportMap; + /* Constructors */ /** @@ -57,9 +64,10 @@ public ObjectRequestHandler() { * @param expand boolean indicating whether to return nested/recursive bundles under 'contents' * @return the prepared request handler */ - public ObjectRequestHandler prepare(String objectId, boolean expand) { + public ObjectRequestHandler prepare(String objectId, boolean expand, UserPassportMap userPassportMap) { this.objectId = objectId; this.expand = expand; + this.userPassportMap = userPassportMap; return this; } @@ -67,13 +75,45 @@ public ObjectRequestHandler prepare(String objectId, boolean expand) { * Obtains information about a DRSObject and formats it to the DRS specification */ public DrsObject handleRequest() { - // Get DrsObject from db DrsObject drsObject = hibernateUtil.loadDrsObject(objectId, true); if (drsObject == null) { throw new ResourceNotFoundException("No DrsObject found by id: " + objectId); } + // check if DrsObject requires auth, if so verify the client's passport + List requiredVisas = drsObject.getPassportVisas(); + boolean requiresAuth = false; + if (requiredVisas != null && requiredVisas.size() > 0) { + requiresAuth = true; + } + boolean noPassport = userPassportMap == null || userPassportMap.getMap().size() == 0; + if (requiresAuth) { + if (noPassport) { + throw new UnauthorizedException("Request for controlled data is missing user passport(s)"); + } + + // need to verify at least 1 visa registered with the DRS Object + boolean matchingVisaFound = false; + for (PassportVisa drsObjectRegisteredVisa : drsObject.getPassportVisas()) { + String passportBrokerIss = drsObjectRegisteredVisa.getPassportBroker().getUrl(); + String visaName = drsObjectRegisteredVisa.getName(); + String visaIssuer = drsObjectRegisteredVisa.getIssuer(); + UserPassport userPassport = userPassportMap.getMap().get(passportBrokerIss); + if (userPassport != null) { + String visaKey = visaName + "@" + visaIssuer; + String visaJwt = userPassport.getVisaJwtMap().get(visaKey); + if (visaJwt != null) { + matchingVisaFound = true; + } + } + } + + if (! matchingVisaFound) { + throw new ForbiddenException("No suitable visa found in user passport(s) for requested DRS object"); + } + } + // post query prep of response drsObject.setSelfURI(prepareSelfURI(objectId)); drsObject.setContents(prepareContents(drsObject)); diff --git a/src/test/java/integration/DemoServerAccessIDTest.java b/src/test/java/integration/DemoServerAccessIDTest.java new file mode 100644 index 0000000..439e890 --- /dev/null +++ b/src/test/java/integration/DemoServerAccessIDTest.java @@ -0,0 +1,188 @@ +package integration; + +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.net.http.HttpRequest.Builder; +import java.net.http.HttpResponse.BodyHandlers; +import java.nio.file.Files; + +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.json.JSONObject; +import org.json.JSONArray; + +import org.ga4gh.starterkit.drs.testutils.ResourceLoader; +import java.util.ArrayList; +import java.util.List; +import org.springframework.http.HttpHeaders; +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +public class DemoServerAccessIDTest +{ + // Define variables and constants + private static final String DEFAULT_PUBLIC_URL = "http://localhost:4500/ga4gh/drs/v1/objects/"; + private static final String CUSTOM_PUBLIC_URL = "http://localhost:7000/ga4gh/drs/v1/objects/"; + + // Raw phenopacket data directory + private static final String OBJ_DIR = "/data/phenopackets/"; + + @DataProvider(name = "cases") + public Object[][] getData() + { + return new Object[][] + { + { + DEFAULT_PUBLIC_URL, + "697907bf-d5bd-433e-aac2-1747f1faf366", + "Zhang-2009-EDA-proband.json", + }, + { + DEFAULT_PUBLIC_URL, + "2506f0e1-29e4-4132-9b37-f7452dc8a89b", + "Mundhofir-2013-FGFR2-Patient_1.json", + }, + { + DEFAULT_PUBLIC_URL, + "456e9ee0-5b60-4f38-82b5-83ba5d338038", + "Lalani-2016-TANGO2-Suject_1.json", + }, + { + CUSTOM_PUBLIC_URL, + "697907bf-d5bd-433e-aac2-1747f1faf366", + "Zhang-2009-EDA-proband.json", + }, + { + CUSTOM_PUBLIC_URL, + "2506f0e1-29e4-4132-9b37-f7452dc8a89b", + "Mundhofir-2013-FGFR2-Patient_1.json", + }, + { + CUSTOM_PUBLIC_URL, + "456e9ee0-5b60-4f38-82b5-83ba5d338038", + "Lalani-2016-TANGO2-Suject_1.json", + } + }; + } + + @Test(dataProvider = "cases") + public void testDemoServerAccessID(String requestURL, String drsObjectId, String expFileName) throws Exception + { + // (0) Will get raw data through each access type + List requestedRawData = new ArrayList(); + + // (1) Load local file containing expected phenopacket data + String drsObjectExpFile = OBJ_DIR + expFileName; + String expResponseBody = ResourceLoader.load(drsObjectExpFile); + JSONObject exp_JSON = new JSONObject(expResponseBody); + + requestedRawData.add(exp_JSON.toString()); + + // (2) Request to get the access methods + HttpClient client = HttpClient.newHttpClient(); + Builder requestBuilder = HttpRequest.newBuilder() + .GET() + .uri(URI.create(requestURL + drsObjectId)); //Ask for the object + + HttpRequest requestDRS = requestBuilder.build(); + HttpResponse responseDRS = client.send(requestDRS, BodyHandlers.ofString()); + JSONObject responseDRS_JO = new JSONObject(responseDRS.body()); + + // (3) Looping through access methods + JSONArray accessMethods = responseDRS_JO.getJSONArray("access_methods"); + + for (int i = 0; i < accessMethods.length(); i++) + { + JSONObject method = accessMethods.getJSONObject(i); + String accessType = method.get("type").toString(); + + if(accessType.equals("https")) + { + String accessID = method.get("access_id").toString(); + + // Getting the access URL + Builder requestBuilderAccessURL = HttpRequest.newBuilder() + .GET() + .uri(URI.create(requestURL + drsObjectId + "/access/" + accessID)); //Ask for the access URL + + HttpRequest requestAccessURL = requestBuilderAccessURL.build(); + HttpResponse responseAccessURL = client.send(requestAccessURL, BodyHandlers.ofString()); + + JSONObject accessURLJson = new JSONObject(responseAccessURL.body()); + String accessURL = accessURLJson.getString("url"); + + // Getting the Raw Data + Builder requestBuilderRawData = HttpRequest.newBuilder() + .GET() + .uri(URI.create(accessURL)); //Ask for the raw data + + HttpRequest requestRawData = requestBuilderRawData.build(); + HttpResponse responseRawData = client.send(requestRawData, BodyHandlers.ofString()); + + requestedRawData.add(responseRawData.body()); // Store the raw data + } + else if(accessType.equals("s3")) + { + String accessURL = method.getJSONObject("access_url").get("url").toString(); // "s3://" + String rawDataPath = accessURL.substring(accessURL.lastIndexOf("//") + 1); // + String region = method.get("region").toString(); // example: "us-east-2" + + // Resolve the s3 access URL into a http URL + String baseURL = "https://s3." + region + ".amazonaws.com/"; + String finalURL = baseURL + rawDataPath; + + // Send the request to get the phenopacket + Builder requestBuilderRawData = HttpRequest.newBuilder() + .GET() + .uri(URI.create(finalURL)); //Ask for the raw data + + HttpRequest requestRawData = requestBuilderRawData.build(); + HttpResponse responseRawData = client.send(requestRawData, BodyHandlers.ofString()); + + requestedRawData.add(responseRawData.body()); // Store the raw data + } + else if(accessType.equals("file")) + { + String accessURL = method.getJSONObject("access_url").get("url").toString(); // "file://./src/test/resources" + String removedPath = "/src/test/resources"; + + String directory = accessURL.substring(accessURL.lastIndexOf(removedPath) + removedPath.length()); // + + // Load local file containing expected response and assert + String loadedFile = ResourceLoader.load(directory); + JSONObject loaded_JSON = new JSONObject(loadedFile); + requestedRawData.add(loaded_JSON.toString()); // Store the raw data + } + } + + // (4) Turn all data into JSON Objects and then back into strings so the keys are all in the same order + for (int i = 0; i < requestedRawData.size(); i++) + { + JSONObject obj = new JSONObject(requestedRawData.get(i)); + requestedRawData.set(i, obj.toString()); + } + + // (5) All raw data should be the same + int result = verifyIDs(requestedRawData); + + Assert.assertEquals(1, result); // If all the raw data are equal, 1 will be returned + } + + public int verifyIDs(List lst) + { + for (String s : lst) + { + if (!s.equals(lst.get(0))) + { + return 0; + } + } + + return 1; + } +} diff --git a/src/test/java/integration/DemoServerAdminTest.java b/src/test/java/integration/DemoServerAdminTest.java new file mode 100644 index 0000000..4ecc135 --- /dev/null +++ b/src/test/java/integration/DemoServerAdminTest.java @@ -0,0 +1,170 @@ +package integration; + +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.net.http.HttpRequest.BodyPublisher; +import java.net.http.HttpRequest.BodyPublishers; +import java.net.http.HttpRequest.Builder; +import java.net.http.HttpResponse.BodyHandlers; +import java.nio.file.Files; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.json.JSONObject; +import org.json.JSONArray; +import org.checkerframework.checker.units.qual.m; +import org.ga4gh.starterkit.drs.testutils.ResourceLoader; +import java.util.ArrayList; +import java.util.List; +import org.springframework.http.HttpHeaders; +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +public class DemoServerAdminTest +{ + /* + TO-DO: + 1. Get a DRS Object + 2. Create it as a new object in the server + 3. Change its name + 4. Confirm it + 5. Delete object + */ + + // Define variables and constants + private static final String DEFAULT_ADMIN_URL = "http://localhost:4501/admin/ga4gh/drs/v1/objects/"; + private static final String CUSTOM_ADMIN_URL = "http://localhost:7001/admin/ga4gh/drs/v1/objects/"; + + // DRS Object directory + private static final String OBJ_DIR = "/responses/objects/getObjectById/"; + + @DataProvider(name = "cases") + public Object[][] getData() + { + return new Object[][] + { + { + DEFAULT_ADMIN_URL, + "697907bf-d5bd-433e-aac2-1747f1faf366", + "New Test Name 1", + "10000000-test-test-test-000000000000" + }, + { + DEFAULT_ADMIN_URL, + "456e9ee0-5b60-4f38-82b5-83ba5d338038", + "New Test Name 2", + "20000000-test-test-test-000000000000" + }, + { + DEFAULT_ADMIN_URL, + "b8cd0667-2c33-4c9f-967b-161b905932c9", + "New Test Name 3", + "30000000-test-test-test-000000000000" + }, + { + CUSTOM_ADMIN_URL, + "697907bf-d5bd-433e-aac2-1747f1faf366", + "New Test Name 1", + "10000000-test-test-test-000000000000" + }, + { + CUSTOM_ADMIN_URL, + "456e9ee0-5b60-4f38-82b5-83ba5d338038", + "New Test Name 2", + "20000000-test-test-test-000000000000" + }, + { + CUSTOM_ADMIN_URL, + "b8cd0667-2c33-4c9f-967b-161b905932c9", + "New Test Name 3", + "30000000-test-test-test-000000000000" + } + }; + } + + @Test(dataProvider = "cases") + public void testDemoServerAdminGetDrsObjects(String requestURL, String drsObjectId, String newName, String newID) throws Exception + { + HttpClient client = HttpClient.newHttpClient(); + + // (1.1) Get DRS Object + Builder requestBuilder = HttpRequest.newBuilder() + .GET() + .uri(URI.create(requestURL + drsObjectId)); //Ask for the object + + HttpRequest request = requestBuilder.build(); + HttpResponse response = client.send(request, BodyHandlers.ofString()); + + String requestedObj = response.body(); + JSONObject req_JSON = new JSONObject(requestedObj); + JSONObject starting_JSON = req_JSON; + + // (1.2) Change the DRS Object's name and id (edit it) + req_JSON = changeDrsobjectValue(req_JSON, "name", newName); // Change the name of the DRS Object + req_JSON = changeDrsobjectValue(req_JSON, "id", newID); + String newObject = req_JSON.toString(); + + // (2.1) Create the edited object as a new DRS Object + HttpRequest createRequest = HttpRequest.newBuilder() + .uri(URI.create(requestURL)) + .header("Content-Type", "application/json") + .POST(BodyPublishers.ofString(newObject)) + .build(); + + HttpResponse createResponse = client.send(createRequest, + HttpResponse.BodyHandlers.ofString()); + + JSONObject createResponseJO = new JSONObject(createResponse.body()); + String createResponseFixed = createResponseJO.toString(); + + Assert.assertEquals(newObject, createResponseFixed); + + // (3.1) Change the DRS Object's name and id (edit it) + req_JSON = changeDrsobjectValue(req_JSON, "name", newName + " Edited"); // Change the name of the DRS Object + String newObjectEdited = req_JSON.toString(); + + // (3.2) Send the edit request to change newly created the DRS Object in the server + HttpRequest editRequest = HttpRequest.newBuilder() + .uri(URI.create(requestURL + newID)) // This is probably wrong + .header("Content-Type", "application/json") + .PUT(BodyPublishers.ofString(newObjectEdited)) // Sending the edited DRS Object with the request + // Sent object should be a DRS Object but a string is sent in the test in DrsAdminTest too + .build(); + + HttpResponse editResponse = client.send(editRequest, + HttpResponse.BodyHandlers.ofString()); + + // (3.2) Confirm object edit + JSONObject editResponseJO = new JSONObject(editResponse.body()); + String editResponseFixed = editResponseJO.toString(); + + Assert.assertEquals(newObjectEdited, editResponseFixed); + + // (4.1) Delete the new object + HttpRequest deleteRequest = HttpRequest.newBuilder() + .uri(URI.create(requestURL + newID)) // This is probably wrong + .header("Content-Type", "application/json") + .DELETE() + .build(); + + HttpResponse deleteResponse = client.send(deleteRequest, + HttpResponse.BodyHandlers.ofString()); + + // (4.2) Confirm object deletion (succesful deletion means and empty body) + Assert.assertEquals("", deleteResponse.body()); + } + + public JSONObject changeDrsobjectValue(JSONObject obj, String key, String value) throws Exception + { + obj.put(key, value); + + return obj; + } +} diff --git a/src/test/java/integration/DemoServerCheckExpandTest.java b/src/test/java/integration/DemoServerCheckExpandTest.java new file mode 100644 index 0000000..48ff1e2 --- /dev/null +++ b/src/test/java/integration/DemoServerCheckExpandTest.java @@ -0,0 +1,101 @@ +package integration; + +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.net.http.HttpRequest.Builder; +import java.net.http.HttpResponse.BodyHandlers; +import java.nio.file.Files; + +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.json.JSONObject; +import org.json.JSONArray; + +import org.ga4gh.starterkit.drs.testutils.ResourceLoader; +import java.util.ArrayList; +import java.util.List; +import org.springframework.http.HttpHeaders; +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +public class DemoServerCheckExpandTest +{ + // Define variables and constants + private static final String DEFAULT_PUBLIC_URL = "http://localhost:4500/ga4gh/drs/v1/objects/"; + private static final String CUSTOM_PUBLIC_URL = "http://localhost:7000/ga4gh/drs/v1/objects/"; + + // DRS Object directory + private static final String OBJ_DIR = "/responses/objects/getObjectById/"; + + @DataProvider(name = "cases") + public Object[][] getData() + { + return new Object[][] + { + { + DEFAULT_PUBLIC_URL, + "b8cd0667-2c33-4c9f-967b-161b905932c9", + "04.json", + false + }, + { + DEFAULT_PUBLIC_URL, + "b8cd0667-2c33-4c9f-967b-161b905932c9", + "05.json", + true + }, + { + CUSTOM_PUBLIC_URL, + "b8cd0667-2c33-4c9f-967b-161b905932c9", + "04-custom.json", + false + }, + { + CUSTOM_PUBLIC_URL, + "b8cd0667-2c33-4c9f-967b-161b905932c9", + "05-custom.json", + true + } + }; + } + + @Test(dataProvider = "cases") + public void testDemoServerCheckexpand(String requestURL, String drsObjectId, String expFileName, Boolean expand) throws Exception + { + HttpClient client = HttpClient.newHttpClient(); + + Builder requestBuilder = HttpRequest.newBuilder() + .GET() + .uri(URI.create(requestURL + drsObjectId + "?expand=" + String.valueOf(expand))); //Ask for the object + + HttpRequest request = requestBuilder.build(); + HttpResponse response = client.send(request, BodyHandlers.ofString()); + + String requestedObj = response.body(); + + // Load local file containing expected response and assert + String drsObjectExpFile = OBJ_DIR + expFileName; + String expResponseBody = ResourceLoader.load(drsObjectExpFile); + JSONObject exp_JSON = new JSONObject(expResponseBody); + + // If access_methods exists, set access_id to 0 + JSONObject req_JSON = new JSONObject(requestedObj); + + if(req_JSON.has("access_methods")) + { + JSONArray JSONarray = req_JSON.getJSONArray("access_methods"); + JSONObject JSONobj_access_methods = JSONarray.getJSONObject(0); + JSONobj_access_methods.put("access_id", "00000000-0000-0000-0000-000000000000"); + } + + requestedObj = req_JSON.toString(); + expResponseBody = exp_JSON.toString(); + + Assert.assertEquals(requestedObj, expResponseBody); + } +} diff --git a/src/test/java/integration/DemoServerGetDrsObjectsTests.java b/src/test/java/integration/DemoServerGetDrsObjectsTests.java new file mode 100644 index 0000000..ae8e584 --- /dev/null +++ b/src/test/java/integration/DemoServerGetDrsObjectsTests.java @@ -0,0 +1,119 @@ +package integration; + +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.net.http.HttpRequest.Builder; +import java.net.http.HttpResponse.BodyHandlers; +import java.nio.file.Files; + +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.json.JSONObject; +import org.json.JSONArray; + +import org.ga4gh.starterkit.drs.testutils.ResourceLoader; +import java.util.ArrayList; +import java.util.List; +import org.springframework.http.HttpHeaders; +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +public class DemoServerGetDrsObjectsTests +{ + // Define variables and constants + private static final String LOCAL_PUBLIC_URL = "http://localhost:4500/ga4gh/drs/v1/objects/"; + private static final String LOCAL_ADMIN_URL = "http://localhost:4501/admin/ga4gh/drs/v1/objects/"; + private static final String CUSTOM_PUBLIC_URL = "http://localhost:7000/ga4gh/drs/v1/objects/"; + private static final String CUSTOM_ADMIN_URL = "http://localhost:7001/admin/ga4gh/drs/v1/objects/"; + + // DRS Object directory + private static final String OBJ_DIR = "/responses/objects/getObjectById/"; + + @DataProvider(name = "cases") + public Object[][] getData() + { + return new Object[][] + { + { // Getting a DrsObject + LOCAL_PUBLIC_URL, + "697907bf-d5bd-433e-aac2-1747f1faf366", + "00.json" + }, + { + LOCAL_PUBLIC_URL, + "456e9ee0-5b60-4f38-82b5-83ba5d338038", + "02.json" + }, + { + LOCAL_PUBLIC_URL, + "b8cd0667-2c33-4c9f-967b-161b905932c9", + "04.json" + }, + { + LOCAL_ADMIN_URL, + "697907bf-d5bd-433e-aac2-1747f1faf366", + "06.json" + }, + { + CUSTOM_PUBLIC_URL, + "697907bf-d5bd-433e-aac2-1747f1faf366", + "00-custom.json" + }, + { + CUSTOM_PUBLIC_URL, + "456e9ee0-5b60-4f38-82b5-83ba5d338038", + "02-custom.json" + }, + { + CUSTOM_PUBLIC_URL, + "b8cd0667-2c33-4c9f-967b-161b905932c9", + "04-custom.json" + }, + { + CUSTOM_ADMIN_URL, + "697907bf-d5bd-433e-aac2-1747f1faf366", + "06.json" + }, + }; + } + + @Test(dataProvider = "cases") + public void testDemoServerGetDrsObjects(String requestURL, String drsObjectId, String expFileName) throws Exception + { + HttpClient client = HttpClient.newHttpClient(); + + Builder requestBuilder = HttpRequest.newBuilder() + .GET() + .uri(URI.create(requestURL + drsObjectId)); //Ask for the object + + HttpRequest request = requestBuilder.build(); + HttpResponse response = client.send(request, BodyHandlers.ofString()); + + String requestedObj = response.body(); + + // Load local file containing expected response and assert + String drsObjectExpFile = OBJ_DIR + expFileName; + String expResponseBody = ResourceLoader.load(drsObjectExpFile); + JSONObject exp_JSON = new JSONObject(expResponseBody); + + // If access_methods exists, set access_id to 0 + JSONObject req_JSON = new JSONObject(requestedObj); + + if(req_JSON.has("access_methods")) + { + JSONArray JSONarray = req_JSON.getJSONArray("access_methods"); + JSONObject JSONobj_access_methods = JSONarray.getJSONObject(0); + JSONobj_access_methods.put("access_id", "00000000-0000-0000-0000-000000000000"); + } + + requestedObj = req_JSON.toString(); + expResponseBody = exp_JSON.toString(); + + Assert.assertEquals(requestedObj, expResponseBody); + } +} diff --git a/src/test/java/org/ga4gh/starterkit/drs/controller/DrsAdminTest.java b/src/test/java/org/ga4gh/starterkit/drs/controller/DrsAdminTest.java index a425841..cd8863c 100644 --- a/src/test/java/org/ga4gh/starterkit/drs/controller/DrsAdminTest.java +++ b/src/test/java/org/ga4gh/starterkit/drs/controller/DrsAdminTest.java @@ -237,8 +237,7 @@ public void testIndexDrsObject() throws Exception { .andReturn(); genericAdminApiRequestTest(result, true, "index", "success-00.json", null); } - - + @Test(dataProvider = "showDrsObjectCases", groups = "show") public void testShowDrsObject(String objectId, ResultMatcher expStatus, boolean expSuccess, String expFilename, String expMessage) throws Exception { MvcResult result = mockMvc.perform(get(API_PREFIX + "/" + objectId)) diff --git a/src/test/java/org/ga4gh/starterkit/drs/model/AwsS3AccessObjectTest.java b/src/test/java/org/ga4gh/starterkit/drs/model/AwsS3AccessObjectTest.java index e595934..3c602af 100644 --- a/src/test/java/org/ga4gh/starterkit/drs/model/AwsS3AccessObjectTest.java +++ b/src/test/java/org/ga4gh/starterkit/drs/model/AwsS3AccessObjectTest.java @@ -14,7 +14,10 @@ public Object[][] getData() { "us-east-1", "ga4gh-demo-data", "/path/to/the/file.bam", - new DrsObject("12345", null, null, null, Long.valueOf(1L)), + new DrsObject() {{ + setId("12345"); + setSize(Long.valueOf(1L)); + }}, "12345" }, { @@ -22,7 +25,10 @@ public Object[][] getData() { "us-east-2", "ga4gh-production-data", "/data/phenopackets/0001.json", - new DrsObject("98765", null, null, null, Long.valueOf(1L)), + new DrsObject() {{ + setId("98765"); + setSize(Long.valueOf(1L)); + }}, "98765" }, }; diff --git a/src/test/java/org/ga4gh/starterkit/drs/model/ChecksumTest.java b/src/test/java/org/ga4gh/starterkit/drs/model/ChecksumTest.java index 4814649..b8dbbe4 100644 --- a/src/test/java/org/ga4gh/starterkit/drs/model/ChecksumTest.java +++ b/src/test/java/org/ga4gh/starterkit/drs/model/ChecksumTest.java @@ -13,21 +13,30 @@ public Object[][] getData() { Long.valueOf(1L), "47a43e2f1981cef23dee95dd85fb8233", "md5", - new DrsObject("12345", null, null, null, Long.valueOf(1L)), + new DrsObject() {{ + setId("12345"); + setSize(Long.valueOf(1L)); + }}, "12345" }, { Long.valueOf(200L), "d3882df45306b4cc6f6e9227b54a6026", "trunc512", - new DrsObject("98765", null, null, null, Long.valueOf(1L)), + new DrsObject() {{ + setId("98765"); + setSize(Long.valueOf(1L)); + }}, "98765" }, { Long.valueOf(4000000L), "92f9760ae68487f8959d6e0e6a5f219e", "crc32c", - new DrsObject("00001", null, null, null, Long.valueOf(1L)), + new DrsObject() {{ + setId("00001"); + setSize(Long.valueOf(1L)); + }}, "00001" } }; diff --git a/src/test/java/org/ga4gh/starterkit/drs/model/DrsObjectTest.java b/src/test/java/org/ga4gh/starterkit/drs/model/DrsObjectTest.java index 7c120ae..8d5da51 100644 --- a/src/test/java/org/ga4gh/starterkit/drs/model/DrsObjectTest.java +++ b/src/test/java/org/ga4gh/starterkit/drs/model/DrsObjectTest.java @@ -80,6 +80,11 @@ public Object[][] getData() throws URISyntaxException { } private void setters(DrsObject drsObject, TestCase testCase) { + drsObject.setId(testCase.id); + drsObject.setSelfURI(testCase.selfURI); + drsObject.setChecksums(testCase.checksums); + drsObject.setCreatedTime(testCase.createdTime); + drsObject.setSize(testCase.size); drsObject.setAccessMethods(testCase.accessMethods); drsObject.setAliases(testCase.aliases); drsObject.setContents(testCase.contents); @@ -107,20 +112,8 @@ private void assertions(DrsObject drsObject, TestCase testCase) { } @Test(dataProvider = "cases") - public void testDrsObjectConstructor(TestCase testCase) { - DrsObject drsObject = new DrsObject(testCase.id, testCase.selfURI, testCase.checksums, testCase.createdTime, testCase.size); - setters(drsObject, testCase); - assertions(drsObject, testCase); - } - - @Test(dataProvider = "cases") - public void testDrsObjectSetters(TestCase testCase) { - DrsObject drsObject = new DrsObject(null, null, null, null, Long.valueOf(0)); - drsObject.setId(testCase.id); - drsObject.setSelfURI(testCase.selfURI); - drsObject.setChecksums(testCase.checksums); - drsObject.setCreatedTime(testCase.createdTime); - drsObject.setSize(testCase.size); + public void testDrsObject(TestCase testCase) { + DrsObject drsObject = new DrsObject(); setters(drsObject, testCase); assertions(drsObject, testCase); } diff --git a/src/test/java/org/ga4gh/starterkit/drs/model/FileAccessObjectTest.java b/src/test/java/org/ga4gh/starterkit/drs/model/FileAccessObjectTest.java index 878f856..00ed445 100644 --- a/src/test/java/org/ga4gh/starterkit/drs/model/FileAccessObjectTest.java +++ b/src/test/java/org/ga4gh/starterkit/drs/model/FileAccessObjectTest.java @@ -12,19 +12,28 @@ public Object[][] getData() { { Long.valueOf(1L), "/path/to/the/file.bam", - new DrsObject("12345", null, null, null, Long.valueOf(1L)), + new DrsObject() {{ + setId("12345"); + setSize(Long.valueOf(1L)); + }}, "12345" }, { Long.valueOf(200L), "/data/phenopackets/0001.json", - new DrsObject("98765", null, null, null, Long.valueOf(1L)), + new DrsObject() {{ + setId("98765"); + setSize(Long.valueOf(1L)); + }}, "98765" }, { Long.valueOf(4000000L), "./data/cram/myfile.cram", - new DrsObject("00001", null, null, null, Long.valueOf(1L)), + new DrsObject() {{ + setId("00001"); + setSize(Long.valueOf(1L)); + }}, "00001" } }; diff --git a/src/test/java/org/ga4gh/starterkit/drs/utils/BundleRecursiveChecksumCalculatorTest.java b/src/test/java/org/ga4gh/starterkit/drs/utils/BundleRecursiveChecksumCalculatorTest.java index d9de82d..78a4af8 100644 --- a/src/test/java/org/ga4gh/starterkit/drs/utils/BundleRecursiveChecksumCalculatorTest.java +++ b/src/test/java/org/ga4gh/starterkit/drs/utils/BundleRecursiveChecksumCalculatorTest.java @@ -94,12 +94,7 @@ public Object[][] getData() { @Test(dataProvider = "cases") public void testRecursiveCalculateChecksums(DrsObject drsObject, Map expChecksumMap) { - System.out.println(drsObject); - System.out.println(expChecksumMap); - - Map checksumMap = BundleRecursiveChecksumCalculator.recursiveCalculateChecksums(drsObject); - System.out.println("DONE"); Assert.assertEquals(checksumMap.size(), expChecksumMap.size()); for (String key : expChecksumMap.keySet()) { diff --git a/src/test/resources/config/test-config-01.yml b/src/test/resources/config/test-config-01.yml new file mode 100644 index 0000000..86abdaf --- /dev/null +++ b/src/test/resources/config/test-config-01.yml @@ -0,0 +1,18 @@ +drs: + serverProps: + scheme: http + hostname: localhost + publicApiPort: 7000 + adminApiPort: 7001 + databaseProps: + username: ga4gh-user + password: password01234 + serviceInfo: + id: org.ga4gh.starterkit.drs.test + name: GA4GH Starter Kit DRS server test deployment + organization: + name: GA4GH + url: https://ga4gh.org + drsServiceProps: + serveFileURLForFileObjects: true + serveStreamURLForFileObjects: false diff --git a/src/test/resources/responses/admin/index/success-00.json b/src/test/resources/responses/admin/index/success-00.json index 932aba7..5c68993 100644 --- a/src/test/resources/responses/admin/index/success-00.json +++ b/src/test/resources/responses/admin/index/success-00.json @@ -1 +1 @@ -[{"id":"b8cd0667-2c33-4c9f-967b-161b905932c9","description":"Open dataset of 384 phenopackets","created_time":"2021-03-12T20:00:00Z","name":"phenopackets.test.dataset","updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"a1dd4ae2-8d26-43b0-a199-342b64c7dff6","description":"Synthetic Phenopacket dataset: Cao family","created_time":"2021-03-12T20:00:00Z","name":"phenopackets.cao.family","updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"1a570e4e-2489-4218-9333-f65549495872","description":"Phenopackets, Cao family, Patient 1","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.cao.1","size":4257,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"4d83ba3f-a476-4c7c-868f-3d1fcf77fe29","description":"Phenopackets, Cao family, Patient 2","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.cao.2","size":7401,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"924901d5-6d31-4c33-b443-7931eadfac4b","description":"Phenopackets, Cao family, Patient 3","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.cao.3","size":4251,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"0f8abce3-e161-4bdf-981f-86257d505d69","description":"Phenopackets, Cao family, Patient 4","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.cao.4","size":9264,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"c69a3d6c-4a28-4b7c-b215-0782f8d62429","description":"Synthetic Phenopacket dataset: Lalani family","created_time":"2021-03-12T20:00:00Z","name":"phenopackets.lalani.family","updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"456e9ee0-5b60-4f38-82b5-83ba5d338038","description":"Phenopackets, Lalani family, Patient 1","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.lalani.1","size":11606,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"1af6b862-7fc8-411a-86c5-d8e280e5b77a","description":"Phenopackets, Lalani family, Patient 2","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.lalani.2","size":17531,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"c37b37fd-0450-432d-b6aa-9ffdece35ad0","description":"Phenopackets, Lalani family, Patient 4","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.lalani.4","size":9827,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"0bb9d297-2710-48f6-ab4d-80d5eb0c9eaa","description":"Phenopackets, Lalani family, Patient 5","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.lalani.5","size":12111,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"a3bb76d7-76ae-414b-81f7-97e663b02206","description":"Phenopackets, Lalani family, Patient 6","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.lalani.6","size":10316,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"1af5cdcf-898c-4dbc-944e-1ac95e82c0ea","description":"Synthetic Phenopacket dataset: Mundhofir family","created_time":"2021-03-12T20:00:00Z","name":"phenopackets.mundhofir.family","updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"2506f0e1-29e4-4132-9b37-f7452dc8a89b","description":"Phenopackets, Mundhofir family, Patient 1","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.mundhofir.1","size":7002,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"c00c264a-8f17-471f-8ded-1a1f10e965ac","description":"Phenopackets, Mundhofir family, Patient 2","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.mundhofir.2","size":7634,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"355a74bd-6571-4d4a-8602-a9989936717f","description":"Synthetic Phenopacket dataset: Zhang family","created_time":"2021-03-12T20:00:00Z","name":"phenopackets.zhang.family","updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"697907bf-d5bd-433e-aac2-1747f1faf366","description":"Phenopackets, Zhang family, 2009, proband","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.zhang.2009.proband","size":6471,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"3a45fab2-f350-445d-a137-4b1f946bf184","description":"Phenopackets, Zhang family, 2011, Patient 2","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.zhang.2011.2","size":4001,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"ac53ca59-46f3-4f61-b1e7-bb75a49bfbfe","description":"Phenopackets, Zhang family, 2016, Patient 1","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.zhang.2016.1","size":4709,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"1275f896-4c8f-47e1-99a1-873a6b2ef5fb","description":"Phenopackets, Zhang family, 2017, Patient 1","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.zhang.2017.1","size":7555,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"8f40acc0-0c54-45c5-8c85-a6f5fb32a1a7","description":"Phenopackets, Zhang family, 2017, Patient 2","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.zhang.2017.2","size":7114,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"41898242-62a9-4129-9a2c-5a4e8f5f0afb","description":"Phenopackets, Zhang family, 2017, Patient 3","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.zhang.2017.3","size":6262,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"6b994f18-6189-4233-bb83-139686490d68","description":"Phenopackets, Zhang family, 2017, Patient 4","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.zhang.2017.4","size":6289,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"}] \ No newline at end of file +[{"id":"b8cd0667-2c33-4c9f-967b-161b905932c9","description":"Open dataset of 384 phenopackets","created_time":"2021-03-12T20:00:00Z","name":"phenopackets.test.dataset","updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"a1dd4ae2-8d26-43b0-a199-342b64c7dff6","description":"Synthetic Phenopacket dataset: Cao family","created_time":"2021-03-12T20:00:00Z","name":"phenopackets.cao.family","updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"1a570e4e-2489-4218-9333-f65549495872","description":"Phenopackets, Cao family, Patient 1","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.cao.1","size":4257,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"4d83ba3f-a476-4c7c-868f-3d1fcf77fe29","description":"Phenopackets, Cao family, Patient 2","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.cao.2","size":7401,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"924901d5-6d31-4c33-b443-7931eadfac4b","description":"Phenopackets, Cao family, Patient 3","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.cao.3","size":4251,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"0f8abce3-e161-4bdf-981f-86257d505d69","description":"Phenopackets, Cao family, Patient 4","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.cao.4","size":9264,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"c69a3d6c-4a28-4b7c-b215-0782f8d62429","description":"Synthetic Phenopacket dataset: Lalani family","created_time":"2021-03-12T20:00:00Z","name":"phenopackets.lalani.family","updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"456e9ee0-5b60-4f38-82b5-83ba5d338038","description":"Phenopackets, Lalani family, Patient 1","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.lalani.1","size":11606,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"1af6b862-7fc8-411a-86c5-d8e280e5b77a","description":"Phenopackets, Lalani family, Patient 2","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.lalani.2","size":17531,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"c37b37fd-0450-432d-b6aa-9ffdece35ad0","description":"Phenopackets, Lalani family, Patient 4","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.lalani.4","size":9827,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"0bb9d297-2710-48f6-ab4d-80d5eb0c9eaa","description":"Phenopackets, Lalani family, Patient 5","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.lalani.5","size":12111,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"a3bb76d7-76ae-414b-81f7-97e663b02206","description":"Phenopackets, Lalani family, Patient 6","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.lalani.6","size":10316,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"1af5cdcf-898c-4dbc-944e-1ac95e82c0ea","description":"Synthetic Phenopacket dataset: Mundhofir family","created_time":"2021-03-12T20:00:00Z","name":"phenopackets.mundhofir.family","updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"2506f0e1-29e4-4132-9b37-f7452dc8a89b","description":"Phenopackets, Mundhofir family, Patient 1","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.mundhofir.1","size":7002,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"c00c264a-8f17-471f-8ded-1a1f10e965ac","description":"Phenopackets, Mundhofir family, Patient 2","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.mundhofir.2","size":7634,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"355a74bd-6571-4d4a-8602-a9989936717f","description":"Synthetic Phenopacket dataset: Zhang family","created_time":"2021-03-12T20:00:00Z","name":"phenopackets.zhang.family","updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"697907bf-d5bd-433e-aac2-1747f1faf366","description":"Phenopackets, Zhang family, 2009, proband","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.zhang.2009.proband","size":6471,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"3a45fab2-f350-445d-a137-4b1f946bf184","description":"Phenopackets, Zhang family, 2011, Patient 2","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.zhang.2011.2","size":4001,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"ac53ca59-46f3-4f61-b1e7-bb75a49bfbfe","description":"Phenopackets, Zhang family, 2016, Patient 1","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.zhang.2016.1","size":4709,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"1275f896-4c8f-47e1-99a1-873a6b2ef5fb","description":"Phenopackets, Zhang family, 2017, Patient 1","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.zhang.2017.1","size":7555,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"8f40acc0-0c54-45c5-8c85-a6f5fb32a1a7","description":"Phenopackets, Zhang family, 2017, Patient 2","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.zhang.2017.2","size":7114,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"41898242-62a9-4129-9a2c-5a4e8f5f0afb","description":"Phenopackets, Zhang family, 2017, Patient 3","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.zhang.2017.3","size":6262,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"6b994f18-6189-4233-bb83-139686490d68","description":"Phenopackets, Zhang family, 2017, Patient 4","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.zhang.2017.4","size":6289,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"e26f8428-1a5d-4465-b389-10998ba7b894","description":"PASSPORT AUTH - Phenopackets, Cao Patient 1","created_time":"2022-05-16T20:00:00Z","mime_type":"application/json","name":"passport.phenopackets.cao.1","size":4257,"updated_time":"2022-05-16T20:00:00Z","version":"1.0.0"},{"id":"996ead07-8dfc-4c5d-b319-5b59bebb507e","description":"PASSPORT AUTH - Phenopackets, Cao Patient 2","created_time":"2022-05-16T20:00:00Z","mime_type":"application/json","name":"passport.phenopackets.cao.2","size":7401,"updated_time":"2022-05-16T20:00:00Z","version":"1.0.0"},{"id":"81e8b499-1054-4846-8df4-d079ea32a44f","description":"PASSPORT AUTH - Phenopackets, Cao Patient 3","created_time":"2022-05-16T20:00:00Z","mime_type":"application/json","name":"passport.phenopackets.cao.3","size":4251,"updated_time":"2022-05-16T20:00:00Z","version":"1.0.0"},{"id":"672d0425-137c-437f-ab12-29a3cce1ffc0","description":"PASSPORT AUTH - Phenopackets, Cao Patient 4","created_time":"2022-05-16T20:00:00Z","mime_type":"application/json","name":"passport.phenopackets.cao.4","size":9264,"updated_time":"2022-05-16T20:00:00Z","version":"1.0.0"}] \ No newline at end of file diff --git a/src/test/resources/responses/objects/getObjectById/00-custom.json b/src/test/resources/responses/objects/getObjectById/00-custom.json new file mode 100644 index 0000000..ae5adb3 --- /dev/null +++ b/src/test/resources/responses/objects/getObjectById/00-custom.json @@ -0,0 +1 @@ +{"checksums":[{"checksum":"b7e976ef2a9d13c65f154ad72a5495139dc6ac32","type":"sha1"},{"checksum":"70722813447e98baf8f79cd5828668f971676bcfe08c286f62a7decd84b03eb0","type":"sha256"},{"checksum":"71611ed3a3246fea6ce80916924c0722","type":"md5"}],"created_time":"2021-03-12T20:00:00Z","updated_time":"2021-03-13T12:30:45Z","aliases":["Zhang-EDA-proband"],"size":6471,"mime_type":"application\/json","access_methods":[{"access_url":{"url":"file:\/\/.\/src\/test\/resources\/data\/phenopackets\/Zhang-2009-EDA-proband.json"},"access_id":"00000000-0000-0000-0000-000000000000","type":"file"},{"access_url":{"url":"s3:\/\/ga4gh-demo-data\/phenopackets\/Zhang-2009-EDA-proband.json"},"type":"s3","region":"us-east-2"}],"name":"phenopackets.zhang.2009.proband","description":"Phenopackets, Zhang family, 2009, proband","id":"697907bf-d5bd-433e-aac2-1747f1faf366","self_uri":"drs:\/\/localhost:7000\/697907bf-d5bd-433e-aac2-1747f1faf366","version":"1.0.0"} \ No newline at end of file diff --git a/src/test/resources/responses/objects/getObjectById/02-custom.json b/src/test/resources/responses/objects/getObjectById/02-custom.json new file mode 100644 index 0000000..d880084 --- /dev/null +++ b/src/test/resources/responses/objects/getObjectById/02-custom.json @@ -0,0 +1 @@ +{"checksums":[{"checksum":"868177570cd7593aeb8e4b244d0ab4da47841037","type":"sha1"},{"checksum":"317a5a0c3f12b607a81e711e776fccac28cadc6d48867bba51b8572bf56e77fa","type":"sha256"},{"checksum":"2262c06d95790324a76f09e2ee0ec418","type":"md5"}],"created_time":"2021-03-12T20:00:00Z","updated_time":"2021-03-13T12:30:45Z","aliases":["Lalani-TANGO2-1"],"size":11606,"mime_type":"application\/json","access_methods":[{"access_url":{"url":"file:\/\/.\/src\/test\/resources\/data\/phenopackets\/Lalani-2016-TANGO2-Suject_1.json"},"access_id":"00000000-0000-0000-0000-000000000000","type":"file"},{"access_url":{"url":"s3:\/\/ga4gh-demo-data\/phenopackets\/Lalani-2016-TANGO2-Suject_1.json"},"type":"s3","region":"us-east-2"}],"name":"phenopackets.lalani.1","description":"Phenopackets, Lalani family, Patient 1","id":"456e9ee0-5b60-4f38-82b5-83ba5d338038","self_uri":"drs:\/\/localhost:7000\/456e9ee0-5b60-4f38-82b5-83ba5d338038","version":"1.0.0"} \ No newline at end of file diff --git a/src/test/resources/responses/objects/getObjectById/04-custom.json b/src/test/resources/responses/objects/getObjectById/04-custom.json new file mode 100644 index 0000000..d62b141 --- /dev/null +++ b/src/test/resources/responses/objects/getObjectById/04-custom.json @@ -0,0 +1 @@ +{"id":"b8cd0667-2c33-4c9f-967b-161b905932c9","description":"Open dataset of 384 phenopackets","created_time":"2021-03-12T20:00:00Z","name":"phenopackets.test.dataset","size":143601,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0","checksums":[{"checksum":"8711d59ca4264b3e3d0ce16349d94d0ab8ce493e","type":"sha1"},{"checksum":"930014c944b655323ade3f4b239178022bfb5443ef6b280a7d7d69292867d010","type":"sha256"},{"checksum":"938b077a59b11ad6e5958f9f34148f18","type":"md5"}],"self_uri":"drs://localhost:7000/b8cd0667-2c33-4c9f-967b-161b905932c9","contents":[{"name":"phenopackets.mundhofir.family","drs_uri":["drs://localhost:7000/1af5cdcf-898c-4dbc-944e-1ac95e82c0ea"],"id":"1af5cdcf-898c-4dbc-944e-1ac95e82c0ea"},{"name":"phenopackets.zhang.family","drs_uri":["drs://localhost:7000/355a74bd-6571-4d4a-8602-a9989936717f"],"id":"355a74bd-6571-4d4a-8602-a9989936717f"},{"name":"phenopackets.cao.family","drs_uri":["drs://localhost:7000/a1dd4ae2-8d26-43b0-a199-342b64c7dff6"],"id":"a1dd4ae2-8d26-43b0-a199-342b64c7dff6"},{"name":"phenopackets.lalani.family","drs_uri":["drs://localhost:7000/c69a3d6c-4a28-4b7c-b215-0782f8d62429"],"id":"c69a3d6c-4a28-4b7c-b215-0782f8d62429"}]} \ No newline at end of file diff --git a/src/test/resources/responses/objects/getObjectById/05-custom.json b/src/test/resources/responses/objects/getObjectById/05-custom.json new file mode 100644 index 0000000..65b5b04 --- /dev/null +++ b/src/test/resources/responses/objects/getObjectById/05-custom.json @@ -0,0 +1 @@ +{"id":"b8cd0667-2c33-4c9f-967b-161b905932c9","description":"Open dataset of 384 phenopackets","created_time":"2021-03-12T20:00:00Z","name":"phenopackets.test.dataset","size":143601,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0","checksums":[{"checksum":"8711d59ca4264b3e3d0ce16349d94d0ab8ce493e","type":"sha1"},{"checksum":"930014c944b655323ade3f4b239178022bfb5443ef6b280a7d7d69292867d010","type":"sha256"},{"checksum":"938b077a59b11ad6e5958f9f34148f18","type":"md5"}],"self_uri":"drs://localhost:7000/b8cd0667-2c33-4c9f-967b-161b905932c9","contents":[{"name":"phenopackets.mundhofir.family","contents":[{"name":"phenopackets.mundhofir.1","drs_uri":["drs://localhost:7000/2506f0e1-29e4-4132-9b37-f7452dc8a89b"],"id":"2506f0e1-29e4-4132-9b37-f7452dc8a89b"},{"name":"phenopackets.mundhofir.2","drs_uri":["drs://localhost:7000/c00c264a-8f17-471f-8ded-1a1f10e965ac"],"id":"c00c264a-8f17-471f-8ded-1a1f10e965ac"}],"drs_uri":["drs://localhost:7000/1af5cdcf-898c-4dbc-944e-1ac95e82c0ea"],"id":"1af5cdcf-898c-4dbc-944e-1ac95e82c0ea"},{"name":"phenopackets.zhang.family","contents":[{"name":"phenopackets.zhang.2017.1","drs_uri":["drs://localhost:7000/1275f896-4c8f-47e1-99a1-873a6b2ef5fb"],"id":"1275f896-4c8f-47e1-99a1-873a6b2ef5fb"},{"name":"phenopackets.zhang.2011.2","drs_uri":["drs://localhost:7000/3a45fab2-f350-445d-a137-4b1f946bf184"],"id":"3a45fab2-f350-445d-a137-4b1f946bf184"},{"name":"phenopackets.zhang.2017.3","drs_uri":["drs://localhost:7000/41898242-62a9-4129-9a2c-5a4e8f5f0afb"],"id":"41898242-62a9-4129-9a2c-5a4e8f5f0afb"},{"name":"phenopackets.zhang.2009.proband","drs_uri":["drs://localhost:7000/697907bf-d5bd-433e-aac2-1747f1faf366"],"id":"697907bf-d5bd-433e-aac2-1747f1faf366"},{"name":"phenopackets.zhang.2017.4","drs_uri":["drs://localhost:7000/6b994f18-6189-4233-bb83-139686490d68"],"id":"6b994f18-6189-4233-bb83-139686490d68"},{"name":"phenopackets.zhang.2017.2","drs_uri":["drs://localhost:7000/8f40acc0-0c54-45c5-8c85-a6f5fb32a1a7"],"id":"8f40acc0-0c54-45c5-8c85-a6f5fb32a1a7"},{"name":"phenopackets.zhang.2016.1","drs_uri":["drs://localhost:7000/ac53ca59-46f3-4f61-b1e7-bb75a49bfbfe"],"id":"ac53ca59-46f3-4f61-b1e7-bb75a49bfbfe"}],"drs_uri":["drs://localhost:7000/355a74bd-6571-4d4a-8602-a9989936717f"],"id":"355a74bd-6571-4d4a-8602-a9989936717f"},{"name":"phenopackets.cao.family","contents":[{"name":"phenopackets.cao.4","drs_uri":["drs://localhost:7000/0f8abce3-e161-4bdf-981f-86257d505d69"],"id":"0f8abce3-e161-4bdf-981f-86257d505d69"},{"name":"phenopackets.cao.1","drs_uri":["drs://localhost:7000/1a570e4e-2489-4218-9333-f65549495872"],"id":"1a570e4e-2489-4218-9333-f65549495872"},{"name":"phenopackets.cao.2","drs_uri":["drs://localhost:7000/4d83ba3f-a476-4c7c-868f-3d1fcf77fe29"],"id":"4d83ba3f-a476-4c7c-868f-3d1fcf77fe29"},{"name":"phenopackets.cao.3","drs_uri":["drs://localhost:7000/924901d5-6d31-4c33-b443-7931eadfac4b"],"id":"924901d5-6d31-4c33-b443-7931eadfac4b"}],"drs_uri":["drs://localhost:7000/a1dd4ae2-8d26-43b0-a199-342b64c7dff6"],"id":"a1dd4ae2-8d26-43b0-a199-342b64c7dff6"},{"name":"phenopackets.lalani.family","contents":[{"name":"phenopackets.lalani.5","drs_uri":["drs://localhost:7000/0bb9d297-2710-48f6-ab4d-80d5eb0c9eaa"],"id":"0bb9d297-2710-48f6-ab4d-80d5eb0c9eaa"},{"name":"phenopackets.lalani.2","drs_uri":["drs://localhost:7000/1af6b862-7fc8-411a-86c5-d8e280e5b77a"],"id":"1af6b862-7fc8-411a-86c5-d8e280e5b77a"},{"name":"phenopackets.lalani.1","drs_uri":["drs://localhost:7000/456e9ee0-5b60-4f38-82b5-83ba5d338038"],"id":"456e9ee0-5b60-4f38-82b5-83ba5d338038"},{"name":"phenopackets.lalani.6","drs_uri":["drs://localhost:7000/a3bb76d7-76ae-414b-81f7-97e663b02206"],"id":"a3bb76d7-76ae-414b-81f7-97e663b02206"},{"name":"phenopackets.lalani.4","drs_uri":["drs://localhost:7000/c37b37fd-0450-432d-b6aa-9ffdece35ad0"],"id":"c37b37fd-0450-432d-b6aa-9ffdece35ad0"}],"drs_uri":["drs://localhost:7000/c69a3d6c-4a28-4b7c-b215-0782f8d62429"],"id":"c69a3d6c-4a28-4b7c-b215-0782f8d62429"}]} \ No newline at end of file diff --git a/src/test/resources/responses/objects/getObjectById/06.json b/src/test/resources/responses/objects/getObjectById/06.json new file mode 100644 index 0000000..fd93bc2 --- /dev/null +++ b/src/test/resources/responses/objects/getObjectById/06.json @@ -0,0 +1,51 @@ +{ + "id": "697907bf-d5bd-433e-aac2-1747f1faf366", + "description": "Phenopackets, Zhang family, 2009, proband", + "created_time": "2021-03-12T20:00:00Z", + "mime_type": "application/json", + "name": "phenopackets.zhang.2009.proband", + "size": 6471, + "updated_time": "2021-03-13T12:30:45Z", + "version": "1.0.0", + "aliases": [ + "Zhang-EDA-proband" + ], + "checksums": [ + { + "checksum": "71611ed3a3246fea6ce80916924c0722", + "type": "md5" + }, + { + "checksum": "b7e976ef2a9d13c65f154ad72a5495139dc6ac32", + "type": "sha1" + }, + { + "checksum": "70722813447e98baf8f79cd5828668f971676bcfe08c286f62a7decd84b03eb0", + "type": "sha256" + } + ], + "is_bundle": false, + "drs_object_parents": [ + { + "id": "355a74bd-6571-4d4a-8602-a9989936717f", + "description": "Synthetic Phenopacket dataset: Zhang family", + "created_time": "2021-03-12T20:00:00Z", + "name": "phenopackets.zhang.family", + "updated_time": "2021-03-13T12:30:45Z", + "version": "1.0.0", + "is_bundle": true + } + ], + "file_access_objects": [ + { + "path": "./src/test/resources/data/phenopackets/Zhang-2009-EDA-proband.json" + } + ], + "aws_s3_access_objects": [ + { + "region": "us-east-2", + "bucket": "ga4gh-demo-data", + "key": "/phenopackets/Zhang-2009-EDA-proband.json" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/responses/service-info/show/00.json b/src/test/resources/responses/service-info/show/00.json index 515a7a5..e8f1037 100644 --- a/src/test/resources/responses/service-info/show/00.json +++ b/src/test/resources/responses/service-info/show/00.json @@ -1 +1 @@ -{"id":"org.ga4gh.starterkit.drs","name":"GA4GH Starter Kit DRS Service","description":"An open source, community-driven implementation of the GA4GH Data Repository Service (DRS) API specification.","contactUrl":"mailto:info@ga4gh.org","documentationUrl":"https://github.com/ga4gh/ga4gh-starter-kit-drs","createdAt":"2020-01-15T12:00:00Z","updatedAt":"2020-01-15T12:00:00Z","environment":"test","version":"0.2.2","type":{"group":"org.ga4gh","artifact":"drs","version":"1.1.0"},"organization":{"name":"Global Alliance for Genomics and Health","url":"https://ga4gh.org"}} \ No newline at end of file +{"id":"org.ga4gh.starterkit.drs","name":"GA4GH Starter Kit DRS Service","description":"An open source, community-driven implementation of the GA4GH Data Repository Service (DRS) API specification.","contactUrl":"mailto:info@ga4gh.org","documentationUrl":"https://github.com/ga4gh/ga4gh-starter-kit-drs","createdAt":"2020-01-15T12:00:00Z","updatedAt":"2020-01-15T12:00:00Z","environment":"test","version":"0.3.0","type":{"group":"org.ga4gh","artifact":"drs","version":"1.1.0"},"organization":{"name":"Global Alliance for Genomics and Health","url":"https://ga4gh.org"}} \ No newline at end of file