From c4126f99746a4e39fb5e8babb2b012da3a7edb85 Mon Sep 17 00:00:00 2001 From: haynescd Date: Thu, 25 May 2023 13:57:16 -0400 Subject: [PATCH 01/10] refactor StudyId extractor --- src/pages/studyView/StudyViewPageStore.ts | 40 +++++++++++++---------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/pages/studyView/StudyViewPageStore.ts b/src/pages/studyView/StudyViewPageStore.ts index ad94f865ec4..d9aa2e57f98 100644 --- a/src/pages/studyView/StudyViewPageStore.ts +++ b/src/pages/studyView/StudyViewPageStore.ts @@ -2136,24 +2136,11 @@ export class StudyViewPageStore @action async updateStoreFromURL(query: StudyViewURLQuery): Promise { - let studyIdsString: string = ''; - let studyIds: string[] = []; - if (query.studyId) { - studyIdsString = query.studyId; - } - if (query.cancer_study_id) { - studyIdsString = query.cancer_study_id; - } - if (query.id) { - studyIdsString = query.id; - } - if (studyIdsString) { - studyIds = studyIdsString.trim().split(','); - if (!_.isEqual(studyIds, toJS(this.studyIds))) { - // update if different - this.studyIds = studyIds; - } - } + const studyIds = this.parseStudyIdsFromUrlQuery( + query.studyId, + query.cancer_study_id, + query.id + ); if (query.sharedGroups) { this.sharedGroupSet = stringListToSet( query.sharedGroups.trim().split(',') @@ -2236,6 +2223,23 @@ export class StudyViewPageStore } } + parseStudyIdsFromUrlQuery( + studyId: string | undefined, + cancer_study_id: string | undefined, + id: string | undefined + ): Array { + let studyIds: Array = []; + const studyIdsString = studyId ?? cancer_study_id ?? id ?? ''; + if (studyIdsString) { + studyIds = studyIdsString.trim().split(','); + if (!_.isEqual(studyIds, toJS(this.studyIds))) { + // update if different + this.studyIds = studyIds; + } + } + return studyIds; + } + parseRawFilterJson(filterJson: string): any { let rawJson; try { From 13347e8606df3ff9b1b7cd9438e683be1ae93d9f Mon Sep 17 00:00:00 2001 From: haynescd Date: Wed, 31 May 2023 14:20:03 -0400 Subject: [PATCH 02/10] :hammer: Refactor Extraction code of StudyViewURL Query --- src/pages/studyView/StudyViewPageStore.ts | 158 ++------------- .../studyView/StudyViewQueryExtractor.ts | 180 ++++++++++++++++++ 2 files changed, 201 insertions(+), 137 deletions(-) create mode 100644 src/pages/studyView/StudyViewQueryExtractor.ts diff --git a/src/pages/studyView/StudyViewPageStore.ts b/src/pages/studyView/StudyViewPageStore.ts index d9aa2e57f98..eab2662033f 100644 --- a/src/pages/studyView/StudyViewPageStore.ts +++ b/src/pages/studyView/StudyViewPageStore.ts @@ -273,6 +273,13 @@ import { PatientIdentifier, PatientIdentifierFilter, } from 'shared/model/PatientIdentifierFilter'; +import { + ClinicalAttributeQueryExtractor, + SharedGroupsAndCustomDataQueryExtractor, + StudyIdQueryExtractor, + StudyViewFilterQueryExtractor, + StudyViewQueryExtractor, +} from './StudyViewQueryExtractor'; export const STUDY_VIEW_FILTER_AUTOSUBMIT = 'study_view_filter_autosubmit'; @@ -2136,118 +2143,27 @@ export class StudyViewPageStore @action async updateStoreFromURL(query: StudyViewURLQuery): Promise { - const studyIds = this.parseStudyIdsFromUrlQuery( - query.studyId, - query.cancer_study_id, - query.id - ); - if (query.sharedGroups) { - this.sharedGroupSet = stringListToSet( - query.sharedGroups.trim().split(',') - ); - // Open group comparison manager if there are shared groups in the url - this.showComparisonGroupUI = true; - } - if (query.sharedCustomData) { - this.sharedCustomChartSet = stringListToSet( - query.sharedCustomData.trim().split(',') - ); - this.showCustomDataSelectionUI = true; - } + const queryExtractors: Array> = [ + new StudyIdQueryExtractor(), + new SharedGroupsAndCustomDataQueryExtractor(), + ]; - // We do not support studyIds in the query filters - let filters: Partial = {}; + const asyncQueryExtractors: Array + >> = []; if (query.filterJson) { - const parsedFilterJson = this.parseRawFilterJson(query.filterJson); - if (query.filterJson.includes('patientIdentifiers')) { - const sampleListIds = studyIds.map(s => s.concat('', '_all')); - const samples = await this.fetchSamplesWithSampleListIds( - sampleListIds - ); - filters = this.getStudyViewFilterFromPatientIdentifierFilter( - parsedFilterJson as PatientIdentifierFilter, - samples - ); - } else { - filters = parsedFilterJson as Partial; - } - this.updateStoreByFilters(filters); + asyncQueryExtractors.push(new StudyViewFilterQueryExtractor()); } else if (query.filterAttributeId && query.filterValues) { - const clinicalAttributes = _.uniqBy( - await defaultClient.fetchClinicalAttributesUsingPOST({ - studyIds: studyIds, - }), - clinicalAttribute => - `${clinicalAttribute.patientAttribute}-${clinicalAttribute.clinicalAttributeId}` - ); - - const matchedAttr = _.find( - clinicalAttributes, - (attr: ClinicalAttribute) => - attr.clinicalAttributeId.toUpperCase() === - query.filterAttributeId!.toUpperCase() - ); - if (matchedAttr !== undefined) { - if (matchedAttr.datatype == DataType.NUMBER) { - filters.clinicalDataFilters = [ - { - attributeId: matchedAttr.clinicalAttributeId, - values: query - .filterValues!.split(',') - .map(range => { - const convertResult = range.split('-'); - return { - start: Number(convertResult[0]), - end: Number(convertResult[1]), - } as DataFilterValue; - }), - } as ClinicalDataFilter, - ]; - } else { - filters.clinicalDataFilters = [ - { - attributeId: matchedAttr.clinicalAttributeId, - values: getClinicalEqualityFilterValuesByString( - query.filterValues - ).map(value => ({ value })), - } as ClinicalDataFilter, - ]; - } - this.updateStoreByFilters(filters); - } else { - this.pageStatusMessages['unknownClinicalAttribute'] = { - message: `The clinical attribute ${query.filterAttributeId} is not available for this study`, - status: 'danger', - }; - } + asyncQueryExtractors.push(new ClinicalAttributeQueryExtractor()); } - } - parseStudyIdsFromUrlQuery( - studyId: string | undefined, - cancer_study_id: string | undefined, - id: string | undefined - ): Array { - let studyIds: Array = []; - const studyIdsString = studyId ?? cancer_study_id ?? id ?? ''; - if (studyIdsString) { - studyIds = studyIdsString.trim().split(','); - if (!_.isEqual(studyIds, toJS(this.studyIds))) { - // update if different - this.studyIds = studyIds; - } + for (const extractor of queryExtractors) { + extractor.accept(query, this); } - return studyIds; - } - parseRawFilterJson(filterJson: string): any { - let rawJson; - try { - rawJson = JSON.parse(decodeURIComponent(filterJson)); - } catch (e) { - console.error('FilterJson invalid Json: error: ', e); - } - return rawJson; + await Promise.all( + asyncQueryExtractors.map(ex => ex.accept(query, this)) + ); } fetchSamplesWithSampleListIds(sampleListIds: string[]) { @@ -2259,38 +2175,6 @@ export class StudyViewPageStore }); } - getStudyViewFilterFromPatientIdentifierFilter( - patientIdentifierFilter: PatientIdentifierFilter, - samples: Sample[] - ): Partial { - const filters: Partial = {}; - const sampleIdentifiers = this.convertPatientIdentifiersToSampleIdentifiers( - patientIdentifierFilter.patientIdentifiers, - samples - ); - if (sampleIdentifiers.length > 0) { - filters.sampleIdentifiers = sampleIdentifiers; - } - return filters; - } - - convertPatientIdentifiersToSampleIdentifiers( - patientIdentifiers: Array, - samples: Sample[] - ): SampleIdentifier[] { - const patientIdentifiersMap = new Map( - patientIdentifiers.map(p => [p.studyId.concat('_', p.patientId), p]) - ); - return samples - .filter(s => - patientIdentifiersMap.has(s.studyId.concat('_', s.patientId)) - ) - .map(s => ({ - sampleId: s.sampleId, - studyId: s.studyId, - })); - } - @computed get initialFilters(): StudyViewFilter { let initialFilter = {} as StudyViewFilter; diff --git a/src/pages/studyView/StudyViewQueryExtractor.ts b/src/pages/studyView/StudyViewQueryExtractor.ts new file mode 100644 index 00000000000..dbee9b399ab --- /dev/null +++ b/src/pages/studyView/StudyViewQueryExtractor.ts @@ -0,0 +1,180 @@ +import { toJS } from 'mobx'; +import { StudyViewPageStore, StudyViewURLQuery } from './StudyViewPageStore'; +import _ from 'lodash'; +import { + ClinicalAttribute, + ClinicalDataFilter, + DataFilterValue, + StudyViewFilter, +} from 'cbioportal-ts-api-client/dist'; +import { + PatientIdentifier, + PatientIdentifierFilter, +} from 'shared/model/PatientIdentifierFilter'; +import { Sample } from 'cbioportal-ts-api-client/dist'; +import { SampleIdentifier } from 'cbioportal-ts-api-client/dist'; +import defaultClient from 'shared/api/cbioportalClientInstance'; +import { + getClinicalEqualityFilterValuesByString, + DataType, +} from './StudyViewUtils'; +import { stringListToSet } from 'cbioportal-frontend-commons'; + +export interface StudyViewQueryExtractor { + accept(query: StudyViewURLQuery, store: StudyViewPageStore): T; +} + +export class StudyIdQueryExtractor implements StudyViewQueryExtractor { + accept(query: StudyViewURLQuery, store: StudyViewPageStore): void { + let studyIds: Array = []; + const studyIdsString = + query.studyId ?? query.cancer_study_id ?? query.id ?? ''; + if (studyIdsString) { + studyIds = studyIdsString.trim().split(','); + if (!_.isEqual(studyIds, toJS(store.studyIds))) { + // update if different + store.studyIds = studyIds; + } + } + } +} + +export class SharedGroupsAndCustomDataQueryExtractor + implements StudyViewQueryExtractor { + accept(query: StudyViewURLQuery, store: StudyViewPageStore): void { + if (query.sharedGroups) { + store.sharedGroupSet = stringListToSet( + query.sharedGroups.trim().split(',') + ); + // Open group comparison manager if there are shared groups in the url + store.showComparisonGroupUI = true; + } + if (query.sharedCustomData) { + store.sharedCustomChartSet = stringListToSet( + query.sharedCustomData.trim().split(',') + ); + store.showCustomDataSelectionUI = true; + } + } +} + +export class StudyViewFilterQueryExtractor + implements StudyViewQueryExtractor> { + async accept( + query: StudyViewURLQuery, + store: StudyViewPageStore + ): Promise { + let filters: Partial = {}; + const parsedFilterJson = this.parseRawFilterJson(query.filterJson!); + if (query.filterJson!.includes('patientIdentifiers')) { + const sampleListIds = store.studyIds.map(s => s.concat('', '_all')); + const samples = await store.fetchSamplesWithSampleListIds( + sampleListIds + ); + filters = this.getStudyViewFilterFromPatientIdentifierFilter( + parsedFilterJson as PatientIdentifierFilter, + samples + ); + } else { + filters = parsedFilterJson as Partial; + } + store.updateStoreByFilters(filters); + } + + parseRawFilterJson(filterJson: string): any { + let rawJson; + try { + rawJson = JSON.parse(decodeURIComponent(filterJson)); + } catch (e) { + console.error('FilterJson invalid Json: error: ', e); + } + return rawJson; + } + + getStudyViewFilterFromPatientIdentifierFilter( + patientIdentifierFilter: PatientIdentifierFilter, + samples: Sample[] + ): Partial { + const filters: Partial = {}; + const sampleIdentifiers = this.convertPatientIdentifiersToSampleIdentifiers( + patientIdentifierFilter.patientIdentifiers, + samples + ); + if (sampleIdentifiers.length > 0) { + filters.sampleIdentifiers = sampleIdentifiers; + } + return filters; + } + + convertPatientIdentifiersToSampleIdentifiers( + patientIdentifiers: Array, + samples: Sample[] + ): SampleIdentifier[] { + const patientIdentifiersMap = new Map( + patientIdentifiers.map(p => [p.studyId.concat('_', p.patientId), p]) + ); + return samples + .filter(s => + patientIdentifiersMap.has(s.studyId.concat('_', s.patientId)) + ) + .map(s => ({ + sampleId: s.sampleId, + studyId: s.studyId, + })); + } +} + +export class ClinicalAttributeQueryExtractor + implements StudyViewQueryExtractor> { + async accept( + query: StudyViewURLQuery, + store: StudyViewPageStore + ): Promise { + const filters: Partial = {}; + const clinicalAttributes = _.uniqBy( + await defaultClient.fetchClinicalAttributesUsingPOST({ + studyIds: store.studyIds, + }), + clinicalAttribute => + `${clinicalAttribute.patientAttribute}-${clinicalAttribute.clinicalAttributeId}` + ); + + const matchedAttr = _.find( + clinicalAttributes, + (attr: ClinicalAttribute) => + attr.clinicalAttributeId.toUpperCase() === + query.filterAttributeId!.toUpperCase() + ); + if (matchedAttr !== undefined) { + if (matchedAttr.datatype == DataType.NUMBER) { + filters.clinicalDataFilters = [ + { + attributeId: matchedAttr.clinicalAttributeId, + values: query.filterValues!.split(',').map(range => { + const convertResult = range.split('-'); + return { + start: Number(convertResult[0]), + end: Number(convertResult[1]), + } as DataFilterValue; + }), + } as ClinicalDataFilter, + ]; + } else { + filters.clinicalDataFilters = [ + { + attributeId: matchedAttr.clinicalAttributeId, + values: getClinicalEqualityFilterValuesByString( + query.filterValues! + ).map(value => ({ value })), + } as ClinicalDataFilter, + ]; + } + store.updateStoreByFilters(filters); + } else { + store.pageStatusMessages['unknownClinicalAttribute'] = { + message: `The clinical attribute ${query.filterAttributeId} is not available for this study`, + status: 'danger', + }; + } + } +} From 923d7cb3934d988beca169d5b051afa5a160ecd3 Mon Sep 17 00:00:00 2001 From: haynescd Date: Fri, 2 Jun 2023 00:16:44 -0400 Subject: [PATCH 03/10] Update StudyViewPage to accept filterJson from PostData --- .../local/specs/core/postedquery.spec.js | 22 +++++++++++++++++++ src/pages/studyView/StudyViewPage.tsx | 20 +++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/end-to-end-test/local/specs/core/postedquery.spec.js b/end-to-end-test/local/specs/core/postedquery.spec.js index e6bce7fc6ec..24836ba0f8c 100644 --- a/end-to-end-test/local/specs/core/postedquery.spec.js +++ b/end-to-end-test/local/specs/core/postedquery.spec.js @@ -7,6 +7,7 @@ var _ = require('lodash'); var { useExternalFrontend, waitForOncoprint, + getElementByTestHandle, } = require('../../../shared/specUtils'); const CBIOPORTAL_URL = process.env.CBIOPORTAL_URL.replace(/\/$/, ''); @@ -56,3 +57,24 @@ describe('posting query parameters (instead of GET) to query page', function() { waitForOncoprint(); }); }); + +describe('Post Data for StudyView Filtering with filterJson via HTTP Post', () => { + it('Send PatientIdentifier Filter via postData', () => { + const filterJsonQuery = { + patientIdentifiers: [ + { study_id: 'msk_impact_2017', patientId: 'P-0000004' }, + ], + }; + + postDataToUrl( + `${CBIOPORTAL_URL}/study/summary?id=msk_impact_2017`, + filterJsonQuery + ); + + getElementByTestHandle('selected-samples').waitForExist({ + timeout: 20000, + }); + + assert.equal(getElementByTestHandle('selected-samples').getText(), 1); + }); +}); diff --git a/src/pages/studyView/StudyViewPage.tsx b/src/pages/studyView/StudyViewPage.tsx index 8c0996ba77a..e31d97e2077 100644 --- a/src/pages/studyView/StudyViewPage.tsx +++ b/src/pages/studyView/StudyViewPage.tsx @@ -191,6 +191,11 @@ export default class StudyViewPage extends React.Component< } } + const postDataFilterJson = this.getPostDataFilterJson(); + if (postDataFilterJson) { + newStudyViewFilter.filterJson = postDataFilterJson; + } + let updateStoreFromURLPromise = remoteData(() => Promise.resolve([])); if (!_.isEqual(newStudyViewFilter, this.store.studyViewQueryFilter)) { this.store.studyViewQueryFilter = newStudyViewFilter; @@ -234,6 +239,21 @@ export default class StudyViewPage extends React.Component< }, 500); } + private getPostDataFilterJson(): string | undefined { + debugger; + let rawFilterJsonStr: string | undefined; + let postData: string = getBrowserWindow()?.postData?.filterJson; + if (postData) { + try { + JSON.parse(postData); + rawFilterJsonStr = postData; + } catch (error) { + console.error('PostData.filterJson does not have valid JSON'); + } + } + return rawFilterJsonStr; + } + @autobind private toolbarRef(ref: any) { this.toolbar = ref; From bce1af521733e90e148cda1510d8b0fea1d2e91e Mon Sep 17 00:00:00 2001 From: haynescd Date: Fri, 2 Jun 2023 00:24:18 -0400 Subject: [PATCH 04/10] Remove debug statement --- src/pages/studyView/StudyViewPage.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pages/studyView/StudyViewPage.tsx b/src/pages/studyView/StudyViewPage.tsx index e31d97e2077..7be31f65b46 100644 --- a/src/pages/studyView/StudyViewPage.tsx +++ b/src/pages/studyView/StudyViewPage.tsx @@ -240,9 +240,8 @@ export default class StudyViewPage extends React.Component< } private getPostDataFilterJson(): string | undefined { - debugger; let rawFilterJsonStr: string | undefined; - let postData: string = getBrowserWindow()?.postData?.filterJson; + const postData = getBrowserWindow()?.postData?.filterJson; if (postData) { try { JSON.parse(postData); From 137c95460b7c36387b25bfad86ce961bca3e4c4f Mon Sep 17 00:00:00 2001 From: haynescd Date: Fri, 2 Jun 2023 12:52:25 -0400 Subject: [PATCH 05/10] Update localdb test --- .../local/specs/core/postedquery.spec.js | 17 ++++++++++++----- src/pages/studyView/StudyViewPage.tsx | 4 +++- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/end-to-end-test/local/specs/core/postedquery.spec.js b/end-to-end-test/local/specs/core/postedquery.spec.js index 24836ba0f8c..4f22921b645 100644 --- a/end-to-end-test/local/specs/core/postedquery.spec.js +++ b/end-to-end-test/local/specs/core/postedquery.spec.js @@ -58,19 +58,26 @@ describe('posting query parameters (instead of GET) to query page', function() { }); }); -describe('Post Data for StudyView Filtering with filterJson via HTTP Post', () => { +describe.only('Post Data for StudyView Filtering with filterJson via HTTP Post', () => { it('Send PatientIdentifier Filter via postData', () => { const filterJsonQuery = { - patientIdentifiers: [ - { study_id: 'msk_impact_2017', patientId: 'P-0000004' }, - ], + filterJson: + '{"patientIdentifiers":[{"study_id":"study_es_0","patientId":"TCGA-A1-A0SB" }]}', }; + goToUrlAndSetLocalStorage(`${CBIOPORTAL_URL}`, true); + postDataToUrl( - `${CBIOPORTAL_URL}/study/summary?id=msk_impact_2017`, + `${CBIOPORTAL_URL}/study/summary?id=study_es_0&localdev=true`, filterJsonQuery ); + const postData = browser.execute(() => { + return window.postData; + }); + + console.log(postData); + //browser.debug(); getElementByTestHandle('selected-samples').waitForExist({ timeout: 20000, }); diff --git a/src/pages/studyView/StudyViewPage.tsx b/src/pages/studyView/StudyViewPage.tsx index 7be31f65b46..f6c34ab21c1 100644 --- a/src/pages/studyView/StudyViewPage.tsx +++ b/src/pages/studyView/StudyViewPage.tsx @@ -241,7 +241,9 @@ export default class StudyViewPage extends React.Component< private getPostDataFilterJson(): string | undefined { let rawFilterJsonStr: string | undefined; - const postData = getBrowserWindow()?.postData?.filterJson; + let postData: string = getBrowserWindow()?.postData?.filterJson; + const regx = /"/; + postData = postData.replace(regx, '"'); if (postData) { try { JSON.parse(postData); From 019771c72c20b23a98df2f2ed87471c7762ee85a Mon Sep 17 00:00:00 2001 From: haynescd Date: Mon, 5 Jun 2023 10:41:00 -0400 Subject: [PATCH 06/10] Update postedQuery.spec.js --- .../local/specs/core/postedquery.spec.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/end-to-end-test/local/specs/core/postedquery.spec.js b/end-to-end-test/local/specs/core/postedquery.spec.js index 4f22921b645..c1ebe524e49 100644 --- a/end-to-end-test/local/specs/core/postedquery.spec.js +++ b/end-to-end-test/local/specs/core/postedquery.spec.js @@ -62,13 +62,18 @@ describe.only('Post Data for StudyView Filtering with filterJson via HTTP Post', it('Send PatientIdentifier Filter via postData', () => { const filterJsonQuery = { filterJson: - '{"patientIdentifiers":[{"study_id":"study_es_0","patientId":"TCGA-A1-A0SB" }]}', + '{"patientIdentifiers":[{"study_id":"lgg_ucsf_2014_test_generic_assay","patientId":"P01" }]}', }; goToUrlAndSetLocalStorage(`${CBIOPORTAL_URL}`, true); - + // browser.execute( + // function(config) { + // this.localStorage.setItem('netlify', config.netlify); + // }, + // { netlify: netlifyDeployPreview } + // ); postDataToUrl( - `${CBIOPORTAL_URL}/study/summary?id=study_es_0&localdev=true`, + `${CBIOPORTAL_URL}/study/summary?id=lgg_ucsf_2014_test_generic_assay`, filterJsonQuery ); @@ -78,10 +83,10 @@ describe.only('Post Data for StudyView Filtering with filterJson via HTTP Post', console.log(postData); //browser.debug(); - getElementByTestHandle('selected-samples').waitForExist({ + getElementByTestHandle('selected-patients').waitForExist({ timeout: 20000, }); - assert.equal(getElementByTestHandle('selected-samples').getText(), 1); + assert.equal(getElementByTestHandle('selected-patients').getText(), 1); }); }); From f8ff6e68e5902f15f3c19fe386e3c1dfbd64195c Mon Sep 17 00:00:00 2001 From: haynescd Date: Mon, 5 Jun 2023 14:28:03 -0400 Subject: [PATCH 07/10] Update to use correct json format of patientIdentifiers --- .../local/specs/core/postedquery.spec.js | 9 +------- end-to-end-test/shared/specUtils.js | 21 +++++++++++++++++++ src/pages/studyView/StudyViewPage.tsx | 5 +++-- 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/end-to-end-test/local/specs/core/postedquery.spec.js b/end-to-end-test/local/specs/core/postedquery.spec.js index c1ebe524e49..33c5cdcd956 100644 --- a/end-to-end-test/local/specs/core/postedquery.spec.js +++ b/end-to-end-test/local/specs/core/postedquery.spec.js @@ -62,16 +62,10 @@ describe.only('Post Data for StudyView Filtering with filterJson via HTTP Post', it('Send PatientIdentifier Filter via postData', () => { const filterJsonQuery = { filterJson: - '{"patientIdentifiers":[{"study_id":"lgg_ucsf_2014_test_generic_assay","patientId":"P01" }]}', + '{"patientIdentifiers":[{"studyId":"lgg_ucsf_2014_test_generic_assay","patientId":"P01"}]}', }; goToUrlAndSetLocalStorage(`${CBIOPORTAL_URL}`, true); - // browser.execute( - // function(config) { - // this.localStorage.setItem('netlify', config.netlify); - // }, - // { netlify: netlifyDeployPreview } - // ); postDataToUrl( `${CBIOPORTAL_URL}/study/summary?id=lgg_ucsf_2014_test_generic_assay`, filterJsonQuery @@ -82,7 +76,6 @@ describe.only('Post Data for StudyView Filtering with filterJson via HTTP Post', }); console.log(postData); - //browser.debug(); getElementByTestHandle('selected-patients').waitForExist({ timeout: 20000, }); diff --git a/end-to-end-test/shared/specUtils.js b/end-to-end-test/shared/specUtils.js index 17e89d6a8aa..8f334020e83 100644 --- a/end-to-end-test/shared/specUtils.js +++ b/end-to-end-test/shared/specUtils.js @@ -188,6 +188,25 @@ function setDropdownOpen( } ); } +function getCorrectUrlAndSetLocalStorage(url) { + if (!useExternalFrontend) { + console.log('Connecting to: ' + url); + } else if (useNetlifyDeployPreview) { + browser.execute( + function(config) { + this.localStorage.setItem('netlify', config.netlify); + }, + { netlify: netlifyDeployPreview } + ); + console.log('Connecting to: ' + url); + } else { + var urlparam = useLocalDist ? 'localdist' : 'localdev'; + var prefix = url.indexOf('?') > 0 ? '&' : '?'; + console.log('Connecting to: ' + `${url}${prefix}${urlparam}=true`); + url = `${url}${prefix}${urlparam}=true`; + } + return url; +} function goToUrlAndSetLocalStorage(url, authenticated = false) { const currentUrl = browser.getUrl(); @@ -558,6 +577,8 @@ function postDataToUrl(url, data, authenticated = true) { const currentUrl = browser.getUrl(); const needToLogin = authenticated && (!currentUrl || !currentUrl.includes('http')); + + url = getCorrectUrlAndSetLocalStorage(url); browser.execute( (url, data) => { function formSubmit(url, params) { diff --git a/src/pages/studyView/StudyViewPage.tsx b/src/pages/studyView/StudyViewPage.tsx index f6c34ab21c1..f0077de15b8 100644 --- a/src/pages/studyView/StudyViewPage.tsx +++ b/src/pages/studyView/StudyViewPage.tsx @@ -242,8 +242,9 @@ export default class StudyViewPage extends React.Component< private getPostDataFilterJson(): string | undefined { let rawFilterJsonStr: string | undefined; let postData: string = getBrowserWindow()?.postData?.filterJson; - const regx = /"/; - postData = postData.replace(regx, '"'); + const regx = /"/g; + postData = postData?.replace(regx, '"'); + console.log(`PostData: ${postData}`); if (postData) { try { JSON.parse(postData); From 6609ba044efd063c6c005add18e27e3f8a1f00c6 Mon Sep 17 00:00:00 2001 From: haynescd Date: Mon, 5 Jun 2023 15:39:42 -0400 Subject: [PATCH 08/10] Remove only from test, and small clean up --- .../local/specs/core/postedquery.spec.js | 17 ++++++----- end-to-end-test/shared/specUtils.js | 29 ++++++++++--------- src/pages/studyView/StudyViewPage.tsx | 28 +++++++++++------- .../studyView/StudyViewQueryExtractor.ts | 25 ++++++++++------ 4 files changed, 58 insertions(+), 41 deletions(-) diff --git a/end-to-end-test/local/specs/core/postedquery.spec.js b/end-to-end-test/local/specs/core/postedquery.spec.js index 33c5cdcd956..53f209bc5ae 100644 --- a/end-to-end-test/local/specs/core/postedquery.spec.js +++ b/end-to-end-test/local/specs/core/postedquery.spec.js @@ -58,28 +58,29 @@ describe('posting query parameters (instead of GET) to query page', function() { }); }); -describe.only('Post Data for StudyView Filtering with filterJson via HTTP Post', () => { - it('Send PatientIdentifier Filter via postData', () => { +describe('Post Data for StudyView Filtering with filterJson via HTTP Post', () => { + it('Verify PatientIdentifier Filter via postData', () => { const filterJsonQuery = { filterJson: '{"patientIdentifiers":[{"studyId":"lgg_ucsf_2014_test_generic_assay","patientId":"P01"}]}', }; + const NUMBER_OF_PATIENTS_AFTER_FILTER = 1; + goToUrlAndSetLocalStorage(`${CBIOPORTAL_URL}`, true); + postDataToUrl( `${CBIOPORTAL_URL}/study/summary?id=lgg_ucsf_2014_test_generic_assay`, filterJsonQuery ); - const postData = browser.execute(() => { - return window.postData; - }); - - console.log(postData); getElementByTestHandle('selected-patients').waitForExist({ timeout: 20000, }); - assert.equal(getElementByTestHandle('selected-patients').getText(), 1); + assert.equal( + getElementByTestHandle('selected-patients').getText(), + NUMBER_OF_PATIENTS_AFTER_FILTER + ); }); }); diff --git a/end-to-end-test/shared/specUtils.js b/end-to-end-test/shared/specUtils.js index 8f334020e83..3127c4ee338 100644 --- a/end-to-end-test/shared/specUtils.js +++ b/end-to-end-test/shared/specUtils.js @@ -188,20 +188,17 @@ function setDropdownOpen( } ); } -function getCorrectUrlAndSetLocalStorage(url) { + +/** + * @param {string} url + * @returns {string} modifiedUrl + */ +function getUrl(url) { if (!useExternalFrontend) { console.log('Connecting to: ' + url); - } else if (useNetlifyDeployPreview) { - browser.execute( - function(config) { - this.localStorage.setItem('netlify', config.netlify); - }, - { netlify: netlifyDeployPreview } - ); - console.log('Connecting to: ' + url); } else { - var urlparam = useLocalDist ? 'localdist' : 'localdev'; - var prefix = url.indexOf('?') > 0 ? '&' : '?'; + const urlparam = useLocalDist ? 'localdist' : 'localdev'; + const prefix = url.indexOf('?') > 0 ? '&' : '?'; console.log('Connecting to: ' + `${url}${prefix}${urlparam}=true`); url = `${url}${prefix}${urlparam}=true`; } @@ -573,14 +570,20 @@ function getOncoprintGroupHeaderOptionsElements(trackGroupIndex) { }; } +/** + * + * @param {string} url + * @param {any} data + * @param {boolean} authenticated + */ function postDataToUrl(url, data, authenticated = true) { const currentUrl = browser.getUrl(); const needToLogin = authenticated && (!currentUrl || !currentUrl.includes('http')); - url = getCorrectUrlAndSetLocalStorage(url); + url = getUrl(url); browser.execute( - (url, data) => { + (/** @type {string} */ url, /** @type {any} */ data) => { function formSubmit(url, params) { // method="smart" means submit with GET iff the URL wouldn't be too long diff --git a/src/pages/studyView/StudyViewPage.tsx b/src/pages/studyView/StudyViewPage.tsx index f0077de15b8..342cd06ec11 100644 --- a/src/pages/studyView/StudyViewPage.tsx +++ b/src/pages/studyView/StudyViewPage.tsx @@ -191,7 +191,8 @@ export default class StudyViewPage extends React.Component< } } - const postDataFilterJson = this.getPostDataFilterJson(); + // Overrite filterJson from URL with what is defined in postData + const postDataFilterJson = this.getFilterJsonFromPostData(); if (postDataFilterJson) { newStudyViewFilter.filterJson = postDataFilterJson; } @@ -239,21 +240,26 @@ export default class StudyViewPage extends React.Component< }, 500); } - private getPostDataFilterJson(): string | undefined { - let rawFilterJsonStr: string | undefined; - let postData: string = getBrowserWindow()?.postData?.filterJson; + private getFilterJsonFromPostData(): string | undefined { + let filterJson: string | undefined; + let rawPostDataFilterJson: string = getBrowserWindow()?.postData + ?.filterJson; + + // Strip potential HTML encoded quotes const regx = /"/g; - postData = postData?.replace(regx, '"'); - console.log(`PostData: ${postData}`); - if (postData) { + rawPostDataFilterJson = rawPostDataFilterJson?.replace(regx, '"'); + + if (rawPostDataFilterJson) { try { - JSON.parse(postData); - rawFilterJsonStr = postData; + JSON.parse(rawPostDataFilterJson); + filterJson = rawPostDataFilterJson; } catch (error) { - console.error('PostData.filterJson does not have valid JSON'); + console.error( + `PostData.filterJson does not have valid JSON, error: ${error}` + ); } } - return rawFilterJsonStr; + return filterJson; } @autobind diff --git a/src/pages/studyView/StudyViewQueryExtractor.ts b/src/pages/studyView/StudyViewQueryExtractor.ts index dbee9b399ab..c1555b9af9a 100644 --- a/src/pages/studyView/StudyViewQueryExtractor.ts +++ b/src/pages/studyView/StudyViewQueryExtractor.ts @@ -24,6 +24,7 @@ export interface StudyViewQueryExtractor { accept(query: StudyViewURLQuery, store: StudyViewPageStore): T; } +// TODO: Refactor even further to abstract all updates to StudyViewPageStore.... Should only be doing this at one place export class StudyIdQueryExtractor implements StudyViewQueryExtractor { accept(query: StudyViewURLQuery, store: StudyViewPageStore): void { let studyIds: Array = []; @@ -82,13 +83,13 @@ export class StudyViewFilterQueryExtractor } parseRawFilterJson(filterJson: string): any { - let rawJson; + let parsedJson; try { - rawJson = JSON.parse(decodeURIComponent(filterJson)); + parsedJson = JSON.parse(decodeURIComponent(filterJson)); } catch (e) { console.error('FilterJson invalid Json: error: ', e); } - return rawJson; + return parsedJson; } getStudyViewFilterFromPatientIdentifierFilter( @@ -96,12 +97,18 @@ export class StudyViewFilterQueryExtractor samples: Sample[] ): Partial { const filters: Partial = {}; - const sampleIdentifiers = this.convertPatientIdentifiersToSampleIdentifiers( - patientIdentifierFilter.patientIdentifiers, - samples - ); - if (sampleIdentifiers.length > 0) { - filters.sampleIdentifiers = sampleIdentifiers; + try { + const sampleIdentifiers = this.convertPatientIdentifiersToSampleIdentifiers( + patientIdentifierFilter.patientIdentifiers, + samples + ); + if (sampleIdentifiers.length > 0) { + filters.sampleIdentifiers = sampleIdentifiers; + } + } catch (err) { + console.error( + `Failure to extract SampleIds from PatientIdentifier filter error: ${err}` + ); } return filters; } From 0b889552eb801b74a5e2a086a48fb82fe67cb3cb Mon Sep 17 00:00:00 2001 From: haynescd Date: Mon, 5 Jun 2023 23:43:01 -0400 Subject: [PATCH 09/10] Update StudyViewPage to use lodash unescape to convert html entity --- src/pages/studyView/StudyViewPage.tsx | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/pages/studyView/StudyViewPage.tsx b/src/pages/studyView/StudyViewPage.tsx index 342cd06ec11..96d632ebe9d 100644 --- a/src/pages/studyView/StudyViewPage.tsx +++ b/src/pages/studyView/StudyViewPage.tsx @@ -76,6 +76,7 @@ import { CustomChartData } from 'shared/api/session-service/sessionServiceModels import { HelpWidget } from 'shared/components/HelpWidget/HelpWidget'; import { buildCBioPortalPageUrl } from 'shared/api/urls'; import StudyViewPageSettingsMenu from 'pages/studyView/menu/StudyViewPageSettingsMenu'; +import QueryString from 'qs'; export interface IStudyViewPageProps { routing: any; @@ -242,17 +243,15 @@ export default class StudyViewPage extends React.Component< private getFilterJsonFromPostData(): string | undefined { let filterJson: string | undefined; - let rawPostDataFilterJson: string = getBrowserWindow()?.postData - ?.filterJson; - // Strip potential HTML encoded quotes - const regx = /"/g; - rawPostDataFilterJson = rawPostDataFilterJson?.replace(regx, '"'); + const parsedFilterJson = _.unescape( + getBrowserWindow()?.postData?.filterJson + ); - if (rawPostDataFilterJson) { + if (parsedFilterJson) { try { - JSON.parse(rawPostDataFilterJson); - filterJson = rawPostDataFilterJson; + JSON.parse(parsedFilterJson); + filterJson = parsedFilterJson; } catch (error) { console.error( `PostData.filterJson does not have valid JSON, error: ${error}` From cda69cfbc1b31c04fff45e47e82f8415ab250b02 Mon Sep 17 00:00:00 2001 From: haynescd Date: Mon, 5 Jun 2023 23:44:48 -0400 Subject: [PATCH 10/10] Remove localdist from getUrl fn --- end-to-end-test/shared/specUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/end-to-end-test/shared/specUtils.js b/end-to-end-test/shared/specUtils.js index 3127c4ee338..4595b2c9800 100644 --- a/end-to-end-test/shared/specUtils.js +++ b/end-to-end-test/shared/specUtils.js @@ -197,7 +197,7 @@ function getUrl(url) { if (!useExternalFrontend) { console.log('Connecting to: ' + url); } else { - const urlparam = useLocalDist ? 'localdist' : 'localdev'; + const urlparam = 'localdev'; const prefix = url.indexOf('?') > 0 ? '&' : '?'; console.log('Connecting to: ' + `${url}${prefix}${urlparam}=true`); url = `${url}${prefix}${urlparam}=true`;