-
-
Notifications
You must be signed in to change notification settings - Fork 55
[WEB-3071] smart-on-fhir launch endpoint #1531
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
base: develop
Are you sure you want to change the base?
Conversation
/deploy dev |
krystophv updated values.yaml file in dev1 |
krystophv updated flux policies file in dev1 |
krystophv deployed blip WEB-3071-smart-on-fhir-launch branch to dev1 namespace |
…fhir-launch * origin/develop: (50 commits) Update pendoMiddleware.js WEB-2965 bump viz WEB-3418 bump viz WEB-3418 adjust comment for redirect url WEB-3418 bump viz WEB-3418 improve comments WEB-3418 always have date available in basics WEB-3418 fix test for oauth connection param WEB-3418 bump viz WEB-3418 test fix for Dexcom logo disappearing WEB-3418 use toast message WEB-3418 add button for redirect back home WEB-3418 fix failing tests WEB-3418 rename justConnectedDataSource param to location?.query?.openDataConnectionsModal || false WEB-3418 remove extraneous comments WEB-3418 send user to device modal page on callback WEB-3418 modify landing callback route with new params WEB-3418 revert pop-up not closing WEB-3418 comment out Popup.close() for debugging WEB-3418 only close provider popup when not mobile ...
* develop: (185 commits) v1.85.0-web-3029-dep-updates.2 Update dependencies: bump @tidepool/viz and tideline WEB-3002 trigger CI v1.84.1 WEB-3002 bump viz v1.84.1-rc.4 v1.84.1-rc.3 Remove unused 'previousSubmitting' state Update tests Ensure redirect away from clinic details page after new clinic creation, and add more robust dependency arrays Reset clinic completed working state when a new create clinic request is issued in case a user creates multiple in a session v1.84.1-rc.2 v1.84.1-rc.1 Hide "Back to Tidepool" button for custodial users on oauth connection page v1.84.0 Bump tideline, viz, platform-client v1.84.0-rc.37 Open data connections modal directly when clicking view link for dexcom connection status on TIDE Make all tests run again Add unique dataSourceJustConnected banner message when import time is indeterminate ...
* develop: (37 commits) v1.85.0-web-3471-one-button-bolus.1 Bump viz WEB-3454 assign null to combinedPatient if clinicPatient is nullish WEB-3454 use combinedPatient to access clinic patient details Prevent temporary display of uploader banner while initial data is loading Update tests Only attempt to fetch clinics for patient if user is viewing own data WEB-2367 add test WEB-2367 show FilterResetBar if search active WEB-2367 hide ClearFilterButtons bar when no filters applied v1.85.0-web-3636-remove-disconnected-from-list.1 Hide disconnected data sources on device settings view WEB-2367 update patient count on update/delete WEB-2367 fix tests WEB-2367 use new API response payload for total count WEB-2367 add comments for clarity WEB-2367 always use older value instead of using _.max() WEB-2367 cache initial value of fetchedPatientCount Revert "WEB-2367 update patientCount when fetching new patienst" WEB-2367 update patientCount when fetching new patienst ...
* develop: (139 commits) add skip for time-sensitive tests v1.85.2 FIX: Disable the 1min cgm toggle v1.85.2-rc.1 Disable the 1min cgm toggle rename sampleFrequency to sampleInterval [RELEASE-1] v1.85.0 [RELEASE-1] Bump viz, tideline, platform-client v1.85.0-rc.15 bump viz v1.85.0-rc.14 WEB-3595 lock packages to exact version; WEB-3501 fix missing return bug WEB-3595 - remove duplicate test WEB-3595 add clarifying comment v1.85.0-rc.13 Bump viz v1.86.0-web-3677-one-five-min-cgm-toggle.1 Update tests v1.85.0-rc.12 ...
…fhir-launch * origin/develop: Bump viz Bump viz
…fhir-launch * origin/develop: (30 commits) WEB-2813 fix tests v1.87.0-web-3720-re-enable-one-min-cgm.1 Bump viz WEB-3511 fix lint error WEB-2813 fix capitalization WEB-3511 trigger ci WEB-3511 bump viz WEB-2813 capitalize access management pills Bump viz v1.86.0-web-3687-new-dosing-decision-extended-duration-props.1 Bump tideline and viz WEB-3719 tests for metrics bug fix WEB-3511 trigger ci WEB-3511 bump viz Show loading dots when updating cgm sample range Re-enable one-min cgm sample interval toggle UI Bump viz WEB-3719 add additional labels for metrics for column header WEB-3511 bump viz and change uses of indigo50 to indigo30 WEB-3511 update text to meet contrast reqs ...
* develop: (52 commits) Remove extra EmptyContentNode that was inadvertently added during merge conflict to develop resolution Revert tideline to 1.33.1 v1.87.0-rc.1 Bump viz and tideline v1.87.0-bump-develop.1 Bump viz and tideline v1.86.1 Bump viz and tideline v1.86.1-rc.6 Bump viz and tideline v1.86.1-rc.4 Bump viz v1.86.1-rc.3 Bump viz v1.86.1-web-3773-re-enable-empty-twiist-bolus-filters.1 Bump viz v1.86.1-rc.2 v1.86.1-rc.1 Bump tideline Send unique combinations of major/minor/patch semvers to Pendo for currentlyViewedDevices ...
/deploy dev |
krystophv updated values.yaml file in dev1 |
krystophv updated flux policies file in dev1 |
krystophv deployed blip WEB-3071-smart-on-fhir-launch branch to dev1 namespace |
/deploy dev |
krystophv updated values.yaml file in dev1 |
krystophv updated flux policies file in dev1 |
krystophv deployed blip WEB-3071-smart-on-fhir-launch branch to dev1 namespace |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review Summary
This is a comprehensive and well-structured implementation of Smart-on-FHIR integration. The code follows established patterns and demonstrates good understanding of the existing codebase architecture.
Key Strengths:
- ✅ Proper use of Redux selectors and state management patterns
- ✅ Good performance optimizations with
useMemo
andcreateSelector
- ✅ Comprehensive error handling with user-friendly messages
- ✅ Consistent prop threading throughout the component hierarchy
- ✅ Well-documented API methods
Main Areas for Improvement:
- Security: Add validation/encryption for sensitive parameters in sessionStorage
- Code Consistency: Use existing selectors instead of direct state access
- Component Responsibility: Reduce direct Redux dependencies in presentational components
- PropTypes Consistency: Standardize required/optional prop declarations
Security Priority:
The session storage of sensitive FHIR parameters should be addressed before merging, as this could pose security risks in production environments.
Overall, this is solid work that integrates well with the existing codebase. The suggested improvements are primarily around security hardening and maintaining consistency with established patterns.
|
||
let clientConfig; | ||
|
||
if (iss && launch) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Security Concern: Session Storage Risk
Using sessionStorage
for sensitive FHIR parameters without encryption poses a security risk. Consider encrypting these sensitive parameters or using more secure storage mechanisms. At minimum, add validation to ensure stored values haven't been tampered with.
// Consider adding validation/encryption
const iss = validateAndDecrypt(sessionStorage.getItem('smart_iss')) || urlParams.get('iss');
@@ -94,6 +96,27 @@ export let Login = withTranslation()(class extends React.Component { | |||
redirectUri += dest; | |||
} | |||
|
|||
const urlParams = new URLSearchParams(routerState?.location?.search); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Security: URL Parameter Validation Needed
Direct URL parameter extraction and storage without validation could be vulnerable to XSS or injection attacks. Add input validation for URL parameters (iss, launch, correlation_id).
// Add validation helpers
const validateFhirIss = (iss) => {
// Validate iss format and whitelist known domains
return /^https:\/\/[\w.-]+\/fhir/.test(iss) ? iss : null;
};
const iss = validateFhirIss(urlParams.get('iss'));
const launch = urlParams.get('launch')?.replace(/[^a-zA-Z0-9-_]/, '') || null;
@@ -448,6 +449,7 @@ export function mapStateToProps(state) { | |||
selectedClinicId: state.blip.selectedClinicId, | |||
showingWelcomeMessage: state.blip.showingWelcomeMessage, | |||
user, | |||
isSmartOnFhirMode: state.blip.smartCorrelationId !== null, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Consistency: Use Existing Selector
Using direct state property access with magic strings is inconsistent with the rest of the codebase. Use the existing selectIsSmartOnFhirMode(state)
selector for consistency.
// Replace this line:
// isSmartOnFhirMode: state.blip.smartCorrelationId !== null,
// With:
isSmartOnFhirMode: selectIsSmartOnFhirMode(state),
@@ -46,6 +46,7 @@ class Basics extends Component { | |||
updateChartPrefs: PropTypes.func.isRequired, | |||
uploadUrl: PropTypes.string.isRequired, | |||
removeGeneratedPDFS: PropTypes.func.isRequired, | |||
isSmartOnFhirMode: PropTypes.bool.isRequired, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PropTypes Consistency Issue
Inconsistent PropTypes declarations across chart components. Some mark isSmartOnFhirMode
as isRequired
while others don't for the same prop. Consider standardizing this - if the prop is always passed down, it should consistently be marked as isRequired
.
Also appears in: app/components/chart/daily.js
, app/components/chart/trends.js
, etc.
|
||
this.props.setSmartCorrelationId(correlationId); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Complexity: Extract Method
The render method has grown quite complex with multiple nested conditions for Smart-on-FHIR handling. Consider extracting the Smart-on-FHIR URL parameter logic into a separate method like handleSmartOnFhirParams()
for better readability and maintainability.
if (state.blip.currentPatientInViewId) { | ||
permsOfLoggedInUser = state.blip.selectedClinicId | ||
return clinicPatient; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good Practice: Selector Performance
✅ Excellent use of createSelector
for the new selectIsSmartOnFhirMode
selector. This provides proper memoization and follows the established patterns in the codebase.
app/pages/smartonfhir/smartonfhir.js
Outdated
setIsProcessing(false); | ||
return; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Error Handling Enhancement
The error handling covers multiple states but could benefit from retry logic for network failures and clearer user guidance for recoverable errors. Consider adding:
- Retry mechanism for API failures
- More specific error messages for different failure types
- Recovery actions where possible
const t = i18next.t.bind(i18next); | ||
|
||
const Footer = ({ version, location, trackMetric }) => { | ||
const inSmartOnFhirMode = useSelector(state => selectIsSmartOnFhirMode(state)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Component Responsibility Concern
The Footer component now depends on Redux state management, which breaks component purity. Consider passing isSmartOnFhirMode
as a prop from the parent component instead of using useSelector
directly to maintain better separation of concerns.
...wrapperProps, | ||
|
||
const { Wrapper, wrapperProps } = React.useMemo(() => { | ||
const Wrapper = keycloakConfig?.url ? ReactKeycloakProvider : React.Fragment; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Performance Optimization
✅ Good use of useMemo
for expensive operations. The component re-rendering logic is well-optimized and follows React best practices.
const [error, setError] = useState(null); | ||
|
||
// Hide Zendesk widget in Smart-on-FHIR mode | ||
useEffect(() => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Organization: Extract Utility
The Zendesk hiding logic with retry mechanism could be extracted into a utility function for reusability across the application, especially if other components need to interact with Zendesk in Smart-on-FHIR mode.
// utils/zendesk.js
export const hideZendeskWidget = (maxAttempts = 50) => {
let attempts = 0;
let timeoutId;
const checkZendesk = () => {
if (window.zE) {
window.zE('webWidget', 'hide');
return;
}
attempts++;
if (attempts < maxAttempts) {
timeoutId = setTimeout(checkZendesk, 100);
}
};
checkZendesk();
return () => timeoutId && clearTimeout(timeoutId);
};
* develop: (326 commits) WEB-3830 bump viz WEB-3830 bump viz WEB-3830 modify Arg Type comment to permit multiple forms of unit representation WEB-3830 use correct id prop for select element label WEB-3830 hide fields in private workspace WEB-3830 minor visual tweaks WEB-3830 remove unneeded variable declaration WEB-3830 refactor utils.getBGPrefsForDataProcessing and write tests WEB-3830 add tests for displaying diagnosisType WEB-3830 migrate tests for NavPatientHeader WEB-3830 extract selectElementStyleOverrides to own files WEB-3830 rename getBgPrefs back to getBGPrefsForDataProcessing WEB-3830 rename to SelectGlycemicRanges and add test WEB-3830 fix jest specs WEB-3830 add spec for SelectDiabetesType WEB-3830 remove toggleBgRange tests WEB-3830 fix PatientForm-related tests WEB-3830 bump viz & tideline WEB-3830 send diagnosisType and glycemicRanges when accepting pt invite WEB-3830 hide BgSummaryCell when non-standard range ...
* develop: (74 commits) Fix broken tests Remove unused import and variable declaration Rename test-watch npm script Ensure button disable prop is a boolean Add tests for DataDonationForm Optimize slideshow images Add safety check to latestRecord in consentRecords reducer when fetching user consents Add tests for DataDOnationConsentDialog Only store active consent records and always fall back to current date for new consent dates Fixing some edge cases that were surfaced during code review Handle caregiver name changes without extra useEffect Derive currentForm directly from currentConsent state Remove unused scrollToBottom state in consent dialog Add a test:jest:watch npm script for general local use More tests Allow use of Set in eslint Remove unused schema from first step of data donation form Add tests for SildeShow component and dataDonation page Add additional tests v1.89.0-web-3771-informed-consent-flow-updated.1 ...
Introduces tracking for SMART on FHIR functionality across the application to better monitor and analyze usage patterns. This includes: - Adds isSmartOnFhir flag to analytics events when SMART on FHIR data is present - Updates pendo middleware to include SMART on FHIR properties in visitor data - Adds tracking for Direct Connect patient lookup success/failure events - Updates tracking middleware to pass SMART on FHIR context to analytics The changes ensure that SMART on FHIR usage is properly measured without cluttering analytics with false values, and maintains consistency across different tracking systems.
Introduces clinic membership validation before patient lookup to ensure clinicians are properly configured in Tidepool. Adds new error component for cases where clinicians have no clinics assigned and updates test coverage to include this scenario. - Adds clinic validation logic that checks if clinician belongs to any clinics before proceeding with patient lookup - Creates new NoClinicsError component to handle cases where clinicians have no clinic assignments - Updates Smart-on-FHIR error handling to include clinic validation errors - Adds comprehensive test coverage for clinic validation scenarios - Updates existing tests to use React Testing Library and Jest mocks instead of Enzyme and sinon This change improves the Smart-on-FHIR workflow by ensuring clinicians are properly onboarded before attempting patient lookups, providing better error messages for common configuration issues.
Switches all import paths from relative to absolute using the @app alias for better maintainability and cleaner module resolution. This change standardizes the import structure across test files, making the codebase more consistent and easier to navigate. The updates affect: - Component import paths in test files - Mock module paths for Icon and theme components - Overall project structure alignment with alias configuration This improves code organization and reduces the risk of broken imports when moving files.
* develop: WEB-3965 fix className prop WEB-3965 fix double calls and last reviewed error WEB-3965 modify clinicianPatients for clickable cards WEB-3965 make clinicPatients entire row clickable
- Updates ESLint parser from babel-eslint to @babel/eslint-parser across multiple configuration files - Adds Jest environment configuration for test files - Improves Smart-on-FHIR error handling with centralized handleError function and better error tracking - Updates footer links to use HTTPS protocol - Removes redundant isSmartOnFhir checks in analytics tracking - Updates patient error components with proper ARIA attributes for accessibility - Removes unused babel-eslint dependency and related eslint configurations - Cleans up test files by removing global eslint comments and improving test setup/teardown
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR implements initial Smart-on-FHIR integration to enable launching Tidepool from Electronic Health Record (EHR) systems. The implementation focuses on handling the SMART authentication flow and displaying patient data in a specialized mode.
- Smart-on-FHIR authentication flow with correlation ID tracking
- Patient lookup functionality using MRN and date of birth from SMART tokens
- Specialized UI mode that hides certain features when in Smart-on-FHIR context
Reviewed Changes
Copilot reviewed 77 out of 79 changed files in this pull request and generated 5 comments.
Show a summary per file
File | Description |
---|---|
app/pages/smartonfhir/ | New Smart-on-FHIR page and error components for patient matching scenarios |
app/redux/reducers/ | New reducers for Smart-on-FHIR data and correlation ID state management |
app/redux/actions/ | New actions for Smart-on-FHIR authentication and patient fetching |
app/keycloak.js | Updated to handle Smart-on-FHIR client configuration and token parsing |
app/routes.js | Added new route and authentication guard for Smart-on-FHIR flow |
test/ | Comprehensive test coverage for new Smart-on-FHIR functionality |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
const state = getState(); | ||
const isSmartOnFhir = !!state?.blip?.smartOnFhirData; | ||
// Only include isSmartOnFhir when it's true to avoid cluttering analytics | ||
return isSmartOnFhir ? { isSmartOnFhir: true } : undefined; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The function returns undefined
when not in Smart-on-FHIR mode, but the calling code uses spread operators that could handle empty objects better. Consider returning an empty object {}
instead of undefined
to simplify the calling code and reduce conditional logic.
return isSmartOnFhir ? { isSmartOnFhir: true } : undefined; | |
return isSmartOnFhir ? { isSmartOnFhir: true } : {}; |
Copilot uses AI. Check for mistakes.
let attempts = 0; | ||
const maxAttempts = 50; // 5 seconds max (50 * 100ms) | ||
let timeoutId; | ||
|
||
const checkZendesk = () => { | ||
if (windowObj.zE) { | ||
windowObj.zE('webWidget', 'hide'); | ||
return; | ||
} | ||
|
||
attempts++; | ||
if (attempts < maxAttempts) { | ||
timeoutId = setTimeout(checkZendesk, 100); | ||
} | ||
}; | ||
|
||
checkZendesk(); | ||
|
||
return () => { | ||
if (timeoutId) clearTimeout(timeoutId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The Zendesk hiding logic uses a polling approach with hardcoded values. Consider using a more robust approach like a MutationObserver to detect when Zendesk is loaded, or make the polling parameters configurable constants.
let attempts = 0; | |
const maxAttempts = 50; // 5 seconds max (50 * 100ms) | |
let timeoutId; | |
const checkZendesk = () => { | |
if (windowObj.zE) { | |
windowObj.zE('webWidget', 'hide'); | |
return; | |
} | |
attempts++; | |
if (attempts < maxAttempts) { | |
timeoutId = setTimeout(checkZendesk, 100); | |
} | |
}; | |
checkZendesk(); | |
return () => { | |
if (timeoutId) clearTimeout(timeoutId); | |
let observer; | |
let zendeskHidden = false; | |
const hideZendesk = () => { | |
if (windowObj.zE && !zendeskHidden) { | |
windowObj.zE('webWidget', 'hide'); | |
zendeskHidden = true; | |
} | |
}; | |
// Try to hide immediately in case zE is already available | |
hideZendesk(); | |
// Observe DOM mutations to detect when Zendesk widget is loaded | |
observer = new MutationObserver(() => { | |
hideZendesk(); | |
}); | |
observer.observe(document.body, { childList: true, subtree: true }); | |
return () => { | |
if (observer) observer.disconnect(); |
Copilot uses AI. Check for mistakes.
getClinics(api, loggedInUserId, (err, clinics) => { | ||
if (err) { | ||
handleError(ErrorMessages.ERR_SMARTONFHIR_CLINICIAN_NO_CLINICS, 'Direct Connect Clinics Fetch Failure'); | ||
return; | ||
} | ||
if (!clinics || clinics.length === 0) { | ||
handleError(ErrorMessages.ERR_SMARTONFHIR_CLINICIAN_NO_CLINICS, 'Direct Connect Clinician No Clinics'); | ||
return; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] Both error conditions use the same error message ERR_SMARTONFHIR_CLINICIAN_NO_CLINICS
but have different underlying causes (fetch failure vs. no clinics available). Consider using distinct error messages to help with debugging and user experience.
Copilot uses AI. Check for mistakes.
if (iss && launch && !correlationId) { | ||
correlationId = crypto.randomUUID(); | ||
|
||
sessionStorage.setItem('smart_correlation_id', correlationId); | ||
sessionStorage.setItem('smart_iss', iss); | ||
sessionStorage.setItem('smart_launch', launch); | ||
|
||
this.props.setSmartCorrelationId(correlationId); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code uses crypto.randomUUID()
without checking browser support. This API is not available in all browsers and could cause runtime errors. Consider adding a fallback or polyfill, or check for API availability before use.
Copilot uses AI. Check for mistakes.
export const updateKeycloakConfig = (info, store) => { | ||
if (!(isEmpty(info) || isEqual(_keycloakConfig, info))) { | ||
const urlParams = new URLSearchParams(window.location.search); | ||
const iss = sessionStorage.getItem('smart_iss') || urlParams.get('iss'); | ||
const launch = sessionStorage.getItem('smart_launch') || urlParams.get('launch'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The function accesses both sessionStorage
and window.location
directly, making it difficult to test and potentially causing issues in non-browser environments. Consider passing these as parameters or using dependency injection for better testability.
Copilot uses AI. Check for mistakes.
WEB-3071 initial work for smart-on-fhir launch endpoint