From b67777d5c7815d71f2eb799c855248cad3d0d405 Mon Sep 17 00:00:00 2001 From: Luke Sikina Date: Sun, 14 Jan 2024 07:45:16 -0500 Subject: [PATCH] - Add field to federated query - Add field to federated query - Add it in agg search resource - Also add some documentation about code formatting --- README.md | 1 + .../avillach/service/PicsureQueryService.java | 682 +++++++++--------- .../main/resources/META-INF/persistence.xml | 7 + .../resources/webapp/META-INF/context.xml | 6 +- .../main/resources/webapp/WEB-INF/beans.xml | 10 +- .../src/main/resources/webapp/WEB-INF/web.xml | 20 + .../domain/FederatedQueryRequest.java | 229 +++--- pom.xml | 1 + 8 files changed, 495 insertions(+), 461 deletions(-) create mode 100644 pic-sure-resources/pic-sure-passthrough-resource/src/main/resources/META-INF/persistence.xml create mode 100644 pic-sure-resources/pic-sure-passthrough-resource/src/main/resources/webapp/WEB-INF/web.xml diff --git a/README.md b/README.md index f262b2bf..4bbc5d93 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ This is the git repository for version 2+ of the PIC-SURE API. * Java 11 * Before contributing code, please set up our git hook: `cp code-formatting/pre-commit.sh .git/hooks/pre-commit` + * To skip formatting on a block of code, wrap in `spotless:off`, `spotless:on` comments ## Build The build consists of the following top level maven modules: diff --git a/pic-sure-api-war/src/main/java/edu/harvard/dbmi/avillach/service/PicsureQueryService.java b/pic-sure-api-war/src/main/java/edu/harvard/dbmi/avillach/service/PicsureQueryService.java index 035cf34d..b0c851bc 100644 --- a/pic-sure-api-war/src/main/java/edu/harvard/dbmi/avillach/service/PicsureQueryService.java +++ b/pic-sure-api-war/src/main/java/edu/harvard/dbmi/avillach/service/PicsureQueryService.java @@ -31,231 +31,224 @@ */ public class PicsureQueryService { - public static final String QUERY_RESULT_METADATA_FIELD = "queryResultMetadata"; - private static final String QUERY_JSON_FIELD = "queryJson"; - - private final Logger logger = LoggerFactory.getLogger(PicsureQueryService.class); - - private final static ObjectMapper mapper = new ObjectMapper(); - - @Inject - JWTFilter jwtFilter; - - @Inject - ResourceRepository resourceRepo; - - @Inject - QueryRepository queryRepo; - - @Inject - ResourceWebClient resourceWebClient; - - @Inject - SiteParsingService siteParsingService; - - /** - * Executes a query on a PIC-SURE resource and creates a Query entity in the - * database for the query. - * - * @param dataQueryRequest - - {@link QueryRequest} containing resource specific credentials object - * and resource specific query (could be a string or a json object) - * @return {@link QueryStatus} - */ - @Transactional - public QueryStatus query(QueryRequest dataQueryRequest, HttpHeaders headers) { - Resource resource = verifyQueryRequest(dataQueryRequest, headers); - - dataQueryRequest.getResourceCredentials().put(ResourceWebClient.BEARER_TOKEN_KEY, resource.getToken()); - - QueryStatus results = resourceWebClient.query(resource.getResourceRSPath(), dataQueryRequest); - - Query queryEntity = copyQuery(dataQueryRequest, resource, results); - queryRepo.persist(queryEntity); - - logger.debug("PicsureQueryService() persisted queryEntity with id: " + queryEntity.getUuid()); - results.setPicsureResultId(queryEntity.getUuid()); - //In cases where there is no resource result id, the picsure result id will stand in - if (queryEntity.getResourceResultId() == null){ - results.setResourceResultId(queryEntity.getUuid().toString()); - queryEntity.setResourceResultId(results.getPicsureResultId().toString()); - queryRepo.persist(queryEntity); - } - results.setResourceID(resource.getUuid()); - return results; - } - - /** - * Retrieves the {@link QueryStatus} for a given queryId by looking up the target resource - * from the database and calling the target resource for an updated status. The Query entities - * in the database are updated each time this is called. - * - * @param queryId - id of targeted resource - * @param credentialsQueryRequest - contains resource specific credentials object - * @return {@link QueryStatus} - */ - @Transactional - public QueryStatus queryStatus(UUID queryId, GeneralQueryRequest credentialsQueryRequest, HttpHeaders headers) { - if (queryId == null){ - throw new ProtocolException(ProtocolException.MISSING_QUERY_ID); - } - Query query = queryRepo.getById(queryId); - if (query == null){ - throw new ProtocolException(ProtocolException.QUERY_NOT_FOUND + queryId.toString()); - } - if (credentialsQueryRequest == null){ - throw new ProtocolException(ProtocolException.MISSING_DATA); - } - Resource resource = query.getResource(); - verifyQueryStatusRequest(resource, credentialsQueryRequest, queryId, headers); - - //Update status on query object - QueryStatus status = resourceWebClient.queryStatus(resource.getResourceRSPath(), query.getResourceResultId(), credentialsQueryRequest); - status.setPicsureResultId(queryId); - query.setStatus(status.getStatus()); - queryRepo.persist(query); - status.setStartTime(query.getStartTime().getTime()); - status.setResourceID(resource.getUuid()); - return status; - } - - /** - * Streams the result for a given queryId by looking up the target resource - * from the database and calling the target resource for a result. The queryStatus - * method should be used to verify that the result is available prior to retrieving it. - * - * @param queryId - id of target resource - * @param credentialsQueryRequest - contains resource specific credentials object - * @return Response - */ - @Transactional - public Response queryResult(UUID queryId, QueryRequest credentialsQueryRequest, HttpHeaders headers) { - if (queryId == null){ - throw new ProtocolException(ProtocolException.MISSING_QUERY_ID); - } - Query query = queryRepo.getById(queryId); - if (query == null){ - throw new ProtocolException(ProtocolException.QUERY_NOT_FOUND + queryId.toString()); - } - Resource resource = query.getResource(); - if (resource == null){ - throw new ApplicationException(ApplicationException.MISSING_RESOURCE); - } - if (resource.getResourceRSPath() == null){ - throw new ApplicationException(ApplicationException.MISSING_RESOURCE_PATH); - } - if (credentialsQueryRequest == null){ - throw new ProtocolException(ProtocolException.MISSING_DATA); - } - if (credentialsQueryRequest.getResourceCredentials() == null){ - credentialsQueryRequest.setResourceCredentials(new HashMap<>()); - } - - logger.info("path=/query/{queryId}/result, resourceId={}, requestSource={}, queryRequest={}", - queryId, - Utilities.getRequestSourceFromHeader(headers), - Utilities.convertQueryRequestToString(mapper, credentialsQueryRequest) - ); - - - credentialsQueryRequest.getResourceCredentials().put(ResourceWebClient.BEARER_TOKEN_KEY, resource.getToken()); - return resourceWebClient.queryResult(resource.getResourceRSPath(), query.getResourceResultId(), credentialsQueryRequest); - } - - /** - * Streams the result for a query by looking up the target resource - * from the database and calling the target resource for a result. - * - * @param queryRequest - contains resource specific credentials object - * @return Response - */ - @Transactional - public Response querySync(QueryRequest queryRequest, HttpHeaders headers) { - if (queryRequest == null){ - throw new ProtocolException(ProtocolException.MISSING_DATA); - } - UUID resourceId = queryRequest.getResourceUUID(); - if (resourceId == null){ - throw new ProtocolException(ProtocolException.MISSING_RESOURCE_ID); - } - Resource resource = resourceRepo.getById(resourceId); - if (resource == null){ - throw new ApplicationException(ApplicationException.MISSING_RESOURCE); - } - - if (resource.getResourceRSPath() == null){ - throw new ApplicationException(ApplicationException.MISSING_RESOURCE_PATH); - } - - if (queryRequest.getResourceCredentials() == null){ - queryRequest.setResourceCredentials(new HashMap<>()); - } - - String requestSource = Utilities.getRequestSourceFromHeader(headers); - logger.info("path=/query/sync, resourceId={}, requestSource={}, queryRequest={}", - queryRequest.getResourceUUID(), - requestSource, - Utilities.convertQueryRequestToString(mapper, queryRequest) - ); - - Query queryEntity = new Query(); - queryEntity.setResource(resource); - queryEntity.setStartTime(new Date(Calendar.getInstance().getTime().getTime())); - - - String queryJson = null; - if( queryRequest.getQuery() != null) { - try { - ObjectMapper mapper = new ObjectMapper(); - queryJson = mapper.writeValueAsString( queryRequest); - } catch (JsonProcessingException e) { - throw new ProtocolException(ProtocolException.INCORRECTLY_FORMATTED_REQUEST); - } - } - - queryEntity.setQuery(queryJson); - queryRepo.persist(queryEntity); - queryRequest.getResourceCredentials().put(ResourceWebClient.BEARER_TOKEN_KEY, resource.getToken()); - Response syncResponse = resourceWebClient.querySync(resource.getResourceRSPath(), queryRequest, requestSource); - String queryMetadata = queryEntity.getUuid().toString(); // if no response ID, use the queryID (maintain behavior) - - if (syncResponse.getHeaders() != null) { - Object metadataHeader = syncResponse.getHeaders().get(ResourceWebClient.QUERY_METADATA_FIELD); - if (metadataHeader != null) { - try { - if (metadataHeader instanceof List) { - queryMetadata = ((List)metadataHeader).get(0).toString(); - logger.debug("found List metadata " + queryMetadata); - } else { - logger.debug("Header is " + metadataHeader.getClass().getCanonicalName() + " :: " + metadataHeader); - } - } catch (ClassCastException | ArrayIndexOutOfBoundsException e) { - logger.warn("failed to parse Header : ", e); - } - } - } - - queryEntity.setResourceResultId(queryMetadata); - queryRepo.persist(queryEntity); - - return syncResponse; - } + public static final String QUERY_RESULT_METADATA_FIELD = "queryResultMetadata"; + private static final String QUERY_JSON_FIELD = "queryJson"; + + private final Logger logger = LoggerFactory.getLogger(PicsureQueryService.class); + + private final static ObjectMapper mapper = new ObjectMapper(); + + @Inject + JWTFilter jwtFilter; + + @Inject + ResourceRepository resourceRepo; + + @Inject + QueryRepository queryRepo; + + @Inject + ResourceWebClient resourceWebClient; + + @Inject + SiteParsingService siteParsingService; /** - * @param queryId The UUID of the query to get metadata about + * Executes a query on a PIC-SURE resource and creates a Query entity in the database for the query. + * + * @param dataQueryRequest - - {@link QueryRequest} containing resource specific credentials object and resource specific query (could + * be a string or a json object) + * @return {@link QueryStatus} + */ + @Transactional + public QueryStatus query(QueryRequest dataQueryRequest, HttpHeaders headers) { + Resource resource = verifyQueryRequest(dataQueryRequest, headers); + + dataQueryRequest.getResourceCredentials().put(ResourceWebClient.BEARER_TOKEN_KEY, resource.getToken()); + + QueryStatus results = resourceWebClient.query(resource.getResourceRSPath(), dataQueryRequest); + + Query queryEntity = copyQuery(dataQueryRequest, resource, results); + queryRepo.persist(queryEntity); + + logger.debug("PicsureQueryService() persisted queryEntity with id: " + queryEntity.getUuid()); + results.setPicsureResultId(queryEntity.getUuid()); + // In cases where there is no resource result id, the picsure result id will stand in + if (queryEntity.getResourceResultId() == null) { + results.setResourceResultId(queryEntity.getUuid().toString()); + queryEntity.setResourceResultId(results.getPicsureResultId().toString()); + queryRepo.persist(queryEntity); + } + results.setResourceID(resource.getUuid()); + return results; + } + + /** + * Retrieves the {@link QueryStatus} for a given queryId by looking up the target resource from the database and calling the target + * resource for an updated status. The Query entities in the database are updated each time this is called. + * + * @param queryId - id of targeted resource + * @param credentialsQueryRequest - contains resource specific credentials object + * @return {@link QueryStatus} + */ + @Transactional + public QueryStatus queryStatus(UUID queryId, GeneralQueryRequest credentialsQueryRequest, HttpHeaders headers) { + if (queryId == null) { + throw new ProtocolException(ProtocolException.MISSING_QUERY_ID); + } + Query query = queryRepo.getById(queryId); + if (query == null) { + throw new ProtocolException(ProtocolException.QUERY_NOT_FOUND + queryId.toString()); + } + if (credentialsQueryRequest == null) { + throw new ProtocolException(ProtocolException.MISSING_DATA); + } + Resource resource = query.getResource(); + verifyQueryStatusRequest(resource, credentialsQueryRequest, queryId, headers); + + // Update status on query object + QueryStatus status = + resourceWebClient.queryStatus(resource.getResourceRSPath(), query.getResourceResultId(), credentialsQueryRequest); + status.setPicsureResultId(queryId); + query.setStatus(status.getStatus()); + queryRepo.persist(query); + status.setStartTime(query.getStartTime().getTime()); + status.setResourceID(resource.getUuid()); + return status; + } + + /** + * Streams the result for a given queryId by looking up the target resource from the database and calling the target resource for a + * result. The queryStatus method should be used to verify that the result is available prior to retrieving it. + * + * @param queryId - id of target resource + * @param credentialsQueryRequest - contains resource specific credentials object + * @return Response + */ + @Transactional + public Response queryResult(UUID queryId, QueryRequest credentialsQueryRequest, HttpHeaders headers) { + if (queryId == null) { + throw new ProtocolException(ProtocolException.MISSING_QUERY_ID); + } + Query query = queryRepo.getById(queryId); + if (query == null) { + throw new ProtocolException(ProtocolException.QUERY_NOT_FOUND + queryId.toString()); + } + Resource resource = query.getResource(); + if (resource == null) { + throw new ApplicationException(ApplicationException.MISSING_RESOURCE); + } + if (resource.getResourceRSPath() == null) { + throw new ApplicationException(ApplicationException.MISSING_RESOURCE_PATH); + } + if (credentialsQueryRequest == null) { + throw new ProtocolException(ProtocolException.MISSING_DATA); + } + if (credentialsQueryRequest.getResourceCredentials() == null) { + credentialsQueryRequest.setResourceCredentials(new HashMap<>()); + } + + logger.info( + "path=/query/{queryId}/result, resourceId={}, requestSource={}, queryRequest={}", queryId, + Utilities.getRequestSourceFromHeader(headers), Utilities.convertQueryRequestToString(mapper, credentialsQueryRequest) + ); + + + credentialsQueryRequest.getResourceCredentials().put(ResourceWebClient.BEARER_TOKEN_KEY, resource.getToken()); + return resourceWebClient.queryResult(resource.getResourceRSPath(), query.getResourceResultId(), credentialsQueryRequest); + } + + /** + * Streams the result for a query by looking up the target resource from the database and calling the target resource for a result. + * + * @param queryRequest - contains resource specific credentials object + * @return Response + */ + @Transactional + public Response querySync(QueryRequest queryRequest, HttpHeaders headers) { + if (queryRequest == null) { + throw new ProtocolException(ProtocolException.MISSING_DATA); + } + UUID resourceId = queryRequest.getResourceUUID(); + if (resourceId == null) { + throw new ProtocolException(ProtocolException.MISSING_RESOURCE_ID); + } + Resource resource = resourceRepo.getById(resourceId); + if (resource == null) { + throw new ApplicationException(ApplicationException.MISSING_RESOURCE); + } + + if (resource.getResourceRSPath() == null) { + throw new ApplicationException(ApplicationException.MISSING_RESOURCE_PATH); + } + + if (queryRequest.getResourceCredentials() == null) { + queryRequest.setResourceCredentials(new HashMap<>()); + } + + String requestSource = Utilities.getRequestSourceFromHeader(headers); + logger.info( + "path=/query/sync, resourceId={}, requestSource={}, queryRequest={}", queryRequest.getResourceUUID(), requestSource, + Utilities.convertQueryRequestToString(mapper, queryRequest) + ); + + Query queryEntity = new Query(); + queryEntity.setResource(resource); + queryEntity.setStartTime(new Date(Calendar.getInstance().getTime().getTime())); + + + String queryJson = null; + if (queryRequest.getQuery() != null) { + try { + ObjectMapper mapper = new ObjectMapper(); + queryJson = mapper.writeValueAsString(queryRequest); + } catch (JsonProcessingException e) { + throw new ProtocolException(ProtocolException.INCORRECTLY_FORMATTED_REQUEST); + } + } + + queryEntity.setQuery(queryJson); + queryRepo.persist(queryEntity); + queryRequest.getResourceCredentials().put(ResourceWebClient.BEARER_TOKEN_KEY, resource.getToken()); + Response syncResponse = resourceWebClient.querySync(resource.getResourceRSPath(), queryRequest, requestSource); + String queryMetadata = queryEntity.getUuid().toString(); // if no response ID, use the queryID (maintain behavior) + + if (syncResponse.getHeaders() != null) { + Object metadataHeader = syncResponse.getHeaders().get(ResourceWebClient.QUERY_METADATA_FIELD); + if (metadataHeader != null) { + try { + if (metadataHeader instanceof List) { + queryMetadata = ((List) metadataHeader).get(0).toString(); + logger.debug("found List metadata " + queryMetadata); + } else { + logger.debug("Header is " + metadataHeader.getClass().getCanonicalName() + " :: " + metadataHeader); + } + } catch (ClassCastException | ArrayIndexOutOfBoundsException e) { + logger.warn("failed to parse Header : ", e); + } + } + } + + queryEntity.setResourceResultId(queryMetadata); + queryRepo.persist(queryEntity); + + return syncResponse; + } + + /** + * @param queryId The UUID of the query to get metadata about * @return a QueryStatus object containing the metadata stored about the given query */ - public QueryStatus queryMetadata(UUID queryId, HttpHeaders headers){ + public QueryStatus queryMetadata(UUID queryId, HttpHeaders headers) { Query query = queryRepo.getById(queryId); - if (query == null){ - throw new ProtocolException(ProtocolException.QUERY_NOT_FOUND + queryId.toString()); + if (query == null) { + throw new ProtocolException(ProtocolException.QUERY_NOT_FOUND + queryId.toString()); } - logger.info("path=/query/{queryId}/metadata, requestSource={}, queryId={}", - Utilities.getRequestSourceFromHeader(headers), - queryId); + logger.info("path=/query/{queryId}/metadata, requestSource={}, queryId={}", Utilities.getRequestSourceFromHeader(headers), queryId); - QueryStatus response = new QueryStatus(); + QueryStatus response = new QueryStatus(); response.setStartTime(query.getStartTime().getTime()); response.setPicsureResultId(query.getUuid()); response.setResourceID(query.getResource().getUuid()); @@ -264,141 +257,138 @@ public QueryStatus queryMetadata(UUID queryId, HttpHeaders headers){ Map metadata = new HashMap(); try { - metadata.put(QUERY_JSON_FIELD, new ObjectMapper().readValue(query.getQuery(), Object.class)); - metadata.put(QUERY_RESULT_METADATA_FIELD, new String(query.getMetadata(), StandardCharsets.UTF_8)); - } catch (JsonProcessingException | NullPointerException e) { - logger.warn("Unable to use object mapper", e); - } + metadata.put(QUERY_JSON_FIELD, new ObjectMapper().readValue(query.getQuery(), Object.class)); + metadata.put(QUERY_RESULT_METADATA_FIELD, new String(query.getMetadata(), StandardCharsets.UTF_8)); + } catch (JsonProcessingException | NullPointerException e) { + logger.warn("Unable to use object mapper", e); + } response.setResultMetadata(metadata); return response; } - /** - * Executes a query on a PIC-SURE resource and creates a Query entity in the - * database for the query. - * - * @param dataQueryRequest - - {@link QueryRequest} containing resource specific credentials object - * and resource specific query (could be a string or a json object) - * @return {@link QueryStatus} - */ - public QueryStatus institutionalQuery(FederatedQueryRequest dataQueryRequest, HttpHeaders headers, String email) { - String siteCode = siteParsingService.parseSiteOfOrigin(email).orElseThrow(() -> new RuntimeException("Bad email")); - dataQueryRequest.setInstitutionOfOrigin(siteCode); - Resource resource = verifyQueryRequest(dataQueryRequest, headers); - dataQueryRequest.getResourceCredentials().put(ResourceWebClient.BEARER_TOKEN_KEY, resource.getToken()); - - QueryStatus response = resourceWebClient.query(resource.getResourceRSPath(), dataQueryRequest); - Query queryEntity = copyQuery(dataQueryRequest, resource, response); - queryRepo.persist(queryEntity); - // we don't want the user to see the common area ID for now, but this could be useful later - // for editing the query - response.getResultMetadata().put("commonAreaId", queryEntity.getUuid().toString()); - - return response; - } - - private Query copyQuery(QueryRequest dataQueryRequest, Resource resource, QueryStatus response) { - Query queryEntity = new Query(); - queryEntity.setResourceResultId(response.getResourceResultId()); - queryEntity.setResource(resource); - queryEntity.setStatus(response.getStatus()); - queryEntity.setStartTime(new Date(response.getStartTime())); - - ObjectMapper mapper = new ObjectMapper(); - String queryJson = null; - if( dataQueryRequest.getQuery() != null) { - try { - queryJson = mapper.writeValueAsString(dataQueryRequest); - } catch (JsonProcessingException e) { - throw new ProtocolException(ProtocolException.INCORRECTLY_FORMATTED_REQUEST); - } - } - Map metaData = response.getResultMetadata(); - metaData = metaData == null ? new HashMap<>() : metaData; - - if (dataQueryRequest instanceof FederatedQueryRequest) { - FederatedQueryRequest gicRequest = (FederatedQueryRequest) dataQueryRequest; - metaData.put("commonAreaUUID", gicRequest.getCommonAreaUUID()); - metaData.put("site", gicRequest.getInstitutionOfOrigin()); - metaData.put("sharingStatus", DataSharingStatus.Unknown); - } - - queryEntity.setQuery(queryJson); - - if (!metaData.isEmpty()) { - try { - queryEntity.setMetadata(mapper.writeValueAsString(metaData).getBytes()); - } catch (JsonProcessingException e) { - logger.warn("Unable to parse metadata ", e); - } - } - return queryEntity; - } - - public QueryStatus institutionQueryStatus(UUID queryId, FederatedQueryRequest credentialsQueryRequest, HttpHeaders headers) { - if (queryId == null) { - throw new ProtocolException(ProtocolException.MISSING_QUERY_ID); - } - if (credentialsQueryRequest == null) { - throw new ProtocolException(ProtocolException.MISSING_DATA); - } - Resource resource = resourceRepo.getById(credentialsQueryRequest.getResourceUUID()); - - verifyQueryStatusRequest(resource, credentialsQueryRequest, queryId, headers); - - //Update status on query object - return resourceWebClient.queryStatus(resource.getResourceRSPath(), queryId.toString(), credentialsQueryRequest); - } - - private void verifyQueryStatusRequest( - Resource resource, QueryRequest credentialsQueryRequest, UUID queryId, HttpHeaders headers - ) throws ProtocolException { - if (resource == null) { - throw new ApplicationException(ApplicationException.MISSING_RESOURCE); - } - if (resource.getResourceRSPath() == null) { - throw new ApplicationException(ApplicationException.MISSING_RESOURCE_PATH); - } - if (credentialsQueryRequest.getResourceCredentials() == null) { - credentialsQueryRequest.setResourceCredentials(new HashMap<>()); - } - if (resource.getToken() != null) { - credentialsQueryRequest.getResourceCredentials().put(ResourceWebClient.BEARER_TOKEN_KEY, resource.getToken()); - } - - logger.info("path=/query/{queryId}/status, queryId={}, requestSource={}, queryRequest={}", - queryId, - Utilities.getRequestSourceFromHeader(headers), - Utilities.convertQueryRequestToString(mapper, credentialsQueryRequest) - ); - } - - private Resource verifyQueryRequest(QueryRequest dataQueryRequest, HttpHeaders headers) throws ProtocolException { - if (dataQueryRequest == null) { - throw new ProtocolException(ProtocolException.MISSING_DATA); - } - UUID resourceId = dataQueryRequest.getResourceUUID(); - if (resourceId == null){ - throw new ProtocolException(ProtocolException.MISSING_RESOURCE_ID); - } - Resource resource = resourceRepo.getById(resourceId); - if (resource == null){ - throw new ProtocolException(ProtocolException.RESOURCE_NOT_FOUND + resourceId); - } - if (resource.getResourceRSPath() == null){ - throw new ApplicationException(ApplicationException.MISSING_RESOURCE_PATH); - } - if (dataQueryRequest.getResourceCredentials() == null){ - dataQueryRequest.setResourceCredentials(new HashMap<>()); - } - - logger.info("path=/query, requestSource={}, queryRequest={}", - Utilities.getRequestSourceFromHeader(headers), - Utilities.convertQueryRequestToString(mapper, dataQueryRequest) - ); - return resource; - } + /** + * Executes a query on a PIC-SURE resource and creates a Query entity in the database for the query. + * + * @param dataQueryRequest - - {@link QueryRequest} containing resource specific credentials object and resource specific query (could + * be a string or a json object) + * @return {@link QueryStatus} + */ + public QueryStatus institutionalQuery(FederatedQueryRequest dataQueryRequest, HttpHeaders headers, String email) { + String siteCode = siteParsingService.parseSiteOfOrigin(email).orElse("Unknown"); + dataQueryRequest.setInstitutionOfOrigin(siteCode); + dataQueryRequest.setRequesterEmail(email); + Resource resource = verifyQueryRequest(dataQueryRequest, headers); + dataQueryRequest.getResourceCredentials().put(ResourceWebClient.BEARER_TOKEN_KEY, resource.getToken()); + + QueryStatus response = resourceWebClient.query(resource.getResourceRSPath(), dataQueryRequest); + Query queryEntity = copyQuery(dataQueryRequest, resource, response); + queryRepo.persist(queryEntity); + return response; + } + + private Query copyQuery(QueryRequest dataQueryRequest, Resource resource, QueryStatus response) { + Query queryEntity = new Query(); + queryEntity.setResourceResultId(response.getResourceResultId()); + queryEntity.setResource(resource); + queryEntity.setStatus(response.getStatus()); + queryEntity.setStartTime(new Date(response.getStartTime())); + + ObjectMapper mapper = new ObjectMapper(); + String queryJson = null; + if (dataQueryRequest.getQuery() != null) { + try { + queryJson = mapper.writeValueAsString(dataQueryRequest); + } catch (JsonProcessingException e) { + throw new ProtocolException(ProtocolException.INCORRECTLY_FORMATTED_REQUEST); + } + } + if (response.getResultMetadata() == null) { + response.setResultMetadata(new HashMap<>()); + } + Map metaData = response.getResultMetadata(); + + if (dataQueryRequest instanceof FederatedQueryRequest) { + FederatedQueryRequest gicRequest = (FederatedQueryRequest) dataQueryRequest; + metaData.put("commonAreaUUID", gicRequest.getCommonAreaUUID()); + metaData.put("site", gicRequest.getInstitutionOfOrigin()); + metaData.put("sharingStatus", DataSharingStatus.Unknown); + metaData.put("requesterEmail", gicRequest.getRequesterEmail()); + } + + queryEntity.setQuery(queryJson); + + if (!metaData.isEmpty()) { + try { + queryEntity.setMetadata(mapper.writeValueAsString(metaData).getBytes()); + } catch (JsonProcessingException e) { + logger.warn("Unable to parse metadata ", e); + } + } + return queryEntity; + } + + public QueryStatus institutionQueryStatus(UUID queryId, FederatedQueryRequest credentialsQueryRequest, HttpHeaders headers) { + if (queryId == null) { + throw new ProtocolException(ProtocolException.MISSING_QUERY_ID); + } + if (credentialsQueryRequest == null) { + throw new ProtocolException(ProtocolException.MISSING_DATA); + } + Resource resource = resourceRepo.getById(credentialsQueryRequest.getResourceUUID()); + + verifyQueryStatusRequest(resource, credentialsQueryRequest, queryId, headers); + + // Update status on query object + return resourceWebClient.queryStatus(resource.getResourceRSPath(), queryId.toString(), credentialsQueryRequest); + } + + private void verifyQueryStatusRequest(Resource resource, QueryRequest credentialsQueryRequest, UUID queryId, HttpHeaders headers) + throws ProtocolException { + if (resource == null) { + throw new ApplicationException(ApplicationException.MISSING_RESOURCE); + } + if (resource.getResourceRSPath() == null) { + throw new ApplicationException(ApplicationException.MISSING_RESOURCE_PATH); + } + if (credentialsQueryRequest.getResourceCredentials() == null) { + credentialsQueryRequest.setResourceCredentials(new HashMap<>()); + } + if (resource.getToken() != null) { + credentialsQueryRequest.getResourceCredentials().put(ResourceWebClient.BEARER_TOKEN_KEY, resource.getToken()); + } + + logger.info( + "path=/query/{queryId}/status, queryId={}, requestSource={}, queryRequest={}", queryId, + Utilities.getRequestSourceFromHeader(headers), Utilities.convertQueryRequestToString(mapper, credentialsQueryRequest) + ); + } + + private Resource verifyQueryRequest(QueryRequest dataQueryRequest, HttpHeaders headers) throws ProtocolException { + if (dataQueryRequest == null) { + throw new ProtocolException(ProtocolException.MISSING_DATA); + } + UUID resourceId = dataQueryRequest.getResourceUUID(); + if (resourceId == null) { + throw new ProtocolException(ProtocolException.MISSING_RESOURCE_ID); + } + Resource resource = resourceRepo.getById(resourceId); + if (resource == null) { + throw new ProtocolException(ProtocolException.RESOURCE_NOT_FOUND + resourceId); + } + if (resource.getResourceRSPath() == null) { + throw new ApplicationException(ApplicationException.MISSING_RESOURCE_PATH); + } + if (dataQueryRequest.getResourceCredentials() == null) { + dataQueryRequest.setResourceCredentials(new HashMap<>()); + } + + logger.info( + "path=/query, requestSource={}, queryRequest={}", Utilities.getRequestSourceFromHeader(headers), + Utilities.convertQueryRequestToString(mapper, dataQueryRequest) + ); + return resource; + } } diff --git a/pic-sure-resources/pic-sure-passthrough-resource/src/main/resources/META-INF/persistence.xml b/pic-sure-resources/pic-sure-passthrough-resource/src/main/resources/META-INF/persistence.xml new file mode 100644 index 00000000..307ff002 --- /dev/null +++ b/pic-sure-resources/pic-sure-passthrough-resource/src/main/resources/META-INF/persistence.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/pic-sure-resources/pic-sure-passthrough-resource/src/main/resources/webapp/META-INF/context.xml b/pic-sure-resources/pic-sure-passthrough-resource/src/main/resources/webapp/META-INF/context.xml index 9f4aa7fd..6d30046d 100755 --- a/pic-sure-resources/pic-sure-passthrough-resource/src/main/resources/webapp/META-INF/context.xml +++ b/pic-sure-resources/pic-sure-passthrough-resource/src/main/resources/webapp/META-INF/context.xml @@ -1,3 +1,3 @@ - - - + + + \ No newline at end of file diff --git a/pic-sure-resources/pic-sure-passthrough-resource/src/main/resources/webapp/WEB-INF/beans.xml b/pic-sure-resources/pic-sure-passthrough-resource/src/main/resources/webapp/WEB-INF/beans.xml index 9a692dbe..1c97df0e 100755 --- a/pic-sure-resources/pic-sure-passthrough-resource/src/main/resources/webapp/WEB-INF/beans.xml +++ b/pic-sure-resources/pic-sure-passthrough-resource/src/main/resources/webapp/WEB-INF/beans.xml @@ -1,6 +1,6 @@ - - + + \ No newline at end of file diff --git a/pic-sure-resources/pic-sure-passthrough-resource/src/main/resources/webapp/WEB-INF/web.xml b/pic-sure-resources/pic-sure-passthrough-resource/src/main/resources/webapp/WEB-INF/web.xml new file mode 100644 index 00000000..c29b44b5 --- /dev/null +++ b/pic-sure-resources/pic-sure-passthrough-resource/src/main/resources/webapp/WEB-INF/web.xml @@ -0,0 +1,20 @@ + + + JAX-RS Simple Service + JAX-RS Simple Service + + contextConfigLocation + webapp/WEB-INF/beans.xml + + + resteasy.scan + true + + + org.springframework.web.context.ContextLoaderListener + + diff --git a/pic-sure-resources/pic-sure-resource-api/src/main/java/edu/harvard/dbmi/avillach/domain/FederatedQueryRequest.java b/pic-sure-resources/pic-sure-resource-api/src/main/java/edu/harvard/dbmi/avillach/domain/FederatedQueryRequest.java index 9497f1cb..e0695f28 100644 --- a/pic-sure-resources/pic-sure-resource-api/src/main/java/edu/harvard/dbmi/avillach/domain/FederatedQueryRequest.java +++ b/pic-sure-resources/pic-sure-resource-api/src/main/java/edu/harvard/dbmi/avillach/domain/FederatedQueryRequest.java @@ -6,117 +6,132 @@ import java.util.Map; import java.util.UUID; +// spotless:off @Schema(name = "QueryRequest", description = "Object containing credentials map under 'resourceCredentials' and query" + - " object under 'query'. The query object expectedResultType can be on of the following " + - "\"COUNT\", \"CROSS_COUNT\", \"INFO_COLUMN_LISTING\", \"OBSERVATION_COUNT\", \"OBSERVATION_CROSS_COUNT\", \"DATAFRAME\". ", - example = "{\n" + - " \"resourceUUID\": \"\",\n" + - " \"query\": {\n" + - " \"categoryFilters\": {\n" + - " \"\\\\demographics\\\\SEX\\\\\": [\n" + - " \"female\",\n" + - " \"male\"\n" + - " ]\n" + - " },\n" + - " \"numericFilters\": {\n" + - " \"\\\\demographics\\\\AGE\\\\\": {\n" + - " \"min\": \"0\",\n" + - " \"max\": \"85\"\n" + - " }\n" + - " },\n" + - " \"requiredFields\": [],\n" + - " \"anyRecordOf\": [\n" + - " \"\\\\demographics\\\\RACE\\\\\"\n" + - " ],\n" + - " \"variantInfoFilters\": [\n" + - " {\n" + - " \"categoryVariantInfoFilters\": {},\n" + - " \"numericVariantInfoFilters\": {}\n" + - " }\n" + - " ],\n" + - " \"expectedResultType\": \"DATAFRAME\",\n" + - " \"fields\": []\n" + - " }\n" + - "}") + " object under 'query'. The query object expectedResultType can be on of the following " + + "\"COUNT\", \"CROSS_COUNT\", \"INFO_COLUMN_LISTING\", \"OBSERVATION_COUNT\", \"OBSERVATION_CROSS_COUNT\", \"DATAFRAME\". ", + example = "{\n" + + " \"resourceUUID\": \"\",\n" + + " \"query\": {\n" + + " \"categoryFilters\": {\n" + + " \"\\\\demographics\\\\SEX\\\\\": [\n" + + " \"female\",\n" + + " \"male\"\n" + + " ]\n" + + " },\n" + + " \"numericFilters\": {\n" + + " \"\\\\demographics\\\\AGE\\\\\": {\n" + + " \"min\": \"0\",\n" + + " \"max\": \"85\"\n" + + " }\n" + + " },\n" + + " \"requiredFields\": [],\n" + + " \"anyRecordOf\": [\n" + + " \"\\\\demographics\\\\RACE\\\\\"\n" + + " ],\n" + + " \"variantInfoFilters\": [\n" + + " {\n" + + " \"categoryVariantInfoFilters\": {},\n" + + " \"numericVariantInfoFilters\": {}\n" + + " }\n" + + " ],\n" + + " \"expectedResultType\": \"DATAFRAME\",\n" + + " \"fields\": []\n" + + " }\n" + + "}") + +//spotless:on /* - * QueryRequests for vanilla GIC PIC-SURE. Includes details about the source - * of the request, the requester, and any unifying identifiers for sharing + * QueryRequests for vanilla GIC PIC-SURE. Includes details about the source of the request, the requester, and any unifying identifiers for + * sharing */ public class FederatedQueryRequest implements QueryRequest { - @Schema(hidden = true) - private Map resourceCredentials = new HashMap<>(); - - @Schema(hidden = true) - private Object query; - - @Schema(hidden = true) - private UUID resourceUUID; - - @Schema(hidden = true) - private UUID commonAreaUUID; - - @Schema(hidden = true) - private String institutionOfOrigin; - - @Override - public Map getResourceCredentials() { - return resourceCredentials; - } - - @Override - public FederatedQueryRequest setResourceCredentials(Map resourceCredentials) { - this.resourceCredentials = resourceCredentials; - return this; - } - - @Override - public Object getQuery() { - return query; - } - - @Override - public FederatedQueryRequest setQuery(Object query) { - this.query = query; - return this; - } - - @Override - public UUID getResourceUUID() { - return resourceUUID; - } - - @Override - public void setResourceUUID(UUID resourceUUID) { - this.resourceUUID = resourceUUID; - } - - @Override - public FederatedQueryRequest copy() { - FederatedQueryRequest request = new FederatedQueryRequest(); - request.setQuery(getQuery()); - request.setResourceCredentials(getResourceCredentials()); - request.setResourceUUID(getResourceUUID()); - request.setCommonAreaUUID(getCommonAreaUUID()); - request.setInstitutionOfOrigin(getInstitutionOfOrigin()); - - return request; - } - - public UUID getCommonAreaUUID() { - return commonAreaUUID; - } - - public void setCommonAreaUUID(UUID commonAreaUUID) { - this.commonAreaUUID = commonAreaUUID; - } - - public String getInstitutionOfOrigin() { - return institutionOfOrigin; - } - - public void setInstitutionOfOrigin(String institutionOfOrigin) { - this.institutionOfOrigin = institutionOfOrigin; - } + @Schema(hidden = true) + private Map resourceCredentials = new HashMap<>(); + + @Schema(hidden = true) + private Object query; + + @Schema(hidden = true) + private UUID resourceUUID; + + @Schema(hidden = true) + private UUID commonAreaUUID; + + @Schema(hidden = true) + private String institutionOfOrigin; + + @Schema(hidden = true) + private String requesterEmail; + + @Override + public Map getResourceCredentials() { + return resourceCredentials; + } + + @Override + public FederatedQueryRequest setResourceCredentials(Map resourceCredentials) { + this.resourceCredentials = resourceCredentials; + return this; + } + + @Override + public Object getQuery() { + return query; + } + + @Override + public FederatedQueryRequest setQuery(Object query) { + this.query = query; + return this; + } + + @Override + public UUID getResourceUUID() { + return resourceUUID; + } + + @Override + public void setResourceUUID(UUID resourceUUID) { + this.resourceUUID = resourceUUID; + } + + @Override + public FederatedQueryRequest copy() { + FederatedQueryRequest request = new FederatedQueryRequest(); + request.setQuery(getQuery()); + request.setResourceCredentials(getResourceCredentials()); + request.setResourceUUID(getResourceUUID()); + request.setCommonAreaUUID(getCommonAreaUUID()); + request.setInstitutionOfOrigin(getInstitutionOfOrigin()); + request.setRequesterEmail(getRequesterEmail()); + + return request; + } + + public UUID getCommonAreaUUID() { + return commonAreaUUID; + } + + public void setCommonAreaUUID(UUID commonAreaUUID) { + this.commonAreaUUID = commonAreaUUID; + } + + public String getInstitutionOfOrigin() { + return institutionOfOrigin; + } + + public void setInstitutionOfOrigin(String institutionOfOrigin) { + this.institutionOfOrigin = institutionOfOrigin; + } + + public String getRequesterEmail() { + return requesterEmail; + } + + public void setRequesterEmail(String requesterEmail) { + this.requesterEmail = requesterEmail; + } } diff --git a/pom.xml b/pom.xml index d09e1d9d..e46782a2 100644 --- a/pom.xml +++ b/pom.xml @@ -48,6 +48,7 @@ 4.26 code-formatting/eclipse-formatter.xml +