├── .nvmrc
├── tests
├── __init__.py
├── database.py
├── static
│ └── e2e
│ │ ├── utils
│ │ ├── common.ts
│ │ └── register.ts
│ │ └── routes.ts
├── case
│ └── test_case_messaging.py
└── report
│ └── test_report_flows.py
├── docs
├── static
│ ├── .nojekyll
│ └── img
│ │ ├── favicon.ico
│ │ ├── thumb-1.png
│ │ ├── thumb-2.png
│ │ ├── thumb-3.png
│ │ ├── thumb-4.png
│ │ ├── thumb-5.png
│ │ ├── thumb-6.png
│ │ ├── docusaurus.png
│ │ ├── admin-ui-admin.png
│ │ ├── admin-ui-tasks.png
│ │ ├── admin-ui-users.png
│ │ ├── admin-ui-project.png
│ │ ├── admin-ui-settings.png
│ │ ├── attorney-section.png
│ │ ├── incident-flow-0.png
│ │ ├── admin-ui-case-types.png
│ │ ├── admin-ui-cost-model.png
│ │ ├── admin-ui-incidents.png
│ │ ├── example-risk-score.png
│ │ ├── slack-setup-dialogs.png
│ │ ├── slack-setup-events.png
│ │ ├── admin-ui-notifications.png
│ │ ├── docusaurus-social-card.jpg
│ │ ├── email-incident-welcome.png
│ │ ├── slack-conversation-pir.png
│ │ ├── slack-setup-commands-0.png
│ │ ├── slack-setup-commands-1.png
│ │ ├── slack-setup-commands-2.png
│ │ ├── admin-ui-case-priorities.png
│ │ ├── admin-ui-case-severities.png
│ │ ├── admin-ui-contacts-teams.png
│ │ ├── admin-ui-dashboard-type.png
│ │ ├── admin-ui-edit-cost-model.png
│ │ ├── admin-ui-incident-report.png
│ │ ├── admin-ui-incident-types.png
│ │ ├── admin-ui-knowledge-tags.png
│ │ ├── google-docs-task-comment.png
│ │ ├── pagerduty-service-setup.png
│ │ ├── admin-ui-contacts-services.png
│ │ ├── admin-ui-dashboard-forecast.png
│ │ ├── admin-ui-dashboard-priority.png
│ │ ├── admin-ui-dashboard-top-line.png
│ │ ├── admin-ui-incident-feedback.png
│ │ ├── admin-ui-incident-plugins.png
│ │ ├── admin-ui-incident-workflows.png
│ │ ├── admin-ui-signal-definition.png
│ │ ├── admin-ui-signal-entity-type.png
│ │ ├── google-setup-credentials-0.png
│ │ ├── admin-ui-contacts-individuals.png
│ │ ├── admin-ui-create-edit-runbook.png
│ │ ├── admin-ui-create-edit-template.png
│ │ ├── admin-ui-incident-cost-types.png
│ │ ├── admin-ui-incident-priorities.png
│ │ ├── admin-ui-knowledge-documents.png
│ │ ├── admin-ui-knowledge-tag-types.png
│ │ ├── admin-ui-signal-filter-dedupe.png
│ │ ├── admin-ui-signal-filter-snooze.png
│ │ ├── slack-conversation-list-tasks.png
│ │ ├── admin-ui-associate-case-template.png
│ │ ├── admin-ui-incident-report-receipt.png
│ │ ├── admin-ui-signal-entity-type-name.png
│ │ ├── slack-conversation-assign-role.png
│ │ ├── slack-conversation-create-task.png
│ │ ├── slack-conversation-edit-incident.png
│ │ ├── slack-conversation-engage-oncall.png
│ │ ├── slack-conversation-list-my-tasks.png
│ │ ├── slack-conversation-run-workflow.png
│ │ ├── slack-conversation-status-report.png
│ │ ├── admin-ui-incident-report-resources.png
│ │ ├── admin-ui-signal-engagement-filter.png
│ │ ├── slack-conversation-list-workflows.png
│ │ ├── slack-conversation-report-incident.png
│ │ ├── admin-ui-associate-incident-template.png
│ │ ├── slack-conversation-add-timeline-event.png
│ │ ├── slack-conversation-list-participants.png
│ │ ├── slack-conversation-report-executive.png
│ │ ├── slack-conversation-update-participant.png
│ │ ├── slack-plugin-conversation-management.png
│ │ ├── slack-conversation-notifications-group.png
│ │ ├── slack-conversation-status-report-response.png
│ │ ├── slack-conversations-incident-notification.png
│ │ ├── slack-plugin-contact-information-resolver.png
│ │ ├── user-guide-incident-feedback-conversation-modal.png
│ │ └── user-guide-incident-feedback-conversation-direct-message.png
├── docs
│ ├── administration
│ │ ├── settings
│ │ │ ├── contact
│ │ │ │ ├── index.mdx
│ │ │ │ └── team.mdx
│ │ │ ├── data
│ │ │ │ ├── index.mdx
│ │ │ │ ├── transport.mdx
│ │ │ │ ├── status.mdx
│ │ │ │ ├── type.mdx
│ │ │ │ ├── data-format.mdx
│ │ │ │ └── environment.mdx
│ │ │ ├── signal
│ │ │ │ ├── index.mdx
│ │ │ │ └── engagement-filter.mdx
│ │ │ ├── knowledge
│ │ │ │ ├── index.mdx
│ │ │ │ ├── definition.mdx
│ │ │ │ ├── term.mdx
│ │ │ │ └── tag-type.mdx
│ │ │ ├── incident
│ │ │ │ ├── index.mdx
│ │ │ │ └── notification.mdx
│ │ │ ├── plugins
│ │ │ │ ├── configuring-opsgenie.mdx
│ │ │ │ └── configuring-pagerduty.mdx
│ │ │ ├── index.mdx
│ │ │ └── case
│ │ │ │ ├── index.mdx
│ │ │ │ ├── case-priority.mdx
│ │ │ │ └── case-severity.mdx
│ │ ├── index.mdx
│ │ ├── contributing
│ │ │ └── plugins
│ │ │ │ └── testing.mdx
│ │ └── faq.mdx
│ ├── user-guide
│ │ ├── data.mdx
│ │ ├── cases
│ │ │ └── index.mdx
│ │ ├── incidents
│ │ │ ├── tasks.mdx
│ │ │ ├── index.mdx
│ │ │ └── feedback.mdx
│ │ ├── dashboard
│ │ │ └── index.mdx
│ │ └── index.mdx
│ └── changelog.mdx
├── babel.config.js
├── branding
│ ├── FullColor_1280x1024.pdf
│ ├── FullColor_1280x1024_300dpi.jpg
│ ├── FullColor_1280x1024_72dpi.jpg
│ ├── FullColor_1280x1024_72dpi.png
│ ├── Grayscale_1280x1024_72dpi.png
│ ├── website_logo_solid_background.png
│ ├── FullColor_IconOnly_1280x1024_72dpi.jpg
│ ├── FullColor_TextOnly_1280x1024_72dpi.jpg
│ ├── website_logo_transparent_background.png
│ ├── FullColor_TransparentBg_1280x1024_72dpi.png
│ ├── website_logo_solid_background_logo_only.png
│ └── website_logo_solid_background_icon_only_square.png
├── src
│ ├── pages
│ │ ├── markdown-page.md
│ │ └── index.module.css
│ └── components
│ │ └── HomepageFeatures
│ │ └── styles.module.css
├── .gitignore
└── sidebars.js
├── Dockerfile
├── src
└── dispatch
│ ├── tag
│ └── __init__.py
│ ├── auth
│ └── __init__.py
│ ├── case
│ ├── __init__.py
│ ├── priority
│ │ └── __init__.py
│ ├── severity
│ │ └── __init__.py
│ ├── type
│ │ ├── __init__.py
│ │ └── config.py
│ ├── scheduled_internal.py
│ └── enums.py
│ ├── common
│ ├── __init__.py
│ └── utils
│ │ ├── __init__.py
│ │ └── views.py
│ ├── data
│ ├── __init__.py
│ ├── query
│ │ └── __init__.py
│ └── source
│ │ ├── status
│ │ └── __init__.py
│ │ ├── type
│ │ └── __init__.py
│ │ ├── data_format
│ │ └── __init__.py
│ │ ├── environment
│ │ └── __init__.py
│ │ └── transport
│ │ └── __init__.py
│ ├── database
│ ├── __init__.py
│ ├── revisions
│ │ ├── __init__.py
│ │ ├── core
│ │ │ ├── README
│ │ │ ├── versions
│ │ │ │ ├── 2021-06-02_f011c050b9ba.py
│ │ │ │ ├── 2021-07-23_e0d568f345c9.py
│ │ │ │ ├── 2023-04-14_3dd4d12844dc.py
│ │ │ │ └── 2023-09-27_5c60513d6e5e.py
│ │ │ └── script.py.mako
│ │ └── tenant
│ │ │ ├── README
│ │ │ ├── versions
│ │ │ ├── 2021-06-02_f011c050b9ba.py
│ │ │ ├── 2022-10-04_3a5e776ddce4.py
│ │ │ ├── 2022-10-31_34aeedc9d09a.py
│ │ │ ├── 2025-04-17_8f324b0f365a.py
│ │ │ ├── 2024-08-26_d6b3853be8e4.py
│ │ │ ├── 2021-10-05_ceaf01079f4f.py
│ │ │ ├── 2023-01-25_e23c468440c9.py
│ │ │ ├── 2023-02-27_20d3801ad5b7.py
│ │ │ ├── 2023-09-13_e875e9544048.py
│ │ │ ├── 2024-07-12_25b8b5829158.py
│ │ │ ├── 2023-03-02_65db2acae3ea.py
│ │ │ ├── 2021-11-01_ecabe49272c8.py
│ │ │ ├── 2024-02-21_27e6558e26a8.py
│ │ │ ├── 2023-03-09_ec78c132ab93.py
│ │ │ ├── 2023-01-27_956eb8f8987e.py
│ │ │ ├── 2023-03-02_5955ed5f76b4.py
│ │ │ ├── 2024-08-05_71cd7ed999c4.py
│ │ │ ├── 2023-06-06_0a19c5a4c2ba.py
│ │ │ ├── 2024-02-13_0283f2bbe9dd.py
│ │ │ ├── 2022-02-15_3097592c0739.py
│ │ │ ├── 2022-12-15_bd61c1e1e7cd.py
│ │ │ ├── 2023-08-04_6b98c28edd86.py
│ │ │ ├── 2023-11-29a_bdaeabba3e53.py
│ │ │ ├── 2024-04-02_71cb25c06fa0.py
│ │ │ ├── 2024-12-05_575ca7d954a8.py
│ │ │ ├── 2022-07-28_847ff5e1f81c.py
│ │ │ ├── 2024-09-20_1f4dc687945d.py
│ │ │ ├── 2022-03-16_03b7e0d36519.py
│ │ │ ├── 2022-08-04_8d7232b7cbd4.py
│ │ │ ├── 2025-06-04_7fc3888c7b9a.py
│ │ │ ├── 2022-04-13_e40aefe7fc4d.py
│ │ │ ├── 2023-03-03_7ddae3ba7822.py
│ │ │ ├── 2023-06-05_35a2ad8b53a2.py
│ │ │ ├── 2025-03-14_92a359040b8e.py
│ │ │ ├── 2023-08-10_74d279f6e4f6.py
│ │ │ └── 2023-12-08_2f06fd73eae6.py
│ │ │ └── script.py.mako
│ └── enums.py
│ ├── document
│ ├── __init__.py
│ └── utils.py
│ ├── entity
│ └── __init__.py
│ ├── event
│ └── __init__.py
│ ├── group
│ ├── __init__.py
│ └── enums.py
│ ├── incident
│ ├── __init__.py
│ ├── type
│ │ ├── __init__.py
│ │ └── config.py
│ ├── priority
│ │ └── __init__.py
│ ├── severity
│ │ └── __init__.py
│ └── enums.py
│ ├── plugin
│ └── __init__.py
│ ├── plugins
│ ├── __init__.py
│ ├── bases
│ │ ├── publish.py
│ │ ├── monitor.py
│ │ ├── tag.py
│ │ ├── term.py
│ │ ├── source.py
│ │ ├── email.py
│ │ ├── conference.py
│ │ ├── participant.py
│ │ ├── signal_consumer.py
│ │ ├── signal_enrichment.py
│ │ ├── auth_provider.py
│ │ ├── workflow.py
│ │ ├── artificial_intelligence.py
│ │ ├── ticket.py
│ │ ├── conversation.py
│ │ ├── metric.py
│ │ ├── auth_mfa.py
│ │ ├── contact.py
│ │ ├── document.py
│ │ ├── oncall.py
│ │ ├── definition.py
│ │ ├── task.py
│ │ ├── investigation_tooling.py
│ │ └── participant_group.py
│ ├── dispatch_google
│ │ ├── __init__.py
│ │ ├── calendar
│ │ │ ├── _version.py
│ │ │ └── __init__.py
│ │ ├── docs
│ │ │ ├── _version.py
│ │ │ └── __init__.py
│ │ ├── drive
│ │ │ ├── _version.py
│ │ │ └── __init__.py
│ │ ├── gmail
│ │ │ ├── _version.py
│ │ │ └── __init__.py
│ │ └── groups
│ │ │ ├── _version.py
│ │ │ └── __init__.py
│ ├── dispatch_test
│ │ ├── __init__.py
│ │ ├── term.py
│ │ ├── conference.py
│ │ ├── participant.py
│ │ ├── document_resolver.py
│ │ ├── ticket.py
│ │ ├── workflow.py
│ │ ├── participant_group.py
│ │ ├── task.py
│ │ ├── contact.py
│ │ ├── definition.py
│ │ └── oncall.py
│ ├── dispatch_opsgenie
│ │ └── __init__.py
│ ├── dispatch_slack
│ │ ├── case
│ │ │ └── __init__.py
│ │ ├── _version.py
│ │ ├── __init__.py
│ │ ├── ack.py
│ │ ├── incident
│ │ │ └── messages.py
│ │ └── exceptions.py
│ ├── dispatch_microsoft_teams
│ │ ├── __init__.py
│ │ └── conference
│ │ │ ├── _version.py
│ │ │ └── __init__.py
│ ├── dispatch_aws
│ │ ├── _version.py
│ │ ├── __init__.py
│ │ └── config.py
│ ├── dispatch_core
│ │ ├── _version.py
│ │ ├── __init__.py
│ │ ├── config.py
│ │ ├── service.py
│ │ └── exceptions.py
│ ├── dispatch_duo
│ │ ├── _version.py
│ │ ├── __init__.py
│ │ ├── enums.py
│ │ ├── service.py
│ │ └── config.py
│ ├── dispatch_jira
│ │ ├── _version.py
│ │ └── __init__.py
│ ├── dispatch_zoom
│ │ ├── _version.py
│ │ ├── __init__.py
│ │ └── config.py
│ ├── dispatch_github
│ │ ├── _version.py
│ │ └── __init__.py
│ ├── dispatch_openai
│ │ ├── _version.py
│ │ ├── __init__.py
│ │ └── config.py
│ ├── dispatch_pagerduty
│ │ ├── _version.py
│ │ └── __init__.py
│ ├── generic_workflow
│ │ ├── _version.py
│ │ └── __init__.py
│ ├── dispatch_atlassian_confluence
│ │ ├── _version.py
│ │ ├── docs
│ │ │ ├── _version.py
│ │ │ └── __init__.py
│ │ └── __init__.py
│ └── base
│ │ └── __init__.py
│ ├── project
│ └── __init__.py
│ ├── report
│ ├── __init__.py
│ └── enums.py
│ ├── route
│ └── __init__.py
│ ├── search
│ └── __init__.py
│ ├── service
│ └── __init__.py
│ ├── signal
│ ├── __init__.py
│ ├── enums.py
│ └── exceptions.py
│ ├── storage
│ ├── __init__.py
│ └── enums.py
│ ├── tag_type
│ └── __init__.py
│ ├── task
│ ├── __init__.py
│ └── enums.py
│ ├── team
│ └── __init__.py
│ ├── term
│ └── __init__.py
│ ├── ticket
│ └── __init__.py
│ ├── workflow
│ ├── __init__.py
│ └── enums.py
│ ├── conference
│ └── __init__.py
│ ├── conversation
│ └── __init__.py
│ ├── cost_model
│ └── __init__.py
│ ├── definition
│ └── __init__.py
│ ├── entity_type
│ └── __init__.py
│ ├── incident_cost
│ └── __init__.py
│ ├── incident_role
│ └── __init__.py
│ ├── individual
│ └── __init__.py
│ ├── messaging
│ ├── __init__.py
│ └── email
│ │ └── templates
│ │ └── notification.mjml
│ ├── notification
│ └── __init__.py
│ ├── organization
│ └── __init__.py
│ ├── participant
│ └── __init__.py
│ ├── search_filter
│ └── __init__.py
│ ├── feedback
│ ├── incident
│ │ ├── __init__.py
│ │ └── enums.py
│ └── service
│ │ ├── __init__.py
│ │ ├── reminder
│ │ └── __init__.py
│ │ └── enums.py
│ ├── incident_cost_type
│ ├── __init__.py
│ └── config.py
│ ├── participant_role
│ ├── __init__.py
│ └── enums.py
│ ├── participant_activity
│ └── __init__.py
│ ├── static
│ └── dispatch
│ │ ├── public
│ │ ├── static
│ │ │ ├── .gitkeep
│ │ │ ├── robots.txt
│ │ │ ├── m.png
│ │ │ └── icon
│ │ │ │ └── file_empty.svg
│ │ ├── robots.txt
│ │ └── favicon.ico
│ │ ├── .npmrc
│ │ ├── jsconfig.json
│ │ ├── src
│ │ ├── assets
│ │ │ ├── logo.png
│ │ │ ├── styles
│ │ │ │ └── timeline.css
│ │ │ └── logo.svg
│ │ ├── auth
│ │ │ ├── customAuthProvider.js
│ │ │ ├── userSettings.js
│ │ │ └── basicAuthProvider.js
│ │ ├── components
│ │ │ ├── layouts
│ │ │ │ ├── AdminLayout.vue
│ │ │ │ └── index.js
│ │ │ ├── Loading.vue
│ │ │ ├── InfoWidget.vue
│ │ │ └── Refresh.vue
│ │ ├── feedback
│ │ │ ├── service
│ │ │ │ └── api.js
│ │ │ └── incident
│ │ │ │ └── api.js
│ │ ├── incident_role
│ │ │ └── api.js
│ │ ├── styles
│ │ │ └── variables.sass
│ │ ├── case
│ │ │ ├── priority
│ │ │ │ ├── CasePriority.vue
│ │ │ │ └── api.js
│ │ │ ├── severity
│ │ │ │ ├── CaseSeverity.vue
│ │ │ │ └── api.js
│ │ │ ├── type
│ │ │ │ └── api.js
│ │ │ └── EscalateButton.vue
│ │ ├── incident
│ │ │ ├── priority
│ │ │ │ ├── IncidentPriority.vue
│ │ │ │ └── api.js
│ │ │ ├── severity
│ │ │ │ ├── IncidentSeverity.vue
│ │ │ │ └── api.js
│ │ │ └── type
│ │ │ │ └── api.js
│ │ ├── cost_model
│ │ │ └── api.js
│ │ ├── entity_type
│ │ │ ├── playground
│ │ │ │ └── store.js
│ │ │ └── utils.js
│ │ ├── team
│ │ │ └── api.js
│ │ ├── term
│ │ │ └── api.js
│ │ ├── signal
│ │ │ ├── SignalInstanceNode.vue
│ │ │ ├── filter
│ │ │ │ └── api.js
│ │ │ └── engagement
│ │ │ │ └── api.js
│ │ ├── data
│ │ │ ├── alert
│ │ │ │ └── api.js
│ │ │ ├── query
│ │ │ │ └── api.js
│ │ │ └── source
│ │ │ │ ├── type
│ │ │ │ └── api.js
│ │ │ │ ├── api.js
│ │ │ │ ├── status
│ │ │ │ └── api.js
│ │ │ │ ├── transport
│ │ │ │ └── api.js
│ │ │ │ ├── dataFormat
│ │ │ │ └── api.js
│ │ │ │ └── environment
│ │ │ │ └── api.js
│ │ ├── tag_type
│ │ │ └── api.js
│ │ ├── document
│ │ │ └── api.js
│ │ ├── definition
│ │ │ └── api.js
│ │ ├── individual
│ │ │ └── api.js
│ │ ├── project
│ │ │ └── api.js
│ │ ├── notification
│ │ │ └── api.js
│ │ ├── organization
│ │ │ └── api.js
│ │ ├── case_cost_type
│ │ │ └── api.js
│ │ ├── email_templates
│ │ │ └── api.js
│ │ ├── forms
│ │ │ └── types
│ │ │ │ └── api.js
│ │ ├── util
│ │ │ └── form.js
│ │ ├── incident_cost_type
│ │ │ └── api.js
│ │ ├── atomics
│ │ │ └── Hotkey.vue
│ │ ├── service
│ │ │ └── api.js
│ │ ├── composables
│ │ │ └── useSavingState.ts
│ │ ├── App.vue
│ │ ├── workflow
│ │ │ └── api.js
│ │ ├── views
│ │ │ └── Widget.vue
│ │ ├── entity
│ │ │ └── api.js
│ │ └── task
│ │ │ └── api.js
│ │ ├── vitest.config.js
│ │ ├── preinstall.js
│ │ └── index.html
│ ├── ai
│ └── exceptions.py
│ ├── rate_limiter.py
│ ├── types.py
│ ├── email_templates
│ └── enums.py
│ ├── utils.py
│ ├── case_cost_type
│ └── config.py
│ └── forms
│ └── enums.py
├── .gitbook.yaml
├── docker
├── .dockerignore
├── .env.example
└── docker-compose.yml
├── .github
├── CODEOWNERS
├── workflows
│ ├── publish-image-test.yml
│ ├── enforce-labels.yml
│ ├── publish-image.yml
│ ├── javascript.yml
│ └── deploy-docs-test.yml
└── ISSUE_TEMPLATE
│ └── feature_request.md
├── bin
└── run.py
├── tox.ini
├── .vscode
├── extensions.json
└── launch.json
├── MANIFEST.in
├── .prettierrc
├── data
├── .env
└── update-example-data.sh
├── .devcontainer
├── .env.example
├── postCreateCommand.sh
└── Dockerfile
├── setup.cfg
├── requirements-dev.in
├── .gitattributes
├── .dockerignore
└── .coveragerc
/.nvmrc:
--------------------------------------------------------------------------------
1 | 20.18.0
2 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/static/.nojekyll:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | docker/Dockerfile
--------------------------------------------------------------------------------
/src/dispatch/tag/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitbook.yaml:
--------------------------------------------------------------------------------
1 | root: ./docs/
2 |
--------------------------------------------------------------------------------
/src/dispatch/auth/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/case/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/common/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/data/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/database/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/document/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/entity/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/event/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/group/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/incident/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/plugin/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/project/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/report/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/route/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/search/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/service/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/signal/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/storage/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/tag_type/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/task/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/team/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/term/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/ticket/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/workflow/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docker/.dockerignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/src/dispatch/case/priority/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/case/severity/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/case/type/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/common/utils/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/conference/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/conversation/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/cost_model/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/data/query/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/definition/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/entity_type/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/incident/type/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/incident_cost/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/incident_role/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/individual/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/messaging/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/notification/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/organization/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/participant/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/bases/publish.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/search_filter/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/data/source/status/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/data/source/type/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/feedback/incident/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/feedback/service/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/incident/priority/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/incident/severity/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/incident_cost_type/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/participant_role/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/data/source/data_format/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/data/source/environment/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/data/source/transport/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/participant_activity/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_google/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_test/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/feedback/service/reminder/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_opsgenie/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_slack/case/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/public/static/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_microsoft_teams/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_aws/_version.py:
--------------------------------------------------------------------------------
1 | __version__ = "0.1.0"
2 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_core/_version.py:
--------------------------------------------------------------------------------
1 | __version__ = "0.1.0"
2 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_duo/_version.py:
--------------------------------------------------------------------------------
1 | __version__ = "0.1.0"
2 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_jira/_version.py:
--------------------------------------------------------------------------------
1 | __version__ = "0.1.0"
2 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_slack/_version.py:
--------------------------------------------------------------------------------
1 | __version__ = "0.1.0"
2 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_zoom/_version.py:
--------------------------------------------------------------------------------
1 | __version__ = "0.1.0"
2 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_github/_version.py:
--------------------------------------------------------------------------------
1 | __version__ = "0.1.0"
2 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_openai/_version.py:
--------------------------------------------------------------------------------
1 | __version__ = "0.1.0"
2 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_pagerduty/_version.py:
--------------------------------------------------------------------------------
1 | __version__ = "0.1.0"
2 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/generic_workflow/_version.py:
--------------------------------------------------------------------------------
1 | __version__ = "0.1.0"
2 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/core/README:
--------------------------------------------------------------------------------
1 | Generic single-database configuration.
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/tenant/README:
--------------------------------------------------------------------------------
1 | Generic single-database configuration.
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_google/calendar/_version.py:
--------------------------------------------------------------------------------
1 | __version__ = "0.1.0"
2 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_google/docs/_version.py:
--------------------------------------------------------------------------------
1 | __version__ = "0.1.0"
2 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_google/drive/_version.py:
--------------------------------------------------------------------------------
1 | __version__ = "0.1.0"
2 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_google/gmail/_version.py:
--------------------------------------------------------------------------------
1 | __version__ = "0.1.0"
2 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_google/groups/_version.py:
--------------------------------------------------------------------------------
1 | __version__ = "0.1.0"
2 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/public/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow: /
3 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/public/static/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow:
--------------------------------------------------------------------------------
/src/dispatch/case/scheduled_internal.py:
--------------------------------------------------------------------------------
1 | def schedule_placeholder():
2 | pass
3 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_atlassian_confluence/_version.py:
--------------------------------------------------------------------------------
1 | __version__ = "0.0.1"
2 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | kevgliss
2 | metroid-samus
3 | mvilanova
4 | whitdog47
5 | wssheldon
6 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_atlassian_confluence/docs/_version.py:
--------------------------------------------------------------------------------
1 | __version__ = "0.0.1"
2 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_aws/__init__.py:
--------------------------------------------------------------------------------
1 | from ._version import __version__ # noqa
2 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_core/__init__.py:
--------------------------------------------------------------------------------
1 | from ._version import __version__ # noqa
2 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_duo/__init__.py:
--------------------------------------------------------------------------------
1 | from ._version import __version__ # noqa
2 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_jira/__init__.py:
--------------------------------------------------------------------------------
1 | from ._version import __version__ # noqa
2 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_slack/__init__.py:
--------------------------------------------------------------------------------
1 | from ._version import __version__ # noqa
2 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_zoom/__init__.py:
--------------------------------------------------------------------------------
1 | from ._version import __version__ # noqa
2 |
--------------------------------------------------------------------------------
/src/dispatch/database/enums.py:
--------------------------------------------------------------------------------
1 | DISPATCH_ORGANIZATION_SCHEMA_PREFIX = "dispatch_organization"
2 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_github/__init__.py:
--------------------------------------------------------------------------------
1 | from ._version import __version__ # noqa
2 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_microsoft_teams/conference/_version.py:
--------------------------------------------------------------------------------
1 | __version__ = "0.1.0"
2 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_openai/__init__.py:
--------------------------------------------------------------------------------
1 | from ._version import __version__ # noqa
2 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_pagerduty/__init__.py:
--------------------------------------------------------------------------------
1 | from ._version import __version__ # noqa
2 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/generic_workflow/__init__.py:
--------------------------------------------------------------------------------
1 | from ._version import __version__ # noqa
2 |
--------------------------------------------------------------------------------
/docs/static/img/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/favicon.ico
--------------------------------------------------------------------------------
/docs/static/img/thumb-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/thumb-1.png
--------------------------------------------------------------------------------
/docs/static/img/thumb-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/thumb-2.png
--------------------------------------------------------------------------------
/docs/static/img/thumb-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/thumb-3.png
--------------------------------------------------------------------------------
/docs/static/img/thumb-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/thumb-4.png
--------------------------------------------------------------------------------
/docs/static/img/thumb-5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/thumb-5.png
--------------------------------------------------------------------------------
/docs/static/img/thumb-6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/thumb-6.png
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_google/calendar/__init__.py:
--------------------------------------------------------------------------------
1 | from ._version import __version__ # noqa
2 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_google/docs/__init__.py:
--------------------------------------------------------------------------------
1 | from ._version import __version__ # noqa
2 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_google/drive/__init__.py:
--------------------------------------------------------------------------------
1 | from ._version import __version__ # noqa
2 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_google/gmail/__init__.py:
--------------------------------------------------------------------------------
1 | from ._version import __version__ # noqa
2 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_google/groups/__init__.py:
--------------------------------------------------------------------------------
1 | from ._version import __version__ # noqa
2 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_atlassian_confluence/__init__.py:
--------------------------------------------------------------------------------
1 | from ._version import __version__ # noqa
2 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/.npmrc:
--------------------------------------------------------------------------------
1 | //registry.npmjs.org/:_authToken=${FORMKIT_ENTERPRISE_TOKEN}
2 |
3 |
--------------------------------------------------------------------------------
/docs/docs/administration/settings/contact/index.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 5
3 | ---
4 |
5 | # Contact
6 |
--------------------------------------------------------------------------------
/docs/static/img/docusaurus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/docusaurus.png
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_atlassian_confluence/docs/__init__.py:
--------------------------------------------------------------------------------
1 | from ._version import __version__ # noqa
2 |
--------------------------------------------------------------------------------
/docs/static/img/admin-ui-admin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/admin-ui-admin.png
--------------------------------------------------------------------------------
/docs/static/img/admin-ui-tasks.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/admin-ui-tasks.png
--------------------------------------------------------------------------------
/docs/static/img/admin-ui-users.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/admin-ui-users.png
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_microsoft_teams/conference/__init__.py:
--------------------------------------------------------------------------------
1 | from ._version import __version__ # noqa
2 |
--------------------------------------------------------------------------------
/bin/run.py:
--------------------------------------------------------------------------------
1 | import uvicorn
2 |
3 | if __name__ == "__main__":
4 | uvicorn.run("dispatch.main:app", host="0.0.0.0")
5 |
--------------------------------------------------------------------------------
/docs/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
3 | };
4 |
--------------------------------------------------------------------------------
/docs/branding/FullColor_1280x1024.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/branding/FullColor_1280x1024.pdf
--------------------------------------------------------------------------------
/docs/static/img/admin-ui-project.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/admin-ui-project.png
--------------------------------------------------------------------------------
/docs/static/img/admin-ui-settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/admin-ui-settings.png
--------------------------------------------------------------------------------
/docs/static/img/attorney-section.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/attorney-section.png
--------------------------------------------------------------------------------
/docs/static/img/incident-flow-0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/incident-flow-0.png
--------------------------------------------------------------------------------
/docs/static/img/admin-ui-case-types.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/admin-ui-case-types.png
--------------------------------------------------------------------------------
/docs/static/img/admin-ui-cost-model.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/admin-ui-cost-model.png
--------------------------------------------------------------------------------
/docs/static/img/admin-ui-incidents.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/admin-ui-incidents.png
--------------------------------------------------------------------------------
/docs/static/img/example-risk-score.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/example-risk-score.png
--------------------------------------------------------------------------------
/docs/static/img/slack-setup-dialogs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/slack-setup-dialogs.png
--------------------------------------------------------------------------------
/docs/static/img/slack-setup-events.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/slack-setup-events.png
--------------------------------------------------------------------------------
/tests/database.py:
--------------------------------------------------------------------------------
1 | from sqlalchemy.orm import scoped_session, sessionmaker
2 |
3 | Session = scoped_session(sessionmaker())
4 |
--------------------------------------------------------------------------------
/docs/static/img/admin-ui-notifications.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/admin-ui-notifications.png
--------------------------------------------------------------------------------
/docs/static/img/docusaurus-social-card.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/docusaurus-social-card.jpg
--------------------------------------------------------------------------------
/docs/static/img/email-incident-welcome.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/email-incident-welcome.png
--------------------------------------------------------------------------------
/docs/static/img/slack-conversation-pir.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/slack-conversation-pir.png
--------------------------------------------------------------------------------
/docs/static/img/slack-setup-commands-0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/slack-setup-commands-0.png
--------------------------------------------------------------------------------
/docs/static/img/slack-setup-commands-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/slack-setup-commands-1.png
--------------------------------------------------------------------------------
/docs/static/img/slack-setup-commands-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/slack-setup-commands-2.png
--------------------------------------------------------------------------------
/docs/branding/FullColor_1280x1024_300dpi.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/branding/FullColor_1280x1024_300dpi.jpg
--------------------------------------------------------------------------------
/docs/branding/FullColor_1280x1024_72dpi.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/branding/FullColor_1280x1024_72dpi.jpg
--------------------------------------------------------------------------------
/docs/branding/FullColor_1280x1024_72dpi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/branding/FullColor_1280x1024_72dpi.png
--------------------------------------------------------------------------------
/docs/branding/Grayscale_1280x1024_72dpi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/branding/Grayscale_1280x1024_72dpi.png
--------------------------------------------------------------------------------
/docs/static/img/admin-ui-case-priorities.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/admin-ui-case-priorities.png
--------------------------------------------------------------------------------
/docs/static/img/admin-ui-case-severities.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/admin-ui-case-severities.png
--------------------------------------------------------------------------------
/docs/static/img/admin-ui-contacts-teams.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/admin-ui-contacts-teams.png
--------------------------------------------------------------------------------
/docs/static/img/admin-ui-dashboard-type.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/admin-ui-dashboard-type.png
--------------------------------------------------------------------------------
/docs/static/img/admin-ui-edit-cost-model.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/admin-ui-edit-cost-model.png
--------------------------------------------------------------------------------
/docs/static/img/admin-ui-incident-report.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/admin-ui-incident-report.png
--------------------------------------------------------------------------------
/docs/static/img/admin-ui-incident-types.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/admin-ui-incident-types.png
--------------------------------------------------------------------------------
/docs/static/img/admin-ui-knowledge-tags.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/admin-ui-knowledge-tags.png
--------------------------------------------------------------------------------
/docs/static/img/google-docs-task-comment.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/google-docs-task-comment.png
--------------------------------------------------------------------------------
/docs/static/img/pagerduty-service-setup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/pagerduty-service-setup.png
--------------------------------------------------------------------------------
/docs/branding/website_logo_solid_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/branding/website_logo_solid_background.png
--------------------------------------------------------------------------------
/docs/static/img/admin-ui-contacts-services.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/admin-ui-contacts-services.png
--------------------------------------------------------------------------------
/docs/static/img/admin-ui-dashboard-forecast.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/admin-ui-dashboard-forecast.png
--------------------------------------------------------------------------------
/docs/static/img/admin-ui-dashboard-priority.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/admin-ui-dashboard-priority.png
--------------------------------------------------------------------------------
/docs/static/img/admin-ui-dashboard-top-line.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/admin-ui-dashboard-top-line.png
--------------------------------------------------------------------------------
/docs/static/img/admin-ui-incident-feedback.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/admin-ui-incident-feedback.png
--------------------------------------------------------------------------------
/docs/static/img/admin-ui-incident-plugins.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/admin-ui-incident-plugins.png
--------------------------------------------------------------------------------
/docs/static/img/admin-ui-incident-workflows.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/admin-ui-incident-workflows.png
--------------------------------------------------------------------------------
/docs/static/img/admin-ui-signal-definition.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/admin-ui-signal-definition.png
--------------------------------------------------------------------------------
/docs/static/img/admin-ui-signal-entity-type.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/admin-ui-signal-entity-type.png
--------------------------------------------------------------------------------
/docs/static/img/google-setup-credentials-0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/google-setup-credentials-0.png
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "paths": {
4 | "@/*": ["./src/*"]
5 | }
6 | }
7 | }
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/src/dispatch/static/dispatch/public/favicon.ico
--------------------------------------------------------------------------------
/tox.ini:
--------------------------------------------------------------------------------
1 | [tox]
2 | envlist = py311
3 |
4 | [testenv]
5 | deps =
6 | pytest
7 | coverage
8 | commands =
9 | pytest
10 |
--------------------------------------------------------------------------------
/docs/static/img/admin-ui-contacts-individuals.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/admin-ui-contacts-individuals.png
--------------------------------------------------------------------------------
/docs/static/img/admin-ui-create-edit-runbook.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/admin-ui-create-edit-runbook.png
--------------------------------------------------------------------------------
/docs/static/img/admin-ui-create-edit-template.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/admin-ui-create-edit-template.png
--------------------------------------------------------------------------------
/docs/static/img/admin-ui-incident-cost-types.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/admin-ui-incident-cost-types.png
--------------------------------------------------------------------------------
/docs/static/img/admin-ui-incident-priorities.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/admin-ui-incident-priorities.png
--------------------------------------------------------------------------------
/docs/static/img/admin-ui-knowledge-documents.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/admin-ui-knowledge-documents.png
--------------------------------------------------------------------------------
/docs/static/img/admin-ui-knowledge-tag-types.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/admin-ui-knowledge-tag-types.png
--------------------------------------------------------------------------------
/docs/static/img/admin-ui-signal-filter-dedupe.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/admin-ui-signal-filter-dedupe.png
--------------------------------------------------------------------------------
/docs/static/img/admin-ui-signal-filter-snooze.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/admin-ui-signal-filter-snooze.png
--------------------------------------------------------------------------------
/docs/static/img/slack-conversation-list-tasks.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/slack-conversation-list-tasks.png
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/public/static/m.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/src/dispatch/static/dispatch/public/static/m.png
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/src/dispatch/static/dispatch/src/assets/logo.png
--------------------------------------------------------------------------------
/docs/branding/FullColor_IconOnly_1280x1024_72dpi.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/branding/FullColor_IconOnly_1280x1024_72dpi.jpg
--------------------------------------------------------------------------------
/docs/branding/FullColor_TextOnly_1280x1024_72dpi.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/branding/FullColor_TextOnly_1280x1024_72dpi.jpg
--------------------------------------------------------------------------------
/docs/static/img/admin-ui-associate-case-template.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/admin-ui-associate-case-template.png
--------------------------------------------------------------------------------
/docs/static/img/admin-ui-incident-report-receipt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/admin-ui-incident-report-receipt.png
--------------------------------------------------------------------------------
/docs/static/img/admin-ui-signal-entity-type-name.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/admin-ui-signal-entity-type-name.png
--------------------------------------------------------------------------------
/docs/static/img/slack-conversation-assign-role.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/slack-conversation-assign-role.png
--------------------------------------------------------------------------------
/docs/static/img/slack-conversation-create-task.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/slack-conversation-create-task.png
--------------------------------------------------------------------------------
/docs/static/img/slack-conversation-edit-incident.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/slack-conversation-edit-incident.png
--------------------------------------------------------------------------------
/docs/static/img/slack-conversation-engage-oncall.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/slack-conversation-engage-oncall.png
--------------------------------------------------------------------------------
/docs/static/img/slack-conversation-list-my-tasks.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/slack-conversation-list-my-tasks.png
--------------------------------------------------------------------------------
/docs/static/img/slack-conversation-run-workflow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/slack-conversation-run-workflow.png
--------------------------------------------------------------------------------
/docs/static/img/slack-conversation-status-report.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/slack-conversation-status-report.png
--------------------------------------------------------------------------------
/src/dispatch/ai/exceptions.py:
--------------------------------------------------------------------------------
1 | from dispatch.exceptions import DispatchException
2 |
3 |
4 | class GenAIException(DispatchException):
5 | pass
6 |
--------------------------------------------------------------------------------
/tests/static/e2e/utils/common.ts:
--------------------------------------------------------------------------------
1 | export function generateRandomString(): string {
2 | return (Math.random() + 1).toString(36).substring(7)
3 | }
4 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "dbaeumer.vscode-eslint",
4 | "esbenp.prettier-vscode",
5 | "vue.volar"
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include setup.py README.md MANIFEST.in LICENSE AUTHORS
2 | recursive-include ./ requirements*.txt
3 | graft src/dispatch
4 | global-exclude *~
5 |
--------------------------------------------------------------------------------
/docs/branding/website_logo_transparent_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/branding/website_logo_transparent_background.png
--------------------------------------------------------------------------------
/docs/static/img/admin-ui-incident-report-resources.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/admin-ui-incident-report-resources.png
--------------------------------------------------------------------------------
/docs/static/img/admin-ui-signal-engagement-filter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/admin-ui-signal-engagement-filter.png
--------------------------------------------------------------------------------
/docs/static/img/slack-conversation-list-workflows.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/slack-conversation-list-workflows.png
--------------------------------------------------------------------------------
/docs/static/img/slack-conversation-report-incident.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/slack-conversation-report-incident.png
--------------------------------------------------------------------------------
/docs/branding/FullColor_TransparentBg_1280x1024_72dpi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/branding/FullColor_TransparentBg_1280x1024_72dpi.png
--------------------------------------------------------------------------------
/docs/branding/website_logo_solid_background_logo_only.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/branding/website_logo_solid_background_logo_only.png
--------------------------------------------------------------------------------
/docs/static/img/admin-ui-associate-incident-template.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/admin-ui-associate-incident-template.png
--------------------------------------------------------------------------------
/docs/static/img/slack-conversation-add-timeline-event.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/slack-conversation-add-timeline-event.png
--------------------------------------------------------------------------------
/docs/static/img/slack-conversation-list-participants.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/slack-conversation-list-participants.png
--------------------------------------------------------------------------------
/docs/static/img/slack-conversation-report-executive.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/slack-conversation-report-executive.png
--------------------------------------------------------------------------------
/docs/static/img/slack-conversation-update-participant.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/slack-conversation-update-participant.png
--------------------------------------------------------------------------------
/docs/static/img/slack-plugin-conversation-management.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/slack-plugin-conversation-management.png
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/auth/customAuthProvider.js:
--------------------------------------------------------------------------------
1 | function login(to, from, next) {
2 | next()
3 | }
4 |
5 | export default {
6 | login,
7 | }
8 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "endOfLine": "auto",
3 | "printWidth": 100,
4 | "semi": false,
5 | "singleQuote": false,
6 | "tabWidth": 2,
7 | "useTabs": false
8 | }
9 |
--------------------------------------------------------------------------------
/docs/static/img/slack-conversation-notifications-group.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/slack-conversation-notifications-group.png
--------------------------------------------------------------------------------
/docs/static/img/slack-conversation-status-report-response.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/slack-conversation-status-report-response.png
--------------------------------------------------------------------------------
/docs/static/img/slack-conversations-incident-notification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/slack-conversations-incident-notification.png
--------------------------------------------------------------------------------
/docs/static/img/slack-plugin-contact-information-resolver.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/slack-plugin-contact-information-resolver.png
--------------------------------------------------------------------------------
/src/dispatch/rate_limiter.py:
--------------------------------------------------------------------------------
1 | from slowapi import Limiter
2 | from slowapi.util import get_remote_address
3 |
4 |
5 | limiter = Limiter(key_func=get_remote_address)
6 |
--------------------------------------------------------------------------------
/docs/branding/website_logo_solid_background_icon_only_square.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/branding/website_logo_solid_background_icon_only_square.png
--------------------------------------------------------------------------------
/docs/src/pages/markdown-page.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Markdown page example
3 | ---
4 |
5 | # Markdown page example
6 |
7 | You don't need React to write simple standalone pages.
8 |
--------------------------------------------------------------------------------
/data/.env:
--------------------------------------------------------------------------------
1 | # General
2 | LOG_LEVEL=ERROR
3 | STATIC_DIR=""
4 | DATABASE_HOSTNAME=localhost
5 | DATABASE_CREDENTIALS=dispatch:dispatch
6 | DISPATCH_ENCRYPTION_KEY=NJHDWDJ3PbHT8h
7 |
--------------------------------------------------------------------------------
/docs/static/img/user-guide-incident-feedback-conversation-modal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/user-guide-incident-feedback-conversation-modal.png
--------------------------------------------------------------------------------
/docs/docs/administration/settings/data/index.mdx:
--------------------------------------------------------------------------------
1 | # Data
2 |
3 | Along with incident orchestration, Dispatch is adept at building a catalog of data that can be used to resolve incidents.
4 |
--------------------------------------------------------------------------------
/src/dispatch/types.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from dispatch.case.models import Case
4 | from dispatch.incident.models import Incident
5 |
6 | Subject = Case | Incident
7 |
--------------------------------------------------------------------------------
/docs/static/img/user-guide-incident-feedback-conversation-direct-message.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Todo/dispatch/main/docs/static/img/user-guide-incident-feedback-conversation-direct-message.png
--------------------------------------------------------------------------------
/src/dispatch/incident/enums.py:
--------------------------------------------------------------------------------
1 | from dispatch.enums import DispatchEnum
2 |
3 |
4 | class IncidentStatus(DispatchEnum):
5 | active = "Active"
6 | stable = "Stable"
7 | closed = "Closed"
8 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/components/layouts/AdminLayout.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/dispatch/storage/enums.py:
--------------------------------------------------------------------------------
1 | from dispatch.enums import DispatchEnum
2 |
3 |
4 | class StorageAction(DispatchEnum):
5 | add_members = "add_members"
6 | remove_members = "remove_members"
7 |
--------------------------------------------------------------------------------
/src/dispatch/signal/enums.py:
--------------------------------------------------------------------------------
1 | from dispatch.enums import DispatchEnum
2 |
3 |
4 | class SignalEngagementStatus(DispatchEnum):
5 | new = "New"
6 | approved = "Approved"
7 | denied = "Denied"
8 |
--------------------------------------------------------------------------------
/src/dispatch/report/enums.py:
--------------------------------------------------------------------------------
1 | from dispatch.enums import DispatchEnum
2 |
3 |
4 | class ReportTypes(DispatchEnum):
5 | tactical_report = "Tactical Report"
6 | executive_report = "Executive Report"
7 |
--------------------------------------------------------------------------------
/docs/docs/user-guide/data.mdx:
--------------------------------------------------------------------------------
1 | # Data
2 |
3 | The data view is how one accesses Dispatch's internal data catalog. It provides the ability to store metadata about data sources likely to be used to resolve incidents.
4 |
--------------------------------------------------------------------------------
/.devcontainer/.env.example:
--------------------------------------------------------------------------------
1 | LOG_LEVEL="ERROR"
2 | STATIC_DIR=""
3 | DATABASE_HOSTNAME="localhost"
4 | DATABASE_CREDENTIALS="dispatch:dispatch"
5 | DISPATCH_ENCRYPTION_KEY="NJHDWDJ3PbHT8h"
6 | DISPATCH_JWT_SECRET="foo"
7 |
--------------------------------------------------------------------------------
/docs/docs/administration/settings/signal/index.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 4
3 | ---
4 |
5 | # Signal
6 |
7 | Signals are a way to define different events that you ingest into Dispatch and raise to the level of cases.
8 |
--------------------------------------------------------------------------------
/src/dispatch/email_templates/enums.py:
--------------------------------------------------------------------------------
1 | from dispatch.enums import DispatchEnum
2 |
3 |
4 | class EmailTemplateTypes(DispatchEnum):
5 | case_welcome = "Case Welcome Email"
6 | incident_welcome = "Incident Welcome Email"
7 |
--------------------------------------------------------------------------------
/docs/src/components/HomepageFeatures/styles.module.css:
--------------------------------------------------------------------------------
1 | .features {
2 | display: flex;
3 | align-items: center;
4 | padding: 2rem 0;
5 | width: 100%;
6 | }
7 |
8 | .featureSvg {
9 | height: 200px;
10 | width: 200px;
11 | }
12 |
--------------------------------------------------------------------------------
/docs/docs/user-guide/cases/index.mdx:
--------------------------------------------------------------------------------
1 | # Cases
2 |
3 | Most participants will never have to use the Dispatch Case UI. But for commanders and power users, this view provides a way to search, filter, and interact with cases even if they are closed.
4 |
--------------------------------------------------------------------------------
/src/dispatch/utils.py:
--------------------------------------------------------------------------------
1 | def deslug_and_capitalize_resource_type(resource_type: str) -> str:
2 | """Deslugs and capitalizes each word of a given resource type string."""
3 | return " ".join([w.capitalize() for w in resource_type.split("-")[1:]])
4 |
--------------------------------------------------------------------------------
/src/dispatch/case_cost_type/config.py:
--------------------------------------------------------------------------------
1 | default_case_cost_type = {
2 | "name": "Response Cost",
3 | "description": "Cost associated with handling a case.",
4 | "category": "Primary",
5 | "details": {},
6 | "default": True,
7 | "editable": False,
8 | }
9 |
--------------------------------------------------------------------------------
/docs/docs/administration/settings/data/transport.mdx:
--------------------------------------------------------------------------------
1 | # Transport
2 |
3 | Allows the user define their own transport layers for their data sources.
4 |
5 | Example source transports:
6 |
7 | - REST API
8 | - Syslog
9 | - Hive
10 | - S3
11 | - Kafka
12 | - Kinesis
13 |
--------------------------------------------------------------------------------
/src/dispatch/document/utils.py:
--------------------------------------------------------------------------------
1 | from dispatch.enums import DocumentResourceTypes
2 |
3 |
4 | def deslug(document_type: DocumentResourceTypes) -> str:
5 | """Deslugs a document resource type."""
6 | return " ".join([w.capitalize() for w in document_type.split("-")[1:]])
7 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_test/term.py:
--------------------------------------------------------------------------------
1 | from dispatch.plugins.bases import TermPlugin
2 |
3 |
4 | class TestTermPlugin(TermPlugin):
5 | title = "Dispatch Test Plugin - Term"
6 | slug = "test-term"
7 |
8 | def get(self, **kwargs):
9 | return
10 |
--------------------------------------------------------------------------------
/src/dispatch/workflow/enums.py:
--------------------------------------------------------------------------------
1 | from dispatch.enums import DispatchEnum
2 |
3 |
4 | class WorkflowInstanceStatus(DispatchEnum):
5 | submitted = "Submitted"
6 | created = "Created"
7 | running = "Running"
8 | completed = "Completed"
9 | failed = "Failed"
10 |
--------------------------------------------------------------------------------
/src/dispatch/incident_cost_type/config.py:
--------------------------------------------------------------------------------
1 | default_incident_cost_type = {
2 | "name": "Response Cost",
3 | "description": "Cost associated with handling an incident.",
4 | "category": "Primary",
5 | "details": {},
6 | "default": True,
7 | "editable": False,
8 | }
9 |
--------------------------------------------------------------------------------
/docs/docs/administration/settings/knowledge/index.mdx:
--------------------------------------------------------------------------------
1 | # Knowledge
2 |
3 | Along with incident orchestration, Dispatch is adept at building a knowledge base of incident data. Below we outline the primitives at your disposal and describe how you can manually add to the incident knowledge base.
4 |
--------------------------------------------------------------------------------
/src/dispatch/group/enums.py:
--------------------------------------------------------------------------------
1 | from dispatch.enums import DispatchEnum
2 |
3 |
4 | class GroupType(DispatchEnum):
5 | tactical = "tactical"
6 | notifications = "notifications"
7 |
8 |
9 | class GroupAction(DispatchEnum):
10 | add_member = "add_member"
11 | remove_member = "remove_member"
12 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_duo/enums.py:
--------------------------------------------------------------------------------
1 | from dispatch.enums import DispatchEnum
2 |
3 |
4 | class PushResponseResult(DispatchEnum):
5 | allow = "allow"
6 | deny = "deny"
7 | fraud = "fraud"
8 | failed = "push_failed"
9 | timeout = "timeout"
10 | user_not_found = "user_not_found"
11 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_test/conference.py:
--------------------------------------------------------------------------------
1 | from dispatch.plugins.bases import ConferencePlugin
2 |
3 |
4 | class TestConferencePlugin(ConferencePlugin):
5 | title = "Dispatch Test Plugin - Conference"
6 | slug = "test-conference"
7 |
8 | def create(self, items, **kwargs):
9 | return
10 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_test/participant.py:
--------------------------------------------------------------------------------
1 | from dispatch.plugins.bases import ParticipantPlugin
2 |
3 |
4 | class TestParticipantPlugin(ParticipantPlugin):
5 | title = "Dispatch Test Plugin - Participant"
6 | slug = "test-participant"
7 |
8 | def get(self, items, **kwargs):
9 | return
10 |
--------------------------------------------------------------------------------
/src/dispatch/case/type/config.py:
--------------------------------------------------------------------------------
1 | from dispatch.enums import Visibility
2 |
3 | default_case_type = {
4 | "name": "Default",
5 | "description": "This is the default case type.",
6 | "visibility": Visibility.open,
7 | "exclude_from_metrics": False,
8 | "default": True,
9 | "enabled": True,
10 | }
11 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/components/layouts/index.js:
--------------------------------------------------------------------------------
1 | import DefaultLayout from "@/components/layouts/DefaultLayout.vue"
2 | import DashboardLayout from "@/components/layouts/DashboardLayout.vue"
3 | import BasicLayout from "@/components/layouts/BasicLayout.vue"
4 |
5 | export { BasicLayout, DefaultLayout, DashboardLayout }
6 |
--------------------------------------------------------------------------------
/src/dispatch/signal/exceptions.py:
--------------------------------------------------------------------------------
1 | from dispatch.exceptions import DispatchException
2 |
3 |
4 | class SignalNotIdentifiedException(DispatchException):
5 | pass
6 |
7 |
8 | class SignalNotDefinedException(DispatchException):
9 | pass
10 |
11 |
12 | class SignalNotEnabledException(DispatchException):
13 | pass
14 |
--------------------------------------------------------------------------------
/src/dispatch/incident/type/config.py:
--------------------------------------------------------------------------------
1 | from dispatch.enums import Visibility
2 |
3 | default_incident_type = {
4 | "name": "Default",
5 | "description": "This is the default incident type.",
6 | "visibility": Visibility.open,
7 | "exclude_from_metrics": False,
8 | "default": True,
9 | "enabled": True,
10 | }
11 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/assets/styles/timeline.css:
--------------------------------------------------------------------------------
1 | .wavy-underline {
2 | text-decoration: underline;
3 | text-decoration-style: wavy;
4 | text-decoration-color: lightgray;
5 | text-decoration-thickness: 1px;
6 | text-underline-offset: 3px;
7 | }
8 |
9 | .pre-formatted {
10 | white-space: pre;
11 | }
12 |
--------------------------------------------------------------------------------
/docs/.gitignore:
--------------------------------------------------------------------------------
1 | # Dependencies
2 | /node_modules
3 |
4 | # Production
5 | /build
6 |
7 | # Generated files
8 | .docusaurus
9 | .cache-loader
10 |
11 | # Misc
12 | .DS_Store
13 | .env.local
14 | .env.development.local
15 | .env.test.local
16 | .env.production.local
17 |
18 | npm-debug.log*
19 | yarn-debug.log*
20 | yarn-error.log*
21 |
--------------------------------------------------------------------------------
/docs/docs/administration/settings/incident/index.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 3
3 | ---
4 |
5 | # Incident
6 |
7 | While Dispatch holds some strong opinions about _how_ to run incidents. It does allow for quite a lot of flexibility. Below you will find several different areas that allow you to make the incident experience fit your organization.
8 |
--------------------------------------------------------------------------------
/docs/docs/administration/settings/knowledge/definition.mdx:
--------------------------------------------------------------------------------
1 | # Definitions
2 |
3 | Definitions collect and manage term definitions from various sources; this enables incident participants to understand the language and terms used throughout an incident.
4 |
5 | A definition can be associated with one or more terms \(in case of term overload\).
6 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/auth/userSettings.js:
--------------------------------------------------------------------------------
1 | import store from "@/store"
2 | import UserApi from "./api"
3 |
4 | function load() {
5 | return UserApi.getUserInfo().then(function (response) {
6 | return store.commit("auth/SET_USER_PROJECTS", response.data.projects)
7 | })
8 | }
9 |
10 | export default {
11 | load,
12 | }
13 |
--------------------------------------------------------------------------------
/docs/docs/administration/settings/data/status.mdx:
--------------------------------------------------------------------------------
1 | # Status
2 |
3 | When using a data source in the coarse of an investigation, one needs to understand the current state of that database. Here, Dispatch allows you define your own data source statuses.
4 |
5 | Some example status:
6 |
7 | - Production
8 | - Testing
9 | - Staging
10 | - Deprecated
11 |
--------------------------------------------------------------------------------
/docs/docs/user-guide/incidents/tasks.mdx:
--------------------------------------------------------------------------------
1 | # Tasks
2 |
3 | Similar to the incident view, incident tasks are managed in the incident channel itself. But when you want to view all incident tasks across the organization, this view gives you that ability.
4 |
5 |
6 |
7 | 
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_test/document_resolver.py:
--------------------------------------------------------------------------------
1 | from dispatch.plugins.bases import DocumentResolverPlugin
2 |
3 |
4 | class TestDocumentResolverPlugin(DocumentResolverPlugin):
5 | title = "Dispatch Test Plugin - Document Resolver"
6 | slug = "test-document-resolver"
7 |
8 | def get(self, items, **kwargs):
9 | return
10 |
--------------------------------------------------------------------------------
/tests/static/e2e/routes.ts:
--------------------------------------------------------------------------------
1 | export const orgSlug = "default"
2 |
3 | export enum Routes {
4 | Login = "/auth/login",
5 | Register = "/auth/register",
6 | Dashboards = "/dashboards/incidents",
7 | Incidents = "/incidents",
8 | ReportIncident = "/incidents/report",
9 | ReportCase = "/cases/report",
10 | ReportEvent = "/events/report",
11 | }
12 |
--------------------------------------------------------------------------------
/docs/docs/changelog.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: Short description of changes.
3 | ---
4 |
5 | # Changelog
6 |
7 | :::info
8 | Dispatch uses the [calver](https://calver.org/) version schema.
9 | :::
10 |
11 | See the the "released" page on the application repository for more information about changes:
12 |
13 | [Releases](https://github.com/Netflix/dispatch/releases)
14 |
--------------------------------------------------------------------------------
/src/dispatch/participant_role/enums.py:
--------------------------------------------------------------------------------
1 | from dispatch.enums import DispatchEnum
2 |
3 |
4 | class ParticipantRoleType(DispatchEnum):
5 | assignee = "Assignee"
6 | incident_commander = "Incident Commander"
7 | liaison = "Liaison"
8 | scribe = "Scribe"
9 | participant = "Participant"
10 | observer = "Observer"
11 | reporter = "Reporter"
12 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_test/ticket.py:
--------------------------------------------------------------------------------
1 | from dispatch.plugins.bases import TicketPlugin
2 |
3 |
4 | class TestTicketPlugin(TicketPlugin):
5 | title = "Dispatch Test Plugin - Ticket"
6 | slug = "test-ticket"
7 |
8 | def create(self, ticket_id, **kwargs):
9 | return
10 |
11 | def update(self, ticket_id, **kwargs):
12 | return
13 |
--------------------------------------------------------------------------------
/docs/docs/administration/settings/data/type.mdx:
--------------------------------------------------------------------------------
1 | # Type
2 |
3 | You may have several different systems you need to interact with in order to access/query a data source. It's type denotes the underlying technology or system that it's most closely associated with.
4 |
5 | Some examples of a data source's type:
6 |
7 | - Hive
8 | - ES (ElasticSearch)
9 | - BigQuery
10 | - Splunk
11 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/feedback/service/api.js:
--------------------------------------------------------------------------------
1 | import API from "@/api"
2 |
3 | const resource = "/service_feedback"
4 |
5 | export default {
6 | getAll(options) {
7 | return API.get(`${resource}`, { params: { ...options } })
8 | },
9 |
10 | delete(feedbackId, individualId) {
11 | return API.delete(`${resource}/${feedbackId}/${individualId}`)
12 | },
13 | }
14 |
--------------------------------------------------------------------------------
/tests/static/e2e/utils/register.ts:
--------------------------------------------------------------------------------
1 | import { AuthPage } from "../pages/auth-page"
2 | import { generateRandomString } from "./common"
3 |
4 | async function register(authPage: AuthPage): Promise {
5 | let email = generateRandomString() + "@example.com"
6 | let password = generateRandomString()
7 | await authPage.registerNewUser(email, password)
8 | }
9 |
10 | export default register
11 |
--------------------------------------------------------------------------------
/data/update-example-data.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | echo "Dropping existing database..."
3 | dispatch database drop
4 | echo "Restoring current dump file..."
5 | dispatch database restore --dump-file ./dispatch-sample-data.dump
6 | echo "Running database migrations..."
7 | dispatch database upgrade
8 | echo "Dumping sql to file..."
9 | dispatch database dump --dump-file ./dispatch-sample-data.dump
10 |
--------------------------------------------------------------------------------
/src/dispatch/feedback/incident/enums.py:
--------------------------------------------------------------------------------
1 | from dispatch.enums import DispatchEnum
2 |
3 |
4 | class FeedbackRating(DispatchEnum):
5 | very_satisfied = "Very satisfied"
6 | somewhat_satisfied = "Somewhat satisfied"
7 | neither_satisfied_nor_dissatisfied = "Neither satisfied nor dissatisfied"
8 | somewhat_dissatisfied = "Somewhat dissatisfied"
9 | very_dissatisfied = "Very dissatisfied"
10 |
--------------------------------------------------------------------------------
/src/dispatch/feedback/service/enums.py:
--------------------------------------------------------------------------------
1 | from dispatch.enums import DispatchEnum
2 |
3 |
4 | class ServiceFeedbackRating(DispatchEnum):
5 | no_effort = "No effort"
6 | little_effort = "Little effort"
7 | moderate_effort = "Moderate effort"
8 | lots_of_effort = "Lots of effort"
9 | very_high_effort = "Very high effort"
10 | extreme_effort = "Extreme effort, everything I could give"
11 |
--------------------------------------------------------------------------------
/src/dispatch/forms/enums.py:
--------------------------------------------------------------------------------
1 | from dispatch.enums import DispatchEnum
2 |
3 |
4 | class FormStatus(DispatchEnum):
5 | new = "New"
6 | draft = "Draft"
7 | complete = "Complete"
8 |
9 |
10 | class FormAttorneyStatus(DispatchEnum):
11 | not_reviewed = "Not reviewed"
12 | reviewed_no_action = "Reviewed: no action required"
13 | reviewed_action_required = "Reviewed: follow up required"
14 |
--------------------------------------------------------------------------------
/.github/workflows/publish-image-test.yml:
--------------------------------------------------------------------------------
1 | name: Test image build
2 |
3 | on: pull_request
4 |
5 | jobs:
6 | build_image:
7 | name: Build Docker image
8 | runs-on: ubuntu-latest
9 | steps:
10 | - name: Check out the repo
11 | uses: actions/checkout@v4
12 | - name: Build without push
13 | uses: docker/build-push-action@v1
14 | with:
15 | push: false
16 |
--------------------------------------------------------------------------------
/docs/docs/user-guide/incidents/index.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 2
3 | ---
4 |
5 | # Incidents
6 |
7 | Most participants will never have to use the Dispatch Incident UI. But for commanders and power users, this view provides a way to search, filter, and interact with incidents even if they are closed.
8 |
9 |
10 |
11 | 
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/dispatch/task/enums.py:
--------------------------------------------------------------------------------
1 | from dispatch.enums import DispatchEnum
2 |
3 |
4 | class TaskStatus(DispatchEnum):
5 | open = "Open"
6 | resolved = "Resolved"
7 |
8 |
9 | class TaskSource(DispatchEnum):
10 | incident = "Incident"
11 | post_incident_review = "Post Incident Review"
12 |
13 |
14 | class TaskPriority(DispatchEnum):
15 | low = "Low"
16 | medium = "Medium"
17 | high = "High"
18 |
--------------------------------------------------------------------------------
/docs/docs/administration/settings/data/data-format.mdx:
--------------------------------------------------------------------------------
1 | # Data Formats
2 |
3 | Data sources are not always uniformed in nature. It's often helpful to understand what the underlying data format for a given data source is. Here, Dispatch allows you to define common data formats that sources in your environment use.
4 |
5 | Some example data formats:
6 |
7 | - JSON
8 | - CSV
9 | - Syslog
10 | - XML
11 | - Non-standard
12 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_zoom/config.py:
--------------------------------------------------------------------------------
1 | from pydantic import Field, SecretStr
2 |
3 | from dispatch.config import BaseConfigurationModel
4 |
5 |
6 | class ZoomConfiguration(BaseConfigurationModel):
7 | """Zoom configuration description."""
8 |
9 | api_user_id: str = Field(title="Zoom API User Id")
10 | api_key: str = Field(title="API Key")
11 | api_secret: SecretStr = Field(title="API Secret")
12 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [tool:pytest]
2 | python_files = test*.py
3 | addopts = --tb=native -p no:doctest -p no:warnings
4 | norecursedirs = bin dist docs htmlcov script hooks node_modules .* {args}
5 | looponfailroots = src tests
6 | selenium_driver = chrome
7 | self-contained-html = true
8 |
9 | [coverage:run]
10 | omit =
11 | dispatch/migrations/*
12 | source =
13 | src
14 | tests
15 |
16 | [black]
17 | line_length=100
18 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_test/workflow.py:
--------------------------------------------------------------------------------
1 | from dispatch.plugins.bases import WorkflowPlugin
2 |
3 |
4 | class TestWorkflowPlugin(WorkflowPlugin):
5 | title = "Dispatch Test Plugin - Workflow"
6 | slug = "test-workflow"
7 |
8 | def get_instance(self, workflow_id: str, instance_id: str, **kwargs):
9 | return
10 |
11 | def run(self, workflow_id: str, params: dict, **kwargs):
12 | return
13 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/bases/monitor.py:
--------------------------------------------------------------------------------
1 | """
2 | .. module: dispatch.plugins.bases.monitor
3 | :platform: Unix
4 | :copyright: (c) 2019 by Netflix Inc., see AUTHORS for more
5 | :license: Apache, see LICENSE for more details.
6 | """
7 |
8 | from dispatch.plugins.base import Plugin
9 |
10 |
11 | class MonitorPlugin(Plugin):
12 | type = "monitor"
13 |
14 | def get_status(self, **kwargs):
15 | raise NotImplementedError
16 |
--------------------------------------------------------------------------------
/docs/docs/administration/settings/knowledge/term.mdx:
--------------------------------------------------------------------------------
1 | # Terms
2 |
3 | Terms are words or phrases that may not have any meaning to an outside observer but have deep organizational meaning (e.g., acronyms and names).
4 |
5 | As an example, take the term PCI. In the security world, this acronym typically refers to the Payment Card Industry security standard.
6 |
7 | Any defined term can be associated with Teams, Services, or Individuals for incident inclusion.
8 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/core/versions/2021-06-02_f011c050b9ba.py:
--------------------------------------------------------------------------------
1 | """Initial core revision
2 |
3 | Revision ID: f011c050b9ba
4 | Revises:
5 | Create Date: 2021-06-02 14:09:15.220737
6 |
7 | """
8 |
9 | # revision identifiers, used by Alembic.
10 | revision = "f011c050b9ba"
11 | down_revision = None
12 | branch_labels = None
13 | depends_on = None
14 |
15 |
16 | def upgrade():
17 | pass
18 |
19 |
20 | def downgrade():
21 | pass
22 |
--------------------------------------------------------------------------------
/requirements-dev.in:
--------------------------------------------------------------------------------
1 | # Note: These requirements intentionally conflict with dispatch package
2 | # To resolve, install dispatch with --no-deps: pip install -e . --no-deps
3 | attrs>=22.2.0 # Required by referencing
4 | black
5 | click
6 | coverage
7 | devtools
8 | easydict
9 | factory-boy
10 | faker
11 | ipython
12 | pre-commit
13 | pytest==7.4.4
14 | pytest-mock
15 | ruff
16 | typing-extensions==4.13.2 # Required by nflx-genai and dispatch
17 | vulture
18 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/tenant/versions/2021-06-02_f011c050b9ba.py:
--------------------------------------------------------------------------------
1 | """Initial tenant revision
2 |
3 | Revision ID: f011c050b9ba
4 | Revises:
5 | Create Date: 2021-06-02 14:09:15.220737
6 |
7 | """
8 |
9 | # revision identifiers, used by Alembic.
10 | revision = "f011c050b9ba"
11 | down_revision = None
12 | branch_labels = None
13 | depends_on = None
14 |
15 |
16 | def upgrade():
17 | pass
18 |
19 |
20 | def downgrade():
21 | pass
22 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/incident_role/api.js:
--------------------------------------------------------------------------------
1 | import API from "@/api"
2 |
3 | const resource = "/incident_roles"
4 |
5 | export default {
6 | getRolePolicies(role, project_name) {
7 | return API.get(`${resource}/${role}`, { params: { projectName: project_name } })
8 | },
9 |
10 | updateRole(role, project_name, payload) {
11 | return API.put(`${resource}/${role}`, payload, { params: { projectName: project_name } })
12 | },
13 | }
14 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_slack/ack.py:
--------------------------------------------------------------------------------
1 | from blockkit import Modal, Section
2 | from slack_bolt import Ack
3 |
4 |
5 | def ack_submission_event(ack: Ack, title: str, close: str, text: str) -> None:
6 | """Handles event acknowledgment."""
7 | ack(
8 | response_action="update",
9 | view=Modal(
10 | title=title,
11 | close=close,
12 | blocks=[Section(text=text)],
13 | ).build(),
14 | )
15 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Set the default behavior, in case people don't have core.autocrlf set.
2 | * text=auto
3 |
4 | # Explicitly declare text files you want to always be normalized and converted
5 | # to native line endings on checkout.
6 | #*.c text
7 |
8 | # Declare files that will always have CRLF line endings on checkout.
9 | #*.sln text eol=crlf
10 |
11 | # Denote all files that are truly binary and should not be modified.
12 | *.png binary
13 | *.jpg binary
14 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/core/versions/2021-07-23_e0d568f345c9.py:
--------------------------------------------------------------------------------
1 | """Merge revision
2 |
3 | Revision ID: e0d568f345c9
4 | Revises:
5 | Create Date: 2021-07-23 15:53:45.523064
6 |
7 | """
8 | # revision identifiers, used by Alembic.
9 | revision = "e0d568f345c9"
10 | down_revision = ("c0bc938b058e", "8f364cf49a23")
11 | branch_labels = None
12 | depends_on = None
13 |
14 |
15 | def upgrade():
16 | pass
17 |
18 |
19 | def downgrade():
20 | pass
21 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/bases/tag.py:
--------------------------------------------------------------------------------
1 | """
2 | .. module: dispatch.plugins.bases.tag
3 | :platform: Unix
4 | :copyright: (c) 2019 by Netflix Inc., see AUTHORS for more
5 | :license: Apache, see LICENSE for more details.
6 | .. moduleauthor:: Kevin Glisson
7 | """
8 |
9 | from dispatch.plugins.base import Plugin
10 |
11 |
12 | class TagPlugin(Plugin):
13 | type = "tag"
14 |
15 | def get(self, **kwargs):
16 | raise NotImplementedError
17 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/styles/variables.sass:
--------------------------------------------------------------------------------
1 | .theme--light,
2 | .theme--dark
3 | .v-bar--underline
4 | border-width: 0 0 thin 0
5 | border-style: solid
6 |
7 | &.theme--light
8 | border-bottom-color: #0000001F !important
9 |
10 | &.theme--dark
11 | border-bottom-color: #FFFFFF1F !important
12 |
13 | .v-application.theme--dark code
14 | background: #1f1f1f
15 | color: #d16f72
16 |
17 | .v-data-table-header th
18 | white-space: nowrap
19 |
--------------------------------------------------------------------------------
/docs/docs/administration/settings/data/environment.mdx:
--------------------------------------------------------------------------------
1 | # Environment
2 |
3 | Data sources often have a corresponding environment to which they apply. For example, one data set may only contain information from the production account. When responding to incidents and using data sources for investigation, it's essential to understand the data source's scope.
4 |
5 | Some examples of a data source's environment could be:
6 |
7 | - Production
8 | - Staging
9 | - Testing
10 | - Development
11 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/bases/term.py:
--------------------------------------------------------------------------------
1 | """
2 | .. module: dispatch.plugins.bases.term
3 | :platform: Unix
4 | :copyright: (c) 2019 by Netflix Inc., see AUTHORS for more
5 | :license: Apache, see LICENSE for more details.
6 | .. moduleauthor:: Kevin Glisson
7 | """
8 |
9 | from dispatch.plugins.base import Plugin
10 |
11 |
12 | class TermPlugin(Plugin):
13 | type = "term"
14 |
15 | def get(self, **kwargs):
16 | raise NotImplementedError
17 |
--------------------------------------------------------------------------------
/docs/src/pages/index.module.css:
--------------------------------------------------------------------------------
1 | /**
2 | * CSS files with the .module.css suffix will be treated as CSS modules
3 | * and scoped locally.
4 | */
5 |
6 | .heroBanner {
7 | padding: 4rem 0;
8 | text-align: center;
9 | position: relative;
10 | overflow: hidden;
11 | }
12 |
13 | @media screen and (max-width: 996px) {
14 | .heroBanner {
15 | padding: 2rem;
16 | }
17 | }
18 |
19 | .buttons {
20 | display: flex;
21 | align-items: center;
22 | justify-content: center;
23 | }
24 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/bases/source.py:
--------------------------------------------------------------------------------
1 | """
2 | .. module: dispatch.plugins.bases.source
3 | :platform: Unix
4 | :copyright: (c) 2019 by Netflix Inc., see AUTHORS for more
5 | :license: Apache, see LICENSE for more details.
6 | .. moduleauthor:: Kevin Glisson
7 | """
8 |
9 | from dispatch.plugins.base import Plugin
10 |
11 |
12 | class SourcePlugin(Plugin):
13 | type = "source"
14 |
15 | def get(self, **kwargs):
16 | raise NotImplementedError
17 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_duo/service.py:
--------------------------------------------------------------------------------
1 | import duo_client
2 | from duo_client.auth import Auth
3 |
4 | from dispatch.plugins.dispatch_duo.config import DuoConfiguration
5 |
6 |
7 | def create_duo_auth_client(config: DuoConfiguration) -> Auth:
8 | """Creates a Duo Auth API client."""
9 | return duo_client.Auth(
10 | ikey=config.integration_key.get_secret_value(),
11 | skey=config.integration_secret_key.get_secret_value(),
12 | host=config.host,
13 | )
14 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/tenant/versions/2022-10-04_3a5e776ddce4.py:
--------------------------------------------------------------------------------
1 | """Merge revision
2 |
3 | Revision ID: 3a5e776ddce4
4 | Revises: cfde1bca6a6e, c1abb0cc40e5
5 | Create Date: 2022-10-04 08:54:16.613236
6 |
7 | """
8 |
9 | # revision identifiers, used by Alembic.
10 | revision = "3a5e776ddce4"
11 | down_revision = ("c1abb0cc40e5", "cfde1bca6a6e")
12 | branch_labels = None
13 | depends_on = None
14 |
15 |
16 | def upgrade():
17 | pass
18 |
19 |
20 | def downgrade():
21 | pass
22 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/bases/email.py:
--------------------------------------------------------------------------------
1 | """
2 | .. module: dispatch.plugins.bases.email
3 | :platform: Unix
4 | :copyright: (c) 2019 by Netflix Inc., see AUTHORS for more
5 | :license: Apache, see LICENSE for more details.
6 | .. moduleauthor:: Kevin Glisson
7 | """
8 |
9 | from dispatch.plugins.base import Plugin
10 |
11 |
12 | class EmailPlugin(Plugin):
13 | type = "email"
14 |
15 | def send(self, items, **kwargs):
16 | raise NotImplementedError
17 |
--------------------------------------------------------------------------------
/docs/docs/administration/settings/knowledge/tag-type.mdx:
--------------------------------------------------------------------------------
1 | # Tag Types
2 |
3 | Within Dispatch, tag types are a way to categorize collections of tags (e.g. actor, action, asset, result, etc.).
4 |
5 |
6 |
7 | 
8 |
9 |
10 |
11 | **Name:** The name for the tag type.
12 |
13 | **Description:** A short description of the tag type.
14 |
15 | **Exclusive:** Whether an incident should only have a tag of this type or not.
16 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_test/participant_group.py:
--------------------------------------------------------------------------------
1 | from dispatch.plugins.bases import ParticipantGroupPlugin
2 |
3 |
4 | class TestParticipantGroupPlugin(ParticipantGroupPlugin):
5 | title = "Dispatch Test Plugin - Participant Group"
6 | slug = "test-participant-group"
7 |
8 | def create(self, participants, **kwargs):
9 | return
10 |
11 | def add(self, participant, **kwargs):
12 | return
13 |
14 | def remove(self, participant, **kwargs):
15 | return
16 |
--------------------------------------------------------------------------------
/.devcontainer/postCreateCommand.sh:
--------------------------------------------------------------------------------
1 | pip install -e /workspaces/dispatch
2 | npm install --prefix /workspaces/dispatch/src/dispatch/static/dispatch
3 |
4 | export LOG_LEVEL="ERROR"
5 | export STATIC_DIR=""
6 | export DATABASE_HOSTNAME="localhost"
7 | export DATABASE_CREDENTIALS="dispatch:dispatch"
8 | export DISPATCH_ENCRYPTION_KEY="NJHDWDJ3PbHT8h"
9 | export DISPATCH_JWT_SECRET="foo"
10 | dispatch database restore --dump-file /workspaces/dispatch/data/dispatch-sample-data.dump
11 | dispatch database upgrade
12 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/case/priority/CasePriority.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ priority }}
4 |
5 |
6 |
7 |
24 |
--------------------------------------------------------------------------------
/src/dispatch/messaging/email/templates/notification.mjml:
--------------------------------------------------------------------------------
1 | {% extends "templates/base.mjml" %}
2 | {% block items %}
3 |
4 |
5 | {% for item in items %}
6 |
7 | {{ item.title }}
8 |
9 |
10 | {{ item.text }}
11 |
12 |
13 | {% endfor %}
14 |
15 |
16 | {% endblock %}
17 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_test/task.py:
--------------------------------------------------------------------------------
1 | from dispatch.plugins.bases import TaskPlugin
2 |
3 |
4 | class TestTaskPlugin(TaskPlugin):
5 | title = "Dispatch Test Plugin - Task"
6 | slug = "test-task"
7 |
8 | def get(self, **kwargs):
9 | return
10 |
11 | def create(self, **kwargs):
12 | return
13 |
14 | def delete(self, **kwargs):
15 | return
16 |
17 | def list(self, **kwargs):
18 | return
19 |
20 | def resolve(self, **kwargs):
21 | return
22 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/auth/basicAuthProvider.js:
--------------------------------------------------------------------------------
1 | import store from "@/store"
2 |
3 | function login(to, from, next) {
4 | let token = localStorage.getItem("token")
5 |
6 | if (token) {
7 | store.commit("auth/SET_USER_LOGIN", token)
8 | next()
9 | } else {
10 | // prevent redirect loop
11 | if (to.path !== "/default/auth/login") {
12 | next("/default/auth/login")
13 | } else {
14 | next()
15 | }
16 | }
17 | }
18 |
19 | export default {
20 | login,
21 | }
22 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/incident/priority/IncidentPriority.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ priority }}
4 |
5 |
6 |
7 |
23 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/bases/conference.py:
--------------------------------------------------------------------------------
1 | """
2 | .. module: dispatch.plugins.bases.conference
3 | :platform: Unix
4 | :copyright: (c) 2019 by Netflix Inc., see AUTHORS for more
5 | :license: Apache, see LICENSE for more details.
6 | .. moduleauthor:: Kevin Glisson
7 | """
8 |
9 | from dispatch.plugins.base import Plugin
10 |
11 |
12 | class ConferencePlugin(Plugin):
13 | type = "conference"
14 |
15 | def create(self, items, **kwargs):
16 | raise NotImplementedError
17 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/bases/participant.py:
--------------------------------------------------------------------------------
1 | """
2 | .. module: dispatch.plugins.bases.participant
3 | :platform: Unix
4 | :copyright: (c) 2019 by Netflix Inc., see AUTHORS for more
5 | :license: Apache, see LICENSE for more details.
6 | .. moduleauthor:: Kevin Glisson
7 | """
8 |
9 | from dispatch.plugins.base import Plugin
10 |
11 |
12 | class ParticipantPlugin(Plugin):
13 | type = "participant"
14 |
15 | def get(self, items, **kwargs):
16 | raise NotImplementedError
17 |
--------------------------------------------------------------------------------
/docs/docs/administration/settings/plugins/configuring-opsgenie.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: Configuration options for the Opsgenie plugin.
3 | ---
4 |
5 | # Configuring Opsgenie
6 |
7 | :::info
8 | Dispatch ships with support for resolving on-call schedules via the Opsgenie API. Below is how to configure the Opsgenie plugin to work with `Dispatch.`
9 | :::
10 |
11 | ## `OPSGENIE_API_KEY` \[Required. Secret: True\]
12 |
13 | > Opsgenie API key.
14 |
15 | ## `OPSGENIE_TEAM_ID` \[Required\]
16 |
17 | > Id for the Opsgenie team.
18 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/tenant/versions/2022-10-31_34aeedc9d09a.py:
--------------------------------------------------------------------------------
1 | """Merge revision
2 |
3 | Revision ID: 34aeedc9d09a
4 | Revises: 4b65941d065a, 01aa49ca0470, 3a5e776ddce4
5 | Create Date: 2022-10-31 09:41:11.971654
6 |
7 | """
8 |
9 | # revision identifiers, used by Alembic.
10 | revision = "34aeedc9d09a"
11 | down_revision = ("4b65941d065a", "01aa49ca0470", "3a5e776ddce4")
12 | branch_labels = None
13 | depends_on = None
14 |
15 |
16 | def upgrade():
17 | pass
18 |
19 |
20 | def downgrade():
21 | pass
22 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/bases/signal_consumer.py:
--------------------------------------------------------------------------------
1 | """
2 | .. module: dispatch.plugins.bases.signal_consumer
3 | :platform: Unix
4 | :copyright: (c) 2022 by Netflix Inc., see AUTHORS for more
5 | :license: Apache, see LICENSE for more details.
6 | .. moduleauthor:: Kevin Glisson
7 | """
8 |
9 | from dispatch.plugins.base import Plugin
10 |
11 |
12 | class SignalConsumerPlugin(Plugin):
13 | type = "signal-consumer"
14 |
15 | def consume(self, **kwargs):
16 | raise NotImplementedError
17 |
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | __pycache__
2 | .git
3 | .dockerignore
4 | .gitignore
5 |
6 | .cache/
7 | .coverage
8 | .storybook-out/
9 | .DS_Store
10 | .venv
11 | *.egg-info
12 | *.pyc
13 | *.log
14 | *.egg
15 | *.db
16 | *.pid
17 | MANIFEST
18 | test.conf
19 | pip-log.txt
20 | package.json
21 | /.artifacts
22 | /coverage/
23 | /cover
24 | /build
25 | /env
26 | /tmp
27 | node_modules
28 | /wheelhouse
29 | /test_cli/
30 | .idea/
31 | *.iml
32 | .pytest_cache/
33 | .vscode/tags
34 | coverage.xml
35 | junit.xml
36 | *.codestyle.xml
37 | package-lock.json
38 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/bases/signal_enrichment.py:
--------------------------------------------------------------------------------
1 | """
2 | .. module: dispatch.plugins.bases.signal_enrichment
3 | :platform: Unix
4 | :copyright: (c) 2022 by Netflix Inc., see AUTHORS for more
5 | :license: Apache, see LICENSE for more details.
6 | .. moduleauthor:: Kevin Glisson
7 | """
8 |
9 | from dispatch.plugins.base import Plugin
10 |
11 |
12 | class SignalEnrichmentPlugin(Plugin):
13 | type = "signal-enrichment"
14 |
15 | def enrich(self, **kwargs):
16 | raise NotImplementedError
17 |
--------------------------------------------------------------------------------
/tests/case/test_case_messaging.py:
--------------------------------------------------------------------------------
1 | def test_case_messaging(session, case):
2 | from dispatch.case.messaging import send_case_close_reminder
3 | from dispatch.case import service as case_service
4 | from dispatch.case.enums import CaseStatus
5 |
6 | case.status = CaseStatus.triage
7 | t_case = case_service.get_all_by_status(
8 | db_session=session, project_id=case.project.id, statuses=[CaseStatus.new, CaseStatus.triage]
9 | )
10 | send_case_close_reminder(case=t_case[0], db_session=session)
11 | pass
12 |
--------------------------------------------------------------------------------
/docs/docs/administration/settings/index.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 4
3 | ---
4 |
5 | # Settings
6 |
7 | The Dispatch Web UI is home to all of Dispatch administration abilities; these contain things that are likely to change frequently \(less often changed items live in the Dispatch configuration file\).
8 |
9 | To access Dispatch settings, look for the `Settings` menu item and select a project which you want to administer.
10 |
11 |
12 |
13 | 
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_test/contact.py:
--------------------------------------------------------------------------------
1 | from dispatch.plugins.bases import ContactPlugin
2 |
3 |
4 | class TestContactPlugin(ContactPlugin):
5 | title = "Dispatch Test Plugin - Contact"
6 | slug = "test-contact"
7 |
8 | def get(self, key, **kwargs):
9 | return
10 |
11 | def create(self, key, **kwargs):
12 | return
13 |
14 | def update(self, key, **kwargs):
15 | return
16 |
17 | def delete(self, key, **kwargs):
18 | return
19 |
20 | def move(self, key, **kwargs):
21 | return
22 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/vitest.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "vite"
2 | import vue from "@vitejs/plugin-vue"
3 | import path from "path"
4 |
5 | // https://vitejs.dev/config/
6 | export default defineConfig({
7 | plugins: [vue()],
8 | test: {
9 | globals: true,
10 | server: {
11 | deps: {
12 | inline: ["vuetify"],
13 | },
14 | },
15 | },
16 | resolve: {
17 | alias: {
18 | "@": path.resolve(__dirname, "src"), // provide an absolute path to your src directory
19 | },
20 | },
21 | })
22 |
--------------------------------------------------------------------------------
/.coveragerc:
--------------------------------------------------------------------------------
1 | [run]
2 | omit =
3 | */__init__.py
4 | */views.py
5 | */scheduled.py
6 | src/dispatch/rate_limiter.py
7 | src/dispatch/plugins/dispatch_test/*
8 | src/dispatch/api.py
9 | src/dispatch/extensions.py
10 | src/dispatch/scheduler.py
11 |
12 | [report]
13 | omit =
14 | */__init__.py
15 | */views.py
16 | */scheduled.py
17 | src/dispatch/rate_limiter.py
18 | src/dispatch/plugins/dispatch_test/*
19 | src/dispatch/api.py
20 | src/dispatch/extensions.py
21 | src/dispatch/scheduler.py
22 |
23 |
--------------------------------------------------------------------------------
/docs/docs/user-guide/dashboard/index.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: How to get useful metrics.
3 | sidebar_position: 1
4 | ---
5 |
6 | # Dashboards
7 |
8 | Dispatch provides basic dashboarding and reporting functionality that allows users to understand how incidents impact their organization. It comes preconfigured with useful aggregations like the number of incidents by type and priority.
9 |
10 | Additionally, all of the dashboard graphs are dynamic. This dynamism allows us to identify interesting subsets of data by filtering by different incident facets.
11 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/cost_model/api.js:
--------------------------------------------------------------------------------
1 | import API from "@/api"
2 |
3 | const resource = "cost_models"
4 |
5 | export default {
6 | getAll(options) {
7 | return API.get(`/${resource}`, {
8 | params: { ...options },
9 | })
10 | },
11 |
12 | create(payload) {
13 | return API.post(`/${resource}`, payload)
14 | },
15 | update(costModelId, payload) {
16 | return API.put(`/${resource}/${costModelId}`, payload)
17 | },
18 | delete(costModelId) {
19 | return API.delete(`/${resource}/${costModelId}`)
20 | },
21 | }
22 |
--------------------------------------------------------------------------------
/docker/.env.example:
--------------------------------------------------------------------------------
1 | # General
2 | DISPATCH_UI_URL=""
3 |
4 | # Persistence
5 | DATABASE_HOSTNAME=""
6 | DATABASE_CREDENTIALS=""
7 |
8 | # Authentication
9 | # For basic authentication see: https://netflix.github.io/dispatch/administration-guide/server#configuration-for-dispatch-auth-provider-basic
10 | # For PKCE authentication see: https://netflix.github.io/dispatch/administration-guide/server#configuration-for-dispatch-auth-provider-pkce
11 |
12 | # For additional server configuration options see: https://netflix.github.io/dispatch/administration-guide/server
13 |
--------------------------------------------------------------------------------
/docs/docs/administration/index.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Introduction
3 | sidebar_position: 0
4 | ---
5 |
6 | # Administration
7 |
8 | The Dispatch Web UI is home to all of Dispatch administration abilities; these contain things that are likely to change frequently \(less often changed items live in the Dispatch configuration file\).
9 |
10 | To access Dispatch settings, look for the `Settings` menu item and select a project which you want to administer.
11 |
12 |
13 |
14 | 
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_test/definition.py:
--------------------------------------------------------------------------------
1 | from dispatch.plugins.bases import DefinitionPlugin
2 |
3 |
4 | class TestDefinitionPlugin(DefinitionPlugin):
5 | title = "Dispatch Test Plugin - Definition"
6 | slug = "test-definition"
7 |
8 | def get(self, key, **kwargs):
9 | return
10 |
11 | def create(self, key, **kwargs):
12 | return
13 |
14 | def update(self, key, **kwargs):
15 | return
16 |
17 | def delete(self, key, **kwargs):
18 | return
19 |
20 | def move(self, key, **kwargs):
21 | return
22 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/assets/logo.svg:
--------------------------------------------------------------------------------
1 | Artboard 46
2 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_core/config.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | from pydantic import Field
4 | from starlette.config import Config
5 |
6 | from dispatch.config import BaseConfigurationModel
7 |
8 | log = logging.getLogger(__name__)
9 |
10 | config = Config(".env")
11 |
12 |
13 | class DispatchTicketConfiguration(BaseConfigurationModel):
14 | """Dispatch ticket configuration"""
15 |
16 | use_incident_name: bool = Field(
17 | True,
18 | title="Use Incident Name",
19 | description="Use the incident name as the ticket title.",
20 | )
21 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/entity_type/playground/store.js:
--------------------------------------------------------------------------------
1 | const state = {
2 | pattern: "",
3 | jpath: "",
4 | }
5 |
6 | const getters = {
7 | pattern({ pattern }) {
8 | return pattern
9 | },
10 | jpath({ jpath }) {
11 | return jpath
12 | },
13 | }
14 |
15 | const mutations = {
16 | updatePattern(state, payload) {
17 | state.pattern = payload
18 | },
19 | updateJsonPath(state, payload) {
20 | state.jpath = payload
21 | },
22 | }
23 |
24 | export default {
25 | namespaced: true,
26 | state,
27 | getters,
28 | mutations,
29 | }
30 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/base/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | .. module: dispatch.plugins.base
3 | :platform: Unix
4 | :copyright: (c) 2019 by Netflix Inc., see AUTHORS for more
5 | :license: Apache, see LICENSE for more details.
6 |
7 | .. moduleauthor:: Kevin Glisson
8 | """
9 |
10 | from __future__ import absolute_import, print_function
11 |
12 | from dispatch.plugins.base.manager import PluginManager
13 | from dispatch.plugins.base.v1 import * # noqa
14 |
15 | plugins = PluginManager()
16 | register = plugins.register
17 | unregister = plugins.unregister
18 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/preinstall.js:
--------------------------------------------------------------------------------
1 | const fs = require("fs")
2 | const dotenv = require("dotenv")
3 | const packageJson = require("./package.json")
4 |
5 | // Load environment variables from .env file
6 | dotenv.config()
7 |
8 | if (
9 | process.env.FORMKIT_ENTERPRISE_TOKEN &&
10 | process.env.FORMKIT_ENTERPRISE_TOKEN.startsWith("npm_")
11 | ) {
12 | console.log("Adding @formkit/pro to package.json")
13 | packageJson.dependencies["@formkit/pro"] = "npm:@formkit-enterprise/pro@^0.119.2"
14 | fs.writeFileSync("./package.json", JSON.stringify(packageJson, null, 2))
15 | }
16 |
--------------------------------------------------------------------------------
/src/dispatch/case/enums.py:
--------------------------------------------------------------------------------
1 | from dispatch.enums import DispatchEnum
2 |
3 |
4 | class CaseStatus(DispatchEnum):
5 | new = "New"
6 | triage = "Triage"
7 | escalated = "Escalated"
8 | closed = "Closed"
9 |
10 |
11 | class CaseResolutionReason(DispatchEnum):
12 | false_positive = "False Positive"
13 | user_acknowledge = "User Acknowledged"
14 | mitigated = "Mitigated"
15 | escalated = "Escalated"
16 |
17 |
18 | class CostModelType(DispatchEnum):
19 | """Type of cost model used to calculate costs."""
20 |
21 | new = "New"
22 | classic = "Classic"
23 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/case/severity/CaseSeverity.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{ severity }}
6 |
7 |
8 |
9 |
10 |
11 |
28 |
--------------------------------------------------------------------------------
/docs/docs/administration/settings/case/index.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 4
3 | ---
4 |
5 | # Case
6 |
7 | Cases are meant to triage events that do not raise to the level of incidents, but can be escalated to incidents if necessary. If you map a case type to an incident type and you escalate the case, then Dispatch will automatically create an incident and link the two.
8 |
9 | While Dispatch holds some strong opinions about _how_ to run cases. It does allow for quite a lot of flexibility. Below you will find several different areas that allow you to make the case experience fit your organization.
10 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/bases/auth_provider.py:
--------------------------------------------------------------------------------
1 | """
2 | .. module: dispatch.plugins.bases.application
3 | :platform: Unix
4 | :copyright: (c) 2019 by Netflix Inc., see AUTHORS for more
5 | :license: Apache, see LICENSE for more details.
6 | .. moduleauthor:: Kevin Glisson
7 | """
8 |
9 | from dispatch.plugins.base import Plugin
10 | from starlette.requests import Request
11 |
12 |
13 | class AuthenticationProviderPlugin(Plugin):
14 | type = "auth-provider"
15 |
16 | def get_current_user(self, request: Request, **kwargs):
17 | raise NotImplementedError
18 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/components/Loading.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
24 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/incident/severity/IncidentSeverity.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{ severity }}
6 |
7 |
8 |
9 |
10 |
11 |
28 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/team/api.js:
--------------------------------------------------------------------------------
1 | import API from "@/api"
2 |
3 | const resource = "/teams"
4 |
5 | export default {
6 | getAll(options) {
7 | return API.get(`${resource}`, { params: { ...options } })
8 | },
9 |
10 | get(teamId) {
11 | return API.get(`${resource}/${teamId}`)
12 | },
13 |
14 | create(payload) {
15 | return API.post(`${resource}`, payload)
16 | },
17 |
18 | update(teamId, payload) {
19 | return API.put(`${resource}/${teamId}`, payload)
20 | },
21 |
22 | delete(teamId) {
23 | return API.delete(`${resource}/${teamId}`)
24 | },
25 | }
26 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/term/api.js:
--------------------------------------------------------------------------------
1 | import API from "@/api"
2 |
3 | const resource = "/terms"
4 |
5 | export default {
6 | getAll(options) {
7 | return API.get(`${resource}`, { params: { ...options } })
8 | },
9 |
10 | get(termId) {
11 | return API.get(`${resource}/${termId}`)
12 | },
13 |
14 | create(payload) {
15 | return API.post(`${resource}`, payload)
16 | },
17 |
18 | update(termId, payload) {
19 | return API.put(`${resource}/${termId}`, payload)
20 | },
21 |
22 | delete(termId) {
23 | return API.delete(`${resource}/${termId}`)
24 | },
25 | }
26 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/signal/SignalInstanceNode.vue:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
{{ label }}
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/data/alert/api.js:
--------------------------------------------------------------------------------
1 | import API from "@/api"
2 |
3 | const resource = "/data/alerts"
4 |
5 | export default {
6 | getAll(options) {
7 | return API.get(`${resource}`, { params: { ...options } })
8 | },
9 |
10 | get(alertId) {
11 | return API.get(`${resource}/${alertId}`)
12 | },
13 |
14 | create(payload) {
15 | return API.post(`${resource}`, payload)
16 | },
17 |
18 | update(alertId, payload) {
19 | return API.put(`${resource}/${alertId}`, payload)
20 | },
21 |
22 | delete(alertId) {
23 | return API.delete(`${resource}/${alertId}`)
24 | },
25 | }
26 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/data/query/api.js:
--------------------------------------------------------------------------------
1 | import API from "@/api"
2 |
3 | const resource = "/data/queries"
4 |
5 | export default {
6 | getAll(options) {
7 | return API.get(`${resource}`, { params: { ...options } })
8 | },
9 |
10 | get(queryId) {
11 | return API.get(`${resource}/${queryId}`)
12 | },
13 |
14 | create(payload) {
15 | return API.post(`${resource}`, payload)
16 | },
17 |
18 | update(queryId, payload) {
19 | return API.put(`${resource}/${queryId}`, payload)
20 | },
21 |
22 | delete(queryId) {
23 | return API.delete(`${resource}/${queryId}`)
24 | },
25 | }
26 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/data/source/type/api.js:
--------------------------------------------------------------------------------
1 | import API from "@/api"
2 |
3 | const resource = "/data/sources/types"
4 |
5 | export default {
6 | getAll(options) {
7 | return API.get(`${resource}`, { params: { ...options } })
8 | },
9 |
10 | get(typeId) {
11 | return API.get(`${resource}/${typeId}`)
12 | },
13 |
14 | create(payload) {
15 | return API.post(`${resource}`, payload)
16 | },
17 |
18 | update(typeId, payload) {
19 | return API.put(`${resource}/${typeId}`, payload)
20 | },
21 |
22 | delete(typeId) {
23 | return API.delete(`${resource}/${typeId}`)
24 | },
25 | }
26 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_slack/incident/messages.py:
--------------------------------------------------------------------------------
1 | """The file/approach to Slack message building, which leverages Bolt SDK & Blockkit SDK instead of Jinja and raw Slack API calls"""
2 |
3 | from blockkit import (
4 | Context,
5 | Message,
6 | Divider,
7 | )
8 | from blockkit.surfaces import Block
9 |
10 |
11 | def create_incident_channel_escalate_message() -> list[Block]:
12 | """Generate a escalation."""
13 |
14 | blocks = [
15 | Context(elements=["This Case has been escalated to an Incident"]),
16 | Divider(),
17 | ]
18 |
19 | return Message(blocks=blocks).build()["blocks"]
20 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/public/static/icon/file_empty.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/data/source/api.js:
--------------------------------------------------------------------------------
1 | import API from "@/api"
2 |
3 | const resource = "/data/sources"
4 |
5 | export default {
6 | getAll(options) {
7 | return API.get(`${resource}`, { params: { ...options } })
8 | },
9 |
10 | get(sourceId) {
11 | return API.get(`${resource}/${sourceId}`)
12 | },
13 |
14 | create(payload) {
15 | return API.post(`${resource}`, payload)
16 | },
17 |
18 | update(sourceId, payload) {
19 | return API.put(`${resource}/${sourceId}`, payload)
20 | },
21 |
22 | delete(sourceId) {
23 | return API.delete(`${resource}/${sourceId}`)
24 | },
25 | }
26 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/tag_type/api.js:
--------------------------------------------------------------------------------
1 | import API from "@/api"
2 |
3 | const resource = "/tag_types"
4 |
5 | export default {
6 | getAll(options) {
7 | return API.get(`${resource}`, { params: { ...options } })
8 | },
9 |
10 | get(tagTypeId) {
11 | return API.get(`${resource}/${tagTypeId}`)
12 | },
13 |
14 | create(payload) {
15 | return API.post(`${resource}`, payload)
16 | },
17 |
18 | update(tagTypeId, payload) {
19 | return API.put(`${resource}/${tagTypeId}`, payload)
20 | },
21 |
22 | delete(tagTypeId) {
23 | return API.delete(`${resource}/${tagTypeId}`)
24 | },
25 | }
26 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/case/type/api.js:
--------------------------------------------------------------------------------
1 | import API from "@/api"
2 |
3 | const resource = "/case_types"
4 |
5 | export default {
6 | getAll(options) {
7 | return API.get(`${resource}`, { params: { ...options } })
8 | },
9 |
10 | get(caseTypeId) {
11 | return API.get(`${resource}/${caseTypeId}`)
12 | },
13 |
14 | create(payload) {
15 | return API.post(`${resource}`, payload)
16 | },
17 |
18 | update(caseTypeId, payload) {
19 | return API.put(`${resource}/${caseTypeId}`, payload)
20 | },
21 |
22 | delete(caseTypeId) {
23 | return API.delete(`${resource}/${caseTypeId}`)
24 | },
25 | }
26 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/document/api.js:
--------------------------------------------------------------------------------
1 | import API from "@/api"
2 |
3 | const resource = "/documents"
4 |
5 | export default {
6 | getAll(options) {
7 | return API.get(`${resource}`, { params: { ...options } })
8 | },
9 |
10 | get(documentId) {
11 | return API.get(`${resource}/${documentId}`)
12 | },
13 |
14 | create(payload) {
15 | return API.post(`${resource}`, payload)
16 | },
17 |
18 | update(documentId, payload) {
19 | return API.put(`${resource}/${documentId}`, payload)
20 | },
21 |
22 | delete(documentId) {
23 | return API.delete(`${resource}/${documentId}`)
24 | },
25 | }
26 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/feedback/incident/api.js:
--------------------------------------------------------------------------------
1 | import API from "@/api"
2 |
3 | const resource = "/feedback"
4 |
5 | export default {
6 | getAll(options) {
7 | return API.get(`${resource}`, { params: { ...options } })
8 | },
9 |
10 | get(feedbackId) {
11 | return API.get(`${resource}/${feedbackId}`)
12 | },
13 |
14 | create(payload) {
15 | return API.post(`${resource}`, payload)
16 | },
17 |
18 | update(feedbackId, payload) {
19 | return API.put(`${resource}/${feedbackId}`, payload)
20 | },
21 |
22 | delete(feedbackId) {
23 | return API.delete(`${resource}/${feedbackId}`)
24 | },
25 | }
26 |
--------------------------------------------------------------------------------
/src/dispatch/common/utils/views.py:
--------------------------------------------------------------------------------
1 | def create_pydantic_include(include):
2 | """Creates a pydantic sets based on dotted notation."""
3 | include_sets = {}
4 | for i in include:
5 | keyset = None
6 | for key in reversed(i.split(".")):
7 | if keyset:
8 | if key.endswith("[]"):
9 | key = key.strip("[]")
10 | keyset = {key: {"__all__": keyset}}
11 | else:
12 | keyset = {key: keyset}
13 | else:
14 | keyset = {key: ...}
15 | include_sets.update(keyset)
16 |
17 | return include_sets
18 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/core/script.py.mako:
--------------------------------------------------------------------------------
1 | """${message}
2 |
3 | Revision ID: ${up_revision}
4 | Revises: ${down_revision | comma,n}
5 | Create Date: ${create_date}
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 | ${imports if imports else ""}
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = ${repr(up_revision)}
14 | down_revision = ${repr(down_revision)}
15 | branch_labels = ${repr(branch_labels)}
16 | depends_on = ${repr(depends_on)}
17 |
18 |
19 | def upgrade():
20 | ${upgrades if upgrades else "pass"}
21 |
22 |
23 | def downgrade():
24 | ${downgrades if downgrades else "pass"}
25 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/tenant/script.py.mako:
--------------------------------------------------------------------------------
1 | """${message}
2 |
3 | Revision ID: ${up_revision}
4 | Revises: ${down_revision | comma,n}
5 | Create Date: ${create_date}
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 | ${imports if imports else ""}
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = ${repr(up_revision)}
14 | down_revision = ${repr(down_revision)}
15 | branch_labels = ${repr(branch_labels)}
16 | depends_on = ${repr(depends_on)}
17 |
18 |
19 | def upgrade():
20 | ${upgrades if upgrades else "pass"}
21 |
22 |
23 | def downgrade():
24 | ${downgrades if downgrades else "pass"}
25 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/data/source/status/api.js:
--------------------------------------------------------------------------------
1 | import API from "@/api"
2 |
3 | const resource = "/data/sources/statuses"
4 |
5 | export default {
6 | getAll(options) {
7 | return API.get(`${resource}`, { params: { ...options } })
8 | },
9 |
10 | get(statusId) {
11 | return API.get(`${resource}/${statusId}`)
12 | },
13 |
14 | create(payload) {
15 | return API.post(`${resource}`, payload)
16 | },
17 |
18 | update(statusId, payload) {
19 | return API.put(`${resource}/${statusId}`, payload)
20 | },
21 |
22 | delete(statusId) {
23 | return API.delete(`${resource}/${statusId}`)
24 | },
25 | }
26 |
--------------------------------------------------------------------------------
/.github/workflows/enforce-labels.yml:
--------------------------------------------------------------------------------
1 | name: Enforce PR labels
2 |
3 | on:
4 | pull_request:
5 | types: [labeled, unlabeled, opened, edited, synchronize]
6 | jobs:
7 | enforce-label:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - uses: yogevbd/enforce-label-action@2.2.2
11 | with:
12 | REQUIRED_LABELS_ANY: "bug,dependencies,documentation,enhancement,feature,skip-changelog,techdebt,tests"
13 | REQUIRED_LABELS_ANY_DESCRIPTION: "Select at least one label from the following list: bug, dependencies, documentation, enhancement, feature, skip-changelog, techdebt, tests"
14 | BANNED_LABELS: "banned"
15 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/definition/api.js:
--------------------------------------------------------------------------------
1 | import API from "@/api"
2 |
3 | const resource = "/definitions"
4 |
5 | export default {
6 | getAll(options) {
7 | return API.get(`${resource}`, { params: { ...options } })
8 | },
9 |
10 | get(definitionId) {
11 | return API.get(`${resource}/${definitionId}`)
12 | },
13 |
14 | create(payload) {
15 | return API.post(`${resource}`, payload)
16 | },
17 |
18 | update(definitionId, payload) {
19 | return API.put(`${resource}/${definitionId}`, payload)
20 | },
21 |
22 | delete(definitionId) {
23 | return API.delete(`${resource}/${definitionId}`)
24 | },
25 | }
26 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/individual/api.js:
--------------------------------------------------------------------------------
1 | import API from "@/api"
2 |
3 | const resource = "/individuals"
4 |
5 | export default {
6 | getAll(options) {
7 | return API.get(`${resource}`, { params: { ...options } })
8 | },
9 |
10 | get(individualId) {
11 | return API.get(`${resource}/${individualId}`)
12 | },
13 |
14 | create(payload) {
15 | return API.post(`${resource}`, payload)
16 | },
17 |
18 | update(individualId, payload) {
19 | return API.put(`${resource}/${individualId}`, payload)
20 | },
21 |
22 | delete(individualId) {
23 | return API.delete(`${resource}/${individualId}`)
24 | },
25 | }
26 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/project/api.js:
--------------------------------------------------------------------------------
1 | import API from "@/api"
2 |
3 | const resource = "projects"
4 |
5 | export default {
6 | getAll(options) {
7 | return API.get(`/${resource}`, {
8 | params: { ...options },
9 | })
10 | },
11 |
12 | get(projectId) {
13 | return API.get(`/${resource}/${projectId}`)
14 | },
15 |
16 | create(payload) {
17 | return API.post(`/${resource}`, payload)
18 | },
19 |
20 | update(projectId, payload) {
21 | return API.put(`/${resource}/${projectId}`, payload)
22 | },
23 |
24 | delete(projectId) {
25 | return API.delete(`/${resource}/${projectId}`)
26 | },
27 | }
28 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/bases/workflow.py:
--------------------------------------------------------------------------------
1 | """
2 | .. module: dispatch.plugins.bases.workflow
3 | :platform: Unix
4 | :copyright: (c) 2019 by Netflix Inc., see AUTHORS for more
5 | :license: Apache, see LICENSE for more details.
6 | .. moduleauthor:: Kevin Glisson
7 | """
8 |
9 | from dispatch.plugins.base import Plugin
10 |
11 |
12 | class WorkflowPlugin(Plugin):
13 | type = "workflow"
14 |
15 | def get_instance(self, workflow_id: str, instance_id: str, **kwargs):
16 | raise NotImplementedError
17 |
18 | def run(self, workflow_id: str, params: dict, **kwargs):
19 | raise NotImplementedError
20 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/case/EscalateButton.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | mdi-fire
6 |
7 |
8 |
9 |
10 |
11 |
22 |
--------------------------------------------------------------------------------
/.github/workflows/publish-image.yml:
--------------------------------------------------------------------------------
1 | name: Publish Docker image
2 | on:
3 | release:
4 | types: [published]
5 | jobs:
6 | push_to_registry:
7 | name: Push Docker image to GitHub Packages
8 | runs-on: ubuntu-latest
9 | steps:
10 | - name: Check out the repo
11 | uses: actions/checkout@v4
12 | - name: Push to GitHub Packages
13 | uses: docker/build-push-action@v1
14 | with:
15 | username: ${{ github.actor }}
16 | password: ${{ secrets.GITHUB_TOKEN }}
17 | registry: docker.pkg.github.com
18 | repository: netflix/dispatch/dispatch-image
19 | tag_with_ref: true
20 |
--------------------------------------------------------------------------------
/.github/workflows/javascript.yml:
--------------------------------------------------------------------------------
1 | name: Javascript Lint and Test
2 |
3 | on: pull_request
4 |
5 | jobs:
6 | build:
7 | runs-on: ubuntu-latest
8 |
9 | steps:
10 | - name: Check out Git repository
11 | uses: actions/checkout@v4
12 | - name: Setup Node.js environment
13 | uses: actions/setup-node@v4
14 | with:
15 | node-version: 16
16 | - name: Install dev deps
17 | working-directory: src/dispatch/static/dispatch
18 | run: |
19 | npm install
20 | - name: Run ESLint
21 | working-directory: src/dispatch/static/dispatch
22 | run: |
23 | npm run lint
24 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/case/priority/api.js:
--------------------------------------------------------------------------------
1 | import API from "@/api"
2 |
3 | const resource = "/case_priorities"
4 |
5 | export default {
6 | getAll(options) {
7 | return API.get(`${resource}`, { params: { ...options } })
8 | },
9 | get(casePriorityId) {
10 | return API.get(`${resource}/${casePriorityId}`)
11 | },
12 |
13 | create(payload) {
14 | return API.post(`${resource}`, payload)
15 | },
16 |
17 | update(casePriorityId, payload) {
18 | return API.put(`${resource}/${casePriorityId}`, payload)
19 | },
20 |
21 | delete(casePriorityId) {
22 | return API.delete(`${resource}/${casePriorityId}`)
23 | },
24 | }
25 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/case/severity/api.js:
--------------------------------------------------------------------------------
1 | import API from "@/api"
2 |
3 | const resource = "/case_severities"
4 |
5 | export default {
6 | getAll(options) {
7 | return API.get(`${resource}`, { params: { ...options } })
8 | },
9 | get(caseSeverityId) {
10 | return API.get(`${resource}/${caseSeverityId}`)
11 | },
12 |
13 | create(payload) {
14 | return API.post(`${resource}`, payload)
15 | },
16 |
17 | update(caseSeverityId, payload) {
18 | return API.put(`${resource}/${caseSeverityId}`, payload)
19 | },
20 |
21 | delete(caseSeverityId) {
22 | return API.delete(`${resource}/${caseSeverityId}`)
23 | },
24 | }
25 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/data/source/transport/api.js:
--------------------------------------------------------------------------------
1 | import API from "@/api"
2 |
3 | const resource = "/data/sources/transports"
4 |
5 | export default {
6 | getAll(options) {
7 | return API.get(`${resource}`, { params: { ...options } })
8 | },
9 |
10 | get(transportId) {
11 | return API.get(`${resource}/${transportId}`)
12 | },
13 |
14 | create(payload) {
15 | return API.post(`${resource}`, payload)
16 | },
17 |
18 | update(transportId, payload) {
19 | return API.put(`${resource}/${transportId}`, payload)
20 | },
21 |
22 | delete(transportId) {
23 | return API.delete(`${resource}/${transportId}`)
24 | },
25 | }
26 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/notification/api.js:
--------------------------------------------------------------------------------
1 | import API from "@/api"
2 |
3 | const resource = "/notifications"
4 |
5 | export default {
6 | getAll(options) {
7 | return API.get(`${resource}`, { params: { ...options } })
8 | },
9 |
10 | get(notificationId) {
11 | return API.get(`${resource}/${notificationId}`)
12 | },
13 |
14 | create(payload) {
15 | return API.post(`${resource}`, payload)
16 | },
17 |
18 | update(notificationId, payload) {
19 | return API.put(`${resource}/${notificationId}`, payload)
20 | },
21 |
22 | delete(notificationId) {
23 | return API.delete(`${resource}/${notificationId}`)
24 | },
25 | }
26 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/organization/api.js:
--------------------------------------------------------------------------------
1 | import API from "@/api"
2 |
3 | const resource = "/organizations"
4 |
5 | export default {
6 | getAll(options) {
7 | return API.get(`${resource}`, { params: { ...options } })
8 | },
9 |
10 | get(organizationId) {
11 | return API.get(`${resource}/${organizationId}`)
12 | },
13 |
14 | create(payload) {
15 | return API.post(`${resource}`, payload)
16 | },
17 |
18 | update(organizationId, payload) {
19 | return API.put(`${resource}/${organizationId}`, payload)
20 | },
21 |
22 | delete(organizationId) {
23 | return API.delete(`${resource}/${organizationId}`)
24 | },
25 | }
26 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/bases/artificial_intelligence.py:
--------------------------------------------------------------------------------
1 | """
2 | .. module: dispatch.plugins.bases.artificial_intelligence
3 | :platform: Unix
4 | :copyright: (c) 2019 by Netflix Inc., see AUTHORS for more
5 | :license: Apache, see LICENSE for more details.
6 | .. moduleauthor:: Marc Vilanova
7 | """
8 |
9 | from dispatch.plugins.base import Plugin
10 |
11 |
12 | class ArtificialIntelligencePlugin(Plugin):
13 | type = "artificial-intelligence"
14 |
15 | def chat_completion(self, items, **kwargs):
16 | raise NotImplementedError
17 |
18 | def list_models(self, items, **kwargs):
19 | raise NotImplementedError
20 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/case_cost_type/api.js:
--------------------------------------------------------------------------------
1 | import API from "@/api"
2 |
3 | const resource = "/case_cost_types"
4 |
5 | export default {
6 | getAll(options) {
7 | return API.get(`${resource}`, { params: { ...options } })
8 | },
9 |
10 | get(caseCostTypeId) {
11 | return API.get(`${resource}/${caseCostTypeId}`)
12 | },
13 |
14 | create(payload) {
15 | return API.post(`${resource}`, payload)
16 | },
17 |
18 | update(caseCostTypeId, payload) {
19 | return API.put(`${resource}/${caseCostTypeId}`, payload)
20 | },
21 |
22 | delete(caseCostTypeId) {
23 | return API.delete(`${resource}/${caseCostTypeId}`)
24 | },
25 | }
26 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/incident/type/api.js:
--------------------------------------------------------------------------------
1 | import API from "@/api"
2 |
3 | const resource = "/incident_types"
4 |
5 | export default {
6 | getAll(options) {
7 | return API.get(`${resource}`, { params: { ...options } })
8 | },
9 |
10 | get(incidentTypeId) {
11 | return API.get(`${resource}/${incidentTypeId}`)
12 | },
13 |
14 | create(payload) {
15 | return API.post(`${resource}`, payload)
16 | },
17 |
18 | update(incidentTypeId, payload) {
19 | return API.put(`${resource}/${incidentTypeId}`, payload)
20 | },
21 |
22 | delete(incidentTypeId) {
23 | return API.delete(`${resource}/${incidentTypeId}`)
24 | },
25 | }
26 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/data/source/dataFormat/api.js:
--------------------------------------------------------------------------------
1 | import API from "@/api"
2 |
3 | const resource = "/data/sources/dataFormats"
4 |
5 | export default {
6 | getAll(options) {
7 | return API.get(`${resource}`, { params: { ...options } })
8 | },
9 |
10 | get(dataFormatId) {
11 | return API.get(`${resource}/${dataFormatId}`)
12 | },
13 |
14 | create(payload) {
15 | return API.post(`${resource}`, payload)
16 | },
17 |
18 | update(dataFormatId, payload) {
19 | return API.put(`${resource}/${dataFormatId}`, payload)
20 | },
21 |
22 | delete(dataFormatId) {
23 | return API.delete(`${resource}/${dataFormatId}`)
24 | },
25 | }
26 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/email_templates/api.js:
--------------------------------------------------------------------------------
1 | import API from "@/api"
2 |
3 | const resource = "/email_template"
4 |
5 | export default {
6 | getAll(options) {
7 | return API.get(`${resource}`, { params: { ...options } })
8 | },
9 |
10 | get(emailTemplateId) {
11 | return API.get(`${resource}/${emailTemplateId}`)
12 | },
13 |
14 | create(payload) {
15 | return API.post(`${resource}`, payload)
16 | },
17 |
18 | update(emailTemplateId, payload) {
19 | return API.put(`${resource}/${emailTemplateId}`, payload)
20 | },
21 |
22 | delete(emailTemplateId) {
23 | return API.delete(`${resource}/${emailTemplateId}`)
24 | },
25 | }
26 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/incident/severity/api.js:
--------------------------------------------------------------------------------
1 | import API from "@/api"
2 |
3 | const resource = "/incident_severities"
4 |
5 | export default {
6 | getAll(options) {
7 | return API.get(`${resource}`, { params: { ...options } })
8 | },
9 | get(incidentSeverityId) {
10 | return API.get(`${resource}/${incidentSeverityId}`)
11 | },
12 | create(payload) {
13 | return API.post(`${resource}`, payload)
14 | },
15 | update(incidentSeverityId, payload) {
16 | return API.put(`${resource}/${incidentSeverityId}`, payload)
17 | },
18 | delete(incidentSeverityId) {
19 | return API.delete(`${resource}/${incidentSeverityId}`)
20 | },
21 | }
22 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/data/source/environment/api.js:
--------------------------------------------------------------------------------
1 | import API from "@/api"
2 |
3 | const resource = "/data/sources/environments"
4 |
5 | export default {
6 | getAll(options) {
7 | return API.get(`${resource}`, { params: { ...options } })
8 | },
9 |
10 | get(environmentId) {
11 | return API.get(`${resource}/${environmentId}`)
12 | },
13 |
14 | create(payload) {
15 | return API.post(`${resource}`, payload)
16 | },
17 |
18 | update(environmentId, payload) {
19 | return API.put(`${resource}/${environmentId}`, payload)
20 | },
21 |
22 | delete(environmentId) {
23 | return API.delete(`${resource}/${environmentId}`)
24 | },
25 | }
26 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/bases/ticket.py:
--------------------------------------------------------------------------------
1 | """
2 | .. module: dispatch.plugins.bases.ticket
3 | :platform: Unix
4 | :copyright: (c) 2019 by Netflix Inc., see AUTHORS for more
5 | :license: Apache, see LICENSE for more details.
6 | .. moduleauthor:: Kevin Glisson
7 | """
8 |
9 | from dispatch.plugins.base import Plugin
10 |
11 |
12 | class TicketPlugin(Plugin):
13 | type = "ticket"
14 |
15 | def create(self, ticket_id, **kwargs):
16 | raise NotImplementedError
17 |
18 | def update(self, ticket_id, **kwargs):
19 | raise NotImplementedError
20 |
21 | def delete(self, ticket_id, **kwargs):
22 | raise NotImplementedError
23 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/forms/types/api.js:
--------------------------------------------------------------------------------
1 | import API from "@/api"
2 |
3 | const resource = "/forms_type"
4 |
5 | export default {
6 | getAll(options) {
7 | return API.get(`${resource}`, { params: { ...options } })
8 | },
9 |
10 | get(formTypeId) {
11 | return API.get(`${resource}/${formTypeId}`)
12 | },
13 |
14 | create(payload) {
15 | return API.post(`${resource}`, payload)
16 | },
17 |
18 | update(formTypeId, creator_id, payload) {
19 | return API.put(`${resource}/${formTypeId}/${creator_id}`, payload)
20 | },
21 |
22 | delete(formTypeId, creator_id) {
23 | return API.delete(`${resource}/${formTypeId}/${creator_id}`)
24 | },
25 | }
26 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/signal/filter/api.js:
--------------------------------------------------------------------------------
1 | import API from "@/api"
2 |
3 | const resource = "/signals/filters"
4 |
5 | export default {
6 | getAll(options) {
7 | return API.get(`${resource}`, {
8 | params: { ...options },
9 | })
10 | },
11 |
12 | get(signalFilterId) {
13 | return API.get(`${resource}/${signalFilterId}`)
14 | },
15 |
16 | create(payload) {
17 | return API.post(`${resource}`, payload)
18 | },
19 |
20 | update(signalFilterId, payload) {
21 | return API.put(`${resource}/${signalFilterId}`, payload)
22 | },
23 |
24 | delete(signalFilterId) {
25 | return API.delete(`${resource}/${signalFilterId}`)
26 | },
27 | }
28 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/util/form.js:
--------------------------------------------------------------------------------
1 | export function required(value) {
2 | let result
3 |
4 | if (value == null || (Array.isArray(value) && !value.length)) {
5 | result = false
6 | } else {
7 | result = !!String(value).trim().length
8 | }
9 |
10 | return result || "This field is required"
11 | }
12 |
13 | /**
14 | * @copyright Abdelrahman Awad
15 | * @licence MIT
16 | */
17 | const emailRe =
18 | /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
19 | export function email(value) {
20 | return emailRe.test(String(value)) || "Must be a valid email"
21 | }
22 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/incident/priority/api.js:
--------------------------------------------------------------------------------
1 | import API from "@/api"
2 |
3 | const resource = "/incident_priorities"
4 |
5 | export default {
6 | getAll(options) {
7 | return API.get(`${resource}`, { params: { ...options } })
8 | },
9 | get(incidentPriorityId) {
10 | return API.get(`${resource}/${incidentPriorityId}`)
11 | },
12 |
13 | create(payload) {
14 | return API.post(`${resource}`, payload)
15 | },
16 |
17 | update(incidentPriorityId, payload) {
18 | return API.put(`${resource}/${incidentPriorityId}`, payload)
19 | },
20 |
21 | delete(incidentPriorityId) {
22 | return API.delete(`${resource}/${incidentPriorityId}`)
23 | },
24 | }
25 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/tenant/versions/2025-04-17_8f324b0f365a.py:
--------------------------------------------------------------------------------
1 | """Adds `event` column to differentiate if the case was reported via the event reporting form
2 |
3 | Revision ID: 8f324b0f365a
4 | Revises: 4aa819afb065
5 | Create Date: 2025-04-17 11:52:11.148817
6 |
7 | """
8 |
9 | from alembic import op
10 | import sqlalchemy as sa
11 |
12 |
13 | # revision identifiers, used by Alembic.
14 | revision = "8f324b0f365a"
15 | down_revision = "4aa819afb065"
16 | branch_labels = None
17 | depends_on = None
18 |
19 |
20 | def upgrade():
21 | op.add_column("case", sa.Column("event", sa.Boolean(), nullable=True))
22 |
23 |
24 | def downgrade():
25 | op.drop_column("case", "event")
26 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/bases/conversation.py:
--------------------------------------------------------------------------------
1 | """
2 | .. module: dispatch.plugins.bases.conversation
3 | :platform: Unix
4 | :copyright: (c) 2019 by Netflix Inc., see AUTHORS for more
5 | :license: Apache, see LICENSE for more details.
6 | .. moduleauthor:: Kevin Glisson
7 | """
8 |
9 | from dispatch.plugins.base import Plugin
10 |
11 |
12 | class ConversationPlugin(Plugin):
13 | type = "conversation"
14 |
15 | def create(self, items, **kwargs):
16 | raise NotImplementedError
17 |
18 | def add(self, items, **kwargs):
19 | raise NotImplementedError
20 |
21 | def send(self, items, **kwargs):
22 | raise NotImplementedError
23 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/incident_cost_type/api.js:
--------------------------------------------------------------------------------
1 | import API from "@/api"
2 |
3 | const resource = "/incident_cost_types"
4 |
5 | export default {
6 | getAll(options) {
7 | return API.get(`${resource}`, { params: { ...options } })
8 | },
9 |
10 | get(incidentCostTypeId) {
11 | return API.get(`${resource}/${incidentCostTypeId}`)
12 | },
13 |
14 | create(payload) {
15 | return API.post(`${resource}`, payload)
16 | },
17 |
18 | update(incidentCostTypeId, payload) {
19 | return API.put(`${resource}/${incidentCostTypeId}`, payload)
20 | },
21 |
22 | delete(incidentCostTypeId) {
23 | return API.delete(`${resource}/${incidentCostTypeId}`)
24 | },
25 | }
26 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/bases/metric.py:
--------------------------------------------------------------------------------
1 | """
2 | .. module: dispatch.plugins.bases.metric
3 | :platform: Unix
4 | :copyright: (c) 2019 by Netflix Inc., see AUTHORS for more
5 | :license: Apache, see LICENSE for more details.
6 | .. moduleauthor:: Kevin Glisson
7 | """
8 |
9 | from dispatch.plugins.base import Plugin
10 |
11 |
12 | class MetricPlugin(Plugin):
13 | type = "metric"
14 |
15 | def gauge(self, name, value, tags=None):
16 | raise NotImplementedError
17 |
18 | def counter(self, name, value=None, tags=None):
19 | raise NotImplementedError
20 |
21 | def timer(self, name, value, tags=None):
22 | raise NotImplementedError
23 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/tenant/versions/2024-08-26_d6b3853be8e4.py:
--------------------------------------------------------------------------------
1 | """Adding column for select_commander_visibility
2 |
3 | Revision ID: d6b3853be8e4
4 | Revises: 71cd7ed999c4
5 | Create Date: 2024-08-26 14:42:48.423369
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 | # revision identifiers, used by Alembic.
12 | revision = 'd6b3853be8e4'
13 | down_revision = '71cd7ed999c4'
14 | branch_labels = None
15 | depends_on = None
16 |
17 |
18 | def upgrade():
19 | op.add_column('project', sa.Column('select_commander_visibility', sa.Boolean(), server_default='t', nullable=True))
20 |
21 | def downgrade():
22 | op.drop_column('project', 'select_commander_visibility')
23 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_duo/config.py:
--------------------------------------------------------------------------------
1 | from pydantic import Field, SecretStr
2 | from dispatch.config import BaseConfigurationModel
3 |
4 |
5 | class DuoConfiguration(BaseConfigurationModel):
6 | """Duo configuration description."""
7 |
8 | integration_key: SecretStr = Field(
9 | title="Integration Key", description="Admin API integration key ('DI...'):"
10 | )
11 | integration_secret_key: SecretStr = Field(
12 | title="Integration Secret Key",
13 | description="Secret token used in conjunction with integration key.",
14 | )
15 | host: str = Field(
16 | title="API Hostname",
17 | description="API hostname ('api-....duosecurity.com'): ",
18 | )
19 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/signal/engagement/api.js:
--------------------------------------------------------------------------------
1 | import API from "@/api"
2 |
3 | const resource = "/signals/engagements"
4 |
5 | export default {
6 | getAll(options) {
7 | return API.get(`${resource}`, {
8 | params: { ...options },
9 | })
10 | },
11 |
12 | get(signalEngagementId) {
13 | return API.get(`${resource}/${signalEngagementId}`)
14 | },
15 |
16 | create(payload) {
17 | return API.post(`${resource}`, payload)
18 | },
19 |
20 | update(signalEngagementId, payload) {
21 | return API.put(`${resource}/${signalEngagementId}`, payload)
22 | },
23 |
24 | delete(signalEngagementId) {
25 | return API.delete(`${resource}/${signalEngagementId}`)
26 | },
27 | }
28 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "Server Debug",
9 | "type": "python",
10 | "request": "launch",
11 | "program": "${workspaceFolder}/bin/run.py"
12 | },
13 | {
14 | "type": "chrome",
15 | "request": "launch",
16 | "name": "Front End Debug",
17 | "url": "http://localhost:8080",
18 | "webRoot": "${workspaceFolder}/src/dispatch/static/dispatch",
19 | "breakOnLoad": true
20 | }
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/tenant/versions/2021-10-05_ceaf01079f4f.py:
--------------------------------------------------------------------------------
1 | """Ensure search expression is not null
2 |
3 | Revision ID: ceaf01079f4f
4 | Revises: 3820a792d88a
5 | Create Date: 2021-10-05 14:01:53.423667
6 |
7 | """
8 | from alembic import op
9 |
10 |
11 | # revision identifiers, used by Alembic.
12 | revision = "ceaf01079f4f"
13 | down_revision = "3820a792d88a"
14 | branch_labels = None
15 | depends_on = None
16 |
17 |
18 | def upgrade():
19 | op.execute("UPDATE search_filter SET expression = '[]' WHERE expression is null")
20 | op.alter_column("search_filter", "expression", nullable=False)
21 |
22 |
23 | def downgrade():
24 | op.alter_column("search_filter", "expression", nullable=True)
25 |
--------------------------------------------------------------------------------
/docs/docs/administration/contributing/plugins/testing.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: How to test your plugins.
3 | ---
4 |
5 | # Testing
6 |
7 | Dispatch provides a basic test-based testing framework for plugins. In a simple plugin, you'll need to do a few things to get it working:
8 |
9 | ## Require Dispatch
10 |
11 | Augment your plugins `setup.py` to ensure that it depends on `dispatch`
12 |
13 | ```python
14 | setup(
15 | # ...
16 | install_requires=[
17 | 'dispatch',
18 | ]
19 | )
20 | ```
21 |
22 | ## Running Tests
23 |
24 | Running tests follows the py.test standard. As long as your test files and methods are named appropriately \(`test_filename.py` and `test_function()`\) you can call out to py.test:
25 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/bases/auth_mfa.py:
--------------------------------------------------------------------------------
1 | """
2 | .. module: dispatch.plugins.bases.mfa
3 | :platform: Unix
4 | :copyright: (c) 2023 by Netflix Inc., see AUTHORS for more
5 | :license: Apache, see LICENSE for more details.
6 | .. moduleauthor:: Will Sheldon
7 | """
8 |
9 | from dispatch.plugins.base import Plugin
10 |
11 |
12 | class MultiFactorAuthenticationPlugin(Plugin):
13 | type = "auth-mfa"
14 |
15 | def send_push_notification(self, items, **kwargs):
16 | raise NotImplementedError
17 |
18 | def validate_mfa(self, items, **kwargs):
19 | raise NotImplementedError
20 |
21 | def create_mfa_challenge(self, items, **kwargs):
22 | raise NotImplementedError
23 |
--------------------------------------------------------------------------------
/tests/report/test_report_flows.py:
--------------------------------------------------------------------------------
1 | def test_create_tactical_report(session, incident, participant):
2 | from dispatch.report.flows import create_tactical_report
3 | from dispatch.report.models import TacticalReportCreate
4 |
5 | participant.incident = incident
6 |
7 | tactical_report_in = TacticalReportCreate(
8 | conditions="sample conditions", actions="sample actions", needs="sample needs"
9 | )
10 |
11 | assert create_tactical_report(
12 | user_email=participant.individual.email,
13 | incident_id=participant.incident.id,
14 | tactical_report_in=tactical_report_in,
15 | organization_slug=incident.project.organization.slug,
16 | db_session=session,
17 | )
18 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/atomics/Hotkey.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 | {{ hotkey }}
7 |
8 |
9 |
27 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/service/api.js:
--------------------------------------------------------------------------------
1 | import API from "@/api"
2 |
3 | const resource = "/services"
4 |
5 | export default {
6 | getAll(options) {
7 | return API.get(`${resource}`, { params: { ...options } })
8 | },
9 |
10 | getAllByIds(ids) {
11 | return API.get(`${resource}/externalids`, { params: { ids } })
12 | },
13 |
14 | get(serviceId) {
15 | return API.get(`${resource}/${serviceId}`)
16 | },
17 |
18 | create(payload) {
19 | return API.post(`${resource}`, payload)
20 | },
21 |
22 | update(serviceId, payload) {
23 | return API.put(`${resource}/${serviceId}`, payload)
24 | },
25 |
26 | delete(serviceId) {
27 | return API.delete(`${resource}/${serviceId}`)
28 | },
29 | }
30 |
--------------------------------------------------------------------------------
/docs/sidebars.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Creating a sidebar enables you to:
3 | - create an ordered group of docs
4 | - render a sidebar for each doc of that group
5 | - provide next/previous navigation
6 |
7 | The sidebars can be generated from the filesystem, or explicitly defined here.
8 |
9 | Create as many sidebars as you want.
10 | */
11 |
12 | // @ts-check
13 |
14 | /** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */
15 | const sidebars = {
16 | // By default, Docusaurus generates a sidebar from the docs folder structure
17 | adminSidebar: [{ type: "autogenerated", dirName: "administration" }],
18 | userGuideSidebar: [{ type: "autogenerated", dirName: "user-guide" }],
19 | }
20 |
21 | module.exports = sidebars
22 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_core/service.py:
--------------------------------------------------------------------------------
1 | import re
2 |
3 |
4 | def create_resource_id(title: str) -> str:
5 | """Creates a Slack-friendly resource id from the incident title."""
6 | resource_id = title.lower()
7 |
8 | # Replace any character that is not a lowercase letter or number with a hyphen
9 | resource_id = re.sub(r"[^a-z0-9]", "-", resource_id)
10 |
11 | # Replace multiple consecutive hyphens with a single hyphen
12 | resource_id = re.sub(r"-+", "-", resource_id)
13 |
14 | # Ensure the channel name is not longer than 80 characters
15 | resource_id = resource_id[:80]
16 |
17 | # Remove leading or trailing hyphens
18 | resource_id = resource_id.strip("-")
19 |
20 | return resource_id
21 |
--------------------------------------------------------------------------------
/docker/docker-compose.yml:
--------------------------------------------------------------------------------
1 | services:
2 | postgres:
3 | image: postgres:14.6
4 | hostname: postgres
5 | ports:
6 | - "5432:5432"
7 | environment:
8 | POSTGRES_USER: postgres
9 | POSTGRES_PASSWORD: dispatch # Default password, change it
10 | POSTGRES_DB: dispatch
11 | volumes:
12 | - postgres-data:/var/lib/postgresql/data
13 | restart: unless-stopped
14 |
15 | pgadmin:
16 | image: dpage/pgadmin4
17 | depends_on:
18 | - postgres
19 | ports:
20 | - "5555:80"
21 | environment:
22 | PGADMIN_DEFAULT_EMAIL: dispatch@netflix.com
23 | PGADMIN_DEFAULT_PASSWORD: admin # Default password, change it
24 | restart: unless-stopped
25 |
26 | volumes:
27 | postgres-data:
28 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/bases/contact.py:
--------------------------------------------------------------------------------
1 | """
2 | .. module: dispatch.plugins.bases.contact
3 | :platform: Unix
4 | :copyright: (c) 2019 by Netflix Inc., see AUTHORS for more
5 | :license: Apache, see LICENSE for more details.
6 | .. moduleauthor:: Kevin Glisson
7 | """
8 |
9 | from dispatch.plugins.base import Plugin
10 |
11 |
12 | class ContactPlugin(Plugin):
13 | type = "contact"
14 |
15 | def get(self, key, **kwargs):
16 | raise NotImplementedError
17 |
18 | def create(self, key, **kwargs):
19 | raise NotImplementedError
20 |
21 | def update(self, key, **kwargs):
22 | raise NotImplementedError
23 |
24 | def delete(self, key, **kwargs):
25 | raise NotImplementedError
26 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/components/InfoWidget.vue:
--------------------------------------------------------------------------------
1 |
15 |
16 |
17 |
18 | {{ text }}
19 |
20 |
21 |
22 |
32 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/bases/document.py:
--------------------------------------------------------------------------------
1 | """
2 | .. module: dispatch.plugins.bases.document
3 | :platform: Unix
4 | :copyright: (c) 2019 by Netflix Inc., see AUTHORS for more
5 | :license: Apache, see LICENSE for more details.
6 | .. moduleauthor:: Kevin Glisson
7 | """
8 |
9 | from dispatch.plugins.base import Plugin
10 |
11 |
12 | class DocumentPlugin(Plugin):
13 | type = "document"
14 |
15 | def get(self, key, **kwargs):
16 | raise NotImplementedError
17 |
18 | def create(self, key, **kwargs):
19 | raise NotImplementedError
20 |
21 | def update(self, key, **kwargs):
22 | raise NotImplementedError
23 |
24 | def delete(self, key, **kwargs):
25 | raise NotImplementedError
26 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/bases/oncall.py:
--------------------------------------------------------------------------------
1 | """
2 | .. module: dispatch.plugins.bases.oncall
3 | :platform: Unix
4 | :copyright: (c) 2019 by Netflix Inc., see AUTHORS for more
5 | :license: Apache, see LICENSE for more details.
6 | .. moduleauthor:: Kevin Glisson
7 | """
8 |
9 | from dispatch.plugins.base import Plugin
10 |
11 |
12 | class OncallPlugin(Plugin):
13 | type = "oncall"
14 |
15 | def get(self, service_id: str, **kwargs):
16 | raise NotImplementedError
17 |
18 | def page(
19 | self,
20 | service_id: str,
21 | incident_name: str,
22 | incident_title: str,
23 | incident_description: str,
24 | **kwargs,
25 | ):
26 | raise NotImplementedError
27 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_openai/config.py:
--------------------------------------------------------------------------------
1 | from pydantic import Field, SecretStr
2 |
3 | from dispatch.config import BaseConfigurationModel
4 |
5 |
6 | class OpenAIConfiguration(BaseConfigurationModel):
7 | """OpenAI configuration description."""
8 |
9 | api_key: SecretStr = Field(title="API Key", description="Your secret OpenAI API key.")
10 | model: str = Field(
11 | "gpt-4o",
12 | title="Model",
13 | description="Available models can be found at https://platform.openai.com/docs/models",
14 | )
15 | system_message: str = Field(
16 | "You are a helpful assistant.",
17 | title="System Message",
18 | description="The system message to help set the behavior of the assistant.",
19 | )
20 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/bases/definition.py:
--------------------------------------------------------------------------------
1 | """
2 | .. module: dispatch.plugins.bases.definition
3 | :platform: Unix
4 | :copyright: (c) 2019 by Netflix Inc., see AUTHORS for more
5 | :license: Apache, see LICENSE for more details.
6 | .. moduleauthor:: Kevin Glisson
7 | """
8 |
9 | from dispatch.plugins.base import Plugin
10 |
11 |
12 | class DefinitionPlugin(Plugin):
13 | type = "definition"
14 |
15 | def get(self, key, **kwargs):
16 | raise NotImplementedError
17 |
18 | def create(self, key, **kwargs):
19 | raise NotImplementedError
20 |
21 | def update(self, key, **kwargs):
22 | raise NotImplementedError
23 |
24 | def delete(self, key, **kwargs):
25 | raise NotImplementedError
26 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/tenant/versions/2023-01-25_e23c468440c9.py:
--------------------------------------------------------------------------------
1 | """Sets a default search filter creator
2 |
3 | Revision ID: e23c468440c9
4 | Revises: 6e53722fa1f2
5 | Create Date: 2023-01-25 11:05:55.742100
6 |
7 | """
8 | from alembic import op
9 |
10 | # revision identifiers, used by Alembic.
11 | revision = "e23c468440c9"
12 | down_revision = "6e53722fa1f2"
13 | branch_labels = None
14 | depends_on = None
15 |
16 |
17 | def upgrade():
18 | # ### commands auto generated by Alembic - please adjust! ###
19 | # default to a known ID if there is none to help with creator migration
20 | op.execute("update search_filter set creator_id = 1 where creator_id is null;")
21 | # ### end Alembic commands ###
22 |
23 |
24 | def downgrade():
25 | pass
26 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_slack/exceptions.py:
--------------------------------------------------------------------------------
1 | from dispatch.exceptions import DispatchException
2 |
3 |
4 | class BotNotPresentError(DispatchException):
5 | code = "bot_not_present"
6 | msg_template = "{msg}"
7 |
8 |
9 | class CommandError(DispatchException):
10 | code = "command"
11 | msg_template = "{msg}"
12 |
13 |
14 | class ContextError(DispatchException):
15 | code = "context"
16 | msg_template = "{msg}"
17 |
18 |
19 | class EventError(DispatchException):
20 | code = "command"
21 | msg_template = "{msg}"
22 |
23 |
24 | class RoleError(DispatchException):
25 | code = "role"
26 | msg_template = "{msg}"
27 |
28 |
29 | class SubmissionError(DispatchException):
30 | code = "submission"
31 | msg_template = "{msg}"
32 |
--------------------------------------------------------------------------------
/.github/workflows/deploy-docs-test.yml:
--------------------------------------------------------------------------------
1 | name: Test documentation deployment
2 |
3 | on:
4 | pull_request:
5 | branches:
6 | - main
7 | # Review gh actions docs if you want to further define triggers, paths, etc
8 | # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#on
9 |
10 | jobs:
11 | test-deploy:
12 | name: Test deployment
13 | runs-on: ubuntu-latest
14 | steps:
15 | - uses: actions/checkout@v4
16 | - uses: actions/setup-node@v4
17 | with:
18 | node-version: 18
19 | cache: npm
20 | cache-dependency-path: docs/package-lock.json
21 |
22 | - name: Install and Build
23 | run: pwd && npm install && npm run build
24 | working-directory: docs
25 |
--------------------------------------------------------------------------------
/.devcontainer/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM mcr.microsoft.com/devcontainers/python:3.11-bullseye
2 |
3 | ENV PYTHONUNBUFFERED 1
4 |
5 | ARG NODE_VERSION="16"
6 | RUN if [ "${NODE_VERSION}" != "none" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi
7 |
8 | # [Optional] If your requirements rarely change, uncomment this section to add them to the image.
9 | # COPY requirements.txt /tmp/pip-tmp/
10 | # RUN pip3 --disable-pip-version-check --no-cache-dir install -r /tmp/pip-tmp/requirements.txt \
11 | # && rm -rf /tmp/pip-tmp
12 |
13 | # [Optional] Uncomment this section to install additional OS packages.
14 | RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
15 | && apt-get -y install --no-install-recommends postgresql postgresql-contrib
16 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/bases/task.py:
--------------------------------------------------------------------------------
1 | """
2 | .. module: dispatch.plugins.bases.task
3 | :platform: Unix
4 | :copyright: (c) 2019 by Netflix Inc., see AUTHORS for more
5 | :license: Apache, see LICENSE for more details.
6 | .. moduleauthor:: Kevin Glisson
7 | """
8 |
9 | from dispatch.plugins.base import Plugin
10 |
11 |
12 | class TaskPlugin(Plugin):
13 | type = "task"
14 |
15 | def get(self, **kwargs):
16 | raise NotImplementedError
17 |
18 | def create(self, **kwargs):
19 | raise NotImplementedError
20 |
21 | def delete(self, **kwargs):
22 | raise NotImplementedError
23 |
24 | def list(self, **kwargs):
25 | raise NotImplementedError
26 |
27 | def resolve(self, **kwargs):
28 | raise NotImplementedError
29 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/composables/useSavingState.ts:
--------------------------------------------------------------------------------
1 | import { computed, ComputedRef } from "vue"
2 | import { useStore } from "vuex"
3 | import { Store } from "vuex"
4 | import { CaseState } from "@/store/case"
5 |
6 | interface UseSavingStateReturns {
7 | saving: ComputedRef
8 | // eslint-disable-next-line no-unused-vars
9 | setSaving: (value: boolean) => void
10 | }
11 |
12 | export function useSavingState(): UseSavingStateReturns {
13 | const store = useStore>()
14 |
15 | const saving = computed(() => store.state.case_management.selected.saving)
16 |
17 | const setSaving = (value: boolean) => {
18 | store.commit("case_management/SET_SELECTED_SAVING", value)
19 | }
20 |
21 | return {
22 | saving,
23 | setSaving,
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/tenant/versions/2023-02-27_20d3801ad5b7.py:
--------------------------------------------------------------------------------
1 | """Adds external id for tags
2 |
3 | Revision ID: 20d3801ad5b7
4 | Revises: 93b517de08e2
5 | Create Date: 2023-02-27 15:38:07.029395
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 | # revision identifiers, used by Alembic.
12 | revision = "20d3801ad5b7"
13 | down_revision = "93b517de08e2"
14 | branch_labels = None
15 | depends_on = None
16 |
17 |
18 | def upgrade():
19 | # ### commands auto generated by Alembic - please adjust! ###
20 | op.add_column("tag", sa.Column("external_id", sa.String(), nullable=True))
21 | # ### end Alembic commands ###
22 |
23 |
24 | def downgrade():
25 | # ### commands auto generated by Alembic - please adjust! ###
26 | op.drop_column("tag", "external_id")
27 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_test/oncall.py:
--------------------------------------------------------------------------------
1 | """
2 | .. module: dispatch.plugins.dispatch_test.oncall
3 | :platform: Unix
4 | :copyright: (c) 2019 by Netflix Inc., see AUTHORS for more
5 | :license: Apache, see LICENSE for more details.
6 | """
7 |
8 | from dispatch.plugins.bases import OncallPlugin
9 |
10 |
11 | class TestOncallPlugin(OncallPlugin):
12 | title = "Dispatch Test Plugin - Oncall"
13 | slug = "test-oncall"
14 | description = "Oncall plugin for testing purposes"
15 |
16 | def get(self, service_id: str, **kwargs):
17 | return "johnsmith@example.com"
18 |
19 | def page(
20 | self,
21 | service_id: str,
22 | incident_name: str,
23 | incident_title: str,
24 | incident_description: str,
25 | **kwargs,
26 | ):
27 | return
28 |
--------------------------------------------------------------------------------
/docs/docs/administration/settings/case/case-priority.mdx:
--------------------------------------------------------------------------------
1 | # Case Priority
2 |
3 | In addition to case types, Dispatch allows you to specify the case's priority.
4 |
5 |
6 |
7 | 
8 |
9 |
10 |
11 | **Name:** The name of the case priority presented to the user.
12 |
13 | **Description:** The description of the case priority presented to the user.
14 |
15 | **View Order:** The order in which the priority will be listed in menus and dropdowns.
16 |
17 | **Color:** The color used for the case priority in the UI.
18 |
19 | **Default Case Priority:** If the reporter of a case does not provide a priority, a default will be used. Enable the setting to make this case priority the default.
20 |
21 | **Enabled:** Whether this case priority is enabled or not.
22 |
--------------------------------------------------------------------------------
/docs/docs/administration/settings/case/case-severity.mdx:
--------------------------------------------------------------------------------
1 | # Case Severity
2 |
3 | In addition to case types, Dispatch allows you to specify the case's severity.
4 |
5 |
6 |
7 | 
8 |
9 |
10 |
11 | **Name:** The name of the case severity presented to the user.
12 |
13 | **Description:** The description of the case severity presented to the user.
14 |
15 | **View Order:** The order in which the severity will be listed in menus and dropdowns.
16 |
17 | **Color:** The color used for the case severity in the UI.
18 |
19 | **Default Case Severity:** If the reporter of a case does not provide a severity, a default will be used. Enable the setting to make this case severity the default.
20 |
21 | **Enabled:** Whether this case severity is enabled or not.
22 |
--------------------------------------------------------------------------------
/docs/docs/user-guide/index.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Introduction
3 | sidebar_position: 0
4 | ---
5 |
6 | # User Guide
7 |
8 | There are two _main_ personas within Dispatch: an incident commander and an incident participant.
9 |
10 | The incident commander is responsible for driving an incident to resolution. They act as a tie-breaking vote on contentious decisions and are responsible for understanding the _entire_ incident context. Depending on the team and scale of the incident, they are also responsible for delegating tasks and asking questions of other incident participants.
11 |
12 | Incident participants are subject matter experts (SMEs) brought into the incident to help resolve tasks or answer the incident commander's questions.
13 |
14 | Next, you will find more information about how each of these personas interacts with Dispatch.
15 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/tenant/versions/2023-09-13_e875e9544048.py:
--------------------------------------------------------------------------------
1 | """Adds canary column to signal_instance
2 |
3 | Revision ID: e875e9544048
4 | Revises: 0560fab4537f
5 | Create Date: 2023-09-13 09:31:24.223488
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 | # revision identifiers, used by Alembic.
12 | revision = "e875e9544048"
13 | down_revision = "0560fab4537f"
14 | branch_labels = None
15 | depends_on = None
16 |
17 |
18 | def upgrade():
19 | # ### commands auto generated by Alembic - please adjust! ###
20 | op.add_column("signal_instance", sa.Column("canary", sa.Boolean(), nullable=True))
21 | # ### end Alembic commands ###
22 |
23 |
24 | def downgrade():
25 | # ### commands auto generated by Alembic - please adjust! ###
26 | op.drop_column("signal_instance", "canary")
27 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/tenant/versions/2024-07-12_25b8b5829158.py:
--------------------------------------------------------------------------------
1 | """Adds score to form model
2 |
3 | Revision ID: 25b8b5829158
4 | Revises: 15a8d3228123
5 | Create Date: 2024-07-12 17:04:12.645228
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 | # revision identifiers, used by Alembic.
12 | revision = "25b8b5829158"
13 | down_revision = "15a8d3228123"
14 | branch_labels = None
15 | depends_on = None
16 |
17 |
18 | def upgrade():
19 | # ### commands auto generated by Alembic - please adjust! ###
20 | op.add_column("forms", sa.Column("score", sa.Integer(), nullable=True))
21 | # ### end Alembic commands ###
22 |
23 |
24 | def downgrade():
25 | # ### commands auto generated by Alembic - please adjust! ###
26 | op.drop_column("forms", "score")
27 | # ### end Alembic commands ###
28 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_aws/config.py:
--------------------------------------------------------------------------------
1 | from pydantic import Field
2 | from dispatch.config import BaseConfigurationModel
3 |
4 |
5 | class AWSSQSConfiguration(BaseConfigurationModel):
6 | """Signal SQS configuration"""
7 |
8 | queue_name: str = Field(
9 | title="Queue Name",
10 | description="Queue Name, not the ARN.",
11 | )
12 |
13 | queue_owner: str = Field(
14 | title="Queue Owner",
15 | description="Queue Owner Account ID.",
16 | )
17 |
18 | region: str = Field(
19 | title="AWS Region",
20 | description="AWS Region.",
21 | default="us-east-1",
22 | )
23 |
24 | batch_size: int = Field(
25 | title="Batch Size",
26 | description="Number of messages to retrieve from SQS.",
27 | default=10,
28 | le=10,
29 | )
30 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/tenant/versions/2023-03-02_65db2acae3ea.py:
--------------------------------------------------------------------------------
1 | """Adds unique constraint in task model
2 |
3 | Revision ID: 65db2acae3ea
4 | Revises: 5955ed5f76b4
5 | Create Date: 2023-03-02 11:16:27.810635
6 |
7 | """
8 | from alembic import op
9 |
10 | # revision identifiers, used by Alembic.
11 | revision = "65db2acae3ea"
12 | down_revision = "5955ed5f76b4"
13 | branch_labels = None
14 | depends_on = None
15 |
16 |
17 | def upgrade():
18 | # ### commands auto generated by Alembic - please adjust! ###
19 | op.create_unique_constraint(None, "task", ["resource_id", "incident_id"])
20 | # ### end Alembic commands ###
21 |
22 |
23 | def downgrade():
24 | # ### commands auto generated by Alembic - please adjust! ###
25 | op.drop_constraint(None, "task", type_="unique")
26 | # ### end Alembic commands ###
27 |
--------------------------------------------------------------------------------
/docs/docs/administration/settings/contact/team.mdx:
--------------------------------------------------------------------------------
1 | # Team
2 |
3 | Like `Individuals`, there are often groups of individuals (teams) that need to be engaged and notified during an incident. Dispatch manages those groups \(typically, team distribution lists\), providing a centralized data store for that contact data.
4 |
5 |
6 |
7 | 
8 |
9 |
10 |
11 | **Name:** Name of the team.
12 |
13 | **Email:** Email address associated with the team.
14 |
15 | **Company:** Company associated with the team.
16 |
17 | #### Engagement
18 |
19 | In addition to fields about the team, Dispatch allows you to associate a team with other Dispatch primitives. For instance, if you would like to involve an entire team for all incidents of a given priority, associate that priority with the team.
20 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/tenant/versions/2021-11-01_ecabe49272c8.py:
--------------------------------------------------------------------------------
1 | """Adds exclusive tag types
2 |
3 | Revision ID: ecabe49272c8
4 | Revises: f95247c63eda
5 | Create Date: 2021-11-01 15:11:53.675447
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 | # revision identifiers, used by Alembic.
12 | revision = "ecabe49272c8"
13 | down_revision = "f95247c63eda"
14 | branch_labels = None
15 | depends_on = None
16 |
17 |
18 | def upgrade():
19 | # ### commands auto generated by Alembic - please adjust! ###
20 | op.add_column("tag_type", sa.Column("exclusive", sa.Boolean(), nullable=True))
21 | # ### end Alembic commands ###
22 |
23 |
24 | def downgrade():
25 | # ### commands auto generated by Alembic - please adjust! ###
26 | op.drop_column("tag_type", "exclusive")
27 | # ### end Alembic commands ###
28 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/tenant/versions/2024-02-21_27e6558e26a8.py:
--------------------------------------------------------------------------------
1 | """Adds required column to tag_type
2 | Revision ID: 27e6558e26a8
3 | Revises: 0283f2bbe9dd
4 | Create Date: 2024-02-21 14:08:48.787183
5 |
6 | """
7 | from alembic import op
8 | import sqlalchemy as sa
9 |
10 | # revision identifiers, used by Alembic.
11 | revision = "27e6558e26a8"
12 | down_revision = "0283f2bbe9dd"
13 | branch_labels = None
14 | depends_on = None
15 |
16 |
17 | def upgrade():
18 | # ### commands auto generated by Alembic - please adjust! ###
19 | op.add_column("tag_type", sa.Column("required", sa.Boolean(), nullable=True))
20 | # ### end Alembic commands ###
21 |
22 |
23 | def downgrade():
24 | # ### commands auto generated by Alembic - please adjust! ###
25 | op.drop_column("tag_type", "required")
26 | # ### end Alembic commands ###
27 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/dispatch_core/exceptions.py:
--------------------------------------------------------------------------------
1 | class MfaException(Exception):
2 | """Base exception for MFA-related errors."""
3 |
4 | pass
5 |
6 |
7 | class InvalidChallengeError(MfaException):
8 | """Raised when the challenge is invalid."""
9 |
10 | pass
11 |
12 |
13 | class UserMismatchError(MfaException):
14 | """Raised when the challenge doesn't belong to the current user."""
15 |
16 | pass
17 |
18 |
19 | class ActionMismatchError(MfaException):
20 | """Raised when the action doesn't match the challenge."""
21 |
22 | pass
23 |
24 |
25 | class ExpiredChallengeError(MfaException):
26 | """Raised when the challenge is no longer valid."""
27 |
28 | pass
29 |
30 |
31 | class InvalidChallengeStateError(MfaException):
32 | """Raised when the challenge is in an invalid state."""
33 |
34 | pass
35 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | An update is available
7 |
8 | Update
9 |
10 |
11 |
12 |
13 |
14 |
15 |
26 |
27 |
38 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/entity_type/utils.js:
--------------------------------------------------------------------------------
1 | import jsonpath from "jsonpath"
2 |
3 | export function isValidRegex(pattern) {
4 | try {
5 | new RegExp(pattern)
6 | return true
7 | } catch (e) {
8 | return false
9 | }
10 | }
11 |
12 | function sanitizeString(str) {
13 | return str.replace(/[&<>"'`=/]/g, function (char) {
14 | return {
15 | "&": "&",
16 | "<": "<",
17 | ">": ">",
18 | '"': """,
19 | "'": "'",
20 | "`": "`",
21 | "=": "=",
22 | "/": "/",
23 | }[char]
24 | })
25 | }
26 |
27 | export function isValidJsonPath(jpath) {
28 | try {
29 | if (sanitizeString(jpath) !== jpath) {
30 | return false
31 | }
32 |
33 | jsonpath.parse(jpath)
34 | return true
35 | } catch (e) {
36 | return false
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/tenant/versions/2023-03-09_ec78c132ab93.py:
--------------------------------------------------------------------------------
1 | """Adds case resolution reason
2 |
3 | Revision ID: ec78c132ab93
4 | Revises: 1d21b28bc553
5 | Create Date: 2023-03-09 10:16:54.885960
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 | # revision identifiers, used by Alembic.
12 | revision = "ec78c132ab93"
13 | down_revision = "1d21b28bc553"
14 | branch_labels = None
15 | depends_on = None
16 |
17 |
18 | def upgrade():
19 | # ### commands auto generated by Alembic - please adjust! ###
20 | op.add_column("case", sa.Column("resolution_reason", sa.String(), nullable=True))
21 | # ### end Alembic commands ###
22 |
23 |
24 | def downgrade():
25 | # ### commands auto generated by Alembic - please adjust! ###
26 | op.drop_column("case", "resolution_reason")
27 | # ### end Alembic commands ###
28 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/components/Refresh.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Refresh Required
5 | {{ message }}
6 |
7 |
8 | Refresh
9 |
10 |
11 |
12 |
13 |
31 |
--------------------------------------------------------------------------------
/docs/docs/administration/faq.mdx:
--------------------------------------------------------------------------------
1 | # FAQ
2 |
3 | ## Running ‘dispatch database upgrade’ seems stuck.
4 |
5 | The upgrade is most likely stuck because an existing query on the database is holding onto a lock that the migration needs.
6 |
7 | To resolve, login to your dispatch database and run:
8 |
9 | > SELECT \* FROM pg_locks l INNER JOIN pg_stat_activity s ON \(l.pid = s.pid\) WHERE waiting AND NOT granted;
10 |
11 | The result of this query will give you a list of queries that are currently waiting to be executed. From there, attempt to identify the PID of the query blocking the migration. Once found, execute:
12 |
13 | > select pg_terminate_backend\(<blocking-pid>\);
14 |
15 | See [http://stackoverflow.com/questions/22896496/alembic-migration-stuck-with-postgresql](http://stackoverflow.com/questions/22896496/alembic-migration-stuck-with-postgresql) for more.
16 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/tenant/versions/2023-01-27_956eb8f8987e.py:
--------------------------------------------------------------------------------
1 | """empty message
2 |
3 | Revision ID: 956eb8f8987e
4 | Revises: e23c468440c9
5 | Create Date: 2023-01-27 11:11:47.149608
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 | # revision identifiers, used by Alembic.
12 | revision = "956eb8f8987e"
13 | down_revision = "e23c468440c9"
14 | branch_labels = None
15 | depends_on = None
16 |
17 |
18 | def upgrade():
19 | # ### commands auto generated by Alembic - please adjust! ###
20 | op.add_column("participant", sa.Column("user_conversation_id", sa.String(), nullable=True))
21 | # ### end Alembic commands ###
22 |
23 |
24 | def downgrade():
25 | # ### commands auto generated by Alembic - please adjust! ###
26 | op.drop_column("participant", "user_conversation_id")
27 | # ### end Alembic commands ###
28 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/tenant/versions/2023-03-02_5955ed5f76b4.py:
--------------------------------------------------------------------------------
1 | """Adds the ability to toggle signal definitions.
2 |
3 | Revision ID: 5955ed5f76b4
4 | Revises: 20d3801ad5b7
5 | Create Date: 2023-03-02 11:12:18.168787
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 | # revision identifiers, used by Alembic.
12 | revision = "5955ed5f76b4"
13 | down_revision = "20d3801ad5b7"
14 | branch_labels = None
15 | depends_on = None
16 |
17 |
18 | def upgrade():
19 | # ### commands auto generated by Alembic - please adjust! ###
20 | op.add_column("signal", sa.Column("enabled", sa.Boolean(), nullable=True))
21 | # ### end Alembic commands ###
22 |
23 |
24 | def downgrade():
25 | # ### commands auto generated by Alembic - please adjust! ###
26 | op.drop_column("signal", "enabled")
27 | # ### end Alembic commands ###
28 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/tenant/versions/2024-08-05_71cd7ed999c4.py:
--------------------------------------------------------------------------------
1 | """Adds lifecycle column to signal model
2 |
3 | Revision ID: 71cd7ed999c4
4 | Revises: ef17416626ff
5 | Create Date: 2024-08-05 15:22:27.578399
6 |
7 | """
8 |
9 | from alembic import op
10 | import sqlalchemy as sa
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = "71cd7ed999c4"
14 | down_revision = "ef17416626ff"
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column("signal", sa.Column("lifecycle", sa.String(), nullable=True))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column("signal", "lifecycle")
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/tenant/versions/2023-06-06_0a19c5a4c2ba.py:
--------------------------------------------------------------------------------
1 | """Renames field column to jpath
2 |
3 | Revision ID: 0a19c5a4c2ba
4 | Revises: 35a2ad8b53a2
5 | Create Date: 2023-06-06 08:54:24.986736
6 |
7 | """
8 | from alembic import op
9 |
10 | # revision identifiers, used by Alembic.
11 | revision = "0a19c5a4c2ba"
12 | down_revision = "35a2ad8b53a2"
13 | branch_labels = None
14 | depends_on = None
15 |
16 |
17 | def upgrade():
18 | # ### commands auto generated by Alembic - please adjust! ###
19 | op.alter_column("entity_type", "field", nullable=False, new_column_name="jpath")
20 | # ### end Alembic commands ###
21 |
22 |
23 | def downgrade():
24 | # ### commands auto generated by Alembic - please adjust! ###
25 | op.alter_column("entity_type", "jpath", nullable=False, new_column_name="field")
26 | # ### end Alembic commands ###
27 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/tenant/versions/2024-02-13_0283f2bbe9dd.py:
--------------------------------------------------------------------------------
1 | """Adds boolean column default to signal model
2 |
3 | Revision ID: 0283f2bbe9dd
4 | Revises: d4bbb234d0bc
5 | Create Date: 2024-02-13 15:31:17.975089
6 |
7 | """
8 |
9 | from alembic import op
10 | import sqlalchemy as sa
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = "0283f2bbe9dd"
14 | down_revision = "d4bbb234d0bc"
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column("signal", sa.Column("default", sa.Boolean(), nullable=True))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column("signal", "default")
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/workflow/api.js:
--------------------------------------------------------------------------------
1 | import API from "@/api"
2 |
3 | const resource = "/workflows"
4 |
5 | export default {
6 | getAll(options) {
7 | return API.get(`${resource}`, { params: { ...options } })
8 | },
9 |
10 | get(workflowId) {
11 | return API.get(`${resource}/${workflowId}`)
12 | },
13 |
14 | getInstance(workflowInstanceId) {
15 | return API.get(`${resource}/instances/${workflowInstanceId}`)
16 | },
17 |
18 | create(payload) {
19 | return API.post(`${resource}`, payload)
20 | },
21 |
22 | update(workflowId, payload) {
23 | return API.put(`${resource}/${workflowId}`, payload)
24 | },
25 |
26 | run(workflowId, payload) {
27 | return API.post(`${resource}/${workflowId}/run`, payload)
28 | },
29 |
30 | delete(workflowId) {
31 | return API.delete(`${resource}/${workflowId}`)
32 | },
33 | }
34 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/tenant/versions/2022-02-15_3097592c0739.py:
--------------------------------------------------------------------------------
1 | """Adds resolution column to incident data model
2 |
3 | Revision ID: 3097592c0739
4 | Revises: 5f96c909a3c0
5 | Create Date: 2022-02-15 15:48:45.724812
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 | # revision identifiers, used by Alembic.
12 | revision = "3097592c0739"
13 | down_revision = "5f96c909a3c0"
14 | branch_labels = None
15 | depends_on = None
16 |
17 |
18 | def upgrade():
19 | # ### commands auto generated by Alembic - please adjust! ###
20 | op.add_column("incident", sa.Column("resolution", sa.String(), nullable=True))
21 | # ### end Alembic commands ###
22 |
23 |
24 | def downgrade():
25 | # ### commands auto generated by Alembic - please adjust! ###
26 | op.drop_column("incident", "resolution")
27 | # ### end Alembic commands ###
28 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/tenant/versions/2022-12-15_bd61c1e1e7cd.py:
--------------------------------------------------------------------------------
1 | """Adds page_assignee column
2 |
3 | Revision ID: bd61c1e1e7cd
4 | Revises: f809d9e76ba3
5 | Create Date: 2022-12-15 14:59:25.077450
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = "bd61c1e1e7cd"
14 | down_revision = "f809d9e76ba3"
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column("case_priority", sa.Column("page_assignee", sa.Boolean(), nullable=True))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column("case_priority", "page_assignee")
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/views/Widget.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
36 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/tenant/versions/2023-08-04_6b98c28edd86.py:
--------------------------------------------------------------------------------
1 | """Adds daily report setting to project
2 |
3 | Revision ID: 6b98c28edd86
4 | Revises: a3fb1380cf76
5 | Create Date: 2023-08-04 17:36:18.646729
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 | # revision identifiers, used by Alembic.
12 | revision = "6b98c28edd86"
13 | down_revision = "a3fb1380cf76"
14 | branch_labels = None
15 | depends_on = None
16 |
17 |
18 | def upgrade():
19 | # ### commands auto generated by Alembic - please adjust! ###
20 | op.add_column("project", sa.Column("send_daily_reports", sa.Boolean(), nullable=True))
21 | # ### end Alembic commands ###
22 |
23 |
24 | def downgrade():
25 | # ### commands auto generated by Alembic - please adjust! ###
26 | op.drop_column("project", "send_daily_reports")
27 | # ### end Alembic commands ###
28 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/tenant/versions/2023-11-29a_bdaeabba3e53.py:
--------------------------------------------------------------------------------
1 | """Removes search vector from signal instance model
2 |
3 | Revision ID: bdaeabba3e53
4 | Revises: f2605bfc1f59
5 | Create Date: 2023-11-29 12:59:45.408085
6 |
7 | """
8 | from alembic import op
9 |
10 | # revision identifiers, used by Alembic.
11 | revision = "bdaeabba3e53"
12 | down_revision = "f2605bfc1f59"
13 | branch_labels = None
14 | depends_on = None
15 |
16 |
17 | def upgrade():
18 | # ### commands auto generated by Alembic - please adjust! ###
19 | op.drop_index(
20 | "signal_instance_search_vector_idx", table_name="signal_instance", postgresql_using="gin"
21 | )
22 | op.drop_column("signal_instance", "search_vector")
23 | # ### end Alembic commands ###
24 |
25 |
26 | def downgrade():
27 | # ### commands auto generated by Alembic - please adjust! ###
28 | pass
29 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/tenant/versions/2024-04-02_71cb25c06fa0.py:
--------------------------------------------------------------------------------
1 | """Adds enabled to project
2 |
3 | Revision ID: 71cb25c06fa0
4 | Revises: 91bd05855ad1
5 | Create Date: 2024-04-02 12:31:20.012288
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 | # revision identifiers, used by Alembic.
12 | revision = "71cb25c06fa0"
13 | down_revision = "91bd05855ad1"
14 | branch_labels = None
15 | depends_on = None
16 |
17 |
18 | def upgrade():
19 | # ### commands auto generated by Alembic - please adjust! ###
20 | op.add_column("project", sa.Column("enabled", sa.Boolean(), server_default="t", nullable=True, default=True))
21 | # ### end Alembic commands ###
22 |
23 |
24 | def downgrade():
25 | # ### commands auto generated by Alembic - please adjust! ###
26 | op.drop_column("project", "enabled")
27 | # ### end Alembic commands ###
28 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/tenant/versions/2024-12-05_575ca7d954a8.py:
--------------------------------------------------------------------------------
1 | """Adds incident summary to the incident table.
2 |
3 | Revision ID: 575ca7d954a8
4 | Revises: 928b725d64f6
5 | Create Date: 2024-12-05 15:05:46.932404
6 |
7 | """
8 |
9 | from alembic import op
10 | import sqlalchemy as sa
11 |
12 |
13 | # revision identifiers, used by Alembic.
14 | revision = "575ca7d954a8"
15 | down_revision = "928b725d64f6"
16 | branch_labels = None
17 | depends_on = None
18 |
19 |
20 | def upgrade():
21 | # ### commands auto generated by Alembic - please adjust! ###
22 | op.add_column("incident", sa.Column("summary", sa.String(), nullable=True))
23 | # ### end Alembic commands ###
24 |
25 |
26 | def downgrade():
27 | # ### commands auto generated by Alembic - please adjust! ###
28 | op.drop_column("incident", "summary")
29 | # ### end Alembic commands ###
30 |
--------------------------------------------------------------------------------
/docs/docs/administration/settings/signal/engagement-filter.mdx:
--------------------------------------------------------------------------------
1 | # Engagement Filter
2 |
3 | Engagement Filters are used to automate the process of reaching out to a user involved in a specific Signal Instance. Engagement Filters make use of Entity Types that match email addresses for users in your environment and custom message you configure to be sent to those users. Engagement Filters also support multi-factor authentication when validating suspicious behavior. This feature is useful when you want to be confident the user you engaged is actually who they say they are and not a malicious actor.
4 |
5 | ### Creating an Engagement Filter
6 |
7 | To create an Engagement Filter, follow these steps:
8 |
9 | 1. Navigate to a Signal Definition edit page.
10 | 2. Click on the '+' icon adjacent to the 'Engagement Filter(s)' dropdown menu.
11 |
12 | 
13 |
--------------------------------------------------------------------------------
/docs/docs/user-guide/incidents/feedback.mdx:
--------------------------------------------------------------------------------
1 | # Feedback
2 |
3 | Dispatch sends a direct message on incident close to all incident participants asking them to rate their satisfaction on how to incident was handled and to provide feedback. Feedback submitted by incident participants is stored and displayed in the feedback section of the UI, and shared with the incident's commander via email on a daily basis.
4 |
5 | #### Direct Message
6 |
7 |
8 |
9 | 
10 |
11 |
12 |
13 | #### Feedback Modal
14 |
15 |
16 |
17 | 
18 |
19 |
20 |
21 | #### Feedback UI Table
22 |
23 |
24 |
25 | 
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/core/versions/2023-04-14_3dd4d12844dc.py:
--------------------------------------------------------------------------------
1 | """Adds last_mfa_time to DispatchUser
2 |
3 | Revision ID: 3dd4d12844dc
4 | Revises: e0d568f345c9
5 | Create Date: 2023-04-14 15:17:00.450716
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = "3dd4d12844dc"
14 | down_revision = "e0d568f345c9"
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column("dispatch_user", sa.Column("last_mfa_time", sa.DateTime(), nullable=True))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column("dispatch_user", "last_mfa_time")
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/tenant/versions/2022-07-28_847ff5e1f81c.py:
--------------------------------------------------------------------------------
1 | """Removes slug column from case type data model
2 |
3 | Revision ID: 847ff5e1f81c
4 | Revises: 2ef7baab2916
5 | Create Date: 2022-07-28 12:00:08.607995
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 | # revision identifiers, used by Alembic.
12 | revision = "847ff5e1f81c"
13 | down_revision = "2ef7baab2916"
14 | branch_labels = None
15 | depends_on = None
16 |
17 |
18 | def upgrade():
19 | # ### commands auto generated by Alembic - please adjust! ###
20 | op.drop_column("case_type", "slug")
21 | # ### end Alembic commands ###
22 |
23 |
24 | def downgrade():
25 | # ### commands auto generated by Alembic - please adjust! ###
26 | op.add_column("case_type", sa.Column("slug", sa.VARCHAR(), autoincrement=False, nullable=True))
27 | # ### end Alembic commands ###
28 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/tenant/versions/2024-09-20_1f4dc687945d.py:
--------------------------------------------------------------------------------
1 | """Adds new genai prompt column to signal definition model
2 |
3 | Revision ID: 1f4dc687945d
4 | Revises: 19c10c121a22
5 | Create Date: 2024-09-20 10:49:11.303112
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 | # revision identifiers, used by Alembic.
12 | revision = '1f4dc687945d'
13 | down_revision = '19c10c121a22'
14 | branch_labels = None
15 | depends_on = None
16 |
17 |
18 | def upgrade():
19 | # ### commands auto generated by Alembic - please adjust! ###
20 | op.add_column('signal', sa.Column('genai_prompt', sa.String(), nullable=True))
21 | # ### end Alembic commands ###
22 |
23 |
24 | def downgrade():
25 | # ### commands auto generated by Alembic - please adjust! ###
26 | op.drop_column('signal', 'genai_prompt')
27 | # ### end Alembic commands ###
28 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Dispatch
8 |
12 |
13 |
14 |
15 |
16 |
17 | Please enable JavaScript to use Dispatch.
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/core/versions/2023-09-27_5c60513d6e5e.py:
--------------------------------------------------------------------------------
1 | """Adds last_mfa_time to DispatchUser
2 |
3 | Revision ID: 5c60513d6e5e
4 | Revises: 3dd4d12844dc
5 | Create Date: 2023-09-27 15:17:00.450716
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = "5c60513d6e5e"
14 | down_revision = "3dd4d12844dc"
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column("dispatch_user", sa.Column("experimental_features", sa.Boolean(), default=False))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column("dispatch_user", "experimental_features")
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/tenant/versions/2022-03-16_03b7e0d36519.py:
--------------------------------------------------------------------------------
1 | """Adds default column to dispatch user project table
2 |
3 | Revision ID: 03b7e0d36519
4 | Revises: b5d3706a1d54
5 | Create Date: 2022-03-16 09:35:00.406534
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 | # revision identifiers, used by Alembic.
12 | revision = "03b7e0d36519"
13 | down_revision = "b5d3706a1d54"
14 | branch_labels = None
15 | depends_on = None
16 |
17 |
18 | def upgrade():
19 | # ### commands auto generated by Alembic - please adjust! ###
20 | op.add_column("dispatch_user_project", sa.Column("default", sa.Boolean(), nullable=True))
21 | # ### end Alembic commands ###
22 |
23 |
24 | def downgrade():
25 | # ### commands auto generated by Alembic - please adjust! ###
26 | op.drop_column("dispatch_user_project", "default")
27 | # ### end Alembic commands ###
28 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/tenant/versions/2022-08-04_8d7232b7cbd4.py:
--------------------------------------------------------------------------------
1 | """Adds column to case type table to support plugin metadata
2 |
3 | Revision ID: 8d7232b7cbd4
4 | Revises: cec9ab5c5c8e
5 | Create Date: 2022-08-04 10:02:21.310067
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = "8d7232b7cbd4"
14 | down_revision = "cec9ab5c5c8e"
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column("case_type", sa.Column("plugin_metadata", sa.JSON(), nullable=True))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column("case_type", "plugin_metadata")
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/tenant/versions/2025-06-04_7fc3888c7b9a.py:
--------------------------------------------------------------------------------
1 | """Add GenAI suggestions column to tag_type table
2 |
3 | Revision ID: 7fc3888c7b9a
4 | Revises: 8f324b0f365a
5 | Create Date: 2025-06-04 14:49:20.592746
6 |
7 | """
8 |
9 | from alembic import op
10 | import sqlalchemy as sa
11 |
12 |
13 | # revision identifiers, used by Alembic.
14 | revision = "7fc3888c7b9a"
15 | down_revision = "8f324b0f365a"
16 | branch_labels = None
17 | depends_on = None
18 |
19 |
20 | def upgrade():
21 | # ### commands auto generated by Alembic - please adjust! ###
22 | op.add_column("tag_type", sa.Column("genai_suggestions", sa.Boolean(), nullable=True))
23 | # ### end Alembic commands ###
24 |
25 |
26 | def downgrade():
27 | # ### commands auto generated by Alembic - please adjust! ###
28 | op.drop_column("tag_type", "genai_suggestions")
29 | # ### end Alembic commands ###
30 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/entity/api.js:
--------------------------------------------------------------------------------
1 | import API from "@/api"
2 |
3 | const resource = "/entity"
4 |
5 | export default {
6 | getAll(options) {
7 | return API.get(`${resource}`, { params: { ...options } })
8 | },
9 |
10 | get(entityId) {
11 | return API.get(`${resource}/${entityId}`)
12 | },
13 |
14 | create(payload) {
15 | return API.post(`${resource}`, payload)
16 | },
17 |
18 | update(entityId, payload) {
19 | return API.put(`${resource}/${entityId}`, payload)
20 | },
21 |
22 | delete(entityId) {
23 | return API.delete(`${resource}/${entityId}`)
24 | },
25 |
26 | async getCases(entityId, daysBack) {
27 | return await API.get(`${resource}/${entityId}/cases/${daysBack}`)
28 | },
29 |
30 | async getSignalInstances(entityId, daysBack) {
31 | return await API.get(`${resource}/${entityId}/signal_instances/${daysBack}`)
32 | },
33 | }
34 |
--------------------------------------------------------------------------------
/src/dispatch/static/dispatch/src/task/api.js:
--------------------------------------------------------------------------------
1 | import API from "@/api"
2 |
3 | const resource = "/tasks"
4 |
5 | export default {
6 | getAll(options) {
7 | return API.get(`${resource}`, { params: { ...options } })
8 | },
9 |
10 | get(taskId) {
11 | return API.get(`${resource}/${taskId}`)
12 | },
13 |
14 | create(payload) {
15 | return API.post(`${resource}`, payload)
16 | },
17 |
18 | update(taskId, payload) {
19 | return API.put(`${resource}/${taskId}`, payload)
20 | },
21 |
22 | bulkUpdate(tasks, payload) {
23 | return Promise.all(
24 | tasks.map((task) => {
25 | return this.update(task.id, { ...task, ...payload })
26 | })
27 | )
28 | },
29 |
30 | delete(taskId) {
31 | return API.delete(`${resource}/${taskId}`)
32 | },
33 |
34 | createTicket(taskId) {
35 | return API.post(`${resource}/ticket/${taskId}`)
36 | },
37 | }
38 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/tenant/versions/2022-04-13_e40aefe7fc4d.py:
--------------------------------------------------------------------------------
1 | """Adds column to incident priority model to allow for setting a color
2 |
3 | Revision ID: e40aefe7fc4d
4 | Revises: 03b7e0d36519
5 | Create Date: 2022-04-13 09:44:48.400912
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 | # revision identifiers, used by Alembic.
12 | revision = "e40aefe7fc4d"
13 | down_revision = "03b7e0d36519"
14 | branch_labels = None
15 | depends_on = None
16 |
17 |
18 | def upgrade():
19 | # ### commands auto generated by Alembic - please adjust! ###
20 | op.add_column("incident_priority", sa.Column("color", sa.String(), nullable=True))
21 | # ### end Alembic commands ###
22 |
23 |
24 | def downgrade():
25 | # ### commands auto generated by Alembic - please adjust! ###
26 | op.drop_column("incident_priority", "color")
27 | # ### end Alembic commands ###
28 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/tenant/versions/2023-03-03_7ddae3ba7822.py:
--------------------------------------------------------------------------------
1 | """Adds signal triggers
2 |
3 | Revision ID: 7ddae3ba7822
4 | Revises: 65db2acae3ea
5 | Create Date: 2023-03-03 10:16:57.890859
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 | from sqlalchemy.schema import MetaData
11 |
12 | from dispatch.search.fulltext import sync_trigger
13 |
14 | # revision identifiers, used by Alembic.
15 | revision = "7ddae3ba7822"
16 | down_revision = "65db2acae3ea"
17 | branch_labels = None
18 | depends_on = None
19 |
20 |
21 | def upgrade():
22 | conn = op.get_context().connection
23 | metadata = MetaData(schema=conn.dialect.default_schema_name)
24 | metadata.bind = conn
25 | table = sa.Table("signal", metadata, autoload_with=conn)
26 | sync_trigger(conn, table, "search_vector", ["name", "description", "variant"])
27 |
28 |
29 | def downgrade():
30 | pass
31 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/tenant/versions/2023-06-05_35a2ad8b53a2.py:
--------------------------------------------------------------------------------
1 | """Remove constraint
2 |
3 | Revision ID: 35a2ad8b53a2
4 | Revises: bad95cd82724
5 | Create Date: 2023-06-05 16:02:46.337434
6 |
7 | """
8 | from alembic import op
9 |
10 | # revision identifiers, used by Alembic.
11 | revision = "35a2ad8b53a2"
12 | down_revision = "bad95cd82724"
13 | branch_labels = None
14 | depends_on = None
15 |
16 |
17 | def upgrade():
18 | # ### commands auto generated by Alembic - please adjust! ###
19 | op.drop_constraint("entity_type_name_project_id_key", "entity_type", type_="unique")
20 | # ### end Alembic commands ###
21 |
22 |
23 | def downgrade():
24 | # ### commands auto generated by Alembic - please adjust! ###
25 | op.create_unique_constraint(
26 | "entity_type_name_project_id_key", "entity_type", ["name", "project_id"]
27 | )
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/bases/investigation_tooling.py:
--------------------------------------------------------------------------------
1 | """
2 | .. module: dispatch.plugins.bases.investigation_tooling
3 | :platform: Unix
4 | :copyright: (c) 2019 by Netflix Inc., see AUTHORS for more
5 | :license: Apache, see LICENSE for more details.
6 | .. moduleauthor:: Marc Vilanova
7 | """
8 |
9 | from dispatch.case.models import Case
10 | from dispatch.plugins.base import Plugin
11 |
12 |
13 | class InvestigationToolingPlugin(Plugin):
14 | """Investigation tooling base plugin class."""
15 |
16 | type = "investigation-tooling"
17 |
18 | def create_investigation(self, case: Case, **kwargs):
19 | """Creates a new investigation.
20 |
21 | Args:
22 | case: Case object
23 | kwargs: Optional kwargs.
24 |
25 | Returns:
26 | Additional context.
27 | """
28 | raise NotImplementedError
29 |
--------------------------------------------------------------------------------
/src/dispatch/plugins/bases/participant_group.py:
--------------------------------------------------------------------------------
1 | """
2 | .. module: dispatch.plugins.bases.participant_group
3 | :platform: Unix
4 | :copyright: (c) 2019 by Netflix Inc., see AUTHORS for more
5 | :license: Apache, see LICENSE for more details.
6 | .. moduleauthor:: Kevin Glisson
7 | """
8 |
9 | from dispatch.plugins.base import Plugin
10 |
11 |
12 | class ParticipantGroupPlugin(Plugin):
13 | type = "participant-group"
14 |
15 | def create(self, participants, **kwargs):
16 | raise NotImplementedError
17 |
18 | def add(self, participant, **kwargs):
19 | raise NotImplementedError
20 |
21 | def remove(self, participant, **kwargs):
22 | raise NotImplementedError
23 |
24 | def delete(self, group, **kwargs):
25 | raise NotImplementedError
26 |
27 | def list(self, group, **kwargs):
28 | raise NotImplementedError
29 |
--------------------------------------------------------------------------------
/docs/docs/administration/settings/plugins/configuring-pagerduty.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: Configuration options for the PagerDuty plugin.
3 | ---
4 |
5 | # Configuring PagerDuty
6 |
7 | :::info
8 | Dispatch ships with support for resolving on-call schedules via the PagerDuty API.
9 | :::
10 |
11 | ### Env Configuration
12 |
13 | Add the following env vars to your `.env` file.
14 |
15 | ## `PAGERDUTY_API_KEY` \[Required. Secret: True\]
16 |
17 | > PagerDuty API key.
18 |
19 | ## `PAGERDUTY_API_FROM_EMAIL` \[Required\]
20 |
21 | > Email to be added to all outgoing incident pages.
22 |
23 | ## Oncall Service Configuration
24 |
25 | Go to /services on your Web server running Dispatch and add a new service. Select type `pagerduty-oncall` and add your PagerDuty Service ID in the external id field.
26 |
27 |
28 |
29 | 
30 |
31 |
32 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/tenant/versions/2025-03-14_92a359040b8e.py:
--------------------------------------------------------------------------------
1 | """Adds column to exclude incident types from review
2 |
3 | Revision ID: 92a359040b8e
4 | Revises: 37406cca756c
5 | Create Date: 2025-03-14 14:05:30.522240
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '92a359040b8e'
14 | down_revision = '37406cca756c'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('incident_type', sa.Column('exclude_from_review', sa.Boolean(), nullable=True))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column('incident_type', 'exclude_from_review')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/docs/docs/administration/settings/incident/notification.mdx:
--------------------------------------------------------------------------------
1 | # Notification
2 |
3 | Notifications allow you to specify who should be sent incident notifications (in addition to those directly involved).
4 |
5 |
6 |
7 | 
8 |
9 |
10 |
11 | **Name:** The name you wish to present to the user.
12 |
13 | **Description:** The description presented to the user when the notification is viewed.
14 |
15 | **Type:** The plugin type that should be used to send the notification (email or conversation).
16 |
17 | **Target:** The recipient of the notification whatever makes sense for the selected plugin type. (e.g. a Slack conversation name without `#` or an email address.)
18 |
19 | **Filters:** The search filter which will be used to determine when a notification should be sent.
20 |
21 | **Enabled:** Whether the notification is enabled or not.
22 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/tenant/versions/2023-08-10_74d279f6e4f6.py:
--------------------------------------------------------------------------------
1 | """Adds column to indicate if health metrics need to be collected for the service
2 |
3 | Revision ID: 74d279f6e4f6
4 | Revises: 6b98c28edd86
5 | Create Date: 2023-03-29 13:51:11.269860
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 | # revision identifiers, used by Alembic.
12 | revision = "74d279f6e4f6"
13 | down_revision = "6b98c28edd86"
14 | branch_labels = None
15 | depends_on = None
16 |
17 |
18 | def upgrade():
19 | # ### commands auto generated by Alembic - please adjust! ###
20 | op.add_column("service", sa.Column("health_metrics", sa.Boolean(), nullable=True))
21 | # ### end Alembic commands ###
22 |
23 |
24 | def downgrade():
25 | # ### commands auto generated by Alembic - please adjust! ###
26 | op.drop_column("service", "health_metrics")
27 | # ### end Alembic commands ###
28 |
--------------------------------------------------------------------------------
/src/dispatch/database/revisions/tenant/versions/2023-12-08_2f06fd73eae6.py:
--------------------------------------------------------------------------------
1 | """Adds the engage_next_oncall column to the incident_role table.
2 |
3 | Revision ID: 2f06fd73eae6
4 | Revises: 580a18ec4c39
5 | Create Date: 2023-12-08 11:22:15.565073
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 | # revision identifiers, used by Alembic.
12 | revision = "2f06fd73eae6"
13 | down_revision = "580a18ec4c39"
14 | branch_labels = None
15 | depends_on = None
16 |
17 |
18 | def upgrade():
19 | # ### commands auto generated by Alembic - please adjust! ###
20 | op.add_column("incident_role", sa.Column("engage_next_oncall", sa.Boolean(), nullable=True))
21 | # ### end Alembic commands ###
22 |
23 |
24 | def downgrade():
25 | # ### commands auto generated by Alembic - please adjust! ###
26 | op.drop_column("incident_role", "engage_next_oncall")
27 | # ### end Alembic commands ###
28 |
--------------------------------------------------------------------------------