From e47ce7a40ca2536be107b6937ecfb9e48a50f705 Mon Sep 17 00:00:00 2001 From: Chris Knoll Date: Wed, 22 Feb 2023 10:37:59 -0500 Subject: [PATCH 1/4] Add refreshTokenThreshold to limit polling for permissions in Route.js. Alter polling intervals to use app config settings. Fixes #2820. --- .../analysis-execution-list.js | 2 +- js/config/app.js | 284 +++++++++--------- js/pages/Route.js | 6 +- .../cohort-definition-manager.js | 4 +- js/pages/configuration/configuration.js | 2 +- js/pages/incidence-rates/ir-manager.js | 2 +- 6 files changed, 153 insertions(+), 147 deletions(-) diff --git a/js/components/analysisExecution/analysis-execution-list.js b/js/components/analysisExecution/analysis-execution-list.js index 968a012ce..4e3f3546b 100644 --- a/js/components/analysisExecution/analysis-execution-list.js +++ b/js/components/analysisExecution/analysis-execution-list.js @@ -152,7 +152,7 @@ define([ startPolling() { this.pollId = this.PollService.add({ callback: silently => this.loadData({ silently }), - interval: 10000, + interval: config.pollInterval, isSilentAfterFirstCall: true, }); } diff --git a/js/config/app.js b/js/config/app.js index f0306da24..59749482e 100644 --- a/js/config/app.js +++ b/js/config/app.js @@ -1,164 +1,166 @@ define(function () { - var appConfig = {}; + var appConfig = {}; - // default configuration - appConfig.api = { - name: 'Local', - url: 'http://localhost:8080/WebAPI/' - }; - appConfig.cacheSources = false; - appConfig.pollInterval = 60000; + // default configuration + appConfig.api = { + name: 'Local', + url: 'http://localhost:8080/WebAPI/' + }; + appConfig.cacheSources = false; + appConfig.pollInterval = 60000; appConfig.cohortComparisonResultsEnabled = false; appConfig.userAuthenticationEnabled = false; appConfig.plpResultsEnabled = false; appConfig.useExecutionEngine = false; appConfig.viewProfileDates = false; - appConfig.enableCosts = false; + appConfig.enableCosts = false; appConfig.supportUrl = "https://github.com/ohdsi/atlas/issues"; appConfig.supportMail = "atlasadmin@your.org"; - appConfig.feedbackContacts = 'For access or questions concerning the Atlas application please contact:'; - appConfig.feedbackCustomHtmlTemplate = ''; - appConfig.companyInfoCustomHtmlTemplate = ''; - appConfig.showCompanyInfo = true; + appConfig.feedbackContacts = 'For access or questions concerning the Atlas application please contact:'; + appConfig.feedbackCustomHtmlTemplate = ''; + appConfig.companyInfoCustomHtmlTemplate = ''; + appConfig.showCompanyInfo = true; appConfig.defaultLocale = "en"; appConfig.authProviders = [ - { - "name": "Windows", - "url": "user/login/windows", - "ajax": true, - "icon": "fab fa-windows" - }, - { - "name": "Kerberos", - "url": "user/login/kerberos", - "ajax": true, - "icon": "fab fa-windows" - }, - { - "name": "OpenID", - "url": "user/login/openid", - "ajax": false, - "icon": "fab fa-openid" - }, - { - "name": "Google", - "url": "user/oauth/google", - "ajax": false, - "icon": "fab fa-google" - }, - { - "name": "Facebook", - "url": "user/oauth/facebook", - "ajax": false, - "icon": "fab fa-facebook-f" - }, - { - "name": "Github", - "url": "user/oauth/github", - "ajax": false, - "icon": "fab fa-github" - }, - { - "name": "DB", - "url": "user/login/db", - "ajax": true, - "icon": "fa fa-database", - "isUseCredentialsForm":true - }, - { - "name": "LDAP", - "url": "user/login/ldap", - "ajax": true, - "icon": "fa fa-cubes", - "isUseCredentialsForm": true - }, - { - "name": "SAML", - "url": "user/login/saml", - "ajax": false, - "icon": "fab fa-openid" - }, - { - "name": "Active Directory LDAP", - "url": "user/login/ad", - "ajax": true, - "icon": "fa fa-cubes", - "isUseCredentialsForm": true - } - ]; - appConfig.strictXSSOptions = { - whiteList: [], - }; - appConfig.xssOptions = { - "whiteList": { - "a": ["href", "class", "data-bind", "data-toggle", "aria-expanded"], + { + "name": "Windows", + "url": "user/login/windows", + "ajax": true, + "icon": "fab fa-windows" + }, + { + "name": "Kerberos", + "url": "user/login/kerberos", + "ajax": true, + "icon": "fab fa-windows" + }, + { + "name": "OpenID", + "url": "user/login/openid", + "ajax": false, + "icon": "fab fa-openid" + }, + { + "name": "Google", + "url": "user/oauth/google", + "ajax": false, + "icon": "fab fa-google" + }, + { + "name": "Facebook", + "url": "user/oauth/facebook", + "ajax": false, + "icon": "fab fa-facebook-f" + }, + { + "name": "Github", + "url": "user/oauth/github", + "ajax": false, + "icon": "fab fa-github" + }, + { + "name": "DB", + "url": "user/login/db", + "ajax": true, + "icon": "fa fa-database", + "isUseCredentialsForm": true + }, + { + "name": "LDAP", + "url": "user/login/ldap", + "ajax": true, + "icon": "fa fa-cubes", + "isUseCredentialsForm": true + }, + { + "name": "SAML", + "url": "user/login/saml", + "ajax": false, + "icon": "fab fa-openid" + }, + { + "name": "Active Directory LDAP", + "url": "user/login/ad", + "ajax": true, + "icon": "fa fa-cubes", + "isUseCredentialsForm": true + } + ]; + appConfig.strictXSSOptions = { + whiteList: [], + }; + appConfig.xssOptions = { + "whiteList": { + "a": ["href", "class", "data-bind", "data-toggle", "aria-expanded"], "button": ["class", "type", "data-toggle", "aria-expanded"], - "span": ["class", "data-bind"], - "i": ["class", "id", "aria-hidden"], - "div": ["class", "style", "id"], - "option": ["value"], - "input": ["type", "class"], - "ui": ["class"], - "path": ["d", "class"], - "br": "", - "li": ["class", "title"], - "ul": ["class"] - }, - "stripIgnoreTag": true, - "stripIgnoreTagBody": ['script'], - }; - appConfig.cemOptions = { - "evidenceLinkoutSources": ["medline_winnenburg","splicer"], - "sourceRestEndpoints": { - "medline_winnenburg": "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esummary.fcgi?db=pubmed&id={@ids}&retmode=json&tool=ohdsi_atlas&email=admin@ohdsi.org", - }, - "externalLinks": { - "medline_winnenburg": "https://www.ncbi.nlm.nih.gov/pubmed/{@id}", - "splicer": "https://dailymed.nlm.nih.gov/dailymed/drugInfo.cfm?setid={@id}" - }, - }; - appConfig.enableTermsAndConditions = true; + "span": ["class", "data-bind"], + "i": ["class", "id", "aria-hidden"], + "div": ["class", "style", "id"], + "option": ["value"], + "input": ["type", "class"], + "ui": ["class"], + "path": ["d", "class"], + "br": "", + "li": ["class", "title"], + "ul": ["class"] + }, + "stripIgnoreTag": true, + "stripIgnoreTagBody": ['script'], + }; + appConfig.cemOptions = { + "evidenceLinkoutSources": ["medline_winnenburg", "splicer"], + "sourceRestEndpoints": { + "medline_winnenburg": "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esummary.fcgi?db=pubmed&id={@ids}&retmode=json&tool=ohdsi_atlas&email=admin@ohdsi.org", + }, + "externalLinks": { + "medline_winnenburg": "https://www.ncbi.nlm.nih.gov/pubmed/{@id}", + "splicer": "https://dailymed.nlm.nih.gov/dailymed/drugInfo.cfm?setid={@id}" + }, + }; + appConfig.enableTermsAndConditions = true; appConfig.webAPIRoot = appConfig.api.url; // todo: move "userAuthenticationEnabled", "plpResultsEnabled", etc into the object appConfig.features = { - locationDistance: false, + locationDistance: false, }; - appConfig.externalLibraries = []; + appConfig.externalLibraries = []; + + appConfig.commonDataTableOptions = { + pageLength: { + ONLY_5: 5, + XS: 5, + S: 10, + M: 25, + L: 50, + }, + lengthMenu: { + ONLY_5: [[5], ['5']], + XS: [ + [5, 10], + ['5', '10'], + ], + S: [ + [10, 15, 20, 25, 50, -1], + ['10', '15', '20', '25', '50', 'All'], + ], + M: [ + [10, 25, 50, 100, -1], + ['10', '25', '50', '100', 'All'], + ], + L: [ + [25, 50, 75, 100, -1], + ['25', '50', '75', '100', 'All'], + ], + } + }; - appConfig.commonDataTableOptions = { - pageLength: { - ONLY_5: 5, - XS: 5, - S: 10, - M: 25, - L: 50, - }, - lengthMenu: { - ONLY_5: [[5], ['5']], - XS: [ - [5, 10], - ['5', '10'], - ], - S: [ - [10, 15, 20, 25, 50, -1], - ['10', '15', '20', '25', '50', 'All'], - ], - M: [ - [10, 25, 50, 100, -1], - ['10', '25', '50', '100', 'All'], - ], - L: [ - [25, 50, 75, 100, -1], - ['25', '50', '75', '100', 'All'], - ], - } - }; + appConfig.enablePersonCount = true; - appConfig.enablePersonCount = true; + // "Tagging" section is hidden by default + appConfig.enableTaggingSection = false; - // "Tagging" section is hidden by default - appConfig.enableTaggingSection = false; + appConfig.refreshTokenThreshold = 1000 * 60 * 60 * 4; // refresh auth token if it will expire within 4 hours return appConfig; }); diff --git a/js/pages/Route.js b/js/pages/Route.js index ed5c5cd9d..b4bc0f608 100644 --- a/js/pages/Route.js +++ b/js/pages/Route.js @@ -11,7 +11,7 @@ define([ checkPermission() { if (authApi.authProvider() === authApi.AUTH_PROVIDERS.IAP) { return authApi.loadUserInfo(); - } else if (appConfig.userAuthenticationEnabled && authApi.token() != null && authApi.tokenExpirationDate() > new Date()) { + } else if (appConfig.userAuthenticationEnabled && authApi.token() != null && this.timeToExpire() < appConfig.refreshTokenThreshold) { return authApi.refreshToken(); } return Promise.resolve(); @@ -25,6 +25,10 @@ define([ handler() { throw new Exception('Handler should be overriden'); } + + timeToExpire() { + return authApi.tokenExpirationDate() - new Date(); + } } class AuthorizedRoute extends Route { diff --git a/js/pages/cohort-definitions/cohort-definition-manager.js b/js/pages/cohort-definitions/cohort-definition-manager.js index 2d92f2cef..c8c1138eb 100644 --- a/js/pages/cohort-definitions/cohort-definition-manager.js +++ b/js/pages/cohort-definitions/cohort-definition-manager.js @@ -611,7 +611,7 @@ define(['jquery', 'knockout', 'text!./cohort-definition-manager.html', this.pollForInfoPeriodically = () => { this.pollTimeoutId = PollService.add({ callback: () => this.pollForInfo(), - interval: 10000, + interval: config.pollInterval, }); } @@ -917,7 +917,7 @@ define(['jquery', 'knockout', 'text!./cohort-definition-manager.html', startPolling(cd, source) { this.pollId = PollService.add({ callback: () => this.queryHeraclesJob(cd, source), - interval: 10000, + interval: config.pollInterval, }); } diff --git a/js/pages/configuration/configuration.js b/js/pages/configuration/configuration.js index 091488fc7..76ac15225 100644 --- a/js/pages/configuration/configuration.js +++ b/js/pages/configuration/configuration.js @@ -81,7 +81,7 @@ define([ this.intervalId = PollService.add({ callback: () => this.checkJobs(), - interval: 5000 + interval: config.pollInterval }); } diff --git a/js/pages/incidence-rates/ir-manager.js b/js/pages/incidence-rates/ir-manager.js index 83b206e6c..741cbfa31 100644 --- a/js/pages/incidence-rates/ir-manager.js +++ b/js/pages/incidence-rates/ir-manager.js @@ -465,7 +465,7 @@ define([ if (!this.pollId) { this.pollId = JobPollService.add({ callback: silently => this.pollForInfo({ silently }), - interval: 10000, + interval: config.pollInterval, isSilentAfterFirstCall: true, }); } From da6c028e29df50adb5d5945866ab8ee705049524 Mon Sep 17 00:00:00 2001 From: Chris Knoll Date: Mon, 10 Apr 2023 18:31:26 -0400 Subject: [PATCH 2/4] Refresh permissions on create/copy of asset. --- .../services/CharacterizationService.js | 10 ++++++++-- .../services/FeatureAnalysisService.js | 12 +++++++++--- js/pages/concept-sets/conceptset-manager.js | 2 +- js/pages/pathways/PathwayService.js | 14 ++++++++++---- js/services/CohortDefinition.js | 1 + js/services/ConceptSet.js | 4 +++- js/services/Estimation.js | 6 ++++-- js/services/IRAnalysis.js | 3 ++- js/services/PatientLevelPrediction.js | 13 ++++++++----- js/services/ReusablesService.js | 12 +++++++++--- 10 files changed, 55 insertions(+), 22 deletions(-) diff --git a/js/pages/characterizations/services/CharacterizationService.js b/js/pages/characterizations/services/CharacterizationService.js index 3cf96f88b..bc203932c 100644 --- a/js/pages/characterizations/services/CharacterizationService.js +++ b/js/pages/characterizations/services/CharacterizationService.js @@ -3,11 +3,13 @@ define([ 'services/file', 'appConfig', 'utils/ExecutionUtils', + 'services/AuthAPI', ], function ( httpService, fileService, config, executionUtils, + authApi ) { function loadCharacterizationList() { return httpService @@ -34,11 +36,15 @@ define([ } function createCharacterization(design) { - return httpService.doPost(config.webAPIRoot + 'cohort-characterization', design).then(res => res.data); + let promise = httpService.doPost(config.webAPIRoot + 'cohort-characterization', design).then(res => res.data); + promise.then(authApi.refreshToken) + return promise; } function copyCharacterization(id) { - return httpService.doPost(config.webAPIRoot + 'cohort-characterization/' + id).then(res => res.data); + let promise = httpService.doPost(config.webAPIRoot + 'cohort-characterization/' + id).then(res => res.data); + promise.then(authApi.refreshToken); + return promise; } function updateCharacterization(id, design) { diff --git a/js/pages/characterizations/services/FeatureAnalysisService.js b/js/pages/characterizations/services/FeatureAnalysisService.js index fa47aec0c..dd0d14620 100644 --- a/js/pages/characterizations/services/FeatureAnalysisService.js +++ b/js/pages/characterizations/services/FeatureAnalysisService.js @@ -2,10 +2,12 @@ define([ 'services/http', 'services/file', 'appConfig', + 'services/AuthAPI', ], function ( httpService, fileService, - config + config, + authApi ) { function loadFeatureAnalysisList() { @@ -21,7 +23,9 @@ define([ } function createFeatureAnalysis(design) { - return request = httpService.doPost(config.webAPIRoot + 'feature-analysis', design).then(res => res.data); + let promise = httpService.doPost(config.webAPIRoot + 'feature-analysis', design).then(res => res.data); + promise.then(authApi.refreshToken); + return promise; } function updateFeatureAnalysis(id, design) { @@ -46,7 +50,9 @@ define([ } function copyFeatureAnalysis(id) { - return httpService.doGet(`${config.webAPIRoot}feature-analysis/${id}/copy`); + let promise = httpService.doGet(`${config.webAPIRoot}feature-analysis/${id}/copy`); + promise.then(authApi.refreshToken); + return promise; } return { diff --git a/js/pages/concept-sets/conceptset-manager.js b/js/pages/concept-sets/conceptset-manager.js index 7e0450aea..64a8514f6 100644 --- a/js/pages/concept-sets/conceptset-manager.js +++ b/js/pages/concept-sets/conceptset-manager.js @@ -536,7 +536,7 @@ define([ this.currentConceptSet().name(responseWithName.copyName); this.currentConceptSet().id = 0; this.currentConceptSetDirtyFlag().reset(); - this.saveConceptSet(this.currentConceptSet(), "#txtConceptSetName"); + await this.saveConceptSet(this.currentConceptSet(), "#txtConceptSetName"); } async optimize() { diff --git a/js/pages/pathways/PathwayService.js b/js/pages/pathways/PathwayService.js index 9f98ec019..0f8d9a526 100644 --- a/js/pages/pathways/PathwayService.js +++ b/js/pages/pathways/PathwayService.js @@ -1,11 +1,13 @@ define([ 'services/http', 'appConfig', - 'utils/ExecutionUtils' + 'utils/ExecutionUtils', + 'services/AuthAPI', ], function ( httpService, config, - executionUtils, + executionUtils, + authApi ) { const servicePath = config.webAPIRoot + 'pathway-analysis'; @@ -16,7 +18,9 @@ define([ } function create(design) { - return request = httpService.doPost(servicePath, design).then(res => res.data); + let promise = httpService.doPost(servicePath, design).then(res => res.data); + promise.then(authApi.refreshToken) + return promise; } function load(id) { @@ -30,7 +34,9 @@ define([ } function copy(id) { - return httpService.doPost(`${servicePath}/${id}`).then(res => res.data); + let promise = httpService.doPost(`${servicePath}/${id}`).then(res => res.data); + promise.then(authApi.refreshToken); + return promise; } function del(id) { diff --git a/js/services/CohortDefinition.js b/js/services/CohortDefinition.js index 0d464d09b..d51a957b0 100644 --- a/js/services/CohortDefinition.js +++ b/js/services/CohortDefinition.js @@ -33,6 +33,7 @@ define(function (require, exports) { authApi.handleAccessDenied(error); } }); + savePromise.then(authApi.refreshToken); return savePromise; } diff --git a/js/services/ConceptSet.js b/js/services/ConceptSet.js index cb78134ce..cf5b2489b 100644 --- a/js/services/ConceptSet.js +++ b/js/services/ConceptSet.js @@ -57,12 +57,13 @@ define(function (require) { } function saveConceptSet(conceptSet) { - let promise = new Promise(r => r()); + let promise; const url = `${config.api.url}conceptset/${conceptSet.id ? conceptSet.id : ''}`; if (conceptSet.id) { promise = httpService.doPut(url, conceptSet); } else { promise = httpService.doPost(url, conceptSet); + promise.then(authApi.refreshToken); } promise.catch(authApi.handleAccessDenied); @@ -112,6 +113,7 @@ define(function (require) { function copyVersion(conceptSetId, versionId) { return httpService.doPut(`${config.webAPIRoot}conceptset/${conceptSetId}/version/${versionId}/createAsset`) + .then(authApi.refreshToken) .then(({ data }) => data); } diff --git a/js/services/Estimation.js b/js/services/Estimation.js index d64eea571..00580814b 100644 --- a/js/services/Estimation.js +++ b/js/services/Estimation.js @@ -16,21 +16,23 @@ define(function (require, exports) { promise = httpService.doPut(url, analysis); } else { promise = httpService.doPost(url, analysis); + promise.then(authApi.refreshToken); } promise.catch((error) => { console.log("Error: " + error); authApi.handleAccessDenied(error); }); - return promise; } function copyEstimation(id) { - return httpService.doGet(config.webAPIRoot + estimationEndpoint + (id || "") + "/copy") + let promise = httpService.doGet(config.webAPIRoot + estimationEndpoint + (id || "") + "/copy") .catch((error) => { console.log("Error: " + error); authApi.handleAccessDenied(error); }); + promise.then(authApi.refreshToken); + return promise; } function deleteEstimation(id) { diff --git a/js/services/IRAnalysis.js b/js/services/IRAnalysis.js index 680f32c88..b99e02d93 100644 --- a/js/services/IRAnalysis.js +++ b/js/services/IRAnalysis.js @@ -50,11 +50,12 @@ define(function (require, exports) { definitionCopy.expression = JSON.stringify(definitionCopy.expression); const url = `${config.webAPIRoot}ir/${definitionCopy.id || ""}`; - let promise = new Promise(r => r()); + let promise; if (definitionCopy.id) { promise = httpService.doPut(url, definitionCopy); } else { promise = httpService.doPost(url, definitionCopy); + promise.then(authApi.refreshToken); } return promise diff --git a/js/services/PatientLevelPrediction.js b/js/services/PatientLevelPrediction.js index c4cb3271f..cedd7c5c0 100644 --- a/js/services/PatientLevelPrediction.js +++ b/js/services/PatientLevelPrediction.js @@ -15,6 +15,7 @@ define(function (require, exports) { promise = httpService.doPut(url, analysis); } else { promise = httpService.doPost(url, analysis); + promise.then(authApi.refreshToken); } promise.catch((error) => { console.log("Error: " + error); @@ -25,11 +26,13 @@ define(function (require, exports) { } function copyPlp(id) { - return httpService.doGet(config.webAPIRoot + 'plp/' + (id || "") + "/copy") - .catch((error) => { - console.log("Error: " + error); - authApi.handleAccessDenied(error); - }); + let promise = httpService.doGet(config.webAPIRoot + 'plp/' + (id || "") + "/copy") + .catch((error) => { + console.log("Error: " + error); + authApi.handleAccessDenied(error); + }); + promise.then(authApi.refreshToken); + return promise; } function deletePlp(id) { diff --git a/js/services/ReusablesService.js b/js/services/ReusablesService.js index e57c95faf..ea4277063 100644 --- a/js/services/ReusablesService.js +++ b/js/services/ReusablesService.js @@ -1,11 +1,13 @@ define([ 'knockout', './http', - 'appConfig', + 'appConfig', + 'services/AuthAPI', ], function ( ko, httpService, config, + authApi, ) { const servicePath = config.webAPIRoot + 'reusable'; @@ -34,7 +36,9 @@ define([ initialEventExpression: design.initialEventExpression, censoringEventExpression: design.censoringEventExpression, }); - return request = httpService.doPost(servicePath, design).then(res => res.data); + let promise = httpService.doPost(servicePath, design).then(res => res.data); + promise.then(authApi.refreshToken); + return promise; } function exists(name, id) { @@ -54,7 +58,9 @@ define([ } function copy(id) { - return httpService.doPost(`${servicePath}/${id}`).then(res => res.data); + let promise = httpService.doPost(`${servicePath}/${id}`).then(res => res.data); + promise.then(authApi.refreshToken); + return promise; } function del(id) { From a92eb3b94a3c97eaefc2c26709b520bc53c93b44 Mon Sep 17 00:00:00 2001 From: Chris Knoll Date: Thu, 13 Apr 2023 12:39:13 -0400 Subject: [PATCH 3/4] Add refresh for cohort definition copy. --- js/services/CohortDefinition.js | 1 + 1 file changed, 1 insertion(+) diff --git a/js/services/CohortDefinition.js b/js/services/CohortDefinition.js index d51a957b0..6485ed7bc 100644 --- a/js/services/CohortDefinition.js +++ b/js/services/CohortDefinition.js @@ -47,6 +47,7 @@ define(function (require, exports) { authApi.handleAccessDenied(error); } }); + copyPromise.then(authApi.refreshToken); return copyPromise; } From 54f7d6d78797f831b9c0aea9254e10590d481eca Mon Sep 17 00:00:00 2001 From: Chris Knoll Date: Fri, 14 Jul 2023 13:39:47 -0400 Subject: [PATCH 4/4] Added executeWithRefresh() in AuthAPI to refresh permissions after HTTP request. Removed unused PatientLevelPrediction.js --- .../services/CharacterizationService.js | 37 ++++++----- .../services/FeatureAnalysisService.js | 16 ++--- js/pages/pathways/PathwayService.js | 36 +++++------ js/services/AuthAPI.js | 7 +++ js/services/CohortDefinition.js | 27 ++++---- js/services/ConceptSet.js | 15 +++-- js/services/Estimation.js | 54 ++++++++-------- js/services/IRAnalysis.js | 43 +++++++------ js/services/PatientLevelPrediction.js | 63 ------------------- js/services/Prediction.js | 38 +++++------ 10 files changed, 139 insertions(+), 197 deletions(-) delete mode 100644 js/services/PatientLevelPrediction.js diff --git a/js/pages/characterizations/services/CharacterizationService.js b/js/pages/characterizations/services/CharacterizationService.js index 856f577c7..3378e63ee 100644 --- a/js/pages/characterizations/services/CharacterizationService.js +++ b/js/pages/characterizations/services/CharacterizationService.js @@ -23,10 +23,13 @@ define([ .then(res => res.data); } - function loadCharacterizationDesign(id) { - return httpService + async function loadCharacterizationDesign(id) { + const result = await httpService .doGet(config.webAPIRoot + 'cohort-characterization/' + id + '/design') .then(res => res.data); + await authApi.refreshToken(); + return result; + } function loadCharacterizationExportDesign(id) { @@ -35,16 +38,12 @@ define([ .then(res => res.data); } - function createCharacterization(design) { - let promise = httpService.doPost(config.webAPIRoot + 'cohort-characterization', design).then(res => res.data); - promise.then(authApi.refreshToken) - return promise; + async function createCharacterization(design) { + return authApi.executeWithRefresh(httpService.doPost(config.webAPIRoot + 'cohort-characterization', design).then(res => res.data)); } - function copyCharacterization(id) { - let promise = httpService.doPost(config.webAPIRoot + 'cohort-characterization/' + id).then(res => res.data); - promise.then(authApi.refreshToken); - return promise; + async function copyCharacterization(id) { + return authApi.executeWithRefresh(httpService.doPost(config.webAPIRoot + 'cohort-characterization/' + id).then(res => res.data)); } function updateCharacterization(id, design) { @@ -81,16 +80,16 @@ define([ .then(res => res.data); } - function generate(ccId, sourcekey) { - return httpService + async function generate(ccId, sourcekey) { + return authApi.executeWithRefresh(httpService .doPost(config.webAPIRoot + 'cohort-characterization/' + ccId + '/generation/' + sourcekey) - .then(res => res.data); + .then(res => res.data)); } - function importCharacterization(design) { - return httpService + async function importCharacterization(design) { + return authApi.executeWithRefresh(httpService .doPost(config.webAPIRoot + 'cohort-characterization/import', design) - .then(res => res.data); + .then(res => res.data)); } function getPrevalenceStatsByGeneration(generationId, analysisId, cohortId, covariateId) { @@ -131,9 +130,9 @@ define([ .then(res => res.data); } - function copyVersion(id, versionNumber) { - return httpService.doPut(`${config.webAPIRoot}cohort-characterization/${id}/version/${versionNumber}/createAsset`) - .then(res => res.data); + async function copyVersion(id, versionNumber) { + return authApi.executeWithRefresh(httpService.doPut(`${config.webAPIRoot}cohort-characterization/${id}/version/${versionNumber}/createAsset`) + .then(res => res.data)); } function updateVersion(version) { diff --git a/js/pages/characterizations/services/FeatureAnalysisService.js b/js/pages/characterizations/services/FeatureAnalysisService.js index dd0d14620..aba64ef63 100644 --- a/js/pages/characterizations/services/FeatureAnalysisService.js +++ b/js/pages/characterizations/services/FeatureAnalysisService.js @@ -14,18 +14,16 @@ define([ return httpService.doGet(config.webAPIRoot + 'feature-analysis?size=100000').then(res => res.data); } - function loadFeatureAnalysis(id) { - return httpService.doGet(config.webAPIRoot + `feature-analysis/${id}`).then(res => res.data); + async function loadFeatureAnalysis(id) { + return authApi.executeWithRefresh(httpService.doGet(config.webAPIRoot + `feature-analysis/${id}`).then(res => res.data)); } function loadFeatureAnalysisDomains() { return httpService.doGet(config.webAPIRoot + 'feature-analysis/domains').then(res => res.data); } - function createFeatureAnalysis(design) { - let promise = httpService.doPost(config.webAPIRoot + 'feature-analysis', design).then(res => res.data); - promise.then(authApi.refreshToken); - return promise; + async function createFeatureAnalysis(design) { + return authApi.executeWithRefresh(httpService.doPost(config.webAPIRoot + 'feature-analysis', design).then(res => res.data)); } function updateFeatureAnalysis(id, design) { @@ -49,10 +47,8 @@ define([ return httpService.doGet(`${config.webAPIRoot}feature-analysis/aggregates`).then(res => res.data); } - function copyFeatureAnalysis(id) { - let promise = httpService.doGet(`${config.webAPIRoot}feature-analysis/${id}/copy`); - promise.then(authApi.refreshToken); - return promise; + async function copyFeatureAnalysis(id) { + return authApi.executeWithRefresh(httpService.doGet(`${config.webAPIRoot}feature-analysis/${id}/copy`)); } return { diff --git a/js/pages/pathways/PathwayService.js b/js/pages/pathways/PathwayService.js index 0f8d9a526..0a56c89d4 100644 --- a/js/pages/pathways/PathwayService.js +++ b/js/pages/pathways/PathwayService.js @@ -17,26 +17,22 @@ define([ .then(res => res.data); } - function create(design) { - let promise = httpService.doPost(servicePath, design).then(res => res.data); - promise.then(authApi.refreshToken) - return promise; + async function create(design) { + return authApi.executeWithRefresh(httpService.doPost(servicePath, design).then(res => res.data)); } - function load(id) { - return httpService + async function load(id) { + return authApi.executeWithRefresh(httpService .doGet(`${servicePath}/${id}`) - .then(res => res.data); + .then(res => res.data)); } function save(id, design) { return httpService.doPut(`${servicePath}/${id}`, design).then(res => res.data); } - function copy(id) { - let promise = httpService.doPost(`${servicePath}/${id}`).then(res => res.data); - promise.then(authApi.refreshToken); - return promise; + async function copy(id) { + return authApi.executeWithRefresh(httpService.doPost(`${servicePath}/${id}`).then(res => res.data)); } function del(id) { @@ -63,10 +59,10 @@ define([ .then(res => res.data); } - function generate(id, sourcekey) { - return httpService + async function generate(id, sourcekey) { + return authApi.executeWithRefresh(httpService .doPost(`${servicePath}/${id}/generation/${sourcekey}`) - .then(res => res.data); + .then(res => res.data)); } function cancelGeneration(id, sourceKey) { @@ -87,10 +83,10 @@ define([ .then(res => res.data); } - function importPathwayDesign(design) { - return httpService + async function importPathwayDesign(design) { + return authApi.executeWithRefresh(httpService .doPost(`${servicePath}/import`, design) - .then(res => res.data); + .then(res => res.data)); } function exists(name, id) { @@ -115,9 +111,9 @@ define([ .then(res => res.data); } - function copyVersion(id, versionNumber) { - return httpService.doPut(`${servicePath}/${id}/version/${versionNumber}/createAsset`) - .then(res => res.data); + async function copyVersion(id, versionNumber) { + return authApi.executeWithRefresh(httpService.doPut(`${servicePath}/${id}/version/${versionNumber}/createAsset`) + .then(res => res.data)); } function updateVersion(version) { diff --git a/js/services/AuthAPI.js b/js/services/AuthAPI.js index 5baf49e30..b81a7c448 100644 --- a/js/services/AuthAPI.js +++ b/js/services/AuthAPI.js @@ -515,6 +515,12 @@ define(function(require, exports) { }); }; + const executeWithRefresh = async function(httpPromise) { + const result = await httpPromise; + await refreshToken(); + return result; + } + var api = { AUTH_PROVIDERS: AUTH_PROVIDERS, AUTH_CLIENTS: AUTH_CLIENTS, @@ -611,6 +617,7 @@ define(function(require, exports) { loadUserInfo, TOKEN_HEADER, runAs, + executeWithRefresh, }; return api; diff --git a/js/services/CohortDefinition.js b/js/services/CohortDefinition.js index 6485ed7bc..13ec4a567 100644 --- a/js/services/CohortDefinition.js +++ b/js/services/CohortDefinition.js @@ -22,8 +22,8 @@ define(function (require, exports) { return promise; } - function saveCohortDefinition(definition) { - var savePromise = $.ajax({ + async function saveCohortDefinition(definition) { + return authApi.executeWithRefresh($.ajax({ url: config.webAPIRoot + 'cohortdefinition/' + (definition.id || ""), method: definition.id ? 'PUT' : 'POST', contentType: 'application/json', @@ -32,13 +32,11 @@ define(function (require, exports) { console.log("Error: " + error); authApi.handleAccessDenied(error); } - }); - savePromise.then(authApi.refreshToken); - return savePromise; + })); } - function copyCohortDefinition(id) { - var copyPromise = $.ajax({ + async function copyCohortDefinition(id) { + return authApi.executeWithRefresh($.ajax({ url: config.webAPIRoot + 'cohortdefinition/' + (id || "") +"/copy", method: 'GET', contentType: 'application/json', @@ -46,9 +44,7 @@ define(function (require, exports) { console.log("Error: " + error); authApi.handleAccessDenied(error); } - }); - copyPromise.then(authApi.refreshToken); - return copyPromise; + })); } function deleteCohortDefinition(id) { @@ -59,8 +55,8 @@ define(function (require, exports) { return deletePromise; } - function getCohortDefinition(id) { - return httpService + async function getCohortDefinition(id) { + return authApi.executeWithRefresh(httpService .doGet(config.webAPIRoot + 'cohortdefinition/' + id) .then(res => { const cohortDef = res.data; @@ -69,7 +65,7 @@ define(function (require, exports) { }).catch(error => { console.log("Error: " + error); authApi.handleAccessDenied(error); - }); + })); } function exists(name, id) { @@ -175,8 +171,9 @@ define(function (require, exports) { } function copyVersion(cohortDefinitionId, versionNumber) { - return httpService.doPut(`${config.webAPIRoot}cohortdefinition/${cohortDefinitionId}/version/${versionNumber}/createAsset`) - .then(({ data }) => data); + return authApi.executeWithRefresh(httpService + .doPut(`${config.webAPIRoot}cohortdefinition/${cohortDefinitionId}/version/${versionNumber}/createAsset`) + .then(({ data }) => data)); } function updateVersion(version) { diff --git a/js/services/ConceptSet.js b/js/services/ConceptSet.js index cf5b2489b..435d050db 100644 --- a/js/services/ConceptSet.js +++ b/js/services/ConceptSet.js @@ -10,8 +10,8 @@ define(function (require) { const _ = require('lodash'); const hash = require('hash-it').default; - function loadConceptSet(id) { - return httpService.doGet(config.api.url + 'conceptset/' + id).then(({ data }) => data); + async function loadConceptSet(id) { + return authApi.executeWithRefresh(httpService.doGet(config.api.url + 'conceptset/' + id).then(({ data }) => data)); } function loadConceptSetExpression(conceptSetId) { @@ -56,14 +56,13 @@ define(function (require) { .then(({ data }) => data); } - function saveConceptSet(conceptSet) { + async function saveConceptSet(conceptSet) { let promise; const url = `${config.api.url}conceptset/${conceptSet.id ? conceptSet.id : ''}`; if (conceptSet.id) { promise = httpService.doPut(url, conceptSet); } else { promise = httpService.doPost(url, conceptSet); - promise.then(authApi.refreshToken); } promise.catch(authApi.handleAccessDenied); @@ -111,10 +110,10 @@ define(function (require) { return httpService.doGet(`${config.webAPIRoot}conceptset/${conceptSetId}/version/${versionNumber}/expression` + (sourceKey ? `/${sourceKey}`: '')).then(({ data }) => data); } - function copyVersion(conceptSetId, versionId) { - return httpService.doPut(`${config.webAPIRoot}conceptset/${conceptSetId}/version/${versionId}/createAsset`) - .then(authApi.refreshToken) - .then(({ data }) => data); + async function copyVersion(conceptSetId, versionId) { + return authApi.executeWithRefresh(httpService + .doPut(`${config.webAPIRoot}conceptset/${conceptSetId}/version/${versionId}/createAsset`) + .then(({ data }) => data)); } function updateVersion(version) { diff --git a/js/services/Estimation.js b/js/services/Estimation.js index 00580814b..af9f18cb3 100644 --- a/js/services/Estimation.js +++ b/js/services/Estimation.js @@ -2,37 +2,40 @@ define(function (require, exports) { const config = require('appConfig'); const authApi = require('services/AuthAPI'); - const httpService = require('services/http'); - const estimationEndpoint = "estimation/" + const httpService = require('services/http'); + const estimationEndpoint = "estimation/" function getEstimationList() { return httpService.doGet(config.webAPIRoot + estimationEndpoint).catch(authApi.handleAccessDenied); } - function saveEstimation(analysis) { + async function saveEstimation(analysis) { const url = config.webAPIRoot + estimationEndpoint + (analysis.id || ""); - let promise; + let result; if (analysis.id) { - promise = httpService.doPut(url, analysis); + result = await httpService + .doPut(url, analysis) + .catch((error) => { + console.log("Error: " + error); + authApi.handleAccessDenied(error); + }) } else { - promise = httpService.doPost(url, analysis); - promise.then(authApi.refreshToken); + result = authApi.executeWithRefresh(httpService + .doPost(url, analysis) + .catch((error) => { + console.log("Error: " + error); + authApi.handleAccessDenied(error); + })) } - promise.catch((error) => { - console.log("Error: " + error); - authApi.handleAccessDenied(error); - }); - return promise; + return result; } - function copyEstimation(id) { - let promise = httpService.doGet(config.webAPIRoot + estimationEndpoint + (id || "") + "/copy") + async function copyEstimation(id) { + return authApi.executeWithRefresh(httpService.doGet(config.webAPIRoot + estimationEndpoint + (id || "") + "/copy") .catch((error) => { console.log("Error: " + error); authApi.handleAccessDenied(error); - }); - promise.then(authApi.refreshToken); - return promise; + })); } function deleteEstimation(id) { @@ -43,12 +46,13 @@ define(function (require, exports) { }); } - function getEstimation(id) { - return httpService.doGet(config.webAPIRoot + estimationEndpoint + id) + async function getEstimation(id) { + return authApi.executeWithRefresh(httpService + .doGet(config.webAPIRoot + estimationEndpoint + id) .catch((error) => { console.log("Error: " + error); authApi.handleAccessDenied(error); - }); + })); } function exportEstimation(id) { @@ -74,11 +78,11 @@ define(function (require, exports) { .catch(error => authApi.handleAccessDenied(error)); } - function importEstimation(specification) { - return httpService - .doPost(config.webAPIRoot + estimationEndpoint + "import", specification) - .then(res => res.data); - } + function importEstimation(specification) { + return authApi.executeWithRefresh(httpService + .doPost(config.webAPIRoot + estimationEndpoint + "import", specification) + .then(res => res.data)); + } function exists(name, id) { return httpService diff --git a/js/services/IRAnalysis.js b/js/services/IRAnalysis.js index b99e02d93..19daf1088 100644 --- a/js/services/IRAnalysis.js +++ b/js/services/IRAnalysis.js @@ -32,49 +32,54 @@ define(function (require, exports) { return promise; } - function getAnalysis(id) { - const promise = httpService.doGet(`${config.webAPIRoot}ir/${id}`) + async function getAnalysis(id) { + return authApi.executeWithRefresh(httpService.doGet(`${config.webAPIRoot}ir/${id}`) .then(parse) .catch(response => { authApi.handleAccessDenied(response); return response; - }); - - return promise; + })); } - function saveAnalysis(definition) { + async function saveAnalysis(definition) { var definitionCopy = JSON.parse(ko.toJSON(definition)); if (typeof definitionCopy.expression != 'string') definitionCopy.expression = JSON.stringify(definitionCopy.expression); const url = `${config.webAPIRoot}ir/${definitionCopy.id || ""}`; - let promise; + let result; if (definitionCopy.id) { - promise = httpService.doPut(url, definitionCopy); + result = await httpService + .doPut(url, definitionCopy) + .catch(response => { + authApi.handleAccessDenied(response); + return response; + }) + .then(parse); } else { - promise = httpService.doPost(url, definitionCopy); - promise.then(authApi.refreshToken); + result = authApi.executeWithRefresh(httpService + .doPost(url, definitionCopy) + .catch(response => { + authApi.handleAccessDenied(response); + return response; + }) + .then(parse)); } - return promise - .then(parse) - .catch(response => { - authApi.handleAccessDenied(response); - return response; - }); + return result; + } - function copyAnalysis(id) { + async function copyAnalysis(id) { const promise = httpService.doGet(`${config.webAPIRoot}ir/${id || ""}/copy`); - return promise + return authApi.executeWithRefresh(promise .then(parse) .catch(response => { authApi.handleAccessDenied(response); return response; - }); + })); } function deleteAnalysis(id) { diff --git a/js/services/PatientLevelPrediction.js b/js/services/PatientLevelPrediction.js deleted file mode 100644 index cedd7c5c0..000000000 --- a/js/services/PatientLevelPrediction.js +++ /dev/null @@ -1,63 +0,0 @@ -define(function (require, exports) { - - const config = require('appConfig'); - const authApi = require('services/AuthAPI'); - const httpService = require('services/http'); - - function getPlpList() { - return httpService.doGet(config.webAPIRoot + 'plp/').catch(authApi.handleAccessDenied); - } - - function savePlp(analysis) { - const url = config.webAPIRoot + 'plp/' + (analysis.analysisId || ""); - let promise; - if (analysis.analysisId) { - promise = httpService.doPut(url, analysis); - } else { - promise = httpService.doPost(url, analysis); - promise.then(authApi.refreshToken); - } - promise.catch((error) => { - console.log("Error: " + error); - authApi.handleAccessDenied(error); - }); - - return promise; - } - - function copyPlp(id) { - let promise = httpService.doGet(config.webAPIRoot + 'plp/' + (id || "") + "/copy") - .catch((error) => { - console.log("Error: " + error); - authApi.handleAccessDenied(error); - }); - promise.then(authApi.refreshToken); - return promise; - } - - function deletePlp(id) { - return httpService.doDelete(config.webAPIRoot + 'plp/' + (id || "")) - .catch((error) => { - console.log("Error: " + error); - authApi.handleAccessDenied(error); - }); - } - - function getPlp(id) { - return httpService.doGet(config.webAPIRoot + 'plp/' + id) - .catch((error) => { - console.log("Error: " + error); - authApi.handleAccessDenied(error); - }); - } - - var api = { - getPlpList: getPlpList, - savePlp: savePlp, - copyPlp: copyPlp, - deletePlp: deletePlp, - getPlp: getPlp, - }; - - return api; -}); diff --git a/js/services/Prediction.js b/js/services/Prediction.js index e7664d33d..348098937 100644 --- a/js/services/Prediction.js +++ b/js/services/Prediction.js @@ -9,28 +9,30 @@ define(function (require, exports) { return httpService.doGet(config.webAPIRoot + predictionEndpoint).catch(authApi.handleAccessDenied); } - function savePrediction(analysis) { + async function savePrediction(analysis) { const url = config.webAPIRoot + predictionEndpoint + (analysis.id || ""); - let promise; + let result; if (analysis.id) { - promise = httpService.doPut(url, analysis); + result = await httpService.doPut(url, analysis).catch((error) => { + console.log("Error: " + error); + authApi.handleAccessDenied(error); + }); } else { - promise = httpService.doPost(url, analysis); + result = authApi.executeWithRefresh(httpService.doPost(url, analysis).catch((error) => { + console.log("Error: " + error); + authApi.handleAccessDenied(error); + })); } - promise.catch((error) => { - console.log("Error: " + error); - authApi.handleAccessDenied(error); - }); - return promise; + return result; } function copyPrediction(id) { - return httpService.doGet(config.webAPIRoot + predictionEndpoint + (id || "") + "/copy") + return authApi.executeWithRefresh(httpService.doGet(config.webAPIRoot + predictionEndpoint + (id || "") + "/copy") .catch((error) => { console.log("Error: " + error); authApi.handleAccessDenied(error); - }); + })); } function deletePrediction(id) { @@ -42,11 +44,11 @@ define(function (require, exports) { } function getPrediction(id) { - return httpService.doGet(config.webAPIRoot + predictionEndpoint + id) + return authApi.executeWithRefresh(httpService.doGet(config.webAPIRoot + predictionEndpoint + id) .catch((error) => { console.log("Error: " + error); authApi.handleAccessDenied(error); - }); + })); } function exportPrediction(id) { @@ -72,11 +74,11 @@ define(function (require, exports) { .catch(error => authApi.handleAccessDenied(error)); } - function importPrediction(specification) { - return httpService - .doPost(config.webAPIRoot + predictionEndpoint + "import", specification) - .then(res => res.data); - } + async function importPrediction(specification) { + return authApi.executeWithRefresh(httpService + .doPost(config.webAPIRoot + predictionEndpoint + "import", specification) + .then(res => res.data)); + } function exists(name, id) { return httpService