diff --git a/README.md b/README.md index 15002cd..c74a838 100644 --- a/README.md +++ b/README.md @@ -9,42 +9,23 @@ * [How it works](#how-it-works) * [Technologies](#technologies) -##What it is -This project contains the BDK (beacon development kit) for Java (EE) developers. The BDK provides all of the tools required to create a basic rest implementation for a beacon almost straight out of the box with only minimal set up required. -Users can extend the BDK to make it fit within their own project, or use it as is defining their own adapter to retrieve data from their preferred data source. - -##System requirements -All you need to build this project is Java 8.0 (Java SDK 1.8) or later, Maven 3.0 or later. Since the project is Java EE based, an application server with support for Java EE 7 is needed to deploy the application (e.g. JBoss EAP or WildFly). - -##Modules -1. [beacon-java-core](/beacon-java-core) - Contains core classes and functions shared across modules in the BJDK. -2. [beacon-java-rest](/beacon-java-rest) - Rest implementation of a beacon, configured to run on a JBOSS server. -3. [sample-beacon-adapter](/sample-beacon-adapter) - Sample beacon adapter implementation - -## Beacon Specification -The BDK implements the beacon specification and relies on the data models generated by the beacon schema. At the moment the dependency is not currently listed on maven central but can be retrived from the [beacon teams git hub repository](https://github.com/ga4gh/beacon-team). - -To retrieve the dependency: - -``` -git clone https://github.com/ga4gh/beacon-team -cd beacon-team -mvn clean install -``` - -## How it Works -The project provides the following: - -- API for beacons -- sample beacon implementation -- conversion of parameters to a normalized form (the same way Beacon of Beacons does) -- sample navigation webpage -- sample test suite - -##How to run it +## What it is +This project contains the BDK (beacon development kit) for Java EE developers. The BDK provides all of the tools required +to create a basic rest implementation for a beacon almost straight out of the box with only minimal set up required. +Extend the BDK to make it fit within their own project, or use it as is defining your own adapter to retrieve data from +your preferred data source. + +## System Requirements +- Java 8 or later; +- Maven 3.0 or later; +- [Beacon Schemas (v.0.3.0)](https://github.com/ga4gh/beacon-team): BDK relies on the generated Java classes; +- an application server with support for Java EE 7 (e.g. JBoss EAP or WildFly). + +## Modules +- [beacon-java-rest](/beacon-java-rest): rest implementation of a beacon, configured to run on a JBoss server; +- [sample-beacon-adapter](/sample-beacon-adapter): sample beacon adapter implementation. + +## How to run it Build the project: mvn clean install @@ -63,20 +44,20 @@ In order to run the tests from beacon-java-rest in a managed (remote) container, mvn test -Ptest-managed -##Creating a beacon -The beacon implementation is designed to use a beacon-adapter provided through the [beacon-adapter-api project](https://github.com/mcupak/beacon-adapter-api) project. The user can either extend the abstract BeaconAdapter class to create a custom implementation, or they can use one of the beacon-adapter implementations that are provided +## Creating a Beacon +Beacon REST API uses Beacon Adapter to retrieve the beacon data. The BDK ships with a sample Beacon Adapter that uses +sample data. The sample Beacon Adapter is intended to be a placeholder to allow the tests to be run, and to give the user +an idea of how to implement a beacon. -Once you have created your own adapter, simply replace the adapter dependency in the beacon-java-rest's pom with your own. The sample beacon adapter is intended to be a placeholder to allow the tests to be run, and to give the user an idea of how to implement a beacon. After this has been completely they are free to implement their own adapter. +Once you are ready, implement your own [beacon-adapter](https://github.com/mcupak/beacon-service) project to query your +own data. To switch to your own Beacon Adapter, simply replace the adapter dependency in the beacon-java-rest's pom with +your own. -- Add your desired beacon adapter to the pom.xml in the commented out section -- Deploy The beacon -- Run the test-managed profile to ensure the beacon works and complies with the beacon spec - -The API takes care of the rest and provides the following endpoints upon deployment of your beacon: +The Rest API provides the following endpoints upon deployment of your beacon: http://localhost:8080/beacon-java - information about your beacon http://localhost:8080/beacon-java/query - access to query service -##Technologies -Java EE. CDI, JAX-RS, JAXB. Tested with Arquillian/ShrinkWrap. +## Technologies +Java EE. CDI, JAX-RS, JAXB. Tested with Arquillian/ShrinkWrap. \ No newline at end of file diff --git a/beacon-java-rest/pom.xml b/beacon-java-rest/pom.xml index 8a7a415..cf99540 100644 --- a/beacon-java-rest/pom.xml +++ b/beacon-java-rest/pom.xml @@ -9,13 +9,11 @@ 1.0-SNAPSHOT - - com.dnastack beacon-java-rest 1.0-SNAPSHOT war - beacon-java-rest + Beacon Java Rest @@ -109,23 +107,29 @@ test - com.dnastack - beacon-java-service - ${project.version} + org.keycloak + keycloak-servlet-filter-adapter + + + org.keycloak + keycloak-authz-client + + + + org.apache.httpcomponents + httpclient com.dnastack beacon-adapter-api + ${version.beacon.adapter.api} - - + com.dnastack sample-beacon-adapter ${project.version} - - @@ -140,6 +144,9 @@ org.wildfly.plugins wildfly-maven-plugin + + 10090 + maven-compiler-plugin @@ -160,16 +167,12 @@ + org.apache.maven.plugins maven-surefire-plugin - ${version.surefire.plugin} true - - org.wildfly.plugins - wildfly-maven-plugin - @@ -181,17 +184,36 @@ wildfly-arquillian-container-managed test - - org.jboss.arquillian.protocol - arquillian-protocol-servlet - test - + + org.apache.maven.plugins + maven-dependency-plugin + + + unpack + process-test-classes + + unpack + + + + + org.wildfly + wildfly-dist + ${version.org.wildfly} + zip + false + ${project.build.directory} + + + + + + maven-surefire-plugin - ${version.surefire.plugin} false @@ -220,7 +242,6 @@ maven-surefire-plugin - ${version.surefire.plugin} false diff --git a/beacon-java-rest/src/main/java/com/dnastack/beacon/providers/BeaconExceptionHandler.java b/beacon-java-rest/src/main/java/com/dnastack/beacon/providers/BeaconExceptionHandler.java deleted file mode 100644 index 6d98a3a..0000000 --- a/beacon-java-rest/src/main/java/com/dnastack/beacon/providers/BeaconExceptionHandler.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * The MIT License - * - * Copyright 2014 Miroslav Cupak (mirocupak@gmail.com). - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.dnastack.beacon.providers; - -import com.dnastack.beacon.exceptions.BeaconAlleleRequestException; -import com.dnastack.beacon.exceptions.BeaconException; -import com.dnastack.beacon.service.api.BeaconService; -import org.ga4gh.beacon.Beacon; -import org.ga4gh.beacon.BeaconAlleleResponse; -import org.ga4gh.beacon.BeaconError; - -import javax.inject.Inject; -import javax.ws.rs.core.Response; -import javax.ws.rs.ext.ExceptionMapper; -import javax.ws.rs.ext.Provider; - -/** - * Beacon exceptions handler for catching beacon errors and returning the appropriate object - * - * @author patmagee - */ -@Provider -public class BeaconExceptionHandler implements ExceptionMapper { - - @Inject - BeaconService service; - - @Override - public Response toResponse(BeaconException exception) { - - BeaconError error = new BeaconError(); - error.setMessage(exception.getMessage()); - switch (exception.getReason()) { - case INVALID_REQUEST: - error.setErrorCode(Response.Status.BAD_REQUEST.getStatusCode()); - break; - default: - error.setErrorCode(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()); - } - - //If this is an alleleRequest then return a BeaconAlleleResponse with the error fields set appropriately - if (exception.getClass().getCanonicalName().equals(BeaconAlleleRequestException.class.getCanonicalName())) { - BeaconAlleleRequestException e = (BeaconAlleleRequestException) exception; - BeaconAlleleResponse response = new BeaconAlleleResponse(); - response.setExists(null); - response.setError(error); - - if (e.getRequest() != null) { - response.setAlleleRequest(e.getRequest()); - } - - try { - Beacon beacon = service.queryBeacon(); - response.setBeaconId(beacon.getId()); - } catch (BeaconException ex) { - response.setBeaconId(null); - } - - return Response.status(error.getErrorCode()).entity(response).build(); - - } else { - BeaconError response = error; - return Response.status(error.getErrorCode()).entity(error).build(); - } - } -} diff --git a/beacon-java-rest/src/main/java/com/dnastack/beacon/application/BeaconApplication.java b/beacon-java-rest/src/main/java/com/dnastack/beacon/rest/BeaconApplication.java similarity index 97% rename from beacon-java-rest/src/main/java/com/dnastack/beacon/application/BeaconApplication.java rename to beacon-java-rest/src/main/java/com/dnastack/beacon/rest/BeaconApplication.java index 39db57e..0829cef 100644 --- a/beacon-java-rest/src/main/java/com/dnastack/beacon/application/BeaconApplication.java +++ b/beacon-java-rest/src/main/java/com/dnastack/beacon/rest/BeaconApplication.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.dnastack.beacon.application; +package com.dnastack.beacon.rest; import javax.ws.rs.ApplicationPath; import javax.ws.rs.core.Application; diff --git a/beacon-java-rest/src/main/java/com/dnastack/beacon/rest/api/BeaconInfo.java b/beacon-java-rest/src/main/java/com/dnastack/beacon/rest/endpoints/BeaconInfo.java similarity index 90% rename from beacon-java-rest/src/main/java/com/dnastack/beacon/rest/api/BeaconInfo.java rename to beacon-java-rest/src/main/java/com/dnastack/beacon/rest/endpoints/BeaconInfo.java index 08224d5..2de6587 100644 --- a/beacon-java-rest/src/main/java/com/dnastack/beacon/rest/api/BeaconInfo.java +++ b/beacon-java-rest/src/main/java/com/dnastack/beacon/rest/endpoints/BeaconInfo.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.dnastack.beacon.rest.api; +package com.dnastack.beacon.rest.endpoints; import com.dnastack.beacon.exceptions.BeaconException; import org.ga4gh.beacon.Beacon; @@ -32,20 +32,16 @@ import javax.ws.rs.core.MediaType; /** - * Beacon rest resource. - * * @author Miroslav Cupak (mirocupak@gmail.com) * @author Patrick Magee (patrickmageee@gmail.com) + * @author Artem (tema.voskoboynick@gmail.com) * @version 1.0 */ @Path("/") public interface BeaconInfo { /** - * Gets Information on the beacon - * - * @return Beacon represenation - * @throws BeaconException + * Gets information on the beacon. */ @GET @Produces({MediaType.APPLICATION_JSON}) diff --git a/beacon-java-rest/src/main/java/com/dnastack/beacon/rest/api/BeaconQuery.java b/beacon-java-rest/src/main/java/com/dnastack/beacon/rest/endpoints/BeaconQuery.java similarity index 52% rename from beacon-java-rest/src/main/java/com/dnastack/beacon/rest/api/BeaconQuery.java rename to beacon-java-rest/src/main/java/com/dnastack/beacon/rest/endpoints/BeaconQuery.java index 99333bd..ebfacae 100644 --- a/beacon-java-rest/src/main/java/com/dnastack/beacon/rest/api/BeaconQuery.java +++ b/beacon-java-rest/src/main/java/com/dnastack/beacon/rest/endpoints/BeaconQuery.java @@ -21,21 +21,24 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.dnastack.beacon.rest.api; +package com.dnastack.beacon.rest.endpoints; import com.dnastack.beacon.exceptions.BeaconException; import org.ga4gh.beacon.BeaconAlleleRequest; import org.ga4gh.beacon.BeaconAlleleResponse; +import javax.servlet.http.HttpServletRequest; import javax.ws.rs.*; +import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import java.util.List; /** - * Beacon rest resource for querying alle information from a beacon + * Beacon rest resource for querying allele information from a beacon. * * @author Miroslav Cupak (mirocupak@gmail.com) * @author Patrick Magee (patrickmageee@gmail.com) + * @author Artem (tema.voskoboynick@gmail.com) * @version 1.0 */ @Path("/query") @@ -43,35 +46,39 @@ public interface BeaconQuery { /** * Query a beacon resource for information on whether an allele exists or not. Optionally includes the datasets. - * Returns the completed BeaconAlleleResponse, Or a BeaconAlleleResponse with a BeaconError object if an error + * Returns the completed BeaconAlleleResponse or a BeaconAlleleResponse with a BeaconError object if an error * was encountered. * - * @param referenceName Name of the chromosome or contig + * @param referenceName reference name (chromosome). Accepted values: 1-22, X, Y * @param start 0-base start position - * @param referenceBases String of reference bases - * @param alternateBases String of alternate bases - * @param assemblyId String of assembly build version - * @param datasetIds List of dataset Ids - * @param includeDatasetResponses Boolean value to include Dataset responses - * @return Completed Beacon response object - * @throws BeaconException + * @param referenceBases reference bases for this variant (starting from `start`). Accepted values: see the + * REF field in VCF 4.2 specification (https://samtools.github.io/hts-specs/VCFv4.2.pdf) + * @param alternateBases the bases that appear instead of the reference bases. Accepted values: see the ALT + * field in VCF 4.2 specification (https://samtools.github.io/hts-specs/VCFv4.2.pdf) + * @param assemblyId assembly identifier (GRC notation, e.g. `GRCh37`) + * @param datasetIds identifiers of datasets. If this field is null, all datasets will be queried + * @param includeDatasetResponses indicator of whether responses for individual datasets should be included (not null) + * in the response. If null, the default value of false is assumed */ @GET @Produces({MediaType.APPLICATION_JSON}) - BeaconAlleleResponse query(@QueryParam("referenceName") String referenceName, @QueryParam("start") Long start, @QueryParam("referenceBases") String referenceBases, @QueryParam("alternateBases") String alternateBases, @QueryParam("assemblyId") String assemblyId, @QueryParam("datasetIds") List datasetIds, @QueryParam("includeDatasetResponses") Boolean includeDatasetResponses) throws BeaconException; + BeaconAlleleResponse query(@QueryParam("referenceName") String referenceName, + @QueryParam("start") Long start, + @QueryParam("referenceBases") String referenceBases, + @QueryParam("alternateBases") String alternateBases, + @QueryParam("assemblyId") String assemblyId, + @QueryParam("datasetIds") List datasetIds, + @QueryParam("includeDatasetResponses") Boolean includeDatasetResponses, + @Context HttpServletRequest servletRequest) throws BeaconException; /** * Query a beacon resource for information on whether an allele exists or not. Optionally includes the datasets. * Returns the completed BeaconAlleleResponse, Or a BeaconAlleleResponse with a BeaconError object if an error * was encountered. - * - * @param request Completed Beacon response object - * @return - * @throws BeaconException */ @POST @Consumes({MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_JSON}) - BeaconAlleleResponse query(BeaconAlleleRequest request) throws BeaconException; + BeaconAlleleResponse query(BeaconAlleleRequest request, @Context HttpServletRequest servletRequest) throws BeaconException; } diff --git a/beacon-java-rest/src/main/java/com/dnastack/beacon/rest/impl/BeaconInfoImpl.java b/beacon-java-rest/src/main/java/com/dnastack/beacon/rest/endpoints/impl/BeaconInfoImpl.java similarity index 77% rename from beacon-java-rest/src/main/java/com/dnastack/beacon/rest/impl/BeaconInfoImpl.java rename to beacon-java-rest/src/main/java/com/dnastack/beacon/rest/endpoints/impl/BeaconInfoImpl.java index 1536a9d..fd97da5 100644 --- a/beacon-java-rest/src/main/java/com/dnastack/beacon/rest/impl/BeaconInfoImpl.java +++ b/beacon-java-rest/src/main/java/com/dnastack/beacon/rest/endpoints/impl/BeaconInfoImpl.java @@ -21,30 +21,32 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.dnastack.beacon.rest.impl; +package com.dnastack.beacon.rest.endpoints.impl; +import com.dnastack.beacon.adapter.api.BeaconAdapter; import com.dnastack.beacon.exceptions.BeaconException; -import com.dnastack.beacon.rest.api.BeaconInfo; -import com.dnastack.beacon.service.api.BeaconService; +import com.dnastack.beacon.rest.endpoints.BeaconInfo; import org.ga4gh.beacon.Beacon; import javax.inject.Inject; +import javax.ws.rs.GET; import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; /** - * Beacon Info Implementation + * @author Artem (tema.voskoboynick@gmail.com) */ @Path("/") public class BeaconInfoImpl implements BeaconInfo { @Inject - private BeaconService service; + private BeaconAdapter adapter; - /** - * {@inheritDoc} - */ + @GET @Override + @Produces({MediaType.APPLICATION_JSON}) public Beacon info() throws BeaconException { - return service.queryBeacon(); + return adapter.getBeacon(); } } diff --git a/beacon-java-rest/src/main/java/com/dnastack/beacon/rest/endpoints/impl/BeaconQueryImpl.java b/beacon-java-rest/src/main/java/com/dnastack/beacon/rest/endpoints/impl/BeaconQueryImpl.java new file mode 100644 index 0000000..7b10820 --- /dev/null +++ b/beacon-java-rest/src/main/java/com/dnastack/beacon/rest/endpoints/impl/BeaconQueryImpl.java @@ -0,0 +1,114 @@ +/* + * The MIT License + * + * Copyright 2014 Miroslav Cupak (mirocupak@gmail.com). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.dnastack.beacon.rest.endpoints.impl; + +import com.dnastack.beacon.adapter.api.BeaconAdapter; +import com.dnastack.beacon.exceptions.BeaconAlleleRequestException; +import com.dnastack.beacon.exceptions.BeaconException; +import com.dnastack.beacon.rest.endpoints.BeaconQuery; +import com.dnastack.beacon.utils.Reason; +import org.apache.commons.lang.BooleanUtils; +import org.apache.commons.lang.StringUtils; +import org.ga4gh.beacon.BeaconAlleleRequest; +import org.ga4gh.beacon.BeaconAlleleResponse; + +import javax.inject.Inject; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Path; +import javax.ws.rs.core.Context; +import java.util.List; + +/** + * @author Artem (tema.voskoboynick@gmail.com) + * @author Patrick Magee (patrickmageee@gmail.com) + */ +@Path("/query") +public class BeaconQueryImpl implements BeaconQuery { + + @Inject + private BeaconAdapter adapter; + + @Override + public BeaconAlleleResponse query(String referenceName, Long start, String referenceBases, String alternateBases, + String assemblyId, List datasetIds, Boolean includeDatasetResponses, @Context HttpServletRequest servletRequest) + throws BeaconException { + validateRequest(referenceName, start, referenceBases, alternateBases, assemblyId); + BeaconAlleleRequest request = BeaconAlleleRequest.newBuilder() + .setReferenceName(referenceName) + .setStart(start) + .setReferenceBases(referenceBases) + .setAlternateBases(alternateBases) + .setAssemblyId(assemblyId) + .setDatasetIds(datasetIds) + .setIncludeDatasetResponses(BooleanUtils.isTrue(includeDatasetResponses)) + .build(); + + return adapter.getBeaconAlleleResponse(request); + } + + @Override + public BeaconAlleleResponse query(BeaconAlleleRequest request, @Context HttpServletRequest servletRequest) throws BeaconException { + validateRequest(request); + return adapter.getBeaconAlleleResponse(request); + } + + private void validateRequest(BeaconAlleleRequest request) throws BeaconAlleleRequestException { + throwIf(request == null, "Request can't be null", request); + try { + validateRequest(request.getReferenceName(), request.getStart(), request.getReferenceBases(), request.getAlternateBases(), request.getAssemblyId()); + } catch (BeaconAlleleRequestException e) { + e.setRequest(request); + throw e; + } + } + + /** + * Validates the request parameters according to the 0.3.0 beacon specifications. + */ + private void validateRequest(String referenceName, Long start, String referenceBases, String alternateBases, String assemblyId) throws BeaconAlleleRequestException { + boolean isValidReferenceName = StringUtils.isNotBlank(referenceName); + boolean isValidStart = start != null && start >= 0; + boolean isValidReferenceBases = StringUtils.isNotBlank(referenceBases); + boolean isValidAlternateBases = StringUtils.isNotBlank(alternateBases); + boolean isValidAssemblyId = StringUtils.startsWith(assemblyId, "GRCh"); + + throwIf(!isValidReferenceName, "Reference name can't be null"); + throwIf(!isValidStart, "Start position can't be null, should be 0-based positive integer"); + throwIf(!isValidReferenceBases, "Reference bases can't be null"); + throwIf(!isValidAlternateBases, "Alternate bases can't be null"); + throwIf(!isValidAssemblyId, "Assembly ID can't be null, should start with GRCh"); + } + + private static void throwIf(boolean expression, String message) throws BeaconAlleleRequestException { + if (expression) { + throw new BeaconAlleleRequestException(Reason.INVALID_REQUEST, message); + } + } + + private static void throwIf(boolean expression, String message, BeaconAlleleRequest request) throws BeaconAlleleRequestException { + if (expression) { + throw new BeaconAlleleRequestException(message, Reason.INVALID_REQUEST, request); + } + } +} diff --git a/beacon-java-rest/src/main/java/com/dnastack/beacon/rest/endpoints/impl/secured/SecuredBeaconQueryImpl.java b/beacon-java-rest/src/main/java/com/dnastack/beacon/rest/endpoints/impl/secured/SecuredBeaconQueryImpl.java new file mode 100644 index 0000000..bb76dd4 --- /dev/null +++ b/beacon-java-rest/src/main/java/com/dnastack/beacon/rest/endpoints/impl/secured/SecuredBeaconQueryImpl.java @@ -0,0 +1,105 @@ +package com.dnastack.beacon.rest.endpoints.impl.secured; + +import com.dnastack.beacon.exceptions.BeaconException; +import com.dnastack.beacon.rest.endpoints.BeaconQuery; +import com.dnastack.beacon.rest.endpoints.impl.BeaconQueryImpl; +import com.dnastack.beacon.rest.sys.security.SecurityContext; +import org.apache.commons.collections.CollectionUtils; +import org.ga4gh.beacon.BeaconAlleleRequest; +import org.ga4gh.beacon.BeaconAlleleResponse; +import org.ga4gh.beacon.BeaconDatasetAlleleResponse; +import org.keycloak.AuthorizationContext; +import org.keycloak.KeycloakSecurityContext; +import org.keycloak.representations.authorization.Permission; + +import javax.decorator.Decorator; +import javax.decorator.Delegate; +import javax.enterprise.inject.Any; +import javax.inject.Inject; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.core.Context; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Decorates the original {@link BeaconQueryImpl} to enforce security constraints. + * Role-based access is automatically checked by the Keycloak filter, this class handles more detailed security constraints. + * + * @author Artem (tema.voskoboynick@gmail.com) + * @version 1.0 + */ +@Decorator +public class SecuredBeaconQueryImpl implements BeaconQuery { + + /** + * Public role is like an admin role - can view anything. + */ + private static final String PUBLIC_ROLE = "public"; + + /** + * Users with Registered role are allowed to view only answers yes/no. + * But individual users are allowed to view individual dataset responses. + */ + private static final String REGISTERED_ROLE = "registered"; + + /** + * In Keycloak, a dataset resource name is in the "Dataset:[dataset_id]" format. + * Allowed dataset IDs are retrieved by removing the "Dataset:" prefix. + */ + private static final String DATASET_RESOURCES_PREFIX = "Dataset:"; + + @Delegate + @Any + @Inject + private BeaconQuery delegate; + + @Inject + private SecurityContext securityContext; + + @Override + public BeaconAlleleResponse query(String referenceName, Long start, String referenceBases, String alternateBases, String assemblyId, List datasetIds, Boolean includeDatasetResponses, @Context HttpServletRequest servletRequest) throws BeaconException { + BeaconAlleleResponse response = delegate.query(referenceName, start, referenceBases, alternateBases, assemblyId, datasetIds, includeDatasetResponses, servletRequest); + ensureSecurityConstraints(servletRequest, response); + return response; + } + + @Override + public BeaconAlleleResponse query(BeaconAlleleRequest request, @Context HttpServletRequest servletRequest) throws BeaconException { + BeaconAlleleResponse response = delegate.query(request, servletRequest); + ensureSecurityConstraints(servletRequest, response); + return response; + } + + private void ensureSecurityConstraints(HttpServletRequest servletRequest, BeaconAlleleResponse response) { + if (!securityContext.isSecurityEnabled()) { + return; + } + + if (servletRequest.isUserInRole(PUBLIC_ROLE)) { + return; + } + + if (servletRequest.isUserInRole(REGISTERED_ROLE)) { + excludeUnauthorizedDatasets(servletRequest, response.getDatasetAlleleResponses()); + } + } + + private void excludeUnauthorizedDatasets(HttpServletRequest servletRequest, List datasetResponses) { + if (CollectionUtils.isNotEmpty(datasetResponses)) { + Set permittedDatasetIds = getPermittedDatasetIds(servletRequest); + datasetResponses.removeIf(datasetResponse -> !permittedDatasetIds.contains(datasetResponse.getDatasetId())); + } + } + + private Set getPermittedDatasetIds(HttpServletRequest servletRequest) { + KeycloakSecurityContext keycloakSecurityContext = (KeycloakSecurityContext) servletRequest.getAttribute(KeycloakSecurityContext.class.getName()); + AuthorizationContext authzContext = keycloakSecurityContext.getAuthorizationContext(); + + List permissions = authzContext.getPermissions(); + return permissions.stream() + .filter(permission -> permission.getResourceSetName().startsWith(DATASET_RESOURCES_PREFIX)) + .map(permission -> permission.getResourceSetName().substring(DATASET_RESOURCES_PREFIX.length())) + .collect(Collectors.toSet()); + } +} diff --git a/beacon-java-rest/src/main/java/com/dnastack/beacon/rest/impl/BeaconQueryImpl.java b/beacon-java-rest/src/main/java/com/dnastack/beacon/rest/impl/BeaconQueryImpl.java deleted file mode 100644 index ff62f8d..0000000 --- a/beacon-java-rest/src/main/java/com/dnastack/beacon/rest/impl/BeaconQueryImpl.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * The MIT License - * - * Copyright 2014 Miroslav Cupak (mirocupak@gmail.com). - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.dnastack.beacon.rest.impl; - -import com.dnastack.beacon.exceptions.BeaconException; -import com.dnastack.beacon.rest.api.BeaconQuery; -import com.dnastack.beacon.service.api.BeaconService; -import org.ga4gh.beacon.BeaconAlleleRequest; -import org.ga4gh.beacon.BeaconAlleleResponse; - -import javax.inject.Inject; -import javax.ws.rs.Path; -import java.util.List; - -/** - * Beacon Query Implementation - */ -@Path("/query") -public class BeaconQueryImpl implements BeaconQuery { - - @Inject - private BeaconService service; - - /** - * {@inheritDoc} - */ - @Override - public BeaconAlleleResponse query(String referenceName, Long start, String referenceBases, String alternateBases, String assemblyId, List datasetIds, Boolean includeDatasetResponses) throws BeaconException { - return service.queryAllele(referenceName, - start, - referenceBases, - alternateBases, - assemblyId, - datasetIds, - includeDatasetResponses); - } - - /** - * {@inheritDoc} - */ - @Override - public BeaconAlleleResponse query(BeaconAlleleRequest request) throws BeaconException { - return service.queryAllele(request); - } -} diff --git a/beacon-java-rest/src/main/java/com/dnastack/beacon/rest/sys/BeaconExceptionHandler.java b/beacon-java-rest/src/main/java/com/dnastack/beacon/rest/sys/BeaconExceptionHandler.java new file mode 100644 index 0000000..aa56589 --- /dev/null +++ b/beacon-java-rest/src/main/java/com/dnastack/beacon/rest/sys/BeaconExceptionHandler.java @@ -0,0 +1,60 @@ +package com.dnastack.beacon.rest.sys; + +import com.dnastack.beacon.exceptions.BeaconAlleleRequestException; +import com.dnastack.beacon.exceptions.BeaconException; +import org.ga4gh.beacon.BeaconAlleleResponse; +import org.ga4gh.beacon.BeaconError; + +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; + +import static javax.ws.rs.core.Response.Status.BAD_REQUEST; +import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; + +/** + * Captures all Beacon exceptions and creates corresponding responses. + * + * @author patmagee + * @author Artem (tema.voskoboynick@gmail.com) + * @version 1.0 + */ +@Provider +public class BeaconExceptionHandler implements ExceptionMapper { + + @Override + public Response toResponse(BeaconException exception) { + if (exception instanceof BeaconAlleleRequestException) { + return handleBeaconAlleleRequestException((BeaconAlleleRequestException) exception); + } else { + return handleDefaultBeaconException(exception); + } + } + + /** + * Due to backward compatibility issues, the response on Beacon Query request should always be of the {@link BeaconAlleleResponse} type, + * and any errors, if any, are stored inside it. + */ + private Response handleBeaconAlleleRequestException(BeaconAlleleRequestException exception) { + BeaconAlleleResponse response = new BeaconAlleleResponse(); + response.setAlleleRequest(exception.getRequest()); + response.setExists(null); + + BeaconError error = BeaconError.newBuilder() + .setErrorCode(BAD_REQUEST.getStatusCode()) + .setMessage(exception.getMessage()) + .build(); + response.setError(error); + + return Response.status(error.getErrorCode()).entity(response).build(); + } + + private Response handleDefaultBeaconException(BeaconException exception) { + BeaconError response = BeaconError.newBuilder() + .setMessage(exception.getMessage()) + .setErrorCode(INTERNAL_SERVER_ERROR.getStatusCode()) + .build(); + + return Response.status(response.getErrorCode()).entity(response).build(); + } +} diff --git a/beacon-java-rest/src/main/java/com/dnastack/beacon/providers/DefaultExceptionHandler.java b/beacon-java-rest/src/main/java/com/dnastack/beacon/rest/sys/DefaultExceptionHandler.java similarity index 61% rename from beacon-java-rest/src/main/java/com/dnastack/beacon/providers/DefaultExceptionHandler.java rename to beacon-java-rest/src/main/java/com/dnastack/beacon/rest/sys/DefaultExceptionHandler.java index 8baa181..6915686 100644 --- a/beacon-java-rest/src/main/java/com/dnastack/beacon/providers/DefaultExceptionHandler.java +++ b/beacon-java-rest/src/main/java/com/dnastack/beacon/rest/sys/DefaultExceptionHandler.java @@ -21,40 +21,33 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.dnastack.beacon.providers; +package com.dnastack.beacon.rest.sys; -import com.dnastack.beacon.util.ResponseMappingResource; import org.ga4gh.beacon.BeaconError; -import org.json.simple.JSONObject; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.ext.ExceptionMapper; import javax.ws.rs.ext.Provider; +import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; + /** - * Default Exception handler to catch all Non Beacon exceptions thrown by the rest resources.z + * Catches all non Beacon exceptions thrown by the rest resources. * * @author patmagee + * @author Artem (tema.voskoboynick@gmail.com) */ @Provider -public class DefaultExceptionHandler implements ExceptionMapper { +public class DefaultExceptionHandler implements ExceptionMapper { - /** - * Default error handler to capture all errors and send the user a JSON response - * with the status code, reason, message and stacktrace of the error - * - * @param e exceptions - * @return response with the status and error entity - */ @Override - public Response toResponse(Exception e) { - JSONObject json = new JSONObject(); - Response.Status s = ResponseMappingResource.getStatus(e); + public Response toResponse(Throwable exception) { + BeaconError response = BeaconError.newBuilder() + .setMessage(exception.getMessage()) + .setErrorCode(INTERNAL_SERVER_ERROR.getStatusCode()) + .build(); - BeaconError error = new BeaconError(); - error.setMessage(e.getMessage()); - error.setErrorCode(s.getStatusCode()); - return Response.status(s).entity(error).type(MediaType.APPLICATION_JSON_TYPE).build(); + return Response.status(response.getErrorCode()).type(MediaType.APPLICATION_JSON).entity(response).build(); } } diff --git a/beacon-java-rest/src/main/java/com/dnastack/beacon/providers/GsonMessageBodyHandler.java b/beacon-java-rest/src/main/java/com/dnastack/beacon/rest/sys/GsonMessageBodyHandler.java similarity index 62% rename from beacon-java-rest/src/main/java/com/dnastack/beacon/providers/GsonMessageBodyHandler.java rename to beacon-java-rest/src/main/java/com/dnastack/beacon/rest/sys/GsonMessageBodyHandler.java index d8bed74..81d3b94 100644 --- a/beacon-java-rest/src/main/java/com/dnastack/beacon/providers/GsonMessageBodyHandler.java +++ b/beacon-java-rest/src/main/java/com/dnastack/beacon/rest/sys/GsonMessageBodyHandler.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.dnastack.beacon.providers; +package com.dnastack.beacon.rest.sys; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -37,11 +37,13 @@ import java.io.*; import java.lang.annotation.Annotation; import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; /** - * Gson Message Body Handler for serializing JSON data to and from auto generated Java classes + * Gson Message Body Handler for serializing JSON data to and from auto generated Java classes. * * @author patmagee + * @author Artem (tema.voskoboynick@gmail.com) * @version 1.0 */ @Provider @@ -49,16 +51,8 @@ @Consumes(MediaType.APPLICATION_JSON) public class GsonMessageBodyHandler implements MessageBodyReader, MessageBodyWriter { - private static final String UTF_8 = "UTF-8"; - - private Gson gson; - - private Gson getGson() { - if (gson == null) { - gson = new GsonBuilder().create(); - } - return gson; - } + private static final Gson GSON = new GsonBuilder().create(); + private static final int UNKNOWN_LENGTH = -1; @Override public boolean isReadable(Class aClass, Type type, Annotation[] annotations, MediaType mediaType) { @@ -66,20 +60,18 @@ public boolean isReadable(Class aClass, Type type, Annotation[] annotations, } @Override - public Object readFrom(Class aClass, Type type, Annotation[] annotations, MediaType mediaType, MultivaluedMap multivaluedMap, InputStream inputStream) throws IOException, WebApplicationException { - InputStreamReader streamReader = new InputStreamReader(inputStream, UTF_8); - try { - Type jsonType; - if (type == null) { - jsonType = aClass; - } else if (aClass.equals(type)) { - jsonType = aClass; - } else { - jsonType = type; - } - return getGson().fromJson(streamReader, jsonType); - } finally { - streamReader.close(); + public Object readFrom(Class aClass, Type type, Annotation[] annotations, MediaType mediaType, + MultivaluedMap multivaluedMap, InputStream inputStream) throws IOException, + WebApplicationException { + Type objectType; + if (type == null || aClass.equals(type)) { + objectType = aClass; + } else { + objectType = type; + } + + try (InputStreamReader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8)) { + return GSON.fromJson(reader, objectType); } } @@ -90,24 +82,22 @@ public boolean isWriteable(Class aClass, Type type, Annotation[] annotations, @Override public long getSize(Object o, Class aClass, Type type, Annotation[] annotations, MediaType mediaType) { - return -1; + return UNKNOWN_LENGTH; } @Override - public void writeTo(Object o, Class aClass, Type type, Annotation[] annotations, MediaType mediaType, MultivaluedMap multivaluedMap, OutputStream outputStream) throws IOException, WebApplicationException { - OutputStreamWriter writer = new OutputStreamWriter(outputStream, UTF_8); - try { - Type jsonType; - if (type == null) { - jsonType = aClass; - } else if (aClass.equals(type)) { - jsonType = aClass; - } else { - jsonType = type; - } - getGson().toJson(o, jsonType, writer); - } finally { - writer.close(); + public void writeTo(Object o, Class aClass, Type type, Annotation[] annotations, MediaType mediaType, + MultivaluedMap multivaluedMap, OutputStream outputStream) throws IOException, + WebApplicationException { + Type objectType; + if (type == null || aClass.equals(type)) { + objectType = aClass; + } else { + objectType = type; + } + + try (OutputStreamWriter writer = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8)) { + GSON.toJson(o, objectType, writer); } } } diff --git a/beacon-java-rest/src/main/java/com/dnastack/beacon/rest/sys/security/ConfigurableKeycloakOIDCFilter.java b/beacon-java-rest/src/main/java/com/dnastack/beacon/rest/sys/security/ConfigurableKeycloakOIDCFilter.java new file mode 100644 index 0000000..586d409 --- /dev/null +++ b/beacon-java-rest/src/main/java/com/dnastack/beacon/rest/sys/security/ConfigurableKeycloakOIDCFilter.java @@ -0,0 +1,35 @@ +package com.dnastack.beacon.rest.sys.security; + +import org.keycloak.adapters.servlet.KeycloakOIDCFilter; + +import javax.inject.Inject; +import javax.servlet.*; +import java.io.IOException; + +/** + * Extends Keycloak security filter to allow enabling/disabling it depending on a context parameter. + * + * @author Artem (tema.voskoboynick@gmail.com) + * @version 1.0 + */ +public class ConfigurableKeycloakOIDCFilter extends KeycloakOIDCFilter { + + @Inject + private SecurityContext securityContext; + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + if (securityContext.isSecurityEnabled()) { + super.init(filterConfig); + } + } + + @Override + public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { + if (securityContext.isSecurityEnabled()) { + super.doFilter(req, res, chain); + } else { + chain.doFilter(req, res); + } + } +} diff --git a/beacon-java-rest/src/main/java/com/dnastack/beacon/rest/sys/security/SecurityContext.java b/beacon-java-rest/src/main/java/com/dnastack/beacon/rest/sys/security/SecurityContext.java new file mode 100644 index 0000000..f66c186 --- /dev/null +++ b/beacon-java-rest/src/main/java/com/dnastack/beacon/rest/sys/security/SecurityContext.java @@ -0,0 +1,19 @@ +package com.dnastack.beacon.rest.sys.security; + +import javax.enterprise.context.Dependent; +import javax.inject.Inject; +import javax.servlet.ServletContext; + +/** + * @author Artem (tema.voskoboynick@gmail.com) + * @version 1.0 + */ +@Dependent +public class SecurityContext { + @Inject + private ServletContext servletContext; + + public boolean isSecurityEnabled() { + return Boolean.parseBoolean(servletContext.getInitParameter("keycloak.security.enabled")); + } +} diff --git a/beacon-java-rest/src/main/java/com/dnastack/beacon/util/ResponseMappingResource.java b/beacon-java-rest/src/main/java/com/dnastack/beacon/util/ResponseMappingResource.java deleted file mode 100644 index 993529a..0000000 --- a/beacon-java-rest/src/main/java/com/dnastack/beacon/util/ResponseMappingResource.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * The MIT License - * - * Copyright 2014 Miroslav Cupak (mirocupak@gmail.com). - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.dnastack.beacon.util; - -import javax.persistence.NoResultException; -import javax.ws.rs.NotFoundException; -import javax.ws.rs.core.Response; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -/** - * Created by patrickmagee on 2016-06-16. - */ -public class ResponseMappingResource { - - private static final Map mapping; - - static { - Map map = new HashMap<>(); - map.put(NotFoundException.class.getCanonicalName(), Response.Status.NOT_FOUND); - map.put(NoResultException.class.getCanonicalName(), Response.Status.NOT_FOUND); - map.put(IllegalArgumentException.class.getCanonicalName(), Response.Status.BAD_REQUEST); - map.put(UnsupportedOperationException.class.getCanonicalName(), Response.Status.METHOD_NOT_ALLOWED); - map.put(SecurityException.class.getCanonicalName(), Response.Status.FORBIDDEN); - mapping = Collections.unmodifiableMap(map); - } - - /** - * Retrieves Response.Status from a given exceptions according to the static mapping. If the mapping does not contain - * the exceptions, INTERNAL_SERVER_ERROR is returned. - * - * @param ex exceptions - * @return response status - */ - public static Response.Status getStatus(Exception ex) { - Response.Status s = mapping.get(ex.getClass().getCanonicalName()); - - return (s == null) ? Response.Status.INTERNAL_SERVER_ERROR : s; - } -} diff --git a/beacon-java-rest/src/main/webapp/META-INF/beans.xml b/beacon-java-rest/src/main/webapp/WEB-INF/beans.xml similarity index 56% rename from beacon-java-rest/src/main/webapp/META-INF/beans.xml rename to beacon-java-rest/src/main/webapp/WEB-INF/beans.xml index 28f1f92..5416028 100644 --- a/beacon-java-rest/src/main/webapp/META-INF/beans.xml +++ b/beacon-java-rest/src/main/webapp/WEB-INF/beans.xml @@ -1,5 +1,8 @@ + bean-discovery-mode="all" version="1.2"> + + com.dnastack.beacon.rest.endpoints.impl.secured.SecuredBeaconQueryImpl + \ No newline at end of file diff --git a/beacon-java-rest/src/main/webapp/WEB-INF/keycloak.json b/beacon-java-rest/src/main/webapp/WEB-INF/keycloak.json new file mode 100644 index 0000000..883a0c0 --- /dev/null +++ b/beacon-java-rest/src/main/webapp/WEB-INF/keycloak.json @@ -0,0 +1,11 @@ +{ + "realm": "beacon-realm", + "realm-public-key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsRX+fPYkN9fXgbRq9BCrVW8F2HrWUV4ss3bpeeT71n04uqD+v0Q9R9rfR3wG7uf/iB3PVASkmVS2Up7UwsuOZn2dmzyCRmom4O+oj6lzFWgSYuGwmY2rpmDg1m7apDR9cMODXI9D8Yqk7miGDo6+W10Xh9vA/IXH24OuuI4Mgs1mEoSZZC068YsRr9J0Dt58ttkr7PHCm7P2wqr5fDRhfY67q2w1lMW384GZxA7x0P7MGUebd1swPuQvZUyZuU/lVLca/lRX7D8g4gK9NYyUI/FBGUf4EPFVvYjmfmIvl3HWerkkZuL9U8Etr0WaOMig1sY4KWbQiaQtQVsV7V4ASQIDAQAB", + "auth-server-url": "http://localhost:8280/auth", + "ssl-required": "external", + "resource": "beacon-rest", + "credentials": { + "secret": "01d0bc41-1365-4922-b1ec-a241196735fa" + }, + "policy-enforcer": {} +} \ No newline at end of file diff --git a/beacon-java-rest/src/main/webapp/WEB-INF/web.xml b/beacon-java-rest/src/main/webapp/WEB-INF/web.xml index 94d318d..abe14a0 100644 --- a/beacon-java-rest/src/main/webapp/WEB-INF/web.xml +++ b/beacon-java-rest/src/main/webapp/WEB-INF/web.xml @@ -16,4 +16,21 @@ 30 + + + + + keycloak.security.enabled + false + + + + Keycloak Filter + com.dnastack.beacon.rest.sys.security.ConfigurableKeycloakOIDCFilter + + + Keycloak Filter + /keycloak/* + /query/* + diff --git a/beacon-java-rest/src/test/java/com/dnastack/beacon/rest/BeaconTests.java b/beacon-java-rest/src/test/java/com/dnastack/beacon/rest/BeaconTests.java index d5843d1..e10c9b8 100644 --- a/beacon-java-rest/src/test/java/com/dnastack/beacon/rest/BeaconTests.java +++ b/beacon-java-rest/src/test/java/com/dnastack/beacon/rest/BeaconTests.java @@ -45,6 +45,8 @@ import java.net.URL; import static com.jayway.restassured.RestAssured.given; +import static javax.ws.rs.core.Response.Status.BAD_REQUEST; +import static javax.ws.rs.core.Response.Status.OK; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.not; @@ -52,6 +54,7 @@ * Test suite run in an arquillian container against a beacon implementation * * @author patrickmagee. + * @author Artem (tema.voskoboynick@gmail.com) */ @RunWith(Arquillian.class) @RunAsClient @@ -67,16 +70,16 @@ public class BeaconTests { @Override protected void starting(Description description) { - System.out.println("Starting test: " + description.getClassName() + " - " + description.getMethodName() + "()"); + System.out.println(String.format("Starting test: %s - %s()", description.getClassName(), description.getMethodName())); } }; @Deployment(testable = false) public static WebArchive createDeployment() { WebArchive war = ShrinkWrap.create(MavenImporter.class) - .loadPomFromFile("pom.xml") - .importBuildOutput() - .as(WebArchive.class); + .loadPomFromFile("pom.xml") + .importBuildOutput() + .as(WebArchive.class); System.out.println("WAR name: " + war.getName()); return war; } @@ -86,12 +89,11 @@ public static WebArchive createDeployment() { */ @Test public void testGetBeacon() { - Beacon beacon = given().accept(ContentType.JSON) - .get(baseUrl) - .then() - .extract() - .as(Beacon.class, ObjectMapperType.GSON); + .get(baseUrl) + .then() + .extract() + .as(Beacon.class, ObjectMapperType.GSON); assertThat(beacon.getName()).isNotNull(); assertThat(beacon.getApiVersion()).isEqualTo(API_VERSION); @@ -105,7 +107,8 @@ public void testGetBeacon() { */ @Test public void testPostBeaconNotSupported() { - given().accept(ContentType.JSON).post(baseUrl).then().assertThat().statusCode(not(200)); + given().accept(ContentType.JSON).post(baseUrl) + .then().assertThat().statusCode(not(OK.getStatusCode())); } @@ -114,7 +117,8 @@ public void testPostBeaconNotSupported() { */ @Test public void testDeleteBeaconNotSupported() { - given().accept(ContentType.JSON).delete(baseUrl).then().assertThat().statusCode(not(200)); + given().accept(ContentType.JSON).delete(baseUrl) + .then().assertThat().statusCode(not(OK.getStatusCode())); } /** @@ -122,7 +126,8 @@ public void testDeleteBeaconNotSupported() { */ @Test public void testPutBeaconNotSupported() { - given().accept(ContentType.JSON).put(baseUrl).then().assertThat().statusCode(not(200)); + given().accept(ContentType.JSON).put(baseUrl) + .then().assertThat().statusCode(not(OK.getStatusCode())); } /** @@ -131,66 +136,63 @@ public void testPutBeaconNotSupported() { */ @Test public void testGetAllele() throws InterruptedException { - Beacon beacon = given().accept(ContentType.JSON) - .get(baseUrl) - .then() - .extract() - .as(Beacon.class, ObjectMapperType.GSON); + .get(baseUrl) + .then() + .extract() + .as(Beacon.class, ObjectMapperType.GSON); BeaconAlleleRequest request = beacon.getSampleAlleleRequests().get(0); - BeaconAlleleResponse out = given().accept(ContentType.JSON) - .queryParam("referenceName", request.getReferenceName()) - .queryParam("start", request.getStart()) - .queryParam("referenceBases", request.getReferenceBases()) - .queryParam("alternateBases", request.getAlternateBases()) - .queryParam("assemblyId", request.getAssemblyId()) - .queryParam("datasetIds", request.getDatasetIds()) - .queryParam("includeDatasetResponses", request.getIncludeDatasetResponses()) - .get(baseUrl + "query") - .then() - .extract() - .as(BeaconAlleleResponse.class, ObjectMapperType.GSON); - - assertThat(out.getAlleleRequest()).isNotNull(); - assertThat(out.getExists()).isTrue(); + BeaconAlleleResponse response = given().accept(ContentType.JSON) + .queryParam("referenceName", request.getReferenceName()) + .queryParam("start", request.getStart()) + .queryParam("referenceBases", request.getReferenceBases()) + .queryParam("alternateBases", request.getAlternateBases()) + .queryParam("assemblyId", request.getAssemblyId()) + .queryParam("datasetIds", request.getDatasetIds()) + .queryParam("includeDatasetResponses", request.getIncludeDatasetResponses()) + .get(baseUrl + "query") + .then() + .extract() + .as(BeaconAlleleResponse.class, ObjectMapperType.GSON); + + assertThat(response.getAlleleRequest()).isNotNull(); + assertThat(response.getExists()).isTrue(); if (request.getIncludeDatasetResponses()) { - assertThat(out.getDatasetAlleleResponses()).isNotEmpty(); + assertThat(response.getDatasetAlleleResponses()).isNotEmpty(); } - assertThat(out.getBeaconId()).isEqualTo(beacon.getId()); - assertThat(out.getError()).isNull(); + assertThat(response.getBeaconId()).isEqualTo(beacon.getId()); + assertThat(response.getError()).isNull(); } /** - * Test to make sure that you can post a BeaconAlleleResponse from /query enpoint, and that it complies + * Test to make sure that you can post a BeaconAlleleResponse from /query endpoint, and that it complies * with the current beacon spec. Uses the sampleAlleleRequest provided by the beacon */ @Test public void testPostAllele() { - Beacon beacon = given().accept(ContentType.JSON) - .get(baseUrl) - .then() - .extract() - .as(Beacon.class, ObjectMapperType.GSON); + .get(baseUrl) + .then() + .extract() + .as(Beacon.class, ObjectMapperType.GSON); BeaconAlleleRequest request = beacon.getSampleAlleleRequests().get(0); - BeaconAlleleResponse out = given().contentType(ContentType.JSON) - .accept(ContentType.JSON) - .body(request, ObjectMapperType.GSON) - .post(baseUrl + "query") - .then() - .extract() - .as(BeaconAlleleResponse.class, ObjectMapperType.GSON); + BeaconAlleleResponse response = given().contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body(request, ObjectMapperType.GSON) + .post(baseUrl + "query") + .then() + .extract() + .as(BeaconAlleleResponse.class, ObjectMapperType.GSON); - assertThat(out.getAlleleRequest()).isEqualByComparingTo(request); - assertThat(out.getExists()).isTrue(); + assertThat(response.getAlleleRequest()).isEqualByComparingTo(request); + assertThat(response.getExists()).isTrue(); if (request.getIncludeDatasetResponses()) { - assertThat(out.getDatasetAlleleResponses()).isNotEmpty(); + assertThat(response.getDatasetAlleleResponses()).isNotEmpty(); } - assertThat(out.getBeaconId()).isEqualTo(beacon.getId()); - assertThat(out.getError()).isNull(); - + assertThat(response.getBeaconId()).isEqualTo(beacon.getId()); + assertThat(response.getError()).isNull(); } /** @@ -198,7 +200,8 @@ public void testPostAllele() { */ @Test public void testDeleteAlleleNotSupported() { - given().delete(baseUrl + "query").then().assertThat().statusCode(not(200)); + given().delete(baseUrl + "query") + .then().assertThat().statusCode(not(OK.getStatusCode())); } /** @@ -206,7 +209,8 @@ public void testDeleteAlleleNotSupported() { */ @Test public void testPutAlleleNotSupported() { - given().put(baseUrl + "query").then().assertThat().statusCode(not(200)); + given().put(baseUrl + "query") + .then().assertThat().statusCode(not(OK.getStatusCode())); } /** @@ -214,26 +218,21 @@ public void testPutAlleleNotSupported() { */ @Test public void testPostInvalidRequest() { - Beacon beacon = given().accept(ContentType.JSON) - .get(baseUrl) - .then() - .extract() - .as(Beacon.class, ObjectMapperType.GSON); - BeaconAlleleRequest request = beacon.getSampleAlleleRequests().get(0); + BeaconAlleleRequest request = getSampleAlleleRequest(); request.setReferenceName(null); request.setReferenceBases(null); BeaconAlleleResponse out = given().accept(ContentType.JSON) - .contentType(ContentType.JSON) - .body(request, ObjectMapperType.GSON) - .post(baseUrl + "query") - .then() - .extract() - .as(BeaconAlleleResponse.class, ObjectMapperType.GSON); + .contentType(ContentType.JSON) + .body(request, ObjectMapperType.GSON) + .post(baseUrl + "query") + .then() + .extract() + .as(BeaconAlleleResponse.class, ObjectMapperType.GSON); assertThat(out.getExists()).isNull(); assertThat(out.getError()).isNotNull(); - assertThat(out.getError().getErrorCode()).isEqualTo(400); + assertThat(out.getError().getErrorCode()).isEqualTo(BAD_REQUEST.getStatusCode()); } /** @@ -241,27 +240,22 @@ public void testPostInvalidRequest() { */ @Test public void testGetAlleleWithMissingRequiredParams() { - Beacon beacon = given().accept(ContentType.JSON) - .get(baseUrl) - .then() - .extract() - .as(Beacon.class, ObjectMapperType.GSON); - BeaconAlleleRequest request = beacon.getSampleAlleleRequests().get(0); - - BeaconAlleleResponse out = given().accept(ContentType.JSON) - .queryParam("referenceBases", request.getReferenceBases()) - .queryParam("alternateBases", request.getAlternateBases()) - .queryParam("assemblyId", request.getAssemblyId()) - .queryParam("datasetIds", request.getDatasetIds()) - .queryParam("includeDatasetResponses", request.getIncludeDatasetResponses()) - .get(baseUrl + "query") - .then() - .extract() - .as(BeaconAlleleResponse.class, ObjectMapperType.GSON); - - assertThat(out.getExists()).isNull(); - assertThat(out.getError()).isNotNull(); - assertThat(out.getError().getErrorCode()).isEqualTo(400); + BeaconAlleleRequest request = getSampleAlleleRequest(); + + BeaconAlleleResponse response = given().accept(ContentType.JSON) + .queryParam("referenceBases", request.getReferenceBases()) + .queryParam("alternateBases", request.getAlternateBases()) + .queryParam("assemblyId", request.getAssemblyId()) + .queryParam("datasetIds", request.getDatasetIds()) + .queryParam("includeDatasetResponses", request.getIncludeDatasetResponses()) + .get(baseUrl + "query") + .then() + .extract() + .as(BeaconAlleleResponse.class, ObjectMapperType.GSON); + + assertThat(response.getExists()).isNull(); + assertThat(response.getError()).isNotNull(); + assertThat(response.getError().getErrorCode()).isEqualTo(BAD_REQUEST.getStatusCode()); } /** @@ -269,30 +263,24 @@ public void testGetAlleleWithMissingRequiredParams() { */ @Test public void testGetAlleleWithMissingOptionalParams() { - Beacon beacon = given().accept(ContentType.JSON) - .get(baseUrl) - .then() - .extract() - .as(Beacon.class, ObjectMapperType.GSON); - BeaconAlleleRequest request = beacon.getSampleAlleleRequests().get(0); + BeaconAlleleRequest request = getSampleAlleleRequest(); request.setIncludeDatasetResponses(false); - BeaconAlleleResponse out = given().accept(ContentType.JSON) - .queryParam("referenceName", request.getReferenceName()) - .queryParam("start", request.getStart()) - .queryParam("referenceBases", request.getReferenceBases()) - .queryParam("alternateBases", request.getAlternateBases()) - .queryParam("assemblyId", request.getAssemblyId()) - .queryParam("datasetIds", request.getDatasetIds()) - .get(baseUrl + "query") - .then() - .extract() - .as(BeaconAlleleResponse.class, ObjectMapperType.GSON); - - assertThat(out.getAlleleRequest()).isEqualByComparingTo(request); - assertThat(out.getExists()).isTrue(); - assertThat(out.getDatasetAlleleResponses()).isNullOrEmpty(); - + BeaconAlleleResponse response = given().accept(ContentType.JSON) + .queryParam("referenceName", request.getReferenceName()) + .queryParam("start", request.getStart()) + .queryParam("referenceBases", request.getReferenceBases()) + .queryParam("alternateBases", request.getAlternateBases()) + .queryParam("assemblyId", request.getAssemblyId()) + .queryParam("datasetIds", request.getDatasetIds()) + .get(baseUrl + "query") + .then() + .extract() + .as(BeaconAlleleResponse.class, ObjectMapperType.GSON); + + assertThat(response.getAlleleRequest()).isEqualByComparingTo(request); + assertThat(response.getExists()).isTrue(); + assertThat(response.getDatasetAlleleResponses()).isNullOrEmpty(); } /** @@ -300,30 +288,25 @@ public void testGetAlleleWithMissingOptionalParams() { */ @Test public void testGetAlleleWithDataSets() { - Beacon beacon = given().accept(ContentType.JSON) - .get(baseUrl) - .then() - .extract() - .as(Beacon.class, ObjectMapperType.GSON); - BeaconAlleleRequest request = beacon.getSampleAlleleRequests().get(0); - - BeaconAlleleResponse out = given().accept(ContentType.JSON) - .queryParam("referenceName", request.getReferenceName()) - .queryParam("start", request.getStart()) - .queryParam("referenceBases", request.getReferenceBases()) - .queryParam("alternateBases", request.getAlternateBases()) - .queryParam("assemblyId", request.getAssemblyId()) - .queryParam("datasetIds", request.getDatasetIds()) - .queryParam("includeDatasetResponses", request.getIncludeDatasetResponses()) - .get(baseUrl + "query") - .then() - .extract() - .as(BeaconAlleleResponse.class, ObjectMapperType.GSON); - assertThat(out.getAlleleRequest()).isEqualByComparingTo(request); - assertThat(out.getExists()).isTrue(); - assertThat(out.getDatasetAlleleResponses()).isNotNull(); - assertThat(out.getDatasetAlleleResponses()).isNotEmpty(); - + BeaconAlleleRequest request = getSampleAlleleRequest(); + + BeaconAlleleResponse response = given().accept(ContentType.JSON) + .queryParam("referenceName", request.getReferenceName()) + .queryParam("start", request.getStart()) + .queryParam("referenceBases", request.getReferenceBases()) + .queryParam("alternateBases", request.getAlternateBases()) + .queryParam("assemblyId", request.getAssemblyId()) + .queryParam("datasetIds", request.getDatasetIds()) + .queryParam("includeDatasetResponses", request.getIncludeDatasetResponses()) + .get(baseUrl + "query") + .then() + .extract() + .as(BeaconAlleleResponse.class, ObjectMapperType.GSON); + + assertThat(response.getAlleleRequest()).isEqualByComparingTo(request); + assertThat(response.getExists()).isTrue(); + assertThat(response.getDatasetAlleleResponses()).isNotNull(); + assertThat(response.getDatasetAlleleResponses()).isNotEmpty(); } /** @@ -331,26 +314,21 @@ public void testGetAlleleWithDataSets() { */ @Test public void testGetAlleleWithoutDatasets() { - Beacon beacon = given().accept(ContentType.JSON) - .get(baseUrl) - .then() - .extract() - .as(Beacon.class, ObjectMapperType.GSON); - BeaconAlleleRequest request = beacon.getSampleAlleleRequests().get(0); + BeaconAlleleRequest request = getSampleAlleleRequest(); request.setIncludeDatasetResponses(false); BeaconAlleleResponse out = given().accept(ContentType.JSON) - .queryParam("referenceName", request.getReferenceName()) - .queryParam("start", request.getStart()) - .queryParam("referenceBases", request.getReferenceBases()) - .queryParam("alternateBases", request.getAlternateBases()) - .queryParam("assemblyId", request.getAssemblyId()) - .queryParam("datasetIds", request.getDatasetIds()) - .queryParam("includeDatasetResponses", false) - .get(baseUrl + "query") - .then() - .extract() - .as(BeaconAlleleResponse.class, ObjectMapperType.GSON); + .queryParam("referenceName", request.getReferenceName()) + .queryParam("start", request.getStart()) + .queryParam("referenceBases", request.getReferenceBases()) + .queryParam("alternateBases", request.getAlternateBases()) + .queryParam("assemblyId", request.getAssemblyId()) + .queryParam("datasetIds", request.getDatasetIds()) + .queryParam("includeDatasetResponses", false) + .get(baseUrl + "query") + .then() + .extract() + .as(BeaconAlleleResponse.class, ObjectMapperType.GSON); assertThat(out.getAlleleRequest()).isEqualByComparingTo(request); assertThat(out.getExists()).isTrue(); @@ -358,4 +336,12 @@ public void testGetAlleleWithoutDatasets() { assertThat(out.getDatasetAlleleResponses()).isNullOrEmpty(); } + private BeaconAlleleRequest getSampleAlleleRequest() { + Beacon beacon = given().accept(ContentType.JSON) + .get(baseUrl) + .then() + .extract() + .as(Beacon.class, ObjectMapperType.GSON); + return beacon.getSampleAlleleRequests().get(0); + } } diff --git a/beacon-java-rest/src/test/resources/arquillian.xml b/beacon-java-rest/src/test/resources/arquillian.xml index aa7193d..c45c19f 100644 --- a/beacon-java-rest/src/test/resources/arquillian.xml +++ b/beacon-java-rest/src/test/resources/arquillian.xml @@ -7,13 +7,15 @@ target/ - - - + + + true + target/wildfly-8.2.1.Final + + -Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=n + -Djboss.socket.binding.port-offset=100 + + 10090 + diff --git a/beacon-java-service/pom.xml b/beacon-java-service/pom.xml deleted file mode 100644 index b8ee25c..0000000 --- a/beacon-java-service/pom.xml +++ /dev/null @@ -1,78 +0,0 @@ - - - - beacon-java - com.dnastack - 1.0-SNAPSHOT - - 4.0.0 - - beacon-java-service - - - - MIT License - http://www.opensource.org/licenses/mit-license.php - repo - - - - - - patmagee - Patrick Magee - patrickmageee@gmail.com - https://github.com/patmagee - - - - - - javax.enterprise - cdi-api - provided - - - javax - javaee-api - provided - - - org.jboss.spec.javax.annotation - jboss-annotations-api_1.2_spec - provided - - - org.jboss.resteasy - jaxrs-api - provided - - - org.ga4gh - beacon - ${version.beacon.api} - - - com.dnastack - beacon-adapter-api - - - - ${project.artifactId} - - - maven-jar-plugin - - - maven-compiler-plugin - - 1.8 - 1.8 - - - - - - \ No newline at end of file diff --git a/beacon-java-service/src/main/java/com/dnastack/beacon/service/api/BeaconService.java b/beacon-java-service/src/main/java/com/dnastack/beacon/service/api/BeaconService.java deleted file mode 100644 index 12e404d..0000000 --- a/beacon-java-service/src/main/java/com/dnastack/beacon/service/api/BeaconService.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * The MIT License - * - * Copyright 2014 Miroslav Cupak (mirocupak@gmail.com). - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.dnastack.beacon.service.api; - -import com.dnastack.beacon.exceptions.BeaconAlleleRequestException; -import com.dnastack.beacon.exceptions.BeaconException; -import org.apache.avro.AvroRemoteException; -import org.ga4gh.beacon.Beacon; -import org.ga4gh.beacon.BeaconAlleleRequest; -import org.ga4gh.beacon.BeaconAlleleResponse; - -import java.util.List; - -/** - * Beacon service abstract class - * - * @author Miroslav Cupak (mirocupak@gmail.com) - * @version 1.0 - */ -public interface BeaconService { - - /** - * Get a beacon allele response from a beacon allele request. - * - * @param referenceName Not null chromosome name - * @param start Not null start position - * @param referenceBases Not null reference bases - * @param alternateBases Not null - * @param assemblyId Not null assembly version prefixed with GRCh - * @param datasetIds Dataset Ids to search, if null search all - * @param includeDatasetResponses Include datasets in return. - * @return Beacon allele response - * @throws AvroRemoteException - */ - BeaconAlleleResponse queryAllele(String referenceName, Long start, String referenceBases, String alternateBases, String assemblyId, List datasetIds, Boolean includeDatasetResponses) throws BeaconException; - - /** - * Get a beacon allele response from the beacon, given a BeaconAlleleRequest - * - * @param request Not null request object - * @return - * @throws BeaconAlleleRequestException - */ - BeaconAlleleResponse queryAllele(BeaconAlleleRequest request) throws BeaconException; - - /** - * Get information for a single beacon. - * - * @return - * @throws BeaconException - */ - Beacon queryBeacon() throws BeaconException; - -} diff --git a/beacon-java-service/src/main/java/com/dnastack/beacon/service/impl/BeaconServiceImpl.java b/beacon-java-service/src/main/java/com/dnastack/beacon/service/impl/BeaconServiceImpl.java deleted file mode 100644 index 97cfe2c..0000000 --- a/beacon-java-service/src/main/java/com/dnastack/beacon/service/impl/BeaconServiceImpl.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * The MIT License - * - * Copyright 2014 Miroslav Cupak (mirocupak@gmail.com). - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.dnastack.beacon.service.impl; - -import com.dnastack.beacon.adapter.api.BeaconAdapter; -import com.dnastack.beacon.exceptions.BeaconAlleleRequestException; -import com.dnastack.beacon.exceptions.BeaconException; -import com.dnastack.beacon.service.api.BeaconService; -import com.dnastack.beacon.utils.Reason; -import org.ga4gh.beacon.Beacon; -import org.ga4gh.beacon.BeaconAlleleRequest; -import org.ga4gh.beacon.BeaconAlleleResponse; - -import javax.enterprise.context.RequestScoped; -import javax.inject.Inject; -import java.util.List; - -/** - * Beacon service implementation. Ensures the request are valid, and throws the appropriate exceptions - * - * @author patrickmagee - */ -@RequestScoped -public class BeaconServiceImpl implements BeaconService { - - @Inject - private BeaconAdapter adapter; - - /** - * Validate the beacon fields according to the 0.3.0 beacon specifications - * - * @param referenceName - * @param start - * @param referenceBases - * @param alternateBases - * @param assemblyId - * @throws BeaconAlleleRequestException - */ - private void validateRequest(String referenceName, Long start, String referenceBases, String alternateBases, String assemblyId) throws BeaconAlleleRequestException { - if (referenceName == null) { - throw new BeaconAlleleRequestException(Reason.INVALID_REQUEST, - "Reference cannot be null. Please provide an appropriate reference name"); - } else if (start == null) { - throw new BeaconAlleleRequestException(Reason.INVALID_REQUEST, - "Start position cannot be null. Please provide a 0-based start position"); - } else if (referenceBases == null) { - throw new BeaconAlleleRequestException(Reason.INVALID_REQUEST, "Reference bases cannot be null"); - } else if (alternateBases == null) { - throw new BeaconAlleleRequestException(Reason.INVALID_REQUEST, "Alternate bases cannot be null"); - } else if (assemblyId == null) { - throw new BeaconAlleleRequestException(Reason.INVALID_REQUEST, - "AssemblyId cannot be null. Please defined a valid GRCh assembly Id"); - } else if (!assemblyId.startsWith("GRCh")) { - throw new BeaconAlleleRequestException(Reason.INVALID_REQUEST, - "Invalid assemblyId. Assemblies must be from GRCh builds"); - } - } - - /** - * @inheritDoc - **/ - @Override - public BeaconAlleleResponse queryAllele(String referenceName, Long start, String referenceBases, String alternateBases, String assemblyId, List datasetIds, Boolean includeDatasetResponses) throws BeaconException { - - validateRequest(referenceName, start, referenceBases, alternateBases, assemblyId); - - if (includeDatasetResponses == null) { - includeDatasetResponses = false; - } - - BeaconAlleleRequest request = new BeaconAlleleRequest(); - request.setReferenceName(referenceName); - request.setReferenceBases(referenceBases); - request.setAlternateBases(alternateBases); - request.setStart(start); - request.setIncludeDatasetResponses(includeDatasetResponses); - request.setAssemblyId(assemblyId); - request.setDatasetIds(datasetIds); - - return adapter.getBeaconAlleleResponse(request); - } - - /** - * @inheritDoc - **/ - @Override - public BeaconAlleleResponse queryAllele(BeaconAlleleRequest request) throws BeaconException { - validateRequest(request.getReferenceName(), - request.getStart(), - request.getReferenceBases(), - request.getAlternateBases(), - request.getAssemblyId()); - - if (request.getIncludeDatasetResponses() == null) { - request.setIncludeDatasetResponses(false); - } - - return adapter.getBeaconAlleleResponse(request); - } - - /** - * @inheritDoc - **/ - @Override - public Beacon queryBeacon() throws BeaconException { - return adapter.getBeacon(); - } -} diff --git a/beacon-java-service/src/main/resources/META-INF/beans.xml b/beacon-java-service/src/main/resources/META-INF/beans.xml deleted file mode 100644 index 28f1f92..0000000 --- a/beacon-java-service/src/main/resources/META-INF/beans.xml +++ /dev/null @@ -1,5 +0,0 @@ - - \ No newline at end of file diff --git a/pom.xml b/pom.xml index 14fa9a0..183b621 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,6 @@ beacon-java - beacon-java-service sample-beacon-adapter beacon-java-rest @@ -65,6 +64,9 @@ 1.1.1 3.4.1 1.0-SNAPSHOT + 8.2.1.Final + 2.0.0.Final + 4.5.2 @@ -110,7 +112,7 @@ org.assertj assertj-core - 3.4.1 + ${version.assertj} org.ga4gh @@ -120,7 +122,7 @@ com.googlecode.json-simple json-simple - 1.1.1 + ${version.json.simple} com.google.code.gson @@ -133,10 +135,37 @@ test ${version.restassured} + + org.wildfly + wildfly-arquillian-container-managed + ${version.org.wildfly} + test + + + org.wildfly + wildfly-arquillian-container-remote + ${version.org.wildfly} + test + + + org.keycloak + keycloak-servlet-filter-adapter + ${version.keycloak} + + + org.keycloak + keycloak-authz-client + ${version.keycloak} + + + org.apache.httpcomponents + httpclient + ${version.httpcomponents} + com.dnastack beacon-adapter-api - 1.0-SNAPSHOT + ${version.beacon.adapter.api} @@ -158,6 +187,11 @@ wildfly-maven-plugin ${version.wildfly.maven.plugin} + + org.apache.maven.plugins + maven-surefire-plugin + ${version.surefire.plugin} + maven-compiler-plugin ${version.compiler.plugin} diff --git a/sample-beacon-adapter/pom.xml b/sample-beacon-adapter/pom.xml index d6e00a6..67ebff3 100644 --- a/sample-beacon-adapter/pom.xml +++ b/sample-beacon-adapter/pom.xml @@ -26,16 +26,16 @@ + 4.0.0 + beacon-java com.dnastack 1.0-SNAPSHOT - 4.0.0 - sample-beacon-adapter - - jar + sample-beacon-adapter + Sample Beacon Adapter @@ -58,15 +58,9 @@ beacon ${version.beacon.api} - - com.dnastack - beacon-java-service - ${project.version} - com.dnastack beacon-adapter-api - 1.0-SNAPSHOT @@ -86,7 +80,4 @@ - sample-beacon-adapter - - \ No newline at end of file diff --git a/sample-beacon-adapter/src/main/java/com/dnastack/beacon/adapter/sample/SampleBeaconAdapter.java b/sample-beacon-adapter/src/main/java/com/dnastack/beacon/adapter/sample/SampleBeaconAdapter.java new file mode 100644 index 0000000..1f54780 --- /dev/null +++ b/sample-beacon-adapter/src/main/java/com/dnastack/beacon/adapter/sample/SampleBeaconAdapter.java @@ -0,0 +1,180 @@ +/* + * The MIT License + * + * Copyright 2014 Miroslav Cupak (mirocupak@gmail.com). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.dnastack.beacon.adapter.sample; + +import com.dnastack.beacon.adapter.api.BeaconAdapter; +import com.dnastack.beacon.utils.AdapterConfig; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang.BooleanUtils; +import org.ga4gh.beacon.*; + +import javax.enterprise.context.Dependent; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static javax.ws.rs.core.Response.Status; + +/** + * Beacon adapter that returns sample data. + * + * @author Artem (tema.voskoboynick@gmail.com) + * @author Patrick Magee (patrickmageee@gmail.com) + */ +@Dependent +public class SampleBeaconAdapter implements BeaconAdapter { + + @Override + public Beacon getBeacon() { + return SampleData.BEACON; + } + + @Override + public void initAdapter(AdapterConfig adapterConfig) { + } + + @Override + public BeaconAlleleResponse getBeaconAlleleResponse(BeaconAlleleRequest request) { + BeaconAlleleResponse response = new BeaconAlleleResponse(); + response.setBeaconId(SampleData.BEACON.getId()); + response.setAlleleRequest(request); + + DatasetsQueryResult datasetsResult = queryDatasets(request.getDatasetIds(), request.getAssemblyId(), + request.getReferenceName(), request.getStart(), request.getReferenceBases(), request.getAlternateBases()); + response.setExists(datasetsResult.exists); + if (BooleanUtils.isTrue(request.getIncludeDatasetResponses())) { + response.setDatasetAlleleResponses(datasetsResult.responses); + } + + return response; + } + + @Override + public BeaconAlleleResponse getBeaconAlleleResponse(String referenceName, Long start, String referenceBases, String alternateBases, String assemblyId, List datasetIds, Boolean includeDatasetResponses) { + BeaconAlleleRequest request = createRequest(referenceName, start, referenceBases, alternateBases, assemblyId, datasetIds, includeDatasetResponses); + return getBeaconAlleleResponse(request); + } + + private BeaconAlleleRequest createRequest(String referenceName, Long start, String referenceBases, String alternateBases, String assemblyId, List datasetIds, Boolean includeDatasetResponses) { + return BeaconAlleleRequest.newBuilder() + .setReferenceName(referenceName) + .setStart(start) + .setReferenceBases(referenceBases) + .setAlternateBases(alternateBases) + .setAssemblyId(assemblyId) + .setDatasetIds(datasetIds) + .setIncludeDatasetResponses(includeDatasetResponses) + .build(); + } + + private DatasetsQueryResult queryDatasets(List datasetIds, String assemblyId, String referenceName, Long start, + String referenceBases, String alternateBases) { + if (CollectionUtils.isEmpty(datasetIds)) { + // All datasets requested. + datasetIds = SampleData.DATASETS.keySet().stream().collect(Collectors.toList()); + } + + return doQueryDatasets(datasetIds, assemblyId, referenceName, start, referenceBases, alternateBases); + } + + private DatasetsQueryResult doQueryDatasets(List datasetIds, String assemblyId, String referenceName, Long start, + String referenceBases, String alternateBases) { + DatasetsQueryResult datasetsResult = new DatasetsQueryResult(); + + for (String datasetId : datasetIds) { + BeaconDatasetAlleleResponse datasetResponse = queryDataset(datasetId, assemblyId, referenceName, start, + referenceBases, alternateBases); + + // Find in descending priority: true, false, null. + Boolean existsInDataset = datasetResponse.getExists(); + if (datasetsResult.exists == null || BooleanUtils.isTrue(existsInDataset)) { + datasetsResult.exists = existsInDataset; + } + + // Collect any error, if exists. + if (datasetResponse.getError() != null) { + datasetsResult.error = datasetResponse.getError(); + } + + datasetsResult.responses.add(datasetResponse); + } + + return datasetsResult; + } + + @SuppressWarnings("unchecked") + private BeaconDatasetAlleleResponse queryDataset(String datasetId, String assemblyId, String referenceName, long start, String referenceBases, String alternateBases) { + Map dataset = SampleData.DATASETS.get(datasetId); + if (dataset == null) { + return createErrorDatasetResponse(datasetId, Status.NOT_FOUND.getStatusCode(), "Dataset not found"); + } + + Map assembly = dataset.get(assemblyId); + if (assembly == null) { + return createErrorDatasetResponse(datasetId, Status.NOT_FOUND.getStatusCode(), "Assembly not found"); + } + + Map reference = assembly.get(referenceName); + if (reference == null) { + return createErrorDatasetResponse(datasetId, Status.NOT_FOUND.getStatusCode(), "Reference not found"); + } + + Map bases = reference.get(start); + if (bases == null) { + return createSuccessfulDatasetResponse(datasetId, false); + } + + if (bases.get("referenceBases").equals(referenceBases) && bases.get("alternateBases").equals(alternateBases)) { + return createSuccessfulDatasetResponse(datasetId, true); + } else { + return createSuccessfulDatasetResponse(datasetId, false); + } + } + + private BeaconDatasetAlleleResponse createErrorDatasetResponse(String datasetId, int errorCode, String message) { + BeaconDatasetAlleleResponse response = new BeaconDatasetAlleleResponse(); + response.setDatasetId(datasetId); + + BeaconError error = BeaconError.newBuilder() + .setErrorCode(errorCode) + .setMessage(message).build(); + response.setError(error); + + return response; + } + + private BeaconDatasetAlleleResponse createSuccessfulDatasetResponse(String datasetId, boolean exists) { + BeaconDatasetAlleleResponse response = BeaconDatasetAlleleResponse.newBuilder(SampleData.BASE_DATASET_RESPONSE).build(); + response.setDatasetId(datasetId); + response.setExists(exists); + return response; + } + + private static class DatasetsQueryResult { + Boolean exists; + BeaconError error; + List responses = new ArrayList<>(); + } +} diff --git a/sample-beacon-adapter/src/main/java/com/dnastack/beacon/adapter/sample/SampleData.java b/sample-beacon-adapter/src/main/java/com/dnastack/beacon/adapter/sample/SampleData.java new file mode 100644 index 0000000..a9de51b --- /dev/null +++ b/sample-beacon-adapter/src/main/java/com/dnastack/beacon/adapter/sample/SampleData.java @@ -0,0 +1,127 @@ +/* + * The MIT License + * + * Copyright 2014 Miroslav Cupak (mirocupak@gmail.com). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.dnastack.beacon.adapter.sample; + +import avro.shaded.com.google.common.collect.ImmutableMap; +import org.ga4gh.beacon.*; + +import java.util.*; + +/** + * Sample data holder. + * + * @author patmagee + * @author Artem (tema.voskoboynick@gmail.com) + */ +public class SampleData { + + public static final Map DATASETS; + public static final Beacon BEACON; + public static final BeaconDatasetAlleleResponse BASE_DATASET_RESPONSE; + + static { + BeaconDataset dataset = BeaconDataset.newBuilder() + .setId("sample_dataset_id_1") + .setName("Sample Dataset") + .setAssemblyId("GRCh37") + .setUpdateDateTime(new Date().toString()) + .setCreateDateTime(new Date().toString()) + .setVersion("0.3.0") + .setCallCount(10L) + .setSampleCount(10L) + .setDescription("Sample implementation") + .setInfo(ImmutableMap.of("note", "This is a Sample Dataset")) + .build(); + + BeaconDataset dataset2 = BeaconDataset.newBuilder(dataset) + .setId("sample_dataset_id_2") + .setName("Second Sample Dataset") + .build(); + + + BASE_DATASET_RESPONSE = BeaconDatasetAlleleResponse.newBuilder() + .setCallCount(1L) + .setFrequency(0.221) + .setVariantCount(1L) + .setSampleCount(1L) + .setExternalUrl("www.google.com") + .setInfo(ImmutableMap.of("Sample Note", "Sample Note Value")) + .setDatasetId(dataset.getId()) + .setExists(null) // Setting nulls explicitly required by avro. + .build(); + + BeaconAlleleRequest sampleRequest = BeaconAlleleRequest.newBuilder() + .setDatasetIds(Arrays.asList(dataset.getId(), dataset2.getId())) + .setReferenceName("1") + .setAssemblyId("GRCh37") + .setIncludeDatasetResponses(true) + .setStart(1000L) + .setReferenceBases("A") + .setAlternateBases("C") + .build(); + + DATASETS = new HashMap() { + { + Map bases = ImmutableMap.of( + "referenceBases", sampleRequest.getReferenceBases(), + "alternateBases", sampleRequest.getAlternateBases() + ); + Map positions = ImmutableMap.of(sampleRequest.getStart(), bases); + Map references = ImmutableMap.of(sampleRequest.getReferenceName(), positions); + Map assemblies = ImmutableMap.of(sampleRequest.getAssemblyId(), references); + + put(dataset.getId(), assemblies); + put(dataset2.getId(), assemblies); + } + }; + + BeaconOrganization organization = BeaconOrganization.newBuilder() + .setAddress("123 Nullpointer Ave, StackTrace, Ohio") + .setContactUrl("www.google.com") + .setDescription("Sample implementation of a beacon organization") + .setWelcomeUrl("www.google.com") + .setLogoUrl("www.logoUrl.com") + .setName("Sample Organization") + .setId("sample_organization_id") + .setInfo(ImmutableMap.of("note", "This is a sample Organization")) + .build(); + + BEACON = Beacon.newBuilder() + .setApiVersion("0.3.0") + .setVersion("0.3.0") + .setDescription("Sample description") + .setName("Sample Beacon") + .setAlternativeUrl("www.google.ca") + .setWelcomeUrl("www.url") + .setDatasets(Collections.singletonList(dataset)) + .setCreateDateTime(new Date().toString()) + .setUpdateDateTime(new Date().toString()) + .setSampleAlleleRequests(Collections.singletonList(sampleRequest)) + .setOrganization(organization) + .setId("sample_beacon_id") + .setInfo(ImmutableMap.of("note", "This is a sample Beacon")) + .build(); + } +} diff --git a/sample-beacon-adapter/src/main/java/com/dnastack/beacon/core/adapter/impl/SampleBeaconAdapterImpl.java b/sample-beacon-adapter/src/main/java/com/dnastack/beacon/core/adapter/impl/SampleBeaconAdapterImpl.java deleted file mode 100644 index c436439..0000000 --- a/sample-beacon-adapter/src/main/java/com/dnastack/beacon/core/adapter/impl/SampleBeaconAdapterImpl.java +++ /dev/null @@ -1,242 +0,0 @@ -/* - * The MIT License - * - * Copyright 2014 Miroslav Cupak (mirocupak@gmail.com). - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.dnastack.beacon.core.adapter.impl; - -import com.dnastack.beacon.adapter.api.BeaconAdapter; -import com.dnastack.beacon.exceptions.BeaconException; -import com.dnastack.beacon.utils.AdapterConfig; -import org.ga4gh.beacon.*; - -import javax.annotation.PostConstruct; -import javax.ejb.Singleton; -import javax.enterprise.context.Dependent; -import java.util.*; - -/** - * @author patrickmagee - */ -@Singleton -public class SampleBeaconAdapterImpl implements BeaconAdapter { - - public static final String API_VERSION = "0.3.0"; - public static final String BEACON_ID = "beacon_id"; - public static final String DATASET_ID = "dataset_id"; - public static final String ORG_ID = "org_id"; - - private SampleDataStore dataStore; - - private BeaconDatasetAlleleResponse lookupDataset(String datasetId, String assemblyId, String referencName, long start, String refBases, String altBases) { - BeaconDatasetAlleleResponse response = new BeaconDatasetAlleleResponse(); - response.setDatasetId(datasetId); - - Map dataset = dataStore.getDATA().get(datasetId); - if (dataset == null || dataset.isEmpty()) { - BeaconError be = new BeaconError(); - be.setErrorCode(404); - be.setMessage("Could not find dataset"); - - response.setExists(null); - response.setError(be); - - return response; - } - Map assembly = ((Map) dataset.get(assemblyId)); - if (assembly == null || assembly.isEmpty()) { - BeaconError be = new BeaconError(); - be.setErrorCode(404); - be.setMessage("Could not find assembly in current dataset"); - - response.setExists(null); - response.setError(be); - - return response; - } - Map reference = ((Map) assembly.get(referencName)); - if (reference == null || reference.isEmpty()) { - response.setExists(null); - return addInfo(response); - } - Map bases = ((Map) reference.get(start)); - if (bases == null || bases.isEmpty()) { - response.setExists(false); - return addInfo(response); - } - if (bases.get("referenceBases").equals(refBases) && bases.get("alternateBases").equals(altBases)) { - response.setExists(true); - return addInfo(response); - } else { - response.setExists(false); - return addInfo(response); - } - } - - private BeaconDatasetAlleleResponse addInfo(BeaconDatasetAlleleResponse datasetResponse) { - datasetResponse.setCallCount(1l); - datasetResponse.setFrequency(0.221); - datasetResponse.setVariantCount(1l); - datasetResponse.setSampleCount(1l); - datasetResponse.setExternalUrl("www.google.com"); - datasetResponse.setNote("This is a sample beacon only"); - Map info = new HashMap<>(); - info.put("note", "Sample Beacon only"); - datasetResponse.setInfo(info); - return datasetResponse; - } - - private Beacon createSampleBeacon() { - Beacon beacon = new Beacon(); - beacon.setApiVersion(API_VERSION); - beacon.setVersion(API_VERSION); - beacon.setDescription("This is the description for a sample beacon"); - beacon.setName("Sample Beacon"); - beacon.setAlternativeUrl("www.google.ca"); - beacon.setWelcomeUrl("www.url"); - beacon.setDatasets(Arrays.asList(createSampleBeaconDataset())); - beacon.setCreateDateTime(new Date().toString()); - beacon.setUpdateDateTime(new Date().toString()); - beacon.setSampleAlleleRequests(Arrays.asList(createSampleRequest())); - beacon.setOrganization(createSampleOrganization()); - beacon.setId(BEACON_ID); - Map info = new HashMap<>(); - info.put("note", "Sample Beacon only"); - beacon.setInfo(info); - - return beacon; - } - - private BeaconAlleleRequest createSampleRequest() { - BeaconAlleleRequest request = new BeaconAlleleRequest(); - request.setDatasetIds(Arrays.asList(DATASET_ID)); - request.setReferenceName("1"); - request.setAssemblyId("GRCh37"); - request.setIncludeDatasetResponses(true); - request.setStart(1000l); - request.setReferenceBases("A"); - request.setAlternateBases("C"); - return request; - - } - - private BeaconOrganization createSampleOrganization() { - BeaconOrganization organization = new BeaconOrganization(); - organization.setAddress("123 Nullpointer Ave, StackTrace, Ohio"); - organization.setContactUrl("www.google.com"); - organization.setDescription("Sample implementation of a beacon organization"); - organization.setWelcomeUrl("www.google.com"); - organization.setLogoUrl("www.logoUrl.com"); - organization.setName("Sample Org"); - organization.setId(ORG_ID); - Map info = new HashMap<>(); - info.put("note", "Sample Beacon only"); - organization.setInfo(info); - - return organization; - } - - private BeaconDataset createSampleBeaconDataset() { - BeaconDataset dataset = new BeaconDataset(); - dataset.setName("Sample Dataset"); - dataset.setAssemblyId("GRCh37"); - dataset.setUpdateDateTime(new Date().toString()); - dataset.setCreateDateTime(new Date().toString()); - dataset.setId(DATASET_ID); - dataset.setVersion(API_VERSION); - dataset.setCallCount(10l); - dataset.setSampleCount(10l); - dataset.setDescription("Sample implementation"); - Map info = new HashMap<>(); - info.put("note", "Sample Beacon only"); - dataset.setInfo(info); - - return dataset; - - } - - @PostConstruct - public void init() { - initAdapter(null); - } - - @Override - public void initAdapter(AdapterConfig adapterConfig) { - dataStore = new SampleDataStore(); - } - - @Override - public BeaconAlleleResponse getBeaconAlleleResponse(BeaconAlleleRequest request) throws BeaconException { - - BeaconAlleleResponse response = new BeaconAlleleResponse(); - response.setBeaconId(BEACON_ID); - response.setAlleleRequest(request); - - List responses = new ArrayList<>(); - for (String datasetId : request.getDatasetIds()) { - responses.add(lookupDataset(datasetId, - request.getAssemblyId(), - request.getReferenceName(), - request.getStart(), - request.getReferenceBases(), - request.getAlternateBases())); - } - - if (!request.getIncludeDatasetResponses() && responses.size() == 1 && responses.get(0).getError() != null) { - response.setExists(null); - response.setError(responses.get(0).getError()); - } else if (request.getIncludeDatasetResponses()) { - response.setDatasetAlleleResponses(responses); - } - boolean exists = false; - for (BeaconDatasetAlleleResponse datasetResponses : responses) { - - if (datasetResponses.getExists()) { - exists = true; - } - } - response.setExists(exists); - return response; - } - - @Override - public BeaconAlleleResponse getBeaconAlleleResponse(String referenceName, Long start, String referenceBases, String alternateBases, String assemblyId, List datasetIds, Boolean includeDatasetResponses) throws BeaconException { - BeaconAlleleRequest request = new BeaconAlleleRequest(); - request.setReferenceName(referenceName); - request.setStart(start); - request.setReferenceBases(referenceBases); - request.setAlternateBases(alternateBases); - request.setAssemblyId(assemblyId); - request.setDatasetIds(datasetIds); - if (includeDatasetResponses == null){ - includeDatasetResponses = false; - } - request.setIncludeDatasetResponses(includeDatasetResponses); - - return getBeaconAlleleResponse(request); - } - - @Override - public Beacon getBeacon() throws BeaconException { - return createSampleBeacon(); - } - -} diff --git a/sample-beacon-adapter/src/main/java/com/dnastack/beacon/core/adapter/impl/SampleDataStore.java b/sample-beacon-adapter/src/main/java/com/dnastack/beacon/core/adapter/impl/SampleDataStore.java deleted file mode 100644 index c7d2a83..0000000 --- a/sample-beacon-adapter/src/main/java/com/dnastack/beacon/core/adapter/impl/SampleDataStore.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * The MIT License - * - * Copyright 2014 Miroslav Cupak (mirocupak@gmail.com). - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package com.dnastack.beacon.core.adapter.impl; - -import java.util.HashMap; -import java.util.Map; - -/** - * This class simply is acting as a datastorage for the BeaconAdapter to perform lookups on - * - * @author patmagee - */ -public class SampleDataStore { - - private final Map DATA; - - public SampleDataStore() { - DATA = new HashMap<>(); - Map assemblies = new HashMap<>(); - Map references = new HashMap<>(); - Map positions = new HashMap<>(); - Map bases = new HashMap<>(); - bases.put("referenceBases", "A"); - bases.put("alternateBases", "C"); - positions.put(1000l, bases); - references.put("1", positions); - assemblies.put("GRCh37", references); - DATA.put(SampleBeaconAdapterImpl.DATASET_ID, assemblies); - - } - - public Map getDATA() { - return DATA; - } - -}