RAID Module¤
The RAID module provides enhanced Jira integration for incident management and external ticket creation.
Overview¤
RAID (Request And Issue Database) extends FireFighter's capabilities with:
- Automatic Jira ticket creation for incidents
- External API for ticket creation (Landbot integration)
- Smart user resolution based on email domains
- Priority mapping from P1-P5 to Jira priorities
- Attachment handling for external submissions
- Slack notifications for ticket updates
Priority Mapping¤
The RAID module supports full P1-P5 priority mapping:
| Priority | Level | Jira Priority | Use Case |
|---|---|---|---|
| P1 | Critical | 1 | System outages, complete service failures |
| P2 | High | 2 | Major functionality impaired, significant impact |
| P3 | Medium | 3 | Minor functionality affected, moderate impact |
| P4 | Low | 4 | Small issues, minimal impact |
| P5 | Lowest | 5 | Cosmetic issues, enhancement requests |
User Resolution¤
The module handles user resolution for external ticket creation:
- Internal users: Direct mapping from email to existing users
- External users: Email domain-based routing to default Jira users
- Slack fallback: Attempts to find users via Slack integration
- Default assignment: Falls back to configured default Jira user
Authentication¤
The external ticket creation endpoint (POST /api/v2/firefighter/raid/jira_bot) requires Bearer token authentication.
Each caller must use a dedicated APIToken, generated from the Django back-office at /admin/api/apitokenproxy/. Requests must include the header:
Common pitfall: HTML formatting in copy-pasted tokens¤
The Django admin renders the token with text styling inherited from rest_framework.authtoken. A regular copy-paste from the admin into a tool that accepts rich-text input (Landbot Webhook header values, some no-code platforms, Notion, etc.) silently carries the styling as inline HTML. The receiving tool displays the value as plain text, but the actual outgoing header looks like:
The endpoint then returns 401 not_authenticated because DRF cannot parse the keyword.
Workaround: when copying a token from the admin, paste with Cmd+Shift+V (paste without formatting) or type the token by hand.
Debug tip: if a third-party integration fails with not_authenticated while curl with the same token succeeds, route the integration's webhook to webhook.site to inspect the actual Authorization header it emits.
API Reference¤
raid ¤
RAID is ManoMano's internal issue dispatcher.
It works with Slack and Jira to create, update and close issues.
It is currently not configurable.
Modules:
-
client– -
forms– -
messages– -
migrations– -
models– -
serializers– -
service– -
signals– -
tasks–RAID Celery tasks.
-
utils– -
views–
client ¤
Classes:
RaidJiraClient ¤
Bases: JiraClient
Methods:
-
add_attachments_to_issue–Add attachments to a Jira issue.
-
add_comment–Add a comment to a Jira issue, as the FireFighter service account.
-
assign_issue–Assign a Jira issue to a user.
-
create_postmortem_issue–Create a Jira post-mortem issue with custom fields.
-
get_issue_status_name–Return the name of the current status of a Jira issue.
-
get_jira_user_from_jira_id–Look for a Jira User in DB, if not found, fetch it from Jira API.
-
get_jira_user_from_user–Fetches a Jira user from the Jira API.
-
get_project_creation_problem–Tell why issues cannot be created in a Jira project, or None if they can.
-
get_watchers_from_jira_ticket–Fetch watchers for a specific Jira ticket from Jira API.
-
transition_issue_auto–Attempts to close an issue by applying transitions to it.
-
update_issue_fields–Update Jira issue fields (supports custom fields like customfield_11064).
Source code in src/firefighter/jira_app/client.py
add_attachments_to_issue staticmethod ¤
Add attachments to a Jira issue.
Parameters:
Raises:
-
JiraAttachmentError–if there is an error while adding any attachment
Source code in src/firefighter/raid/client.py
add_comment ¤
Add a comment to a Jira issue, as the FireFighter service account.
Parameters:
Source code in src/firefighter/jira_app/client.py
assign_issue ¤
Assign a Jira issue to a user.
Parameters:
-
issue_key(str) –Jira issue key (e.g., "INCIDENT-123")
-
account_id(str) –Jira account ID of the user
Returns:
-
bool–True if assignment succeeded, False otherwise
Note
This method does not raise exceptions. Assignment failures are logged as warnings since assignment is typically an optional operation.
Source code in src/firefighter/jira_app/client.py
create_postmortem_issue ¤
create_postmortem_issue(project_key: str, issue_type: str, fields: dict[str, Any], parent_issue_key: str | None = None) -> dict[str, Any]
Create a Jira post-mortem issue with custom fields.
Parameters:
-
project_key(str) –Jira project key (e.g., "INCIDENT")
-
issue_type(str) –Issue type name (e.g., "Post-mortem")
-
fields(dict[str, Any]) –Dictionary of field IDs to values
-
parent_issue_key(str | None, default:None) –Optional parent issue key to link this post-mortem to
Returns:
Raises:
-
JiraAPIError–If issue creation fails
Source code in src/firefighter/jira_app/client.py
get_issue_status_name ¤
Return the name of the current status of a Jira issue.
Parameters:
Source code in src/firefighter/jira_app/client.py
get_jira_user_from_jira_id ¤
Look for a Jira User in DB, if not found, fetch it from Jira API.
Parameters:
-
jira_account_id(str) –Jira account id
Raises:
-
JiraUserNotFoundError–User not found in Jira nor in DB
-
JiraUserDatabaseError–Unable to create user in DB
-
ValueError–Empty jira_account_id
Returns:
-
JiraUser(JiraUser) –Jira user object
Source code in src/firefighter/jira_app/client.py
get_jira_user_from_user ¤
Fetches a Jira user from the Jira API.
Parameters:
-
user(User) –User object
Raises:
-
JiraUserNotFoundError–User not found in Jira
Returns:
-
JiraAPIUser(JiraUser) –Jira API user object
Source code in src/firefighter/jira_app/client.py
get_project_creation_problem ¤
Tell why issues cannot be created in a Jira project, or None if they can.
Catches projects archived or deleted in Jira, and projects our Jira account cannot see or create issues in.
Source code in src/firefighter/raid/client.py
get_watchers_from_jira_ticket ¤
Fetch watchers for a specific Jira ticket from Jira API.
Parameters:
Raises:
-
ValueError–Empty issue id
Returns:
-
list(User) –List of Jira users object, or empty list if ticket doesn't exist
Source code in src/firefighter/jira_app/client.py
transition_issue_auto ¤
Attempts to close an issue by applying transitions to it.
Parameters:
-
issue_id(str | int) –Jira issue id
-
target_status_name(str) –target status name
-
workflow_name(str) –workflow name
Source code in src/firefighter/jira_app/client.py
update_issue_fields ¤
Update Jira issue fields (supports custom fields like customfield_11064).
forms ¤
Functions:
-
prepare_jira_fields–Prepare all fields for jira_client.create_issue().
prepare_jira_fields ¤
prepare_jira_fields(
*,
title: str,
description: str,
priority: int,
reporter: str,
incident_category: str,
environments: list[str],
platforms: list[str],
impacts_data: dict[str, ImpactLevel],
optional_fields: dict[str, Any] | None = None
) -> dict[str, Any]
Prepare all fields for jira_client.create_issue().
This function centralizes Jira field preparation for both P1-P3 and P4-P5 incidents, ensuring all custom fields are properly passed.
Parameters:
-
title(str) –Incident title
-
description(str) –Incident description
-
priority(int) –Priority value (1-5)
-
reporter(str) –Jira user account ID
-
incident_category(str) –Category name
-
environments(list[str]) –List of environment values (e.g. ["PRD", "STG"])
-
platforms(list[str]) –List of platform values (e.g. ["platform-FR", "platform-DE"])
-
impacts_data(dict[str, ImpactLevel]) –Dictionary of impact data for business_impact computation
-
optional_fields(dict[str, Any] | None, default:None) –Optional dictionary containing: - zendesk_ticket_id: Zendesk ticket ID (customer-specific) - seller_contract_id: Seller contract ID (seller-specific) - zoho_desk_ticket_id: Zoho Desk ticket ID (seller-specific) - is_key_account: Key account flag (seller-specific) - is_seller_in_golden_list: Golden list flag (seller-specific) - suggested_team_routing: Suggested team routing (P4-P5 only)
Returns:
Source code in src/firefighter/raid/forms.py
messages ¤
Classes:
-
SlackMessageJiraClosedBlockedByCanBeClosed–Sent to the incident channel when a Jira 'Closed' webhook was received but the incident cannot be automatically closed (e.g. missing key events).
SlackMessageJiraClosedBlockedByCanBeClosed ¤
Bases: SlackMessageSurface
Sent to the incident channel when a Jira 'Closed' webhook was received but the incident cannot be automatically closed (e.g. missing key events).
Methods:
-
get_metadata–The value of
event_typeshould be an alphanumeric string, and human-readable. The value of this field may appear in the UI to developers, so keep this in mind when choosing a value. Developers should make an effort to name with the patternand -
post_message–Deprecated. Only use for Global Channel, until the global channel is set in DB.
Attributes:
-
strategy(SlackMessageStrategy) –Alphanumeric ID, starting with a letter, that may contain underscores.
Source code in src/firefighter/raid/messages.py
strategy class-attribute instance-attribute ¤
strategy: SlackMessageStrategy = SlackMessageStrategy.APPEND
Alphanumeric ID, starting with a letter, that may contain underscores.
get_metadata ¤
The value of event_type should be an alphanumeric string, and human-readable. The value of this field may appear in the UI to developers, so keep this in mind when choosing a value. Developers should make an effort to name with the pattern
Returns:
-
Metadata(Metadata) –Slack Metadata for the message.
Source code in src/firefighter/slack/messages/base.py
post_message ¤
post_message(conversation_id: str, client: WebClient = DefaultWebClient, **kwargs: Never) -> SlackResponse
Deprecated. Only use for Global Channel, until the global channel is set in DB.
Source code in src/firefighter/slack/messages/base.py
migrations ¤
Modules:
0004_add_check_feature_team_projects_periodic_task ¤
Functions:
-
create_periodic_task–Check every Monday at 9:00 (Europe/Paris) that feature team Jira projects accept tickets.
create_periodic_task ¤
Check every Monday at 9:00 (Europe/Paris) that feature team Jira projects accept tickets.
Source code in src/firefighter/raid/migrations/0004_add_check_feature_team_projects_periodic_task.py
models ¤
serializers ¤
Classes:
Functions:
-
parse_attachment_urls–Normalise the attachments payload sent by Landbot into a list of URLs.
-
validate_no_spaces–Ensure the string does not contain spaces.
LandbotIssueRequestSerializer ¤
Bases: ModelSerializer[JiraTicket]
Methods:
-
validate_labels–Transform null labels to empty list.
validate_labels ¤
parse_attachment_urls ¤
Normalise the attachments payload sent by Landbot into a list of URLs.
Landbot historically sends a Python-stringified list (e.g. "['https://a', 'https://b']") rather than a JSON array. This helper tolerates that legacy format along with a plain comma-separated string or a single URL.
Source code in src/firefighter/raid/serializers.py
service ¤
Functions:
-
get_jira_user_from_user–Returns the JiraUser object for a given user, if it exists in DB or can be fetched on Jira API. Returns the default JiraUser if not found.
get_jira_user_from_user ¤
Returns the JiraUser object for a given user, if it exists in DB or can be fetched on Jira API. Returns the default JiraUser if not found.
Source code in src/firefighter/raid/service.py
signals ¤
Modules:
incident_updated ¤
Functions:
-
incident_priority_post_save_fallback–Fallback to push priority to Jira when Incident saves with priority_id in update_fields
-
incident_status_post_save_fallback–Fallback to push status to Jira when Incident saves with status in update_fields
-
incident_updated_close_ticket_when_mitigated_or_postmortem–Close Jira incident ticket based on incident status and priority.
-
incident_updated_sync_priority_to_jira–Push Impact priority changes to Jira custom priority field (customfield_11064).
incident_priority_post_save_fallback ¤
incident_priority_post_save_fallback(
sender: Any, instance: Incident, *, created: bool, update_fields: set[str] | None, **kwargs: Any
) -> None
Fallback to push priority to Jira when Incident saves with priority_id in update_fields but no incident_updated signal fired (e.g., admin edits). Skips when marked to avoid loops.
Source code in src/firefighter/raid/signals/incident_updated.py
incident_status_post_save_fallback ¤
incident_status_post_save_fallback(
sender: Any, instance: Incident, *, created: bool, update_fields: set[str] | None, **kwargs: Any
) -> None
Fallback to push status to Jira when Incident saves with status in update_fields but no incident_updated signal fired (e.g., admin edits). Skips when marked to avoid loops.
Source code in src/firefighter/raid/signals/incident_updated.py
incident_updated_close_ticket_when_mitigated_or_postmortem ¤
incident_updated_close_ticket_when_mitigated_or_postmortem(
sender: Any, incident: Incident, incident_update: IncidentUpdate, updated_fields: list[str], **kwargs: Any
) -> None
Close Jira incident ticket based on incident status and priority.
Closure logic: - P1/P2 (needs_postmortem): Close only when incident is CLOSED - P3+ (no postmortem): Close when incident is MITIGATED or CLOSED - POST_MORTEM status never closes the ticket (it remains open during PM phase)
Source code in src/firefighter/raid/signals/incident_updated.py
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 | |
incident_updated_sync_priority_to_jira ¤
incident_updated_sync_priority_to_jira(
sender: Any, incident: Incident, incident_update: IncidentUpdate, updated_fields: list[str], **kwargs: Any
) -> None
Push Impact priority changes to Jira custom priority field (customfield_11064). Skips if change originated from Jira (event_type='jira_priority_sync') to avoid loops.
Source code in src/firefighter/raid/signals/incident_updated.py
tasks ¤
RAID Celery tasks.
Modules:
check_feature_team_projects ¤
Functions:
-
check_feature_team_projects–Log an error for each feature team whose Jira project cannot receive tickets.
check_feature_team_projects ¤
Log an error for each feature team whose Jira project cannot receive tickets.
Jira projects get archived or restricted without Impact being told: the tickets of such a team silently fall back to the default project.
Source code in src/firefighter/raid/tasks/check_feature_team_projects.py
utils ¤
Functions:
-
get_domain_from_email–Returns the domain from an email address.
-
normalize_cache_value–Normalize cache values for loop-prevention keys.
get_domain_from_email cached ¤
Returns the domain from an email address.
Removes any subdomain(s) and the @, applies .lower().
Parameters:
-
email(str) –The email address to extract the domain from.
Returns:
-
str–The domain part of the email address.
Raises:
-
ValueError–If the email is not well-formed.
Examples:
john.doe@example.com=>example.comalice.bob@test.example.ORG=>example.orgwebmaster@localhost=>localhost
Source code in src/firefighter/raid/utils.py
normalize_cache_value ¤
Normalize cache values for loop-prevention keys.
Source code in src/firefighter/raid/utils.py
views ¤
Classes:
CreateJiraBotView ¤
Bases: CreateModelMixin, GenericAPIView[JiraTicket]
Methods:
-
post–Allow to create a Jira ticket through Landbot.
post ¤
Allow to create a Jira ticket through Landbot. Requires a valid Bearer token, that you can create in the back-office if you have the right permissions.
Source code in src/firefighter/raid/views/__init__.py
JiraCommentAlertView ¤
Bases: CreateAPIView[Any]
Methods:
-
post–Allow to send a message in Slack when a comment in a Jira ticket is created or modified.
post ¤
Allow to send a message in Slack when a comment in a Jira ticket is created or modified.
Authentication: Jira signs the raw request body with settings.RAID_JIRA_WEBHOOK_SECRET and sends the HMAC-SHA256 signature in the X-Hub-Signature: sha256=<hex> header. Configure the same secret in the Atlassian webhook "Secret" field.
Source code in src/firefighter/raid/views/__init__.py
JiraUpdateAlertView ¤
Bases: CreateAPIView[Any]
Methods:
-
post–Allow to send a message in Slack when some fields ("Priority", "project", "description", "status") of a Jira ticket are updated.
post ¤
Allow to send a message in Slack when some fields ("Priority", "project", "description", "status") of a Jira ticket are updated.
Authentication: Jira signs the raw request body with settings.RAID_JIRA_WEBHOOK_SECRET and sends the HMAC-SHA256 signature in the X-Hub-Signature: sha256=<hex> header. Configure the same secret in the Atlassian webhook "Secret" field.