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: Make virtual study available for all users on their landing pages #4923

Merged
merged 7 commits into from
Jul 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions end-to-end-test/local/runtime-config/portal.properties
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,8 @@ session.service.url=
# session.service.user=user
# session.service.password=pass

session.endpoint.publisher-api-key=SECRETKEY

# disabled tabs, | delimited
# possible values: cancer_types_summary, mutual_exclusivity, plots, mutations, co_expression, enrichments, survival, network, download, bookmark, IGV
disabled_tabs=
Expand Down
171 changes: 171 additions & 0 deletions end-to-end-test/local/specs/virtual-study.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
var assert = require('assert');
var goToUrlAndSetLocalStorage = require('../../shared/specUtils')
.goToUrlAndSetLocalStorage;

const CBIOPORTAL_URL = process.env.CBIOPORTAL_URL.replace(/\/$/, '');
const studyEs0Summary = CBIOPORTAL_URL + '/study/summary?id=study_es_0';

describe('Virtual Study life cycle', function() {
const vsTitle = 'Test VS ' + Date.now();
let link;
let vsId;
const X_PUBLISHER_API_KEY = 'SECRETKEY';

it('Login and navigate to the study_es_0 study summary page', function() {
goToUrlAndSetLocalStorage(studyEs0Summary, true);
});
it('Click Share Virtual Study button', function() {
const studyView = $('.studyView');
const shareVSBtn = studyView.$(
'button[data-tour="action-button-bookmark"]'
);
shareVSBtn.waitForClickable();
shareVSBtn.click();
});
it('Provide the title and save', function() {
const modalDialog = $('.modal-dialog');
modalDialog.waitForDisplayed();
const titleInput = modalDialog.$('input#sniglet');
titleInput.setValue(vsTitle);
const saveBtn = modalDialog.$(
'[data-tour="virtual-study-summary-save-btn"]'
);
saveBtn.click();
modalDialog.$('.text-success').waitForDisplayed();
const linkInput = modalDialog.$('input[type="text"]');
link = linkInput.getValue();
assert.ok(
link.startsWith('http'),
'The value should be link, but was ' + link
);
vsId = link
.split('?')[1]
.split('&')
.map(paramEqValue => paramEqValue.split('='))
.find(([key, value]) => key === 'id')[1];
assert.ok(vsId, 'Virtual Study ID has not to be empty');
});
it('See the VS in My Virtual Studies section on the landing page', function() {
goToUrlAndSetLocalStorage(CBIOPORTAL_URL, true);
const vsSection = $(`//*[text()="${vsTitle}"]/ancestor::ul[1]`);
vsSection.waitForDisplayed();
const sectionTitle = vsSection.$('li label span');
assert.equal(sectionTitle.getText(), 'My Virtual Studies');
});
it('Publish the VS', function() {
const result = browser.executeAsync(
function(cbioUrl, vsId, key, done) {
const url = cbioUrl + '/api/public_virtual_studies/' + vsId;
const headers = new Headers();
headers.append('X-PUBLISHER-API-KEY', key);
fetch(url, {
method: 'POST',
headers: headers,
})
.then(response => {
done({
success: response.ok,
message: 'HTTP Status: ' + response.status,
});
})
.catch(error => {
done({ success: false, message: error.message });
});
},
CBIOPORTAL_URL,
vsId,
X_PUBLISHER_API_KEY
);
assert.ok(result.success, result.message);
});
it('See the VS in Public Virtual Studies section on the landing page', function() {
goToUrlAndSetLocalStorage(CBIOPORTAL_URL, true);
const vsSection = $(`//*[text()="${vsTitle}"]/ancestor::ul[1]`);
vsSection.waitForDisplayed();
const sectionTitle = vsSection.$('li label span');
assert.equal(sectionTitle.getText(), 'Public Virtual Studies');
});
it('Re-publish the VS specifying PubMed ID and type of cancer', function() {
const result = browser.executeAsync(
function(cbioUrl, vsId, key, done) {
const headers = new Headers();
headers.append('X-PUBLISHER-API-KEY', key);
fetch(
cbioUrl +
'/api/public_virtual_studies/' +
vsId +
'?pmid=28783718&typeOfCancerId=aca',
{
method: 'POST',
headers: headers,
}
)
.then(response => {
done({
success: response.ok,
message: 'HTTP Status: ' + response.status,
});
})
.catch(error => {
done({ success: false, message: error.message });
});
},
CBIOPORTAL_URL,
vsId,
X_PUBLISHER_API_KEY
);
assert.ok(result.success, result.message);
});
it('See the VS in the Adrenocortical Adenoma section with PubMed link', function() {
goToUrlAndSetLocalStorage(CBIOPORTAL_URL, true);
const vsRow = $(`//*[text()="${vsTitle}"]/ancestor::li[1]`);
const vsSection = vsRow.parentElement();
vsSection.waitForDisplayed();
const sectionTitle = vsSection.$('li label span');
assert.equal(sectionTitle.getText(), 'Adrenocortical Adenoma');
//has PubMed link
assert.ok(vsRow.$('.fa-book').isExisting());
});
it('Un-publish the VS', function() {
const result = browser.executeAsync(
function(cbioUrl, vsId, key, done) {
const headers = new Headers();
headers.append('X-PUBLISHER-API-KEY', key);
fetch(cbioUrl + '/api/public_virtual_studies/' + vsId, {
method: 'DELETE',
headers: headers,
})
.then(response => {
done({
success: response.ok,
message: 'HTTP Status: ' + response.status,
});
})
.catch(error => {
done({ success: false, message: error.message });
});
},
CBIOPORTAL_URL,
vsId,
X_PUBLISHER_API_KEY
);
assert.ok(result.success, result.message);
});

it('Removing the VS', function() {
goToUrlAndSetLocalStorage(CBIOPORTAL_URL, true);
const vsRow = $(`//*[text()="${vsTitle}"]/ancestor::li[1]`);
vsRow.waitForDisplayed();

const removeBtn = vsRow.$('.fa-trash');
removeBtn.click();
});

it('The VS disappears from the landing page', function() {
goToUrlAndSetLocalStorage(CBIOPORTAL_URL, true);
$('[data-test="cancerTypeListContainer"]').waitForDisplayed();
const vsRowTitle = $(`//*[text()="${vsTitle}"]`);
browser.pause(100);
assert.ok(!vsRowTitle.isExisting());
});
});
16 changes: 16 additions & 0 deletions src/shared/api/session-service/sessionServiceAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ export default class sessionServiceAPI {
return `${getSessionUrl()}/virtual_study`;
}

