Skip to content

Commit

Permalink
[ALS-5056] Get Query Site of Origin
Browse files Browse the repository at this point in the history
- Requirement: in order to know where to send data, we need
to associate each query with the requester's site
- Create table of site code, site name, site email domain
- When an institutional query request is made:
  - Parse the site code from the email of the requesting user
  - Add that site code to the query request
- When persisting a query object after starting a query request
  - Check for a site code in the query request
  - Add it to the query meta if it exists
- Add some extra niceness for later data transfer work
  - S3 status code
- Interface, implementations for general use, GIC
- Repos I verified this change with:
  - pic-sure-auth-microapp: master and fence branches
  - pic-sure-gic-common-frontend: Search resource, UUID gen resource
  • Loading branch information
Luke Sikina authored and Luke-Sikina committed Nov 16, 2023
1 parent 519ecc2 commit 2dc525d
Show file tree
Hide file tree
Showing 36 changed files with 632 additions and 242 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package edu.harvard.dbmi.avillach.data.entity;

public enum DataSharingStatus {
Unknown, Pending, Error, Complete, NotShared
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package edu.harvard.dbmi.avillach.data.entity;

import io.swagger.v3.oas.annotations.media.Schema;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;

@Schema(description = "A site that contains a PIC-SURE installation that we can send data to")
@Table(uniqueConstraints = {
@UniqueConstraint(name = "unique_code", columnNames = { "code" }),
@UniqueConstraint(name = "unique_email", columnNames = { "domain" })
})
@Entity(name = "site")
public class Site extends BaseEntity {

@Schema(description = "The site code. Ex: BCH")
@Column(length = 15)
private String code;

@Schema(description = "The site name. Ex: Boston Children's")
@Column(length = 255)
private String name;

@Schema(description = "The email domain of users for this site. Ex: childrens.harvard.edu")
@Column(length = 255)
private String domain;

public String getCode() {
return code;
}

public void setCode(String code) {
this.code = code;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getDomain() {
return domain;
}

public void setDomain(String domain) {
this.domain = domain;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package edu.harvard.dbmi.avillach.data.repository;

import edu.harvard.dbmi.avillach.data.entity.NamedDataset;
import edu.harvard.dbmi.avillach.data.entity.Site;

import javax.enterprise.context.ApplicationScoped;
import javax.transaction.Transactional;
import java.util.UUID;

@Transactional
@ApplicationScoped
public class SiteRepository extends BaseRepository<Site, UUID>{
protected SiteRepository() {super(Site.class);}
}
11 changes: 11 additions & 0 deletions pic-sure-api-data/src/main/resources/db/sql/V6__ADD_SITE_TABLE.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
USE `picsure`;

CREATE TABLE `site` (
`uuid` binary(16) NOT NULL,
`code` varchar(15) COLLATE utf8_bin DEFAULT NULL,
`name` varchar(255) COLLATE utf8_bin DEFAULT NULL,
`domain` varchar(255) COLLATE utf8_bin DEFAULT NULL,
PRIMARY KEY (`uuid`),
CONSTRAINT `unique_code` UNIQUE (`code`),
CONSTRAINT `unique_domain` UNIQUE (`domain`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;

import edu.harvard.dbmi.avillach.domain.*;
import edu.harvard.dbmi.avillach.service.*;
Expand Down Expand Up @@ -153,11 +154,16 @@ public QueryStatus query(

@Parameter
@QueryParam("isInstitute")
Boolean isInstitutionQuery
Boolean isInstitutionQuery,

@Context SecurityContext context
) {
return isInstitutionQuery == null || !isInstitutionQuery ?
queryService.query(dataQueryRequest, headers) :
queryService.institutionalQuery(dataQueryRequest, headers);
if (isInstitutionQuery == null || !isInstitutionQuery) {
return queryService.query(dataQueryRequest, headers);
} else {
String email = context.getUserPrincipal().getName();
return queryService.institutionalQuery((FederatedQueryRequest) dataQueryRequest, headers, email);
}
}

@POST
Expand Down Expand Up @@ -194,9 +200,11 @@ public QueryStatus queryStatus(
@QueryParam("isInstitute")
Boolean isInstitutionQuery
) {
return isInstitutionQuery == null || !isInstitutionQuery ?
queryService.queryStatus(queryId, credentialsQueryRequest, headers) :
queryService.institutionQueryStatus(queryId, credentialsQueryRequest, headers);
if (credentialsQueryRequest instanceof GeneralQueryRequest) {
return queryService.queryStatus(queryId, (GeneralQueryRequest) credentialsQueryRequest, headers);
} else {
return queryService.institutionQueryStatus(queryId, (FederatedQueryRequest) credentialsQueryRequest, headers);
}
}

@POST
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import edu.harvard.dbmi.avillach.data.entity.AuthUser;
import edu.harvard.dbmi.avillach.data.repository.QueryRepository;
import edu.harvard.dbmi.avillach.data.repository.ResourceRepository;
import edu.harvard.dbmi.avillach.domain.QueryRequest;
import edu.harvard.dbmi.avillach.domain.GeneralQueryRequest;
import edu.harvard.dbmi.avillach.service.ResourceWebClient;
import edu.harvard.dbmi.avillach.util.exception.ApplicationException;
import edu.harvard.dbmi.avillach.util.response.PICSUREResponse;
Expand Down Expand Up @@ -204,7 +204,7 @@ private AuthUser callTokenIntroEndpoint(ContainerRequestContext requestContext,
edu.harvard.dbmi.avillach.data.entity.Resource resource = resourceRepo.getById(resourceUUID);
//logger.info("resource obj: " + resource + " path: " + resource.getResourceRSPath());
if (resource != null && resource.getResourceRSPath() != null){
QueryRequest queryRequest = new QueryRequest();
GeneralQueryRequest queryRequest = new GeneralQueryRequest();
queryRequest.getResourceCredentials().put(ResourceWebClient.BEARER_TOKEN_KEY, resource.getToken());
queryRequest.setResourceUUID(resourceUUID);
queryRequest.setQuery(((Map)queryObject).get("query"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import edu.harvard.dbmi.avillach.data.entity.Resource;
import edu.harvard.dbmi.avillach.data.repository.ResourceRepository;
import edu.harvard.dbmi.avillach.domain.GeneralQueryRequest;
import edu.harvard.dbmi.avillach.domain.QueryRequest;
import edu.harvard.dbmi.avillach.domain.ResourceInfo;
import edu.harvard.dbmi.avillach.util.Utilities;
Expand Down Expand Up @@ -46,7 +47,7 @@ public ResourceInfo info(UUID resourceId, QueryRequest credentialsQueryRequest,
throw new ApplicationException(ApplicationException.MISSING_RESOURCE_PATH);
}
if (credentialsQueryRequest == null){
credentialsQueryRequest = new QueryRequest();
credentialsQueryRequest = new GeneralQueryRequest();
}
if (credentialsQueryRequest.getResourceCredentials() == null){
credentialsQueryRequest.setResourceCredentials(new HashMap<String, String>());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import edu.harvard.dbmi.avillach.data.entity.DataSharingStatus;
import edu.harvard.dbmi.avillach.data.entity.Query;
import edu.harvard.dbmi.avillach.data.entity.Resource;
import edu.harvard.dbmi.avillach.data.repository.QueryRepository;
import edu.harvard.dbmi.avillach.data.repository.ResourceRepository;
import edu.harvard.dbmi.avillach.domain.FederatedQueryRequest;
import edu.harvard.dbmi.avillach.domain.GeneralQueryRequest;
import edu.harvard.dbmi.avillach.domain.QueryRequest;
import edu.harvard.dbmi.avillach.domain.QueryStatus;
import edu.harvard.dbmi.avillach.security.JWTFilter;
Expand Down Expand Up @@ -47,6 +50,9 @@ public class PicsureQueryService {
@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.
Expand Down Expand Up @@ -88,7 +94,7 @@ public QueryStatus query(QueryRequest dataQueryRequest, HttpHeaders headers) {
* @return {@link QueryStatus}
*/
@Transactional
public QueryStatus queryStatus(UUID queryId, QueryRequest credentialsQueryRequest, HttpHeaders headers) {
public QueryStatus queryStatus(UUID queryId, GeneralQueryRequest credentialsQueryRequest, HttpHeaders headers) {
if (queryId == null){
throw new ProtocolException(ProtocolException.MISSING_QUERY_ID);
}
Expand Down Expand Up @@ -277,7 +283,9 @@ public QueryStatus queryMetadata(UUID queryId, HttpHeaders headers){
* and resource specific query (could be a string or a json object)
* @return {@link QueryStatus}
*/
public QueryStatus institutionalQuery(QueryRequest dataQueryRequest, HttpHeaders headers) {
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());

Expand Down Expand Up @@ -307,11 +315,14 @@ private Query copyQuery(QueryRequest dataQueryRequest, Resource resource, QueryS
throw new ProtocolException(ProtocolException.INCORRECTLY_FORMATTED_REQUEST);
}
}

Map<String, Object> metaData = response.getResultMetadata();
metaData = metaData == null ? new HashMap<>() : metaData;
if (dataQueryRequest.getCommonAreaUUID() != null) {
metaData.put("commonAreaUUID", dataQueryRequest.getCommonAreaUUID());

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);
Expand All @@ -326,7 +337,7 @@ private Query copyQuery(QueryRequest dataQueryRequest, Resource resource, QueryS
return queryEntity;
}

public QueryStatus institutionQueryStatus(UUID queryId, QueryRequest credentialsQueryRequest, HttpHeaders headers) {
public QueryStatus institutionQueryStatus(UUID queryId, FederatedQueryRequest credentialsQueryRequest, HttpHeaders headers) {
if (queryId == null) {
throw new ProtocolException(ProtocolException.MISSING_QUERY_ID);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import edu.harvard.dbmi.avillach.data.entity.Resource;
import edu.harvard.dbmi.avillach.data.repository.ResourceRepository;
import edu.harvard.dbmi.avillach.domain.PaginatedSearchResult;
import edu.harvard.dbmi.avillach.domain.GeneralQueryRequest;
import edu.harvard.dbmi.avillach.domain.QueryRequest;
import edu.harvard.dbmi.avillach.domain.SearchResults;
import edu.harvard.dbmi.avillach.util.Utilities;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package edu.harvard.dbmi.avillach.service;

import edu.harvard.dbmi.avillach.data.entity.Site;
import edu.harvard.dbmi.avillach.data.repository.SiteRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import java.util.List;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class SiteParsingService {

private static final Logger LOG = LoggerFactory.getLogger(SiteParsingService.class);

private static final Pattern emailRegex = Pattern.compile("^([^@]+)(@)(.*)$");

@Inject
SiteRepository repository;

public Optional<String> parseSiteOfOrigin(String email) {
Matcher matcher = emailRegex.matcher(email);
if (!matcher.find()) {
LOG.warn("Unable to parse domain for email: {}", email);
return Optional.empty();
}

List<Site> matchingDomains = repository.getByColumn("domain", matcher.group(3));
if (matchingDomains.isEmpty()) {
LOG.warn("Unable to match domain for email: {}, looked for domain: {}", email, matcher.group(3));
return Optional.empty();
}
if (matchingDomains.size() > 1) {
LOG.warn("Multiple domains match email. This should never happen! Email: {}", email);
return Optional.empty();
}
return Optional.of(matchingDomains.get(0).getCode());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
import edu.harvard.dbmi.avillach.PicSureWarInit;
import edu.harvard.dbmi.avillach.data.entity.Resource;
import edu.harvard.dbmi.avillach.data.repository.ResourceRepository;
import edu.harvard.dbmi.avillach.domain.QueryRequest;
import edu.harvard.dbmi.avillach.domain.GeneralQueryRequest;
import edu.harvard.dbmi.avillach.domain.ResourceInfo;
import edu.harvard.dbmi.avillach.util.exception.ApplicationException;

Expand Down Expand Up @@ -126,7 +126,7 @@ private boolean testPSAMAResponds() throws UnsupportedOperationException, IOExce

private boolean testResourcesRespond(List<Resource> resourcesToTest) {
for(Resource resource : resourcesToTest) {
ResourceInfo info = new ResourceWebClient().info(resource.getResourceRSPath(), new QueryRequest());
ResourceInfo info = new ResourceWebClient().info(resource.getResourceRSPath(), new GeneralQueryRequest());
if(info==null) {
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<class>edu.harvard.dbmi.avillach.data.entity.Query</class>
<class>edu.harvard.dbmi.avillach.data.entity.Resource</class>
<class>edu.harvard.dbmi.avillach.data.entity.NamedDataset</class>
<class>edu.harvard.dbmi.avillach.data.entity.Site</class>

<properties>
<property name="hibernate.archive.autodetection" value="class" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import edu.harvard.dbmi.avillach.data.entity.Resource;
import edu.harvard.dbmi.avillach.data.repository.ResourceRepository;
import edu.harvard.dbmi.avillach.domain.QueryRequest;
import edu.harvard.dbmi.avillach.domain.GeneralQueryRequest;
import edu.harvard.dbmi.avillach.domain.ResourceInfo;
import edu.harvard.dbmi.avillach.service.PicsureInfoService;
import edu.harvard.dbmi.avillach.service.ResourceWebClient;
Expand Down Expand Up @@ -60,7 +60,7 @@ public void setUp() {

@Test
public void testInfoEndpoints() {
QueryRequest infoRequest = new QueryRequest();
GeneralQueryRequest infoRequest = new GeneralQueryRequest();
Map<String, String> clientCredentials = new HashMap<String, String>();
infoRequest.setResourceCredentials(clientCredentials);

Expand Down
Loading

0 comments on commit 2dc525d

Please sign in to comment.