Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC83: Add admin call to make virtual study available for all users on their landing pages #10829

Merged
merged 28 commits into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
25b34ae
Implement endpoints for public virtual studies
forus Jun 7, 2024
23bba70
Add possibility to specify cancer type id and pmi for virt. study dur…
forus Jun 12, 2024
d3c2f3c
Filter out forbidden study ids from virtual studies
forus Jun 13, 2024
f102f61
Do not allow set * user for user virtual study
forus Jun 13, 2024
fc78963
Add integration tests for (un)publishing virtual study
forus Jun 19, 2024
7cc1c16
Assert fields fo published virtual studies
forus Jun 19, 2024
f231dd9
Use recommended ways to inject dependencies in spring
forus Jun 20, 2024
c247aa9
Add issue link to session service FIXMEs
forus Jun 20, 2024
522bd8a
Fix sonar reported NPE bugs
forus Jun 20, 2024
7e86b34
Remove unnecessary checks for null
forus Jun 26, 2024
7f23ad0
Remove obsolete TODO comment
forus Jun 26, 2024
d81832f
Throw AccessForbiddenException and use GlobalExceptionHadler instead
forus Jun 26, 2024
837435d
Throw IllegalStateException is downstream server return unsuccessful …
forus Jun 26, 2024
bde0e62
Remove raw use of ResponseEntity
forus Jun 26, 2024
1f2dc38
Throw bad request exception instead of returning ResponseEntity
forus Jun 26, 2024
8d70521
Do not filter out VS when user does not hava access to underlying stu…
forus Jun 26, 2024
cc3a7ee
Fix integration tests
haynescd Jun 27, 2024
faaf3fa
Extract http calls to the session service to the handler
forus Jun 27, 2024
0e3e59b
Remove todo comment
forus Jun 27, 2024
5793671
Fix sonarcloud issues
forus Jun 27, 2024
67dcf90
Deduplicate ensuring publisher api key is correct
forus Jun 27, 2024
a72884a
Remove usage of generic wildcard type
forus Jun 27, 2024
ca1b3a9
Extract logic to update VS metadata fields into a method
forus Jun 27, 2024
8616737
Document publishing virtual study feature
forus Jun 28, 2024
3347010
Update docs/Create-And-Publish-Virtual-Study.md
forus Jul 2, 2024
4cd40c4
Publish virtual study by modifying it instead of making copy
forus Jul 2, 2024
b99e106
Improve name and docs of method to retrieve VS for user
forus Jul 2, 2024
84249a3
Assign VM after un-publshing to the owner
forus Jul 2, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions docs/Create-And-Publish-Virtual-Study.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Create and Publish Virtual Study

