@tellescope/sdk 1.256.16 → 2.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +5 -0
- package/env.example +39 -0
- package/lib/cjs/enduser.d.ts +1 -0
- package/lib/cjs/enduser.d.ts.map +1 -1
- package/lib/cjs/sdk.d.ts +6 -1
- package/lib/cjs/sdk.d.ts.map +1 -1
- package/lib/cjs/sdk.js +2 -0
- package/lib/cjs/sdk.js.map +1 -1
- package/lib/cjs/session.d.ts +1 -0
- package/lib/cjs/session.d.ts.map +1 -1
- package/lib/cjs/tests/api_tests/active_patient_count.test.js +1 -1
- package/lib/cjs/tests/api_tests/active_patient_count.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/appointment_rescheduled_trigger.test.js +1 -1
- package/lib/cjs/tests/api_tests/appointment_rescheduled_trigger.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/calendar_event_limits.test.js +5 -5
- package/lib/cjs/tests/api_tests/calendar_event_limits.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/calendar_event_references_update.test.js +1 -1
- package/lib/cjs/tests/api_tests/calendar_event_references_update.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/enduser_cross_access_isolation.test.js +1 -1
- package/lib/cjs/tests/api_tests/enduser_cross_access_isolation.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/enduser_fields_sync.test.d.ts +21 -0
- package/lib/cjs/tests/api_tests/enduser_fields_sync.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/enduser_fields_sync.test.js +215 -0
- package/lib/cjs/tests/api_tests/enduser_fields_sync.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/enduser_login_rate_limits.test.js +6 -6
- package/lib/cjs/tests/api_tests/enduser_login_rate_limits.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/enduser_session_invalidation.test.js +3 -3
- package/lib/cjs/tests/api_tests/enduser_session_invalidation.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/load_threads_care_team.test.d.ts +32 -0
- package/lib/cjs/tests/api_tests/load_threads_care_team.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/load_threads_care_team.test.js +257 -0
- package/lib/cjs/tests/api_tests/load_threads_care_team.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/managed_content_enduser_access.test.js +1 -1
- package/lib/cjs/tests/api_tests/managed_content_enduser_access.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/managed_content_file_access.test.js +1 -1
- package/lib/cjs/tests/api_tests/managed_content_file_access.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/mdi_case_offerings.test.d.ts.map +1 -1
- package/lib/cjs/tests/api_tests/mdi_case_offerings.test.js +139 -9
- package/lib/cjs/tests/api_tests/mdi_case_offerings.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/mdi_webhooks.test.d.ts.map +1 -1
- package/lib/cjs/tests/api_tests/mdi_webhooks.test.js +21 -8
- package/lib/cjs/tests/api_tests/mdi_webhooks.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/nutrition_tracking.test.js +1 -1
- package/lib/cjs/tests/api_tests/nutrition_tracking.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/openloop_webhooks.test.js +1 -1
- package/lib/cjs/tests/api_tests/openloop_webhooks.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/programs.test.d.ts +5 -0
- package/lib/cjs/tests/api_tests/programs.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/programs.test.js +370 -0
- package/lib/cjs/tests/api_tests/programs.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/programs_trigger_filters.test.d.ts +5 -0
- package/lib/cjs/tests/api_tests/programs_trigger_filters.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/programs_trigger_filters.test.js +295 -0
- package/lib/cjs/tests/api_tests/programs_trigger_filters.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/security/add-to-journey-foreign-enduserid.test.d.ts.map +1 -1
- package/lib/cjs/tests/api_tests/security/add-to-journey-foreign-enduserid.test.js +55 -47
- package/lib/cjs/tests/api_tests/security/add-to-journey-foreign-enduserid.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/security/allowed-paths-confinement.test.js +1 -1
- package/lib/cjs/tests/api_tests/security/allowed-paths-confinement.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/security/appointment-reschedule-ownership.test.d.ts +42 -0
- package/lib/cjs/tests/api_tests/security/appointment-reschedule-ownership.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/security/appointment-reschedule-ownership.test.js +484 -0
- package/lib/cjs/tests/api_tests/security/appointment-reschedule-ownership.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/security/booking-info-idor-token-mint.test.js +1 -1
- package/lib/cjs/tests/api_tests/security/booking-info-idor-token-mint.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/security/enduser-ai-summary-trust-gate.test.js +1 -1
- package/lib/cjs/tests/api_tests/security/enduser-ai-summary-trust-gate.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/security/enduser-cross-create.test.js +1 -1
- package/lib/cjs/tests/api_tests/security/enduser-cross-create.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/security/enduser-write-restrictions.test.js +1 -1
- package/lib/cjs/tests/api_tests/security/enduser-write-restrictions.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/security/invite-user-enumeration.test.js +2 -2
- package/lib/cjs/tests/api_tests/security/invite-user-enumeration.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.d.ts +32 -0
- package/lib/cjs/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.js +297 -0
- package/lib/cjs/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/security/sanitize-user-html-with-iframes.test.d.ts.map +1 -1
- package/lib/cjs/tests/api_tests/security/sanitize-user-html-with-iframes.test.js +4 -1
- package/lib/cjs/tests/api_tests/security/sanitize-user-html-with-iframes.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/templated_message_date_fields.test.d.ts +3 -0
- package/lib/cjs/tests/api_tests/templated_message_date_fields.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/templated_message_date_fields.test.js +202 -0
- package/lib/cjs/tests/api_tests/templated_message_date_fields.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/templated_message_link_in_href.test.d.ts +3 -0
- package/lib/cjs/tests/api_tests/templated_message_link_in_href.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/templated_message_link_in_href.test.js +233 -0
- package/lib/cjs/tests/api_tests/templated_message_link_in_href.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/templated_message_links.test.d.ts +3 -0
- package/lib/cjs/tests/api_tests/templated_message_links.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/templated_message_links.test.js +235 -0
- package/lib/cjs/tests/api_tests/templated_message_links.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/time_tracks.test.js +1 -1
- package/lib/cjs/tests/api_tests/time_tracks.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/trigger_contains_matching.test.d.ts +5 -0
- package/lib/cjs/tests/api_tests/trigger_contains_matching.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/trigger_contains_matching.test.js +395 -0
- package/lib/cjs/tests/api_tests/trigger_contains_matching.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/user_logs_restore_deleted.test.d.ts +5 -0
- package/lib/cjs/tests/api_tests/user_logs_restore_deleted.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/user_logs_restore_deleted.test.js +471 -0
- package/lib/cjs/tests/api_tests/user_logs_restore_deleted.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/user_portal_settings.test.js +1 -1
- package/lib/cjs/tests/api_tests/user_portal_settings.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/validation_behavior_locks.test.d.ts +2 -0
- package/lib/cjs/tests/api_tests/validation_behavior_locks.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/validation_behavior_locks.test.js +233 -0
- package/lib/cjs/tests/api_tests/validation_behavior_locks.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/vital_review_trigger_action.test.d.ts +5 -0
- package/lib/cjs/tests/api_tests/vital_review_trigger_action.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/vital_review_trigger_action.test.js +569 -0
- package/lib/cjs/tests/api_tests/vital_review_trigger_action.test.js.map +1 -0
- package/lib/cjs/tests/public_endpoint_tests.js +3 -2
- package/lib/cjs/tests/public_endpoint_tests.js.map +1 -1
- package/lib/cjs/tests/setup.d.ts +16 -0
- package/lib/cjs/tests/setup.d.ts.map +1 -1
- package/lib/cjs/tests/setup.js +55 -8
- package/lib/cjs/tests/setup.js.map +1 -1
- package/lib/cjs/tests/setup_isolated.d.ts +9 -0
- package/lib/cjs/tests/setup_isolated.d.ts.map +1 -1
- package/lib/cjs/tests/setup_isolated.js +23 -2
- package/lib/cjs/tests/setup_isolated.js.map +1 -1
- package/lib/cjs/tests/socket_tests.d.ts.map +1 -1
- package/lib/cjs/tests/socket_tests.js +3 -2
- package/lib/cjs/tests/socket_tests.js.map +1 -1
- package/lib/cjs/tests/tests.d.ts.map +1 -1
- package/lib/cjs/tests/tests.js +244 -216
- package/lib/cjs/tests/tests.js.map +1 -1
- package/lib/cjs/tests/unit_tests/trigger_title_partials.test.d.ts +3 -0
- package/lib/cjs/tests/unit_tests/trigger_title_partials.test.d.ts.map +1 -0
- package/lib/cjs/tests/unit_tests/trigger_title_partials.test.js +118 -0
- package/lib/cjs/tests/unit_tests/trigger_title_partials.test.js.map +1 -0
- package/lib/cjs/tests/webhooks_tests.js +3 -2
- package/lib/cjs/tests/webhooks_tests.js.map +1 -1
- package/lib/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/lib/esm/enduser.d.ts +2 -0
- package/lib/esm/enduser.d.ts.map +1 -1
- package/lib/esm/sdk.d.ts +9 -3
- package/lib/esm/sdk.d.ts.map +1 -1
- package/lib/esm/sdk.js +2 -0
- package/lib/esm/sdk.js.map +1 -1
- package/lib/esm/session.d.ts +2 -0
- package/lib/esm/session.d.ts.map +1 -1
- package/lib/esm/tests/api_tests/active_patient_count.test.js +1 -1
- package/lib/esm/tests/api_tests/active_patient_count.test.js.map +1 -1
- package/lib/esm/tests/api_tests/appointment_rescheduled_trigger.test.js +1 -1
- package/lib/esm/tests/api_tests/appointment_rescheduled_trigger.test.js.map +1 -1
- package/lib/esm/tests/api_tests/calendar_event_limits.test.js +5 -5
- package/lib/esm/tests/api_tests/calendar_event_limits.test.js.map +1 -1
- package/lib/esm/tests/api_tests/calendar_event_references_update.test.js +1 -1
- package/lib/esm/tests/api_tests/calendar_event_references_update.test.js.map +1 -1
- package/lib/esm/tests/api_tests/enduser_cross_access_isolation.test.js +1 -1
- package/lib/esm/tests/api_tests/enduser_cross_access_isolation.test.js.map +1 -1
- package/lib/esm/tests/api_tests/enduser_fields_sync.test.d.ts +21 -0
- package/lib/esm/tests/api_tests/enduser_fields_sync.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/enduser_fields_sync.test.js +210 -0
- package/lib/esm/tests/api_tests/enduser_fields_sync.test.js.map +1 -0
- package/lib/esm/tests/api_tests/enduser_login_rate_limits.test.js +7 -7
- package/lib/esm/tests/api_tests/enduser_login_rate_limits.test.js.map +1 -1
- package/lib/esm/tests/api_tests/enduser_session_invalidation.test.js +4 -4
- package/lib/esm/tests/api_tests/enduser_session_invalidation.test.js.map +1 -1
- package/lib/esm/tests/api_tests/load_threads_care_team.test.d.ts +32 -0
- package/lib/esm/tests/api_tests/load_threads_care_team.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/load_threads_care_team.test.js +252 -0
- package/lib/esm/tests/api_tests/load_threads_care_team.test.js.map +1 -0
- package/lib/esm/tests/api_tests/managed_content_enduser_access.test.js +1 -1
- package/lib/esm/tests/api_tests/managed_content_enduser_access.test.js.map +1 -1
- package/lib/esm/tests/api_tests/managed_content_file_access.test.js +1 -1
- package/lib/esm/tests/api_tests/managed_content_file_access.test.js.map +1 -1
- package/lib/esm/tests/api_tests/mdi_case_offerings.test.d.ts.map +1 -1
- package/lib/esm/tests/api_tests/mdi_case_offerings.test.js +139 -9
- package/lib/esm/tests/api_tests/mdi_case_offerings.test.js.map +1 -1
- package/lib/esm/tests/api_tests/mdi_webhooks.test.d.ts.map +1 -1
- package/lib/esm/tests/api_tests/mdi_webhooks.test.js +21 -8
- package/lib/esm/tests/api_tests/mdi_webhooks.test.js.map +1 -1
- package/lib/esm/tests/api_tests/nutrition_tracking.test.js +1 -1
- package/lib/esm/tests/api_tests/nutrition_tracking.test.js.map +1 -1
- package/lib/esm/tests/api_tests/openloop_webhooks.test.js +1 -1
- package/lib/esm/tests/api_tests/openloop_webhooks.test.js.map +1 -1
- package/lib/esm/tests/api_tests/programs.test.d.ts +5 -0
- package/lib/esm/tests/api_tests/programs.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/programs.test.js +365 -0
- package/lib/esm/tests/api_tests/programs.test.js.map +1 -0
- package/lib/esm/tests/api_tests/programs_trigger_filters.test.d.ts +5 -0
- package/lib/esm/tests/api_tests/programs_trigger_filters.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/programs_trigger_filters.test.js +290 -0
- package/lib/esm/tests/api_tests/programs_trigger_filters.test.js.map +1 -0
- package/lib/esm/tests/api_tests/security/add-to-journey-foreign-enduserid.test.d.ts.map +1 -1
- package/lib/esm/tests/api_tests/security/add-to-journey-foreign-enduserid.test.js +55 -47
- package/lib/esm/tests/api_tests/security/add-to-journey-foreign-enduserid.test.js.map +1 -1
- package/lib/esm/tests/api_tests/security/allowed-paths-confinement.test.js +1 -1
- package/lib/esm/tests/api_tests/security/allowed-paths-confinement.test.js.map +1 -1
- package/lib/esm/tests/api_tests/security/appointment-reschedule-ownership.test.d.ts +42 -0
- package/lib/esm/tests/api_tests/security/appointment-reschedule-ownership.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/security/appointment-reschedule-ownership.test.js +479 -0
- package/lib/esm/tests/api_tests/security/appointment-reschedule-ownership.test.js.map +1 -0
- package/lib/esm/tests/api_tests/security/booking-info-idor-token-mint.test.js +1 -1
- package/lib/esm/tests/api_tests/security/booking-info-idor-token-mint.test.js.map +1 -1
- package/lib/esm/tests/api_tests/security/enduser-ai-summary-trust-gate.test.js +1 -1
- package/lib/esm/tests/api_tests/security/enduser-ai-summary-trust-gate.test.js.map +1 -1
- package/lib/esm/tests/api_tests/security/enduser-cross-create.test.js +1 -1
- package/lib/esm/tests/api_tests/security/enduser-cross-create.test.js.map +1 -1
- package/lib/esm/tests/api_tests/security/enduser-write-restrictions.test.js +1 -1
- package/lib/esm/tests/api_tests/security/enduser-write-restrictions.test.js.map +1 -1
- package/lib/esm/tests/api_tests/security/invite-user-enumeration.test.js +3 -3
- package/lib/esm/tests/api_tests/security/invite-user-enumeration.test.js.map +1 -1
- package/lib/esm/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.d.ts +32 -0
- package/lib/esm/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.js +292 -0
- package/lib/esm/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.js.map +1 -0
- package/lib/esm/tests/api_tests/security/sanitize-user-html-with-iframes.test.d.ts.map +1 -1
- package/lib/esm/tests/api_tests/security/sanitize-user-html-with-iframes.test.js +4 -1
- package/lib/esm/tests/api_tests/security/sanitize-user-html-with-iframes.test.js.map +1 -1
- package/lib/esm/tests/api_tests/templated_message_date_fields.test.d.ts +3 -0
- package/lib/esm/tests/api_tests/templated_message_date_fields.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/templated_message_date_fields.test.js +198 -0
- package/lib/esm/tests/api_tests/templated_message_date_fields.test.js.map +1 -0
- package/lib/esm/tests/api_tests/templated_message_link_in_href.test.d.ts +3 -0
- package/lib/esm/tests/api_tests/templated_message_link_in_href.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/templated_message_link_in_href.test.js +229 -0
- package/lib/esm/tests/api_tests/templated_message_link_in_href.test.js.map +1 -0
- package/lib/esm/tests/api_tests/templated_message_links.test.d.ts +3 -0
- package/lib/esm/tests/api_tests/templated_message_links.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/templated_message_links.test.js +231 -0
- package/lib/esm/tests/api_tests/templated_message_links.test.js.map +1 -0
- package/lib/esm/tests/api_tests/time_tracks.test.js +1 -1
- package/lib/esm/tests/api_tests/time_tracks.test.js.map +1 -1
- package/lib/esm/tests/api_tests/trigger_contains_matching.test.d.ts +5 -0
- package/lib/esm/tests/api_tests/trigger_contains_matching.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/trigger_contains_matching.test.js +390 -0
- package/lib/esm/tests/api_tests/trigger_contains_matching.test.js.map +1 -0
- package/lib/esm/tests/api_tests/user_logs_restore_deleted.test.d.ts +5 -0
- package/lib/esm/tests/api_tests/user_logs_restore_deleted.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/user_logs_restore_deleted.test.js +466 -0
- package/lib/esm/tests/api_tests/user_logs_restore_deleted.test.js.map +1 -0
- package/lib/esm/tests/api_tests/user_portal_settings.test.js +1 -1
- package/lib/esm/tests/api_tests/user_portal_settings.test.js.map +1 -1
- package/lib/esm/tests/api_tests/validation_behavior_locks.test.d.ts +2 -0
- package/lib/esm/tests/api_tests/validation_behavior_locks.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/validation_behavior_locks.test.js +229 -0
- package/lib/esm/tests/api_tests/validation_behavior_locks.test.js.map +1 -0
- package/lib/esm/tests/api_tests/vital_review_trigger_action.test.d.ts +5 -0
- package/lib/esm/tests/api_tests/vital_review_trigger_action.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/vital_review_trigger_action.test.js +564 -0
- package/lib/esm/tests/api_tests/vital_review_trigger_action.test.js.map +1 -0
- package/lib/esm/tests/public_endpoint_tests.js +3 -2
- package/lib/esm/tests/public_endpoint_tests.js.map +1 -1
- package/lib/esm/tests/setup.d.ts +16 -0
- package/lib/esm/tests/setup.d.ts.map +1 -1
- package/lib/esm/tests/setup.js +53 -7
- package/lib/esm/tests/setup.js.map +1 -1
- package/lib/esm/tests/setup_isolated.d.ts +9 -0
- package/lib/esm/tests/setup_isolated.d.ts.map +1 -1
- package/lib/esm/tests/setup_isolated.js +21 -1
- package/lib/esm/tests/setup_isolated.js.map +1 -1
- package/lib/esm/tests/socket_tests.d.ts.map +1 -1
- package/lib/esm/tests/socket_tests.js +3 -2
- package/lib/esm/tests/socket_tests.js.map +1 -1
- package/lib/esm/tests/tests.d.ts.map +1 -1
- package/lib/esm/tests/tests.js +244 -216
- package/lib/esm/tests/tests.js.map +1 -1
- package/lib/esm/tests/unit_tests/trigger_title_partials.test.d.ts +3 -0
- package/lib/esm/tests/unit_tests/trigger_title_partials.test.d.ts.map +1 -0
- package/lib/esm/tests/unit_tests/trigger_title_partials.test.js +115 -0
- package/lib/esm/tests/unit_tests/trigger_title_partials.test.js.map +1 -0
- package/lib/esm/tests/webhooks_tests.js +3 -2
- package/lib/esm/tests/webhooks_tests.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +22 -19
- package/src/sdk.ts +11 -1
- package/src/tests/api_tests/active_patient_count.test.ts +1 -1
- package/src/tests/api_tests/appointment_rescheduled_trigger.test.ts +1 -1
- package/src/tests/api_tests/calendar_event_references_update.test.ts +1 -1
- package/src/tests/api_tests/enduser_cross_access_isolation.test.ts +1 -1
- package/src/tests/api_tests/enduser_fields_sync.test.ts +150 -0
- package/src/tests/api_tests/enduser_login_rate_limits.test.ts +7 -7
- package/src/tests/api_tests/enduser_session_invalidation.test.ts +4 -4
- package/src/tests/api_tests/load_threads_care_team.test.ts +210 -0
- package/src/tests/api_tests/managed_content_enduser_access.test.ts +1 -1
- package/src/tests/api_tests/managed_content_file_access.test.ts +1 -1
- package/src/tests/api_tests/mdi_case_offerings.test.ts +138 -0
- package/src/tests/api_tests/mdi_webhooks.test.ts +9 -0
- package/src/tests/api_tests/nutrition_tracking.test.ts +1 -1
- package/src/tests/api_tests/openloop_webhooks.test.ts +1 -1
- package/src/tests/api_tests/security/add-to-journey-foreign-enduserid.test.ts +33 -26
- package/src/tests/api_tests/security/allowed-paths-confinement.test.ts +1 -1
- package/src/tests/api_tests/security/appointment-reschedule-ownership.test.ts +432 -0
- package/src/tests/api_tests/security/booking-info-idor-token-mint.test.ts +1 -1
- package/src/tests/api_tests/security/enduser-ai-summary-trust-gate.test.ts +1 -1
- package/src/tests/api_tests/security/enduser-cross-create.test.ts +1 -1
- package/src/tests/api_tests/security/enduser-write-restrictions.test.ts +1 -1
- package/src/tests/api_tests/security/invite-user-enumeration.test.ts +3 -3
- package/src/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.ts +197 -0
- package/src/tests/api_tests/security/sanitize-user-html-with-iframes.test.ts +4 -1
- package/src/tests/api_tests/templated_message_date_fields.test.ts +146 -0
- package/src/tests/api_tests/templated_message_links.test.ts +192 -0
- package/src/tests/api_tests/time_tracks.test.ts +1 -1
- package/src/tests/api_tests/trigger_contains_matching.test.ts +345 -0
- package/src/tests/api_tests/user_logs_restore_deleted.test.ts +415 -0
- package/src/tests/api_tests/user_portal_settings.test.ts +1 -1
- package/src/tests/api_tests/validation_behavior_locks.test.ts +204 -0
- package/src/tests/public_endpoint_tests.ts +3 -2
- package/src/tests/setup.ts +43 -7
- package/src/tests/setup_isolated.ts +10 -0
- package/src/tests/socket_tests.ts +3 -2
- package/src/tests/tests.ts +22 -2
- package/src/tests/unit_tests/trigger_title_partials.test.ts +174 -0
- package/src/tests/webhooks_tests.ts +4 -2
- package/test_generated.pdf +0 -0
- package/tsconfig.cjs.json +17 -0
package/package.json
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tellescope/sdk",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0-beta.1",
|
|
4
4
|
"description": "Code for interacting with the Tellescope API",
|
|
5
5
|
"main": "./lib/cjs/sdk.js",
|
|
6
6
|
"module": "./lib/esm/sdk.js",
|
|
7
7
|
"types": "./lib/esm/sdk.d.ts",
|
|
8
8
|
"scripts": {
|
|
9
|
-
"test": "npm run-script build:cjs && npm run-script retest",
|
|
9
|
+
"test": "npm run-script build:cjs && node ../../../scripts/dev/check-build-fresh.mjs && npm run-script retest",
|
|
10
10
|
"retest": "npm run-script retest-api && npm run-script retest-public-api && npm run-script retest-webhooks",
|
|
11
|
-
"test-api": "npm run-script build:cjs && npm run-script retest-api",
|
|
11
|
+
"test-api": "npm run-script build:cjs && node ../../../scripts/dev/check-build-fresh.mjs && npm run-script retest-api",
|
|
12
12
|
"retest-api": "node -r dotenv/config lib/cjs/tests/tests.js",
|
|
13
|
-
"test-public-api": "npm run-script build:cjs && npm run-script retest-public-api",
|
|
13
|
+
"test-public-api": "npm run-script build:cjs && node ../../../scripts/dev/check-build-fresh.mjs && npm run-script retest-public-api",
|
|
14
14
|
"retest-public-api": "node -r dotenv/config lib/cjs/tests/public_endpoint_tests.js",
|
|
15
|
-
"test-sockets": "npm run-script build:cjs && npm run-script retest-sockets",
|
|
15
|
+
"test-sockets": "npm run-script build:cjs && node ../../../scripts/dev/check-build-fresh.mjs && npm run-script retest-sockets",
|
|
16
16
|
"retest-sockets": "node -r dotenv/config lib/cjs/tests/socket_tests.js",
|
|
17
|
-
"test-webhooks": "npm run-script build:cjs && npm run-script retest-webhooks",
|
|
17
|
+
"test-webhooks": "npm run-script build:cjs && node ../../../scripts/dev/check-build-fresh.mjs && npm run-script retest-webhooks",
|
|
18
18
|
"retest-webhooks": "node -r dotenv/config lib/cjs/tests/webhooks_tests.js",
|
|
19
19
|
"build": "npm run-script build:esm && npm run-script build:cjs",
|
|
20
20
|
"build:esm": "tsc",
|
|
21
|
-
"build:cjs": "tsc
|
|
22
|
-
"watch:cjs": "tsc
|
|
21
|
+
"build:cjs": "tsc -b tsconfig.cjs.json",
|
|
22
|
+
"watch:cjs": "tsc -b -w --preserveWatchOutput tsconfig.cjs.json"
|
|
23
23
|
},
|
|
24
24
|
"repository": {
|
|
25
25
|
"type": "git",
|
|
@@ -31,18 +31,21 @@
|
|
|
31
31
|
"url": "https://github.com/tellescope-os/tellescope/issues"
|
|
32
32
|
},
|
|
33
33
|
"homepage": "https://github.com/tellescope-os/tellescope#readme",
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=22.12.0"
|
|
36
|
+
},
|
|
34
37
|
"dependencies": {
|
|
35
|
-
"@tellescope/constants": "
|
|
36
|
-
"@tellescope/schema": "
|
|
37
|
-
"@tellescope/testing": "
|
|
38
|
-
"@tellescope/types-client": "
|
|
39
|
-
"@tellescope/types-models": "
|
|
40
|
-
"@tellescope/types-utilities": "
|
|
41
|
-
"@tellescope/utilities": "
|
|
42
|
-
"@tellescope/validation": "
|
|
43
|
-
"axios": "0.
|
|
38
|
+
"@tellescope/constants": "2.0.0-beta.1",
|
|
39
|
+
"@tellescope/schema": "2.0.0-beta.1",
|
|
40
|
+
"@tellescope/testing": "2.0.0-beta.1",
|
|
41
|
+
"@tellescope/types-client": "2.0.0-beta.1",
|
|
42
|
+
"@tellescope/types-models": "2.0.0-beta.1",
|
|
43
|
+
"@tellescope/types-utilities": "2.0.0-beta.1",
|
|
44
|
+
"@tellescope/utilities": "2.0.0-beta.1",
|
|
45
|
+
"@tellescope/validation": "2.0.0-beta.1",
|
|
46
|
+
"axios": "0.33.0",
|
|
44
47
|
"dotenv": "14.3.2",
|
|
45
|
-
"form-data": "4.0.
|
|
48
|
+
"form-data": "4.0.6",
|
|
46
49
|
"source-map-support": "0.5.21"
|
|
47
50
|
},
|
|
48
51
|
"devDependencies": {
|
|
@@ -55,5 +58,5 @@
|
|
|
55
58
|
"publishConfig": {
|
|
56
59
|
"access": "public"
|
|
57
60
|
},
|
|
58
|
-
"gitHead": "
|
|
61
|
+
"gitHead": "20732ce4e382a7d81dde795ce9c7194c2aca1ce6"
|
|
59
62
|
}
|
package/src/sdk.ts
CHANGED
|
@@ -234,6 +234,11 @@ const loadDefaultQueries = (s: Session): { [K in keyof ClientModelForName] : API
|
|
|
234
234
|
})
|
|
235
235
|
|
|
236
236
|
type Queries = { [K in keyof ClientModelForName]: APIQuery<K> } & {
|
|
237
|
+
user_logs: {
|
|
238
|
+
restore_deleted: (args: extractFields<CustomActions['user_logs']['restore_deleted']['parameters']>) => (
|
|
239
|
+
Promise<extractFields<CustomActions['user_logs']['restore_deleted']['returns']>>
|
|
240
|
+
),
|
|
241
|
+
},
|
|
237
242
|
inbox_threads: {
|
|
238
243
|
build_threads: (args: extractFields<CustomActions['inbox_threads']['build_threads']['parameters']>) => (
|
|
239
244
|
Promise<extractFields<CustomActions['inbox_threads']['build_threads']['returns']>>
|
|
@@ -313,6 +318,9 @@ type Queries = { [K in keyof ClientModelForName]: APIQuery<K> } & {
|
|
|
313
318
|
get_form_statistics: (args: extractFields<CustomActions['forms']['get_form_statistics']['parameters']>) => (
|
|
314
319
|
Promise<extractFields<CustomActions['forms']['get_form_statistics']['returns']>>
|
|
315
320
|
),
|
|
321
|
+
public_form_details: (args: extractFields<PublicActions['forms']['public_form_details']['parameters']>) => (
|
|
322
|
+
Promise<extractFields<PublicActions['forms']['public_form_details']['returns']>>
|
|
323
|
+
),
|
|
316
324
|
},
|
|
317
325
|
phone_trees: {
|
|
318
326
|
start_outbound_call: (args: extractFields<CustomActions['phone_trees']['start_outbound_call']['parameters']>) => (
|
|
@@ -993,6 +1001,7 @@ export class Session extends SessionManager {
|
|
|
993
1001
|
queries.api_keys.get_organization_api_keys = () => this._GET(`/v1${schema.api_keys.customActions.get_organization_api_keys.path}`)
|
|
994
1002
|
queries.api_keys.delete_organization_api_key = ({ id }) => this._DELETE(`/v1/organization-api-key/${id}`)
|
|
995
1003
|
|
|
1004
|
+
queries.user_logs.restore_deleted = a => this._POST(`/v1${schema.user_logs.customActions.restore_deleted.path}`, a)
|
|
996
1005
|
queries.enduser_orders.get_available_tests = a => this._GET(`/v1${schema.enduser_orders.customActions.get_available_tests.path}`, a)
|
|
997
1006
|
queries.enduser_orders.create_lab_order = a => this._POST(`/v1${schema.enduser_orders.customActions.create_lab_order.path}`, a)
|
|
998
1007
|
queries.enduser_orders.cancel_order = a => this._POST(`/v1${schema.enduser_orders.customActions.cancel_order.path}`, a)
|
|
@@ -1052,6 +1061,7 @@ export class Session extends SessionManager {
|
|
|
1052
1061
|
queries.organizations.invite_user = a => this._POST(`/v1/${schema.users.customActions.invite_user.path}`, a)
|
|
1053
1062
|
|
|
1054
1063
|
queries.forms.get_form_statistics = args => this._GET(`/v1${schema.forms.customActions.get_form_statistics.path}`, args)
|
|
1064
|
+
queries.forms.public_form_details = args => this._GET(`/v1${schema.forms.publicActions.public_form_details.path}`, args)
|
|
1055
1065
|
|
|
1056
1066
|
queries.form_fields.load_choices_from_database = args => this._GET(`/v1${schema.form_fields.customActions.load_choices_from_database.path}`, args)
|
|
1057
1067
|
queries.form_fields.booking_info = args => this._GET(`/v1${schema.form_fields.customActions.booking_info.path}`, args)
|
|
@@ -1350,4 +1360,4 @@ export class Session extends SessionManager {
|
|
|
1350
1360
|
sync = (a: { from: Date }) => this.GET<typeof a, { results: Pick<DataSyncRecord, 'modelName' | 'recordId' | 'data'>[], to: string }>('/v1/data-sync', a)
|
|
1351
1361
|
}
|
|
1352
1362
|
|
|
1353
|
-
export { SessionOptions }
|
|
1363
|
+
export type { SessionOptions }
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
import { authenticate_enduser_via_token, setup_tests } from "../setup"
|
|
11
11
|
|
|
12
12
|
const host = process.env.API_URL || 'http://localhost:8080' as const
|
|
13
|
-
const businessId = '60398b1131a295e64f084ff6'
|
|
13
|
+
const businessId = process.env.TEST_BUSINESS_ID || '60398b1131a295e64f084ff6'
|
|
14
14
|
|
|
15
15
|
// Second-tenant session, used to prove the active patient count never crosses tenants.
|
|
16
16
|
// Same key used by the org-isolation tests in tests.ts.
|
|
@@ -3,7 +3,7 @@ import { log_header, wait, async_test } from "@tellescope/testing"
|
|
|
3
3
|
import { Enduser } from "@tellescope/types-client"
|
|
4
4
|
import { setup_tests } from "../setup"
|
|
5
5
|
|
|
6
|
-
const host = process.env.TEST_HOST || "http://localhost:8080"
|
|
6
|
+
const host = process.env.API_URL || process.env.TEST_HOST || "http://localhost:8080"
|
|
7
7
|
|
|
8
8
|
export const appointment_rescheduled_trigger_tests = async ({ sdk, sdkNonAdmin } : { sdk: Session, sdkNonAdmin: Session }) => {
|
|
9
9
|
log_header("Automation Trigger Tests (Appointment Rescheduled)")
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
import { setup_tests, authenticate_enduser_via_token } from "../setup"
|
|
9
9
|
|
|
10
10
|
const host = process.env.API_URL || 'http://localhost:8080' as const
|
|
11
|
-
const businessId = '60398b1131a295e64f084ff6'
|
|
11
|
+
const businessId = process.env.TEST_BUSINESS_ID || '60398b1131a295e64f084ff6'
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* CU-86e2dvrn9: calendar_events.references switched from updatesDisabled to
|
|
@@ -12,7 +12,7 @@ import { ModelName } from "@tellescope/types-models"
|
|
|
12
12
|
import { authenticate_enduser_via_token, setup_tests } from "../setup"
|
|
13
13
|
|
|
14
14
|
const host = process.env.API_URL || 'http://localhost:8080' as const
|
|
15
|
-
const businessId = '60398b1131a295e64f084ff6'
|
|
15
|
+
const businessId = process.env.TEST_BUSINESS_ID || '60398b1131a295e64f084ff6'
|
|
16
16
|
|
|
17
17
|
type OwnerField = 'enduserId' | 'enduserIds' | 'attendees.id'
|
|
18
18
|
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
require('source-map-support').install();
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
assert_checks,
|
|
5
|
+
Check,
|
|
6
|
+
setup_isolated_tenant,
|
|
7
|
+
} from "../setup_isolated"
|
|
8
|
+
|
|
9
|
+
const host = process.env.API_URL || 'http://localhost:8080' as const
|
|
10
|
+
|
|
11
|
+
const RAND = () => Math.random().toString(36).slice(2, 10)
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Schema round-trip for `Form.syncEnduserFieldsToEHR` and `Form.enduserFieldsSync`, the two fields
|
|
15
|
+
* behind "Sync Patient Fields to Healthie".
|
|
16
|
+
*
|
|
17
|
+
* The names are deliberately EHR-agnostic — each row's `externalId` is the question id in whichever
|
|
18
|
+
* EHR the form syncs to (`Form.externalId`) — even though only the Healthie push reads them today.
|
|
19
|
+
*
|
|
20
|
+
* What this file can and cannot cover: the actual `createFormAnswerGroup` round trip needs live
|
|
21
|
+
* Healthie credentials that no suite here has, and there is no Healthie mock or fixture layer in the
|
|
22
|
+
* repository, so the push itself is covered by the resolver's unit tests
|
|
23
|
+
* (api/modules/forms.test.ts) plus manual QA against Healthie staging. What IS covered here is
|
|
24
|
+
* everything the API owns: that the fields persist and read back unchanged, that validation rejects
|
|
25
|
+
* malformed rows, and — the one that guards a real decision in the form-editor modal — that an
|
|
26
|
+
* update with `replaceObjectFields` actually SHRINKS the list rather than merging into it, which is
|
|
27
|
+
* what makes deleting a mapping row persist.
|
|
28
|
+
*/
|
|
29
|
+
export const enduser_fields_sync_checks = async (): Promise<Check[]> => {
|
|
30
|
+
const checks: Check[] = []
|
|
31
|
+
const record = (label: string, pass: boolean, detail: string) => checks.push({ label, pass, detail })
|
|
32
|
+
|
|
33
|
+
/** records a check that the given write is rejected (any 4xx), not silently accepted */
|
|
34
|
+
const record_rejected = async (label: string, write: () => Promise<any>) => {
|
|
35
|
+
try {
|
|
36
|
+
const accepted = await write()
|
|
37
|
+
record(label, false, `accepted, expected a validation error — got ${JSON.stringify(accepted?.enduserFieldsSync)}`)
|
|
38
|
+
} catch (err: any) {
|
|
39
|
+
const message = err?.message || String(err)
|
|
40
|
+
record(label, true, `rejected: ${message.slice(0, 200)}`)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
let tenant: Awaited<ReturnType<typeof setup_isolated_tenant>> | undefined
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
tenant = await setup_isolated_tenant({ name: `FieldSync ${RAND()}`, users: [{ isAdmin: true }], apiKeys: 0 })
|
|
48
|
+
const sdk = tenant.sdk
|
|
49
|
+
|
|
50
|
+
const TWO_ROWS = [
|
|
51
|
+
{ field: 'dateOfBirth', externalId: '111111' },
|
|
52
|
+
{ field: 'Referring Provider', externalId: '222222' },
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
/* ===== 1. Create with both fields set, and read them back ===== */
|
|
56
|
+
const created = await sdk.api.forms.createOne({
|
|
57
|
+
title: `Field sync ${RAND()}`,
|
|
58
|
+
externalId: 'healthie-form-id',
|
|
59
|
+
syncEnduserFieldsToEHR: true,
|
|
60
|
+
enduserFieldsSync: TWO_ROWS,
|
|
61
|
+
})
|
|
62
|
+
const readBack = await sdk.api.forms.getOne(created.id)
|
|
63
|
+
|
|
64
|
+
record('1: create persists syncEnduserFieldsToEHR', readBack.syncEnduserFieldsToEHR === true,
|
|
65
|
+
`got ${JSON.stringify(readBack.syncEnduserFieldsToEHR)}`)
|
|
66
|
+
record('1: create persists enduserFieldsSync verbatim',
|
|
67
|
+
JSON.stringify(readBack.enduserFieldsSync) === JSON.stringify(TWO_ROWS),
|
|
68
|
+
`got ${JSON.stringify(readBack.enduserFieldsSync)} expected ${JSON.stringify(TWO_ROWS)}`)
|
|
69
|
+
|
|
70
|
+
/* ===== 2. replaceObjectFields must SHRINK the list, not merge into it =====
|
|
71
|
+
This is the guard for the form-editor modal's `replaceObjectFields` change. Without it,
|
|
72
|
+
deleting a mapping row in the UI would save and then silently come back on reload. */
|
|
73
|
+
const ONE_ROW = [{ field: 'insurance.memberId', externalId: '333333' }]
|
|
74
|
+
await sdk.api.forms.updateOne(created.id, { enduserFieldsSync: ONE_ROW }, { replaceObjectFields: true })
|
|
75
|
+
const shrunk = await sdk.api.forms.getOne(created.id)
|
|
76
|
+
|
|
77
|
+
record('2: replaceObjectFields shrinks a two-row list to one row',
|
|
78
|
+
JSON.stringify(shrunk.enduserFieldsSync) === JSON.stringify(ONE_ROW),
|
|
79
|
+
`got ${JSON.stringify(shrunk.enduserFieldsSync)} expected ${JSON.stringify(ONE_ROW)}`)
|
|
80
|
+
|
|
81
|
+
/* ===== 3. Malformed rows are rejected ===== */
|
|
82
|
+
await record_rejected('3: a row with a non-string field is rejected', () =>
|
|
83
|
+
sdk.api.forms.updateOne(created.id, { enduserFieldsSync: [{ field: 1, externalId: '1' }] } as any,
|
|
84
|
+
{ replaceObjectFields: true }))
|
|
85
|
+
|
|
86
|
+
await record_rejected('3: a row missing externalId is rejected', () =>
|
|
87
|
+
sdk.api.forms.updateOne(created.id, { enduserFieldsSync: [{ field: 'fname' }] } as any,
|
|
88
|
+
{ replaceObjectFields: true }))
|
|
89
|
+
|
|
90
|
+
await record_rejected('3: an externalId over 100 characters is rejected', () =>
|
|
91
|
+
sdk.api.forms.updateOne(created.id,
|
|
92
|
+
{ enduserFieldsSync: [{ field: 'fname', externalId: 'x'.repeat(101) }] },
|
|
93
|
+
{ replaceObjectFields: true }))
|
|
94
|
+
|
|
95
|
+
// a rejected write must not have partially applied - the one-row list from step 2 stands
|
|
96
|
+
const afterRejections = await sdk.api.forms.getOne(created.id)
|
|
97
|
+
record('3: rejected writes leave the stored list untouched',
|
|
98
|
+
JSON.stringify(afterRejections.enduserFieldsSync) === JSON.stringify(ONE_ROW),
|
|
99
|
+
`got ${JSON.stringify(afterRejections.enduserFieldsSync)} expected ${JSON.stringify(ONE_ROW)}`)
|
|
100
|
+
|
|
101
|
+
/* ===== 4. An empty list is accepted (clearing every row) ===== */
|
|
102
|
+
try {
|
|
103
|
+
await sdk.api.forms.updateOne(created.id, { enduserFieldsSync: [] }, { replaceObjectFields: true })
|
|
104
|
+
const cleared = await sdk.api.forms.getOne(created.id)
|
|
105
|
+
const rows = cleared.enduserFieldsSync
|
|
106
|
+
record('4: an empty list is accepted and clears the mapping',
|
|
107
|
+
Array.isArray(rows) && rows.length === 0,
|
|
108
|
+
`got ${JSON.stringify(rows)}`)
|
|
109
|
+
} catch (err: any) {
|
|
110
|
+
record('4: an empty list is accepted and clears the mapping', false,
|
|
111
|
+
`rejected: ${(err?.message || String(err)).slice(0, 200)}`)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/* ===== 5. Backward compatibility: a form that sets neither field reads back with neither ===== */
|
|
115
|
+
const plain = await sdk.api.forms.createOne({ title: `No field sync ${RAND()}` })
|
|
116
|
+
const plainReadBack = await sdk.api.forms.getOne(plain.id)
|
|
117
|
+
|
|
118
|
+
record('5: a form created without either field has no syncEnduserFieldsToEHR',
|
|
119
|
+
plainReadBack.syncEnduserFieldsToEHR === undefined,
|
|
120
|
+
`got ${JSON.stringify(plainReadBack.syncEnduserFieldsToEHR)}`)
|
|
121
|
+
record('5: a form created without either field has no enduserFieldsSync',
|
|
122
|
+
plainReadBack.enduserFieldsSync === undefined,
|
|
123
|
+
`got ${JSON.stringify(plainReadBack.enduserFieldsSync)}`)
|
|
124
|
+
} finally {
|
|
125
|
+
// Teardown BEFORE anything asserts — assert calls process.exit, which skips pending finallys and
|
|
126
|
+
// would strand the provisioned tenant permanently.
|
|
127
|
+
if (tenant) { try { await tenant.teardown() } catch (e) { console.error('teardown failed', e) } }
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return checks
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Asserting wrapper, for standalone runs and for sequential use in the suite. */
|
|
134
|
+
export const enduser_fields_sync_tests = async () => {
|
|
135
|
+
assert_checks('Enduser fields sync schema round-trip', await enduser_fields_sync_checks())
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Allow running this test file independently
|
|
139
|
+
if (require.main === module) {
|
|
140
|
+
console.log(`🌐 Using API URL: ${host}`)
|
|
141
|
+
enduser_fields_sync_tests()
|
|
142
|
+
.then(() => {
|
|
143
|
+
console.log("✅ enduser fields sync test suite completed successfully")
|
|
144
|
+
process.exit(0)
|
|
145
|
+
})
|
|
146
|
+
.catch((error) => {
|
|
147
|
+
console.error("❌ enduser fields sync test suite failed:", error)
|
|
148
|
+
process.exit(1)
|
|
149
|
+
})
|
|
150
|
+
}
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
async_test,
|
|
7
7
|
log_header,
|
|
8
8
|
} from "@tellescope/testing"
|
|
9
|
-
import { setup_tests } from "../setup"
|
|
9
|
+
import { reset_test_tenant, setup_tests } from "../setup"
|
|
10
10
|
|
|
11
11
|
const host = process.env.API_URL || 'http://localhost:8080' as const
|
|
12
12
|
|
|
@@ -43,7 +43,7 @@ export const enduser_login_rate_limits_tests = async ({ sdk, sdkNonAdmin } : { s
|
|
|
43
43
|
const businessId = sdk.userInfo.businessId
|
|
44
44
|
|
|
45
45
|
// Ensure throttled_events from prior tests don't bleed in.
|
|
46
|
-
await sdk
|
|
46
|
+
await reset_test_tenant(sdk)
|
|
47
47
|
|
|
48
48
|
// ---- /v1/login-enduser per-IP cap (20/min) ----
|
|
49
49
|
// Bogus emails ensure we never reach the actual DB lookup / login work.
|
|
@@ -58,7 +58,7 @@ export const enduser_login_rate_limits_tests = async ({ sdk, sdkNonAdmin } : { s
|
|
|
58
58
|
{ expectedResult: 'tripped' }
|
|
59
59
|
)
|
|
60
60
|
|
|
61
|
-
await sdk
|
|
61
|
+
await reset_test_tenant(sdk)
|
|
62
62
|
|
|
63
63
|
// ---- /v1/begin-enduser-login-flow per-IP cap (10/min) ----
|
|
64
64
|
// Use distinct emails so the per-identifier limit (5/10min) does NOT trip first;
|
|
@@ -73,7 +73,7 @@ export const enduser_login_rate_limits_tests = async ({ sdk, sdkNonAdmin } : { s
|
|
|
73
73
|
{ expectedResult: 'tripped' }
|
|
74
74
|
)
|
|
75
75
|
|
|
76
|
-
await sdk
|
|
76
|
+
await reset_test_tenant(sdk)
|
|
77
77
|
|
|
78
78
|
// ---- /v1/begin-enduser-login-flow per-identifier cap (5 / 10 min per email) ----
|
|
79
79
|
// Hit a single email below the per-IP cap.
|
|
@@ -88,7 +88,7 @@ export const enduser_login_rate_limits_tests = async ({ sdk, sdkNonAdmin } : { s
|
|
|
88
88
|
{ expectedResult: 'tripped' }
|
|
89
89
|
)
|
|
90
90
|
|
|
91
|
-
await sdk
|
|
91
|
+
await reset_test_tenant(sdk)
|
|
92
92
|
|
|
93
93
|
// ---- /v1/endusers/send-otp-code per-IP cap (5/min) ----
|
|
94
94
|
// Use a bogus JWT-shaped token so we trip the IP guard first (it runs before any DB work).
|
|
@@ -116,7 +116,7 @@ export const enduser_login_rate_limits_tests = async ({ sdk, sdkNonAdmin } : { s
|
|
|
116
116
|
|
|
117
117
|
// ---- Legitimate-login regression: a single successful login should still go through ----
|
|
118
118
|
// Reset state, then create a real enduser with a password and confirm one login succeeds.
|
|
119
|
-
await sdk
|
|
119
|
+
await reset_test_tenant(sdk)
|
|
120
120
|
|
|
121
121
|
const ts = Date.now()
|
|
122
122
|
const enduser = await sdk.api.endusers.createOne({
|
|
@@ -151,7 +151,7 @@ export const enduser_login_rate_limits_tests = async ({ sdk, sdkNonAdmin } : { s
|
|
|
151
151
|
)
|
|
152
152
|
} finally {
|
|
153
153
|
await sdk.api.endusers.deleteOne(enduser.id).catch(() => null)
|
|
154
|
-
await sdk
|
|
154
|
+
await reset_test_tenant(sdk).catch(() => null)
|
|
155
155
|
}
|
|
156
156
|
}
|
|
157
157
|
|
|
@@ -7,10 +7,10 @@ import {
|
|
|
7
7
|
log_header,
|
|
8
8
|
wait,
|
|
9
9
|
} from "@tellescope/testing"
|
|
10
|
-
import { setup_tests } from "../setup"
|
|
10
|
+
import { reset_test_tenant, setup_tests } from "../setup"
|
|
11
11
|
|
|
12
12
|
const host = process.env.API_URL || 'http://localhost:8080' as const
|
|
13
|
-
const businessId = '60398b1131a295e64f084ff6'
|
|
13
|
+
const businessId = process.env.TEST_BUSINESS_ID || '60398b1131a295e64f084ff6'
|
|
14
14
|
|
|
15
15
|
// Main test function that can be called independently
|
|
16
16
|
export const enduser_session_invalidation_tests = async ({ sdk, sdkNonAdmin } : { sdk: Session, sdkNonAdmin: Session }) => {
|
|
@@ -126,7 +126,7 @@ export const enduser_session_invalidation_tests = async ({ sdk, sdkNonAdmin } :
|
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
// Ensure OTP is off and db is clean before starting
|
|
129
|
-
await sdk
|
|
129
|
+
await reset_test_tenant(sdk)
|
|
130
130
|
await wait(undefined, 500)
|
|
131
131
|
await resetOTPSettings(sdk)
|
|
132
132
|
|
|
@@ -242,7 +242,7 @@ export const enduser_session_invalidation_tests = async ({ sdk, sdkNonAdmin } :
|
|
|
242
242
|
)
|
|
243
243
|
|
|
244
244
|
// Reset rate limiting state before continuing
|
|
245
|
-
await sdk
|
|
245
|
+
await reset_test_tenant(sdk)
|
|
246
246
|
await wait(undefined, 500)
|
|
247
247
|
|
|
248
248
|
// Test 8: Disabling OTP does NOT invalidate sessions
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
require('source-map-support').install();
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
assert_checks,
|
|
5
|
+
Check,
|
|
6
|
+
setup_isolated_tenant,
|
|
7
|
+
} from "../setup_isolated"
|
|
8
|
+
|
|
9
|
+
const host = process.env.API_URL || 'http://localhost:8080' as const
|
|
10
|
+
|
|
11
|
+
const RAND = () => Math.random().toString(36).slice(2, 10)
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Coverage for the `careTeamUserIds` parameter on `load_threads` (GET /v1/inbox-threads/load).
|
|
15
|
+
*
|
|
16
|
+
* The caller used to resolve "every patient these users are on the care team for" client-side and
|
|
17
|
+
* push the resulting enduser ids into `mdbFilter`. For a care team of hundreds of patients that
|
|
18
|
+
* overflows the GET query string (Node's 16KB header limit) before any Tellescope code runs. The
|
|
19
|
+
* parameter moves the join to the server: the API resolves the user ids to endusers via
|
|
20
|
+
* `assignedTo`, and ANDs `{ enduserIds: { $in: [...] } }` onto the thread filter.
|
|
21
|
+
*
|
|
22
|
+
* `setup_isolated` rather than `setup_tests` because several checks assert on the COMPLETE thread
|
|
23
|
+
* set of the tenant. On the shared fixture org, `build_threads` and the other inbox suites leave
|
|
24
|
+
* threads behind and every "returns exactly these" assertion would be flaky.
|
|
25
|
+
*
|
|
26
|
+
* The threads are created directly with `inbox_threads.createOne` (the model exposes create for
|
|
27
|
+
* exactly this reason) rather than via `build_threads`, so the tenant contains exactly three.
|
|
28
|
+
*
|
|
29
|
+
* Check 4 is the one that catches a wrong implementation: a care team with no patients resolves to
|
|
30
|
+
* an empty enduser list, and the clause must still be applied (returning nothing) rather than
|
|
31
|
+
* dropped (returning everything). Check 5 is its counterweight — it rules out an implementation
|
|
32
|
+
* that returns nothing for every call, which would satisfy 3, 4 and 7c vacuously.
|
|
33
|
+
*
|
|
34
|
+
* Check 7 is the permission boundary. A provisioned non-admin gets
|
|
35
|
+
* `sms_messages: ASSIGNED_AND_DEFAULT_ACCESS` (read: 'Assigned'), and the fixture threads carry
|
|
36
|
+
* `userIds: []`, so that session sees a thread only through the assigned-enduser branch. 7c fails
|
|
37
|
+
* if the care-team clause is ever pushed into the access `$or` instead of `filter.$and`, i.e. if
|
|
38
|
+
* the parameter could be used to WIDEN what a session can see.
|
|
39
|
+
*/
|
|
40
|
+
export const load_threads_care_team_checks = async (): Promise<Check[]> => {
|
|
41
|
+
const checks: Check[] = []
|
|
42
|
+
const record = (label: string, pass: boolean, detail: string) => checks.push({ label, pass, detail })
|
|
43
|
+
|
|
44
|
+
let tenant: Awaited<ReturnType<typeof setup_isolated_tenant>> | undefined
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
/* ================= fixtures ================= */
|
|
48
|
+
|
|
49
|
+
tenant = await setup_isolated_tenant({
|
|
50
|
+
name: `LoadThreadsCareTeam ${RAND()}`,
|
|
51
|
+
users: [{ isAdmin: true }, { roles: ['Non-Admin'] }],
|
|
52
|
+
apiKeys: 0,
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
const { sdk, sdkNonAdmin } = tenant
|
|
56
|
+
const NA = tenant.users[1].id // the non-admin's own user id
|
|
57
|
+
|
|
58
|
+
// care-team users: they never log in, they exist only to appear in endusers.assignedTo
|
|
59
|
+
const [U1, U2, U3] = await Promise.all([1, 2, 3].map(n => sdk.api.users.createOne({
|
|
60
|
+
email: `dontsend-care-team-${n}-${RAND()}@tellescope.com`,
|
|
61
|
+
fname: 'CareTeam', lname: `User${n}`,
|
|
62
|
+
verifiedEmail: true,
|
|
63
|
+
notificationEmailsDisabled: true,
|
|
64
|
+
})))
|
|
65
|
+
|
|
66
|
+
// E1 is also assigned to the non-admin, so check 7a has a non-empty baseline to measure against
|
|
67
|
+
const E1 = await sdk.api.endusers.createOne({ fname: 'CareTeam', lname: 'PatientOne', assignedTo: [U1.id, NA] })
|
|
68
|
+
const E2 = await sdk.api.endusers.createOne({ fname: 'CareTeam', lname: 'PatientTwo', assignedTo: [U2.id] })
|
|
69
|
+
const E3 = await sdk.api.endusers.createOne({ fname: 'CareTeam', lname: 'PatientThree', assignedTo: [] })
|
|
70
|
+
|
|
71
|
+
const now = Date.now()
|
|
72
|
+
const MINUTE = 60 * 1000
|
|
73
|
+
const defaultThreadFields = {
|
|
74
|
+
type: 'SMS' as const,
|
|
75
|
+
assignedTo: [],
|
|
76
|
+
userIds: [],
|
|
77
|
+
inboxStatus: 'New',
|
|
78
|
+
preview: 'care team test',
|
|
79
|
+
title: 'Care team test',
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const T1 = await sdk.api.inbox_threads.createOne({
|
|
83
|
+
...defaultThreadFields,
|
|
84
|
+
enduserIds: [E1.id], threadId: `care-team-t1-${RAND()}`, timestamp: new Date(now - 3 * MINUTE),
|
|
85
|
+
})
|
|
86
|
+
const T2 = await sdk.api.inbox_threads.createOne({
|
|
87
|
+
...defaultThreadFields,
|
|
88
|
+
enduserIds: [E2.id], threadId: `care-team-t2-${RAND()}`, timestamp: new Date(now - 2 * MINUTE),
|
|
89
|
+
})
|
|
90
|
+
const T3 = await sdk.api.inbox_threads.createOne({
|
|
91
|
+
...defaultThreadFields,
|
|
92
|
+
enduserIds: [E3.id], threadId: `care-team-t3-${RAND()}`, timestamp: new Date(now - 1 * MINUTE),
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
const label_for: { [id: string]: string } = { [T1.id]: 'T1', [T2.id]: 'T2', [T3.id]: 'T3' }
|
|
96
|
+
const describe = (ids: string[]) => `[${ids.map(id => label_for[id] || id).sort().join(', ')}]`
|
|
97
|
+
const load = async (args: any, session = sdk) => (
|
|
98
|
+
(await session.api.inbox_threads.load_threads(args)).threads.map(t => t.id)
|
|
99
|
+
)
|
|
100
|
+
const same_set = (a: string[], b: string[]) => (
|
|
101
|
+
a.length === b.length && [...a].sort().join() === [...b].sort().join()
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
/* ===== 1: a single care-team user ===== */
|
|
105
|
+
|
|
106
|
+
const one = await load({ careTeamUserIds: [U1.id] })
|
|
107
|
+
record(
|
|
108
|
+
'1: careTeamUserIds=[U1] returns exactly the thread for U1\'s patient',
|
|
109
|
+
same_set(one, [T1.id]),
|
|
110
|
+
`got ${describe(one)} (want [T1])`,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
/* ===== 2: multiple care-team users union their patients ===== */
|
|
114
|
+
|
|
115
|
+
const two = await load({ careTeamUserIds: [U1.id, U2.id] })
|
|
116
|
+
record(
|
|
117
|
+
'2: careTeamUserIds=[U1, U2] returns both their threads and not the unassigned one',
|
|
118
|
+
same_set(two, [T1.id, T2.id]),
|
|
119
|
+
`got ${describe(two)} (want [T1, T2] — T3\'s enduser has an empty care team)`,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
/* ===== 3: ANDs with enduserIds rather than ORing ===== */
|
|
123
|
+
|
|
124
|
+
const intersect = await load({ careTeamUserIds: [U2.id], enduserIds: [E1.id] })
|
|
125
|
+
record(
|
|
126
|
+
'3: careTeamUserIds AND enduserIds narrows to the intersection',
|
|
127
|
+
intersect.length === 0,
|
|
128
|
+
`got ${describe(intersect)} (want [] — E1 is not on U2\'s care team; a non-empty result means the`
|
|
129
|
+
+ ` two filters OR instead of AND)`,
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
/* ===== 4: an empty resolution must not degrade to "no filter" ===== */
|
|
133
|
+
|
|
134
|
+
const emptyCareTeam = await load({ careTeamUserIds: [U3.id] })
|
|
135
|
+
record(
|
|
136
|
+
'4: a care-team user with no patients returns no threads, not every thread',
|
|
137
|
+
emptyCareTeam.length === 0,
|
|
138
|
+
`got ${describe(emptyCareTeam)} (want [] — returning all three means the clause was dropped when`
|
|
139
|
+
+ ` the enduser lookup came back empty)`,
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
/* ===== 5: the feature-off path is unchanged ===== */
|
|
143
|
+
|
|
144
|
+
const baseline = await load({})
|
|
145
|
+
const emptyParam = await load({ careTeamUserIds: [] })
|
|
146
|
+
record(
|
|
147
|
+
'5: omitting careTeamUserIds, and passing [], both return exactly the three fixture threads',
|
|
148
|
+
same_set(baseline, [T1.id, T2.id, T3.id]) && same_set(emptyParam, baseline),
|
|
149
|
+
`omitted=${describe(baseline)} empty=${describe(emptyParam)} (want [T1, T2, T3] for both)`,
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
/* ===== 6: the count path shares the filter ===== */
|
|
153
|
+
|
|
154
|
+
const { count } = await sdk.api.inbox_threads.load_threads({ careTeamUserIds: [U1.id], returnCount: true })
|
|
155
|
+
record(
|
|
156
|
+
'6: returnCount applies the care-team filter too',
|
|
157
|
+
count === 1,
|
|
158
|
+
`count=${count} (want 1 — 3 means the count path skipped the filter)`,
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
/* ===== 7: permission boundary — the parameter can only narrow, never widen ===== */
|
|
162
|
+
|
|
163
|
+
const naBaseline = await load({}, sdkNonAdmin)
|
|
164
|
+
record(
|
|
165
|
+
'7a: the non-admin\'s unfiltered inbox is exactly the thread for the patient assigned to them',
|
|
166
|
+
same_set(naBaseline, [T1.id]),
|
|
167
|
+
`got ${describe(naBaseline)} (want [T1] — the baseline the next two checks are measured against;`
|
|
168
|
+
+ ` if this is empty, 7b and 7c pass vacuously)`,
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
const naOwnCareTeam = await load({ careTeamUserIds: [U1.id] }, sdkNonAdmin)
|
|
172
|
+
record(
|
|
173
|
+
'7b: the parameter does not remove a thread the non-admin legitimately sees',
|
|
174
|
+
same_set(naOwnCareTeam, [T1.id]),
|
|
175
|
+
`got ${describe(naOwnCareTeam)} (want [T1])`,
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
const naForeignCareTeam = await load({ careTeamUserIds: [U2.id] }, sdkNonAdmin)
|
|
179
|
+
record(
|
|
180
|
+
'7c: the parameter cannot reach a thread outside the non-admin\'s access',
|
|
181
|
+
naForeignCareTeam.length === 0,
|
|
182
|
+
`got ${describe(naForeignCareTeam)} (want [] — T2 matches U2\'s care team but is outside this`
|
|
183
|
+
+ ` session's access; a non-empty result means the clause was pushed into the access $or)`,
|
|
184
|
+
)
|
|
185
|
+
} finally {
|
|
186
|
+
// Teardown BEFORE anything asserts — assert calls process.exit, which would strand the tenant.
|
|
187
|
+
if (tenant) { try { await tenant.teardown() } catch (e) { console.error('teardown failed', e) } }
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return checks
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** Asserting wrapper, for standalone runs and for sequential use in the suite. */
|
|
194
|
+
export const load_threads_care_team_tests = async () => {
|
|
195
|
+
assert_checks('Load threads care team', await load_threads_care_team_checks())
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Allow running this test file independently
|
|
199
|
+
if (require.main === module) {
|
|
200
|
+
console.log(`🌐 Using API URL: ${host}`)
|
|
201
|
+
load_threads_care_team_tests()
|
|
202
|
+
.then(() => {
|
|
203
|
+
console.log("✅ Load threads care team test suite completed successfully")
|
|
204
|
+
process.exit(0)
|
|
205
|
+
})
|
|
206
|
+
.catch((error) => {
|
|
207
|
+
console.error("❌ Load threads care team test suite failed:", error)
|
|
208
|
+
process.exit(1)
|
|
209
|
+
})
|
|
210
|
+
}
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
import { setup_tests } from "../setup"
|
|
10
10
|
|
|
11
11
|
const host = process.env.API_URL || 'http://localhost:8080' as const
|
|
12
|
-
const businessId = '60398b1131a295e64f084ff6'
|
|
12
|
+
const businessId = process.env.TEST_BUSINESS_ID || '60398b1131a295e64f084ff6'
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Tests for managed_content_records enduser access filtering.
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
import { setup_tests } from "../setup"
|
|
10
10
|
|
|
11
11
|
const host = process.env.API_URL || 'http://localhost:8080' as const
|
|
12
|
-
const businessId = '60398b1131a295e64f084ff6'
|
|
12
|
+
const businessId = process.env.TEST_BUSINESS_ID || '60398b1131a295e64f084ff6'
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Tests for file_download_URL enduser access via managed content fallback.
|