getPublicVirtualStudyServiceUrl() {
return getSessionUrl('api/public_virtual_studies');
}

getSessionServiceUrl() {
return `${getSessionUrl()}/main_session`;
}
Expand Down Expand Up @@ -44,6 +48,18 @@ export default class sessionServiceAPI {
);
}

getPublicVirtualStudies(): Promise<Array<VirtualStudy>> {
return (
request
.get(this.getPublicVirtualStudyServiceUrl())
// @ts-ignore: this method comes from caching plugin and isn't in typing
.forceUpdate(true)
.then((res: any) => {
return res.body;
})
);
}

getVirtualStudy(id: string): Promise<VirtualStudy> {
return (
request
Expand Down
2 changes: 2 additions & 0 deletions src/shared/api/session-service/sessionServiceModels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ export interface VirtualStudyData {
studies: { id: string; samples: string[] }[];
origin: string[];
studyViewFilter: StudyViewFilter;
typeOfCancerId?: string;
pmid?: string;
}

export type GroupData = Omit<VirtualStudyData, 'studyViewFilter'>;
Expand Down
6 changes: 3 additions & 3 deletions src/shared/api/urls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,14 +245,14 @@ export function getGenomeNexusHgvsgUrl(
: `${getServerConfig().genomenexus_website_url}/variant/${hgvsg}`;
}

export function getSessionUrl() {
export function getSessionUrl(path = 'api/session') {
if (getServerConfig() && getServerConfig().hasOwnProperty('apiRoot')) {
// TODO: remove this after switch to AWS. This is a hack to use proxy
// session-service from non apiRoot. We'll have to come up with a better
// solution for auth portals
return buildCBioPortalPageUrl('api/session');
return buildCBioPortalPageUrl(path);
} else {
return buildCBioPortalAPIUrl('api/session');
return buildCBioPortalAPIUrl(path);
}
}

Expand Down
34 changes: 34 additions & 0 deletions src/shared/components/query/CancerStudyTreeData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { VirtualStudy } from 'shared/api/session-service/sessionServiceModels';

export const CANCER_TYPE_ROOT = 'tissue';
export const VIRTUAL_STUDY_NAME = 'My Virtual Studies';
export const PUBLIC_VIRTUAL_STUDY_NAME = 'Public Virtual Studies';
export const PHYSICAL_STUDY_NAME = 'Studies';

export type CancerTypeWithVisibility = CancerType & {
Expand Down Expand Up @@ -63,6 +64,16 @@ export default class CancerStudyTreeData {
alwaysVisible: true,
};

publicVirtualStudyCategory: CancerTypeWithVisibility = {
id: 'public_virtual_studies_list',
dedicatedColor: '',
name: PUBLIC_VIRTUAL_STUDY_NAME,
parent: CANCER_TYPE_ROOT,
shortName: PUBLIC_VIRTUAL_STUDY_NAME,
cancerTypeId: PUBLIC_VIRTUAL_STUDY_NAME,
alwaysVisible: true,
};

physicalStudyCategory: CancerTypeWithVisibility = {
dedicatedColor: '',
name: PHYSICAL_STUDY_NAME,
Expand All @@ -83,13 +94,15 @@ export default class CancerStudyTreeData {
allStudyTags = [],
priorityStudies = {},
virtualStudies = [],
publicVirtualStudies = [],
maxTreeDepth = 0,
}: {
cancerTypes: CancerTypeWithVisibility[];
studies: CancerStudy[];
allStudyTags: StudyTags[];
priorityStudies?: CategorizedConfigItems;
virtualStudies?: VirtualStudy[];
publicVirtualStudies?: VirtualStudy[];
maxTreeDepth: number;
}) {
let nodes: CancerTreeNode[];
Expand All @@ -99,6 +112,25 @@ export default class CancerStudyTreeData {
// sort by name
cancerTypes = CancerStudyTreeData.sortNodes(cancerTypes);

//map public virtual study to cancer study
const _publicVirtualStudies = publicVirtualStudies.map(
publicVirtualStudy => {
return {
allSampleCount: _.sumBy(
publicVirtualStudy.data.studies,
study => study.samples.length
),
studyId: publicVirtualStudy.id,
name: publicVirtualStudy.data.name,
description: publicVirtualStudy.data.description,
cancerTypeId:
publicVirtualStudy.data.typeOfCancerId ||
PUBLIC_VIRTUAL_STUDY_NAME,
pmid: publicVirtualStudy.data.pmid,
} as CancerStudy;
}
);

//map virtual study to cancer study
const _virtualStudies = virtualStudies
.map(virtualstudy => {
Expand Down Expand Up @@ -140,13 +172,15 @@ export default class CancerStudyTreeData {
}
// add virtual study category, and studies
cancerTypes = [
this.publicVirtualStudyCategory,
this.virtualStudyCategory,
this.physicalStudyCategory,
...this.priorityCategories,
this.rootCancerType,
...cancerTypes,
];
studies = CancerStudyTreeData.sortNodes([
..._publicVirtualStudies,
..._virtualStudies,
...studies,
]);
Expand Down
Loading
Loading