A [Virtual Study](./user-guide/faq.md#what-is-a-virtual-study) defines a subset or a combination of samples from one or more studies in the system.

*Note*: To publish or un-publish a virtual study, your cBioPortal instance must be configured with `session.endpoint.publisher-api-key` in the `application.properties`.

## Create Virtual Study

To create a virtual study in cBioPortal, follow these steps:

1. Define the desired filters on the study or multiple studies summary page.
2. Click the button with the bookmark icon () in the top right corner of the screen.
3. Provide a title and description, then click the Save button. You will see a link that looks like:

```
https://<cbioportal_host>/study?id=<virtual_study_id>
```

4. Save the virtual study link or ID if you want to publish it.

If you are logged in, this virtual study will appear in the `My Virtual Studies` section on the landing page.
You can always find the ID of the virtual study from the URL of the page that opens after clicking on it.

## Publish Virtual Study

To publish a virtual study, you need to supply the publisher API key in the `X-PUBLISHER-API-KEY` header.

Here is a curl command to publish a virtual study:
```shell
curl \
-X POST \
-H 'X-PUBLISHER-API-KEY: <session.endpoint.publisher-api-key>' \
-v 'http://<cbioportal_host>/api/public_virtual_studies/<virtual_study_id>'
```
The published virtual study will appear under the `Public Virtual Studies` section (next to the `My Virtual Studies` section) on the landing page for all users of cBioPortal.

While publishing, you can specify the PubMed ID (`pmid`) and `typeOfCancerId` of the virtual study using the following command:
```shell
curl \
-X POST \
-H 'X-PUBLISHER-API-KEY: <session.endpoint.publisher-api-key>' \
-v 'http://<cbioportal_host>/api/public_virtual_studies/<virtual_study_id>?pmid=<pmid>&typeOfCancerId=<code>'
```

The type of cancer code should match the known types of cancers in the cBioPortal database.
If the type of cancer is specified, the published virtual study will appear under the respective cancer section on the landing page.
Specifying the `pmid` enables a link to the PubMed page of the study.

## Un-publish Virtual Study

To un-publish a virtual study, you need to supply the publisher API key in the `X-PUBLISHER-API-KEY` header.
After un-publishing, virtual study will no longer be displayed in the `Public Virtual Studies` section on the landing page.
However, it reappears in the `My Virtual Studies` section for the owner.

Here is the command to un-publish a virtual study:
```shell
curl \
-X DELETE \
-H 'X-PUBLISHER-API-KEY: <session.endpoint.publisher-api-key>' \
-v 'http://<cbioportal_host>/api/public_virtual_studies/<virtual_study_id>'
```
3 changes: 3 additions & 0 deletions docs/Data-Loading.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,6 @@ To remove a study, the [cbioportalImporter script](/Data-Loading-Maintaining-Stu

## Example studies
Examples for the different types of data are available on the [File Formats](/File-Formats.md) page. The Provisional TCGA studies, downloadable from the [Data Sets section](https://www.cbioportal.org/datasets) are complete studies that can be used as reference when creating data files.

## Public Virtual Studies
If your new study data is a subset or a combination of existing studies in the system, consider using [Public Virtual Studies](./Create-And-Publish-Virtual-Study.md) instead of duplicating data.
10 changes: 9 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@
<selenium_chrome_driver.version>3.14.0</selenium_chrome_driver.version>
<selenium.version>4.17.0</selenium.version>
<sentry.version>7.1.0</sentry.version>
<apache_httpclient.version>5.2.1</apache_httpclient.version>


<!-- No sure what these are for -->
Expand Down Expand Up @@ -346,7 +347,14 @@
<artifactId>sentry-spring-boot-starter-jakarta</artifactId>
<version>${sentry.version}</version>
</dependency>
</dependencies>
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>${apache_httpclient.version}</version>
<scope>test</scope>
</dependency>

</dependencies>

<dependencyManagement>
<dependencies>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@

import org.cbioportal.persistence.cachemaputil.CacheMapUtil;
import org.cbioportal.security.CancerStudyPermissionEvaluator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
Expand All @@ -17,28 +15,22 @@
// We are allowing users to enable method_authorization if optional_oauth2 is selected
@ConditionalOnExpression("{'oauth2','saml', 'saml_plus_basic'}.contains('${authenticate}') or ('optional_oauth2' eq '${authenticate}' and 'true' eq '${security.method_authorization_enabled}')")
public class MethodSecurityConfig {
@Value("${app.name:}")
private String appName;

@Value("${filter_groups_by_appname:true}")
private String doFilterGroupsByAppName;

@Value("${always_show_study_group:}")
private String alwaysShowCancerStudyGroup;

@Autowired
private CacheMapUtil cacheMapUtil;

@Bean
public MethodSecurityExpressionHandler createExpressionHandler() {
public CancerStudyPermissionEvaluator cancerStudyPermissionEvaluator(
@Value("${app.name:}") String appName,
@Value("${filter_groups_by_appname:true}") String doFilterGroupsByAppName,
@Value("${always_show_study_group:}") String alwaysShowCancerStudyGroup,
CacheMapUtil cacheMapUtil
) {
return new CancerStudyPermissionEvaluator(appName, doFilterGroupsByAppName, alwaysShowCancerStudyGroup, cacheMapUtil);
}

@Bean
public MethodSecurityExpressionHandler createExpressionHandler(CancerStudyPermissionEvaluator cancerStudyPermissionEvaluator) {
DefaultMethodSecurityExpressionHandler expressionHandler =
new DefaultMethodSecurityExpressionHandler();
expressionHandler.setPermissionEvaluator(cancerStudyPermissionEvaluator());
expressionHandler.setPermissionEvaluator(cancerStudyPermissionEvaluator);
return expressionHandler;
}

@Bean
public CancerStudyPermissionEvaluator cancerStudyPermissionEvaluator() {
return new CancerStudyPermissionEvaluator(appName, doFilterGroupsByAppName, alwaysShowCancerStudyGroup, cacheMapUtil);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.cbioportal.service.exception;

public class AccessForbiddenException extends RuntimeException {
public AccessForbiddenException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,31 @@


import java.nio.charset.Charset;
import java.util.Collections;
import java.util.List;

import com.mongodb.BasicDBObject;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.cbioportal.web.parameter.VirtualStudy;
import org.cbioportal.web.parameter.VirtualStudyData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

@Component
public class SessionServiceRequestHandler {

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

@Value("${session.service.url:}")
private String sessionServiceURL;

Expand Down Expand Up @@ -62,4 +73,81 @@ public String getSessionDataJson(SessionType type, String id) throws Exception {
return responseEntity.getBody();
}

/**
* Gets virtual study by id
* @param id - id of the virtual study to read
* @return virtual study
*/
public VirtualStudy getVirtualStudyById(String id) {
ResponseEntity<VirtualStudy> responseEntity = new RestTemplate()
.exchange(sessionServiceURL + "/virtual_study/" + id,
HttpMethod.GET,
new HttpEntity<>(getHttpHeaders()),
VirtualStudy.class);
HttpStatusCode statusCode = responseEntity.getStatusCode();
VirtualStudy virtualStudy = responseEntity.getBody();
if (!statusCode.is2xxSuccessful() || virtualStudy == null) {
LOG.error("The downstream server replied with statusCode={} and body={}." +
" Replying with the same status code to the client.",
statusCode, virtualStudy);
throw new IllegalStateException("The downstream server response is not successful");
}
return responseEntity.getBody();
}

/**
* Get list of virtual studies accessible to user
* @param username - user for whom get list of virtual studies
* @return - list of virtual studies
*/
public List<VirtualStudy> getVirtualStudiesAccessibleToUser(String username) {
BasicDBObject basicDBObject = new BasicDBObject();
basicDBObject.put("data.users", username);
ResponseEntity<List<VirtualStudy>> responseEntity = new RestTemplate().exchange(
sessionServiceURL + "/virtual_study/query/fetch",
HttpMethod.POST,
new HttpEntity<>(basicDBObject.toString(), getHttpHeaders()),
new ParameterizedTypeReference<>() {
});

return responseEntity.getBody();
}

/**
* Creates a virtual study out of virtual study definition (aka virtualStudyData)
* @param virtualStudyData - definition of virtual study
* @return virtual study object with id and the virtualStudyData
*/
public VirtualStudy createVirtualStudy(VirtualStudyData virtualStudyData) {
ResponseEntity<VirtualStudy> responseEntity = new RestTemplate().exchange(
sessionServiceURL + "/virtual_study",
HttpMethod.POST,
new HttpEntity<>(virtualStudyData, getHttpHeaders()),
new ParameterizedTypeReference<>() {
});

return responseEntity.getBody();
}


/**
* Soft delete of the virtual study by de-associating all assigned users.
* @param id - id of virtual study to soft delete
*/
public void softRemoveVirtualStudy(String id) {
VirtualStudy virtualStudy = getVirtualStudyById(id);
VirtualStudyData data = virtualStudy.getData();
data.setUsers(Collections.emptySet());
updateVirtualStudy(virtualStudy);
}

/**
* Updates virtual study
* @param virtualStudy - virtual study to update
*/
public void updateVirtualStudy(VirtualStudy virtualStudy) {
new RestTemplate()
.put(sessionServiceURL + "/virtual_study/" + virtualStudy.getId(),
new HttpEntity<>(virtualStudy.getData(), getHttpHeaders()));
}
}
Loading
Loading