canvas 0.25.0__tar.gz → 0.27.0__tar.gz
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.
Potentially problematic release.
This version of canvas might be problematic. Click here for more details.
- {canvas-0.25.0 → canvas-0.27.0}/PKG-INFO +1 -1
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/handlers/simple_api/api.py +193 -28
- canvas-0.27.0/canvas_sdk/handlers/simple_api/tools.py +115 -0
- canvas-0.27.0/canvas_sdk/tests/handlers/test_simple_api.py +986 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/v1/data/__init__.py +4 -2
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/v1/data/appointment.py +22 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/v1/data/staff.py +25 -1
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/sandbox.py +1 -0
- {canvas-0.25.0 → canvas-0.27.0}/pyproject.toml +1 -1
- canvas-0.25.0/canvas_sdk/tests/handlers/test_simple_api.py +0 -828
- canvas-0.25.0/plugin_runner/tests/data/plugins/.gitkeep +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/.gitignore +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/README.md +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/auth/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/auth/tests.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/auth/utils.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/emit.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/ALLERGY_INTOLERANCE_CREATED.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/ALLERGY_INTOLERANCE_UPDATED.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/APPOINTMENT_CANCELED.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/APPOINTMENT_CHECKED_IN.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/APPOINTMENT_CREATED.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/APPOINTMENT_NO_SHOWED.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/APPOINTMENT_RESCHEDULED.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/APPOINTMENT_RESTORED.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/APPOINTMENT_UPDATED.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/ASSESS_COMMAND__CONDITION_SELECTED.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/ASSESS_COMMAND__POST_COMMIT.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/ASSESS_COMMAND__POST_ORIGINATE.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/ASSESS_COMMAND__POST_UPDATE.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/ASSESS_COMMAND__PRE_COMMIT.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/ASSESS_COMMAND__PRE_ORIGINATE.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/ASSESS_COMMAND__PRE_UPDATE.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/BILLING_LINE_ITEM_CREATED.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/BILLING_LINE_ITEM_UPDATED.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/CONDITION_ASSESSED.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/CONDITION_CREATED.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/CONDITION_UPDATED.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/CRON.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/ENCOUNTER_CREATED.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/ENCOUNTER_UPDATED.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/IMMUNIZATION_CREATED.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/IMMUNIZATION_STATEMENT_CREATED.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/IMMUNIZATION_STATEMENT_UPDATED.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/IMMUNIZATION_UPDATED.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/INTERVIEW_CREATED.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/INTERVIEW_UPDATED.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/LAB_ORDER_CREATED.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/LAB_ORDER_UPDATED.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/MEDICATION_LIST_ITEM_CREATED.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/MEDICATION_LIST_ITEM_UPDATED.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/MEDICATION_STATEMENT_COMMAND__POST_COMMIT.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/MEDICATION_STATEMENT_COMMAND__POST_ORIGINATE.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/MEDICATION_STATEMENT_COMMAND__POST_UPDATE.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/MEDICATION_STATEMENT_COMMAND__PRE_COMMIT.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/MEDICATION_STATEMENT_COMMAND__PRE_ORIGINATE.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/MEDICATION_STATEMENT_COMMAND__PRE_UPDATE.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/MEDICATION_STATEMENT__MEDICATION__POST_SEARCH.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/PATIENT_CREATED.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/PATIENT_UPDATED.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/PLAN_COMMAND__POST_ORIGINATE.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/PLAN_COMMAND__PRE_ORIGINATE.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/QUESTIONNAIRE_COMMAND__POST_COMMIT.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/QUESTIONNAIRE_COMMAND__POST_ORIGINATE.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/QUESTIONNAIRE_COMMAND__POST_UPDATE.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/QUESTIONNAIRE_COMMAND__PRE_COMMIT.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/QUESTIONNAIRE_COMMAND__PRE_ORIGINATE.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/QUESTIONNAIRE_COMMAND__PRE_UPDATE.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/QUESTIONNAIRE__QUESTIONNAIRE__POST_SEARCH.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/TASK_COMMENT_CREATED.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/TASK_CREATED.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/TASK_UPDATED.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/VITAL_SIGN_CREATED.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/emit/event_fixtures/VITAL_SIGN_UPDATED.ndjson +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/logs/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/logs/logs.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/plugin/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/plugin/plugin.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/plugin/tests.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/run_plugins/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/apps/run_plugins/run_plugins.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/conftest.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/main.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/templates/plugins/application/cookiecutter.json +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/templates/plugins/application/{{ cookiecutter.__project_slug }}/CANVAS_MANIFEST.json +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/templates/plugins/application/{{ cookiecutter.__project_slug }}/README.md +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/templates/plugins/application/{{ cookiecutter.__project_slug }}/applications/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/templates/plugins/application/{{ cookiecutter.__project_slug }}/applications/my_application.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/templates/plugins/application/{{ cookiecutter.__project_slug }}/assets/python-logo.png +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/templates/plugins/default/cookiecutter.json +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/templates/plugins/default/{{ cookiecutter.__project_slug }}/CANVAS_MANIFEST.json +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/templates/plugins/default/{{ cookiecutter.__project_slug }}/README.md +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/templates/plugins/default/{{ cookiecutter.__project_slug }}/protocols/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/templates/plugins/default/{{ cookiecutter.__project_slug }}/protocols/my_protocol.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/tests.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/utils/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/utils/context/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/utils/context/context.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/utils/context/tests.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/utils/print/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/utils/print/print.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/utils/print/tests.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/utils/urls/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/utils/urls/tests.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/utils/urls/urls.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/utils/validators/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/utils/validators/manifest_schema.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/utils/validators/tests.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_cli/utils/validators/validators.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_generated/messages/effects_pb2.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_generated/messages/effects_pb2.pyi +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_generated/messages/effects_pb2_grpc.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_generated/messages/events_pb2.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_generated/messages/events_pb2.pyi +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_generated/messages/events_pb2_grpc.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_generated/messages/plugins_pb2.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_generated/messages/plugins_pb2.pyi +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_generated/messages/plugins_pb2_grpc.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_generated/services/plugin_runner_pb2.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_generated/services/plugin_runner_pb2.pyi +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_generated/services/plugin_runner_pb2_grpc.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/base.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/base.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/commands/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/commands/adjust_prescription.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/commands/allergy.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/commands/assess.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/commands/close_goal.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/commands/diagnose.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/commands/exam.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/commands/family_history.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/commands/follow_up.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/commands/goal.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/commands/history_present_illness.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/commands/imaging_order.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/commands/instruct.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/commands/lab_order.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/commands/medical_history.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/commands/medication_statement.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/commands/past_surgical_history.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/commands/perform.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/commands/plan.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/commands/prescribe.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/commands/questionnaire/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/commands/questionnaire/question.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/commands/reason_for_visit.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/commands/refer.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/commands/refill.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/commands/remove_allergy.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/commands/resolve_condition.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/commands/review_of_systems.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/commands/stop_medication.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/commands/structured_assessment.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/commands/task.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/commands/update_diagnosis.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/commands/update_goal.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/commands/vitals.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/constants.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/tests/protocol/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/tests/protocol/tests.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/tests/schema/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/tests/schema/tests.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/tests/test_base_command.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/tests/test_utils.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/tests/unit/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/commands/tests/unit/tests.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/effects/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/effects/banner_alert/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/effects/banner_alert/add_banner_alert.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/effects/banner_alert/remove_banner_alert.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/effects/banner_alert/tests.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/effects/base.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/effects/billing_line_item/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/effects/billing_line_item/add_billing_line_item.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/effects/billing_line_item/remove_billing_line_item.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/effects/billing_line_item/update_billing_line_item.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/effects/launch_modal.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/effects/patient_chart_summary_configuration.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/effects/patient_portal/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/effects/patient_portal/form_result.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/effects/patient_portal_menu_configuration.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/effects/patient_profile_configuration.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/effects/protocol_card/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/effects/protocol_card/protocol_card.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/effects/protocol_card/tests.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/effects/questionnaire_result.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/effects/show_button.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/effects/simple_api.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/effects/surescripts/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/effects/surescripts/surescripts_messages.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/effects/task/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/effects/task/task.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/effects/widgets/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/effects/widgets/portal_widget.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/events/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/events/base.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/handlers/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/handlers/action_button.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/handlers/application.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/handlers/base.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/handlers/cron_task.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/handlers/simple_api/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/handlers/simple_api/exceptions.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/handlers/simple_api/security.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/protocols/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/protocols/base.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/protocols/clinical_quality_measure.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/protocols/timeframe.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/questionnaires/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/questionnaires/tests/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/questionnaires/tests/test_utils.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/questionnaires/utils.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/templates/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/templates/tests/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/templates/tests/test_utils.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/templates/utils.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/tests/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/tests/handlers/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/utils/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/utils/http.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/utils/plugins.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/utils/stats.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/utils/tests.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/v1/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/v1/apps.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/v1/data/allergy_intolerance.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/v1/data/assessment.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/v1/data/base.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/v1/data/billing.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/v1/data/care_team.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/v1/data/command.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/v1/data/common.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/v1/data/condition.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/v1/data/coverage.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/v1/data/detected_issue.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/v1/data/device.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/v1/data/imaging.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/v1/data/lab.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/v1/data/medication.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/v1/data/note.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/v1/data/observation.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/v1/data/organization.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/v1/data/patient.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/v1/data/practicelocation.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/v1/data/protocol_override.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/v1/data/questionnaire.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/v1/data/reason_for_visit.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/v1/data/task.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/v1/data/team.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/v1/data/user.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/v1/models.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/value_set/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/value_set/custom.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/value_set/hcc2018.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/value_set/tests/test_value_sets.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/value_set/v2022/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/value_set/v2022/adverse_event.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/value_set/v2022/allergy.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/value_set/v2022/assessment.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/value_set/v2022/communication.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/value_set/v2022/condition.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/value_set/v2022/device.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/value_set/v2022/diagnostic_study.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/value_set/v2022/encounter.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/value_set/v2022/immunization.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/value_set/v2022/individual_characteristic.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/value_set/v2022/intervention.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/value_set/v2022/laboratory_test.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/value_set/v2022/medication.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/value_set/v2022/physical_exam.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/value_set/v2022/procedure.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/value_set/v2022/symptom.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/value_set/value_set.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/canvas_sdk/views/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/logger/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/logger/logger.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/authentication.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/aws_headers.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/exceptions.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/installation.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/plugin_runner.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/example_plugin/CANVAS_MANIFEST.json +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/example_plugin/README.md +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/example_plugin/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/example_plugin/protocols/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/example_plugin/protocols/my_protocol.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_implicit_imports_plugin/CANVAS_MANIFEST.json +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_implicit_imports_plugin/README.md +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_implicit_imports_plugin/protocols/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_implicit_imports_plugin/protocols/my_protocol.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_implicit_imports_plugin/templates/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_implicit_imports_plugin/templates/base.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_implicit_imports_plugin/utils/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_implicit_imports_plugin/utils/base.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_load_questionnaire/CANVAS_MANIFEST.json +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_load_questionnaire/README.md +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_load_questionnaire/protocols/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_load_questionnaire/protocols/my_protocol.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_load_questionnaire/questionnaires/example_questionnaire.yml +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_module_forbidden_imports_plugin/CANVAS_MANIFEST.json +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_module_forbidden_imports_plugin/README.md +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_module_forbidden_imports_plugin/other_module/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_module_forbidden_imports_plugin/other_module/base.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_module_forbidden_imports_plugin/protocols/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_module_forbidden_imports_plugin/protocols/my_protocol.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_module_forbidden_imports_runtime_plugin/CANVAS_MANIFEST.json +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_module_forbidden_imports_runtime_plugin/README.md +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_module_forbidden_imports_runtime_plugin/other_module/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_module_forbidden_imports_runtime_plugin/other_module/base.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_module_forbidden_imports_runtime_plugin/protocols/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_module_forbidden_imports_runtime_plugin/protocols/my_protocol.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v1/CANVAS_MANIFEST.json +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v1/README.md +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v1/other_module/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v1/other_module/base.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v1/protocols/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v1/protocols/my_protocol.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v2/CANVAS_MANIFEST.json +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v2/README.md +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v2/other_module/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v2/other_module/base.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v2/protocols/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v2/protocols/my_protocol.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v3/CANVAS_MANIFEST.json +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v3/README.md +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v3/other_module/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v3/other_module/base.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v3/protocols/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v3/protocols/my_protocol.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_module_imports_plugin/CANVAS_MANIFEST.json +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_module_imports_plugin/README.md +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_module_imports_plugin/other_module/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_module_imports_plugin/other_module/base.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_module_imports_plugin/protocols/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_module_imports_plugin/protocols/my_protocol.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_render_template/CANVAS_MANIFEST.json +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_render_template/README.md +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_render_template/protocols/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_render_template/protocols/my_protocol.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_render_template/templates/template.html +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_simple_api/CANVAS_MANIFEST.json +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_simple_api/README.md +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_simple_api/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_simple_api/protocols/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/fixtures/plugins/test_simple_api/protocols/my_protocol.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/test_application.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/test_plugin_installer.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/test_plugin_runner.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/plugin_runner/tests/test_sandbox.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/protobufs/canvas_generated/messages/effects.proto +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/protobufs/canvas_generated/messages/events.proto +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/protobufs/canvas_generated/messages/plugins.proto +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/protobufs/canvas_generated/services/plugin_runner.proto +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/pubsub/__init__.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/pubsub/pubsub.py +0 -0
- {canvas-0.25.0 → canvas-0.27.0}/settings.py +0 -0
|
@@ -5,10 +5,8 @@ from base64 import b64decode
|
|
|
5
5
|
from collections.abc import Callable
|
|
6
6
|
from functools import cached_property
|
|
7
7
|
from http import HTTPStatus
|
|
8
|
-
from typing import Any, TypeVar
|
|
9
|
-
from urllib.parse import
|
|
10
|
-
|
|
11
|
-
from requests.structures import CaseInsensitiveDict
|
|
8
|
+
from typing import Any, ClassVar, Protocol, TypeVar, cast
|
|
9
|
+
from urllib.parse import parse_qsl
|
|
12
10
|
|
|
13
11
|
from canvas_sdk.effects import Effect, EffectType
|
|
14
12
|
from canvas_sdk.effects.simple_api import JSONResponse, Response
|
|
@@ -19,9 +17,9 @@ from plugin_runner.exceptions import PluginError
|
|
|
19
17
|
|
|
20
18
|
from .exceptions import AuthenticationError, InvalidCredentialsError
|
|
21
19
|
from .security import Credentials
|
|
20
|
+
from .tools import CaseInsensitiveMultiDict, MultiDict, separate_headers
|
|
22
21
|
|
|
23
22
|
# TODO: Routing by path regex?
|
|
24
|
-
# TODO: Support multipart/form-data by adding helpers to the request class
|
|
25
23
|
# TODO: Log requests in a format similar to other API frameworks (probably need effect metadata)
|
|
26
24
|
# TODO: Support Effect metadata that is separate from payload
|
|
27
25
|
# TODO: Encode event payloads with MessagePack instead of JSON
|
|
@@ -30,6 +28,137 @@ from .security import Credentials
|
|
|
30
28
|
JSON = dict[str, "JSON"] | list["JSON"] | int | float | str | bool | None
|
|
31
29
|
|
|
32
30
|
|
|
31
|
+
class FormPart(Protocol):
|
|
32
|
+
"""
|
|
33
|
+
Protocol for representing a form part in the body of a multipart/form-data request.
|
|
34
|
+
|
|
35
|
+
A form part can represent a simple string value, or a file with a content type.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
@staticmethod
|
|
39
|
+
def is_file() -> bool:
|
|
40
|
+
"""Return True or False depending on whether the form part represents a file."""
|
|
41
|
+
...
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class StringFormPart(FormPart):
|
|
45
|
+
"""Class for representing a form part that is a simple string value."""
|
|
46
|
+
|
|
47
|
+
def __init__(self, name: str, value: str) -> None:
|
|
48
|
+
self.name = name
|
|
49
|
+
self.value = value
|
|
50
|
+
|
|
51
|
+
@staticmethod
|
|
52
|
+
def is_file() -> bool:
|
|
53
|
+
"""Return True or False depending on whether the form part represents a file."""
|
|
54
|
+
return False
|
|
55
|
+
|
|
56
|
+
def __eq__(self, other: object) -> bool:
|
|
57
|
+
if isinstance(other, FileFormPart):
|
|
58
|
+
return False
|
|
59
|
+
|
|
60
|
+
if not isinstance(other, StringFormPart):
|
|
61
|
+
return NotImplemented
|
|
62
|
+
|
|
63
|
+
return self.name == other.name and self.value == other.value
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class FileFormPart(FormPart):
|
|
67
|
+
"""Class for representing a form part that is a file."""
|
|
68
|
+
|
|
69
|
+
def __init__(
|
|
70
|
+
self, name: str, filename: str, content: bytes, content_type: str | None = None
|
|
71
|
+
) -> None:
|
|
72
|
+
self.name = name
|
|
73
|
+
self.filename = filename
|
|
74
|
+
self.content = content
|
|
75
|
+
self.content_type = content_type
|
|
76
|
+
|
|
77
|
+
@staticmethod
|
|
78
|
+
def is_file() -> bool:
|
|
79
|
+
"""Return True or False depending on whether the form part represents a file."""
|
|
80
|
+
return True
|
|
81
|
+
|
|
82
|
+
def __eq__(self, other: object) -> bool:
|
|
83
|
+
if isinstance(other, StringFormPart):
|
|
84
|
+
return False
|
|
85
|
+
|
|
86
|
+
if not isinstance(other, FileFormPart):
|
|
87
|
+
return NotImplemented
|
|
88
|
+
|
|
89
|
+
return all(
|
|
90
|
+
(
|
|
91
|
+
self.name == other.name,
|
|
92
|
+
self.filename == other.filename,
|
|
93
|
+
self.content == other.content,
|
|
94
|
+
self.content_type == other.content_type,
|
|
95
|
+
)
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def parse_multipart_form(form: bytes, boundary: str) -> MultiDict[str, FormPart]:
|
|
100
|
+
"""Parse a multipart form and return a dict of string to list of form parts."""
|
|
101
|
+
form_data: list[tuple[str, FormPart]] = []
|
|
102
|
+
|
|
103
|
+
# Split the body by the boundary value and iterate over the parts. The first and last
|
|
104
|
+
# parts can be skipped because there are delimiters on at the start and end of the body
|
|
105
|
+
parts = form.split(f"--{boundary}".encode())
|
|
106
|
+
for part in parts[1:-1]:
|
|
107
|
+
# Each part may be either a simple string value or a file. Simple string values
|
|
108
|
+
# will just have a name and a value, whereas files will have a name, content type,
|
|
109
|
+
# filename, and value.
|
|
110
|
+
name = None
|
|
111
|
+
content_type = None
|
|
112
|
+
filename = None
|
|
113
|
+
|
|
114
|
+
# Split the part into the headers and the value (i.e. the content)
|
|
115
|
+
value: str | bytes
|
|
116
|
+
headers, value = part.split(b"\r\n\r\n", maxsplit=1)
|
|
117
|
+
|
|
118
|
+
# Iterate over the headers and extract the name, filename, and content type
|
|
119
|
+
for header in headers.decode().split("\r\n"):
|
|
120
|
+
# There are only two possible headers: Content-Disposition and Content-Type
|
|
121
|
+
if header.lower().startswith("content-disposition: form-data;"):
|
|
122
|
+
# Iterate over the content disposition parameters to get the form name and
|
|
123
|
+
# filename
|
|
124
|
+
for parameter in header.split(";")[1:]:
|
|
125
|
+
parameter_name, parameter_value = parameter.strip().split("=")
|
|
126
|
+
|
|
127
|
+
# Strip the quotes from the value
|
|
128
|
+
parameter_value = parameter_value[1:-1]
|
|
129
|
+
|
|
130
|
+
if parameter_name == "name":
|
|
131
|
+
name = parameter_value
|
|
132
|
+
elif parameter_name == "filename":
|
|
133
|
+
filename = parameter_value
|
|
134
|
+
elif header.lower().startswith("content-type"):
|
|
135
|
+
# Files will have a content type, so grab it
|
|
136
|
+
content_type = header.split(":")[1].strip()
|
|
137
|
+
|
|
138
|
+
if not name or not value:
|
|
139
|
+
raise RuntimeError("Invalid multipart/form-data request body")
|
|
140
|
+
|
|
141
|
+
# Strip off the trailing newline characters from the value
|
|
142
|
+
value = value[:-2]
|
|
143
|
+
|
|
144
|
+
# Now we have all the data, so append it to the list of form data
|
|
145
|
+
if filename:
|
|
146
|
+
# Because a filename was provided, we know it's a file and not a simple string value
|
|
147
|
+
form_data.append(
|
|
148
|
+
(
|
|
149
|
+
name,
|
|
150
|
+
FileFormPart(
|
|
151
|
+
name=name, filename=filename, content=value, content_type=content_type
|
|
152
|
+
),
|
|
153
|
+
)
|
|
154
|
+
)
|
|
155
|
+
else:
|
|
156
|
+
# Decode the string before adding it
|
|
157
|
+
form_data.append((name, StringFormPart(name, value.decode())))
|
|
158
|
+
|
|
159
|
+
return MultiDict(form_data)
|
|
160
|
+
|
|
161
|
+
|
|
33
162
|
class Request:
|
|
34
163
|
"""Request class for incoming requests to the API."""
|
|
35
164
|
|
|
@@ -38,10 +167,21 @@ class Request:
|
|
|
38
167
|
self.path = event.context["path"]
|
|
39
168
|
self.query_string = event.context["query_string"]
|
|
40
169
|
self._body = event.context["body"]
|
|
41
|
-
self.headers
|
|
42
|
-
|
|
43
|
-
self.query_params =
|
|
44
|
-
|
|
170
|
+
self.headers = CaseInsensitiveMultiDict(separate_headers(event.context["headers"]))
|
|
171
|
+
|
|
172
|
+
self.query_params = MultiDict(parse_qsl(self.query_string))
|
|
173
|
+
|
|
174
|
+
# Parse the content type and any included content type parameters
|
|
175
|
+
content_type = self.headers.get("Content-Type")
|
|
176
|
+
self._content_type_parameters = {}
|
|
177
|
+
if content_type:
|
|
178
|
+
content_type, *parameters = content_type.split(";")
|
|
179
|
+
self.content_type = content_type.strip()
|
|
180
|
+
for parameter in parameters:
|
|
181
|
+
name, value = parameter.strip().split("=")
|
|
182
|
+
self._content_type_parameters[name] = value
|
|
183
|
+
else:
|
|
184
|
+
self.content_type = None
|
|
45
185
|
|
|
46
186
|
@cached_property
|
|
47
187
|
def body(self) -> bytes:
|
|
@@ -49,13 +189,31 @@ class Request:
|
|
|
49
189
|
return b64decode(self._body)
|
|
50
190
|
|
|
51
191
|
def json(self) -> JSON:
|
|
52
|
-
"""Return the response JSON."""
|
|
192
|
+
"""Return the response body as a JSON dict."""
|
|
53
193
|
return json.loads(self.body)
|
|
54
194
|
|
|
55
195
|
def text(self) -> str:
|
|
56
196
|
"""Return the response body as plain text."""
|
|
57
197
|
return self.body.decode()
|
|
58
198
|
|
|
199
|
+
def form_data(self) -> MultiDict[str, FormPart]:
|
|
200
|
+
"""Return the response body as a dict of string to list of FormPart objects."""
|
|
201
|
+
form_data: MultiDict[str, FormPart]
|
|
202
|
+
|
|
203
|
+
if self.content_type == "application/x-www-form-urlencoded":
|
|
204
|
+
# For request bodies that are URL-encoded, just parse them and return them as simple
|
|
205
|
+
# form parts
|
|
206
|
+
form_data = MultiDict(
|
|
207
|
+
(name, StringFormPart(name, value)) for name, value in parse_qsl(self.body.decode())
|
|
208
|
+
)
|
|
209
|
+
elif self.content_type == "multipart/form-data":
|
|
210
|
+
# Parse request bodies that are multipart forms
|
|
211
|
+
form_data = parse_multipart_form(self.body, self._content_type_parameters["boundary"])
|
|
212
|
+
else:
|
|
213
|
+
raise RuntimeError(f"Cannot parse content type {self.content_type} as form data")
|
|
214
|
+
|
|
215
|
+
return form_data
|
|
216
|
+
|
|
59
217
|
|
|
60
218
|
SimpleAPIType = TypeVar("SimpleAPIType", bound="SimpleAPIBase")
|
|
61
219
|
|
|
@@ -108,20 +266,23 @@ class SimpleAPIBase(BaseHandler, ABC):
|
|
|
108
266
|
EventType.Name(EventType.SIMPLE_API_REQUEST),
|
|
109
267
|
]
|
|
110
268
|
|
|
111
|
-
|
|
112
|
-
|
|
269
|
+
_ROUTES: ClassVar[dict[tuple[str, str], RouteHandler]]
|
|
270
|
+
|
|
271
|
+
def __init_subclass__(cls, **kwargs: Any) -> None:
|
|
272
|
+
super().__init_subclass__(**kwargs)
|
|
113
273
|
|
|
114
274
|
# Build the registry of routes so that requests can be routed to the correct handler. This
|
|
115
|
-
# is done by iterating over the methods on the class
|
|
116
|
-
#
|
|
117
|
-
|
|
118
|
-
for attr in
|
|
275
|
+
# is done by iterating over the methods on the class and looking for methods that have been
|
|
276
|
+
# marked by the handler decorators (get, post, etc.).
|
|
277
|
+
cls._ROUTES = {}
|
|
278
|
+
for attr in cls.__dict__.values():
|
|
119
279
|
if callable(attr) and (route := getattr(attr, "route", None)):
|
|
120
280
|
method, relative_path = route
|
|
121
|
-
path = f"{
|
|
122
|
-
|
|
281
|
+
path = f"{cls._path_prefix()}{relative_path}"
|
|
282
|
+
cls._ROUTES[(method, path)] = attr
|
|
123
283
|
|
|
124
|
-
|
|
284
|
+
@classmethod
|
|
285
|
+
def _path_prefix(cls) -> str:
|
|
125
286
|
return ""
|
|
126
287
|
|
|
127
288
|
@cached_property
|
|
@@ -174,7 +335,7 @@ class SimpleAPIBase(BaseHandler, ABC):
|
|
|
174
335
|
def _handle_request(self) -> list[Effect]:
|
|
175
336
|
"""Route the incoming request to the handler method based on the HTTP method and path."""
|
|
176
337
|
# Get the handler method
|
|
177
|
-
handler = self.
|
|
338
|
+
handler = self._ROUTES[(self.request.method, self.request.path)]
|
|
178
339
|
|
|
179
340
|
# Handle the request
|
|
180
341
|
effects = handler(self)
|
|
@@ -191,7 +352,7 @@ class SimpleAPIBase(BaseHandler, ABC):
|
|
|
191
352
|
if isinstance(effect, Response):
|
|
192
353
|
effects[index] = effect.apply()
|
|
193
354
|
|
|
194
|
-
if effects[index].type == EffectType.SIMPLE_API_RESPONSE:
|
|
355
|
+
if cast(Effect, effects[index]).type == EffectType.SIMPLE_API_RESPONSE:
|
|
195
356
|
# If a response has already been found, return an error response immediately
|
|
196
357
|
if response_found:
|
|
197
358
|
log.error(f"Multiple responses provided by {SimpleAPI.__name__} handler")
|
|
@@ -210,13 +371,13 @@ class SimpleAPIBase(BaseHandler, ABC):
|
|
|
210
371
|
# If the handler returned an error response, return only that response effect
|
|
211
372
|
# and omit any other included effects
|
|
212
373
|
if 400 <= status_code <= 599:
|
|
213
|
-
return [effects[index]]
|
|
374
|
+
return [cast(Effect, effects[index])]
|
|
214
375
|
|
|
215
|
-
return effects
|
|
376
|
+
return cast(list[Effect], effects)
|
|
216
377
|
|
|
217
378
|
def accept_event(self) -> bool:
|
|
218
379
|
"""Ignore the event if the handler does not implement the route."""
|
|
219
|
-
return (self.request.method, self.request.path) in self.
|
|
380
|
+
return (self.request.method, self.request.path) in self._ROUTES
|
|
220
381
|
|
|
221
382
|
def authenticate(self, credentials: Credentials) -> bool:
|
|
222
383
|
"""Method the user should override to authenticate requests."""
|
|
@@ -257,8 +418,12 @@ class SimpleAPI(SimpleAPIBase, ABC):
|
|
|
257
418
|
f"class attributes: {', '.join(f'{cls.__name__}.{name}' for name in names)}"
|
|
258
419
|
)
|
|
259
420
|
|
|
260
|
-
|
|
261
|
-
|
|
421
|
+
@classmethod
|
|
422
|
+
def _path_prefix(cls) -> str:
|
|
423
|
+
# getattr needs a default else it will raise an exception. We also need to ensure that the
|
|
424
|
+
# final value is a string if the user specifies "None" as the prefix because this value gets
|
|
425
|
+
# prepended to the URL path
|
|
426
|
+
return getattr(cls, "PREFIX", "") or ""
|
|
262
427
|
|
|
263
428
|
|
|
264
429
|
class SimpleAPIRoute(SimpleAPIBase, ABC):
|
|
@@ -271,8 +436,6 @@ class SimpleAPIRoute(SimpleAPIBase, ABC):
|
|
|
271
436
|
f"Setting a PREFIX value on a {SimpleAPIRoute.__name__} is not allowed"
|
|
272
437
|
)
|
|
273
438
|
|
|
274
|
-
super().__init_subclass__(**kwargs)
|
|
275
|
-
|
|
276
439
|
for attr_name, attr_value in cls.__dict__.items():
|
|
277
440
|
decorator: Callable | None
|
|
278
441
|
match attr_name:
|
|
@@ -307,6 +470,8 @@ class SimpleAPIRoute(SimpleAPIBase, ABC):
|
|
|
307
470
|
|
|
308
471
|
decorator(path)(attr_value)
|
|
309
472
|
|
|
473
|
+
super().__init_subclass__(**kwargs)
|
|
474
|
+
|
|
310
475
|
def get(self) -> list[Response | Effect]:
|
|
311
476
|
"""Stub method for GET handler."""
|
|
312
477
|
return []
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
from collections.abc import ItemsView, Iterable, Iterator, KeysView, Mapping, ValuesView
|
|
2
|
+
from typing import Any, TypeVar, overload
|
|
3
|
+
|
|
4
|
+
KeyType = TypeVar("KeyType")
|
|
5
|
+
ValueType = TypeVar("ValueType", covariant=True)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class MultiDict(Mapping[KeyType, ValueType]):
|
|
9
|
+
"""Immutable key-value data structure that can store multiple values per key."""
|
|
10
|
+
|
|
11
|
+
def __init__(self, items: Iterable[tuple[KeyType, ValueType]] | None = None) -> None:
|
|
12
|
+
self._dict: dict[KeyType, ValueType] = {}
|
|
13
|
+
self._items = []
|
|
14
|
+
for key, value in items or ():
|
|
15
|
+
if key not in self._dict:
|
|
16
|
+
self._dict[key] = value
|
|
17
|
+
self._items.append((key, value))
|
|
18
|
+
|
|
19
|
+
def __len__(self) -> int:
|
|
20
|
+
return len(self._dict)
|
|
21
|
+
|
|
22
|
+
def __iter__(self) -> Iterator[KeyType]:
|
|
23
|
+
return iter(self.keys())
|
|
24
|
+
|
|
25
|
+
def __getitem__(self, key: KeyType, /) -> ValueType:
|
|
26
|
+
return self._dict[key]
|
|
27
|
+
|
|
28
|
+
def __contains__(self, x: object, /) -> bool:
|
|
29
|
+
return x in self._dict
|
|
30
|
+
|
|
31
|
+
@overload
|
|
32
|
+
def get(self, __key: KeyType, /) -> Any: ...
|
|
33
|
+
|
|
34
|
+
@overload
|
|
35
|
+
def get(self, __key: KeyType, /, __default: Any = None) -> Any: ...
|
|
36
|
+
|
|
37
|
+
def get(self, __key: KeyType, __default: Any | None = None) -> Any:
|
|
38
|
+
"""Get a value for a key if present, and if not, return the default value."""
|
|
39
|
+
return self._dict.get(__key, __default)
|
|
40
|
+
|
|
41
|
+
def get_list(self, __key: KeyType) -> list[ValueType]:
|
|
42
|
+
"""Get the values for a key if present, and if not, return an empty list."""
|
|
43
|
+
return [value for key, value in self._items if key == __key]
|
|
44
|
+
|
|
45
|
+
def items(self) -> ItemsView[KeyType, ValueType]:
|
|
46
|
+
"""Return an items view of the dict."""
|
|
47
|
+
return self._dict.items()
|
|
48
|
+
|
|
49
|
+
def multi_items(self) -> Iterable[tuple[KeyType, ValueType]]:
|
|
50
|
+
"""Return an iterable of tuples of the keys and values in the dict."""
|
|
51
|
+
yield from self._items
|
|
52
|
+
|
|
53
|
+
def keys(self) -> KeysView[KeyType]:
|
|
54
|
+
"""Return a keys view of the dict."""
|
|
55
|
+
return self._dict.keys()
|
|
56
|
+
|
|
57
|
+
def __reversed__(self) -> Iterator[KeyType]:
|
|
58
|
+
return iter(reversed(list(self.keys())))
|
|
59
|
+
|
|
60
|
+
def values(self) -> ValuesView[ValueType]:
|
|
61
|
+
"""Return a values view of the dict."""
|
|
62
|
+
return self._dict.values()
|
|
63
|
+
|
|
64
|
+
def __eq__(self, other: object) -> bool:
|
|
65
|
+
if not isinstance(other, MultiDict):
|
|
66
|
+
return NotImplemented
|
|
67
|
+
|
|
68
|
+
return other._items == self._items
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class CaseInsensitiveMultiDict(MultiDict[str, ValueType]):
|
|
72
|
+
"""
|
|
73
|
+
Case-insensitive immutable key-value data structure that can store multiple values per key.
|
|
74
|
+
|
|
75
|
+
Keys in the dict are interpreted in a case-insensitive manner.
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
def __init__(self, items: Iterable[tuple[str, ValueType]] | None = None) -> None:
|
|
79
|
+
super().__init__(((key.lower(), value) for key, value in items or ()))
|
|
80
|
+
|
|
81
|
+
def __getitem__(self, key: str, /) -> ValueType:
|
|
82
|
+
return super().__getitem__(key.lower())
|
|
83
|
+
|
|
84
|
+
def __contains__(self, x: object, /) -> bool:
|
|
85
|
+
if not isinstance(x, str):
|
|
86
|
+
return False
|
|
87
|
+
|
|
88
|
+
return super().__contains__(x.lower())
|
|
89
|
+
|
|
90
|
+
def get(self, __key: KeyType, __default: Any | None = None) -> Any:
|
|
91
|
+
"""Get a value for a key if present, and if not, return the default value."""
|
|
92
|
+
if not isinstance(__key, str):
|
|
93
|
+
return __default
|
|
94
|
+
|
|
95
|
+
return super().get(__key.lower(), __default)
|
|
96
|
+
|
|
97
|
+
def get_list(self, __key: KeyType) -> list[ValueType]:
|
|
98
|
+
"""Get the values for a key if present, and if not, return an empty list."""
|
|
99
|
+
if not isinstance(__key, str):
|
|
100
|
+
return []
|
|
101
|
+
|
|
102
|
+
return super().get_list(__key.lower())
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def separate_headers(headers: Mapping[str, str]) -> list[tuple[str, str]]:
|
|
106
|
+
"""
|
|
107
|
+
Break apart header values containing comma-separated lists into discrete key-value pairs.
|
|
108
|
+
"""
|
|
109
|
+
headers_list = []
|
|
110
|
+
|
|
111
|
+
for key, values in headers.items():
|
|
112
|
+
for value in values.split(","):
|
|
113
|
+
headers_list.append((key, value.strip()))
|
|
114
|
+
|
|
115
|
+
return headers_list
|