firefighter-incident 0.0.27__py3-none-any.whl → 0.0.29__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- firefighter/_version.py +2 -2
- firefighter/incidents/views/views.py +16 -4
- firefighter/jira_app/templates/jira/postmortem/impact.txt +9 -4
- firefighter/slack/messages/slack_messages.py +73 -19
- firefighter/slack/views/modals/postmortem.py +75 -7
- {firefighter_incident-0.0.27.dist-info → firefighter_incident-0.0.29.dist-info}/METADATA +1 -1
- {firefighter_incident-0.0.27.dist-info → firefighter_incident-0.0.29.dist-info}/RECORD +11 -10
- firefighter_tests/test_slack/views/modals/test_postmortem_modal.py +72 -0
- {firefighter_incident-0.0.27.dist-info → firefighter_incident-0.0.29.dist-info}/WHEEL +0 -0
- {firefighter_incident-0.0.27.dist-info → firefighter_incident-0.0.29.dist-info}/entry_points.txt +0 -0
- {firefighter_incident-0.0.27.dist-info → firefighter_incident-0.0.29.dist-info}/licenses/LICENSE +0 -0
firefighter/_version.py
CHANGED
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '0.0.
|
|
32
|
-
__version_tuple__ = version_tuple = (0, 0,
|
|
31
|
+
__version__ = version = '0.0.29'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 0, 29)
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
|
@@ -148,10 +148,22 @@ class IncidentDetailView(CustomDetailView[Incident]):
|
|
|
148
148
|
"conversation",
|
|
149
149
|
"created_by",
|
|
150
150
|
]
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
if
|
|
154
|
-
|
|
151
|
+
|
|
152
|
+
# Always load post-mortem relationships to display existing data
|
|
153
|
+
# even if creation is disabled
|
|
154
|
+
try:
|
|
155
|
+
# Only add if confluence app is installed
|
|
156
|
+
if "firefighter.confluence" in settings.INSTALLED_APPS:
|
|
157
|
+
select_related.append("postmortem_for")
|
|
158
|
+
except ImportError:
|
|
159
|
+
pass
|
|
160
|
+
|
|
161
|
+
try:
|
|
162
|
+
# Only add if jira_app is installed
|
|
163
|
+
if "firefighter.jira_app" in settings.INSTALLED_APPS:
|
|
164
|
+
select_related.append("jira_postmortem_for")
|
|
165
|
+
except ImportError:
|
|
166
|
+
pass
|
|
155
167
|
queryset = Incident.objects.select_related(*select_related).prefetch_related(
|
|
156
168
|
Prefetch(
|
|
157
169
|
"incidentupdate_set",
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
h2. Impact
|
|
2
2
|
|
|
3
|
+
h3. Business Impact
|
|
4
|
+
|
|
5
|
+
*{color:red}_TODO: Describe the business impact and precise the BV daily loss._{color}*
|
|
6
|
+
*{color:red}_For the BV daily loss, use [this documentation|https://manomano.atlassian.net/wiki/spaces/TC/pages/4089315451/How+to+calculate+BV+loss]._{color}*
|
|
7
|
+
|
|
8
|
+
h3. User Impact
|
|
9
|
+
|
|
10
|
+
*{color:red}_TODO: Describe the impact on users._{color}*
|
|
11
|
+
|
|
3
12
|
h3. Affected Systems
|
|
4
13
|
{% if components %}
|
|
5
14
|
{% for component in components %}* {{ component.name }}
|
|
6
15
|
{% endfor %}
|
|
7
16
|
{% else %}_No components recorded._
|
|
8
17
|
{% endif %}
|
|
9
|
-
|
|
10
|
-
h3. User Impact
|
|
11
|
-
|
|
12
|
-
*{color:red}_TODO: Describe the impact on users and business._{color}*
|
|
@@ -109,15 +109,36 @@ class SlackMessageIncidentPostMortemReminder(SlackMessageSurface):
|
|
|
109
109
|
action_id=UpdateStatusModal.open_action,
|
|
110
110
|
),
|
|
111
111
|
),
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
112
|
+
]
|
|
113
|
+
|
|
114
|
+
# Add post-mortem editing options based on available services
|
|
115
|
+
if hasattr(self.incident, "postmortem_for") and self.incident.postmortem_for:
|
|
116
|
+
blocks.append(
|
|
117
|
+
SectionBlock(
|
|
118
|
+
text="2. Edit your post-mortem on Confluence",
|
|
119
|
+
accessory=ButtonElement(
|
|
120
|
+
text="Edit post-mortem",
|
|
121
|
+
value=self.incident.postmortem_for.page_edit_url,
|
|
122
|
+
url=self.incident.postmortem_for.page_edit_url,
|
|
123
|
+
action_id="open_link",
|
|
124
|
+
),
|
|
125
|
+
)
|
|
126
|
+
)
|
|
127
|
+
elif hasattr(self.incident, "jira_postmortem_for") and self.incident.jira_postmortem_for:
|
|
128
|
+
jira_pm = self.incident.jira_postmortem_for
|
|
129
|
+
blocks.append(
|
|
130
|
+
SectionBlock(
|
|
131
|
+
text="2. Edit your post-mortem on Jira",
|
|
132
|
+
accessory=ButtonElement(
|
|
133
|
+
text=f"Edit Jira post-mortem ({jira_pm.jira_issue_key})",
|
|
134
|
+
url=jira_pm.issue_url,
|
|
135
|
+
action_id="open_link",
|
|
136
|
+
),
|
|
137
|
+
)
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
# Continue with remaining steps
|
|
141
|
+
blocks.extend([
|
|
121
142
|
SectionBlock(
|
|
122
143
|
text=f"3. Submit the key events to {APP_DISPLAY_NAME}",
|
|
123
144
|
**accessory_kwargs,
|
|
@@ -138,7 +159,8 @@ class SlackMessageIncidentPostMortemReminder(SlackMessageSurface):
|
|
|
138
159
|
)
|
|
139
160
|
]
|
|
140
161
|
),
|
|
141
|
-
]
|
|
162
|
+
])
|
|
163
|
+
|
|
142
164
|
if POSTMORTEM_HELP_URL:
|
|
143
165
|
blocks.insert(
|
|
144
166
|
4,
|
|
@@ -184,14 +206,30 @@ class SlackMessageIncidentFixedNextActions(SlackMessageSurface):
|
|
|
184
206
|
),
|
|
185
207
|
),
|
|
186
208
|
DividerBlock(),
|
|
187
|
-
ContextBlock(
|
|
188
|
-
elements=[
|
|
189
|
-
MarkdownTextObject(
|
|
190
|
-
text="A post-mortem is *not* required for this incident.\nIf you want to create one, use `/incident postmortem` to create a new post-mortem page on Confluence."
|
|
191
|
-
)
|
|
192
|
-
]
|
|
193
|
-
),
|
|
194
209
|
]
|
|
210
|
+
|
|
211
|
+
# Add post-mortem context message if any post-mortem service is enabled
|
|
212
|
+
enable_confluence = getattr(settings, "ENABLE_CONFLUENCE", False)
|
|
213
|
+
enable_jira_postmortem = getattr(settings, "ENABLE_JIRA_POSTMORTEM", False)
|
|
214
|
+
|
|
215
|
+
if enable_confluence or enable_jira_postmortem:
|
|
216
|
+
postmortem_text = "A post-mortem is *not* required for this incident.\nIf you want to create one, use `/incident postmortem` to create a new post-mortem page"
|
|
217
|
+
|
|
218
|
+
if enable_confluence and enable_jira_postmortem:
|
|
219
|
+
postmortem_text += " on Confluence or Jira."
|
|
220
|
+
elif enable_confluence:
|
|
221
|
+
postmortem_text += " on Confluence."
|
|
222
|
+
elif enable_jira_postmortem:
|
|
223
|
+
postmortem_text += " on Jira."
|
|
224
|
+
|
|
225
|
+
blocks.append(
|
|
226
|
+
ContextBlock(
|
|
227
|
+
elements=[
|
|
228
|
+
MarkdownTextObject(text=postmortem_text)
|
|
229
|
+
]
|
|
230
|
+
)
|
|
231
|
+
)
|
|
232
|
+
|
|
195
233
|
return blocks
|
|
196
234
|
|
|
197
235
|
|
|
@@ -612,7 +650,23 @@ class SlackMessageIncidentPostMortemCreated(SlackMessageSurface):
|
|
|
612
650
|
return "\n".join(parts)
|
|
613
651
|
|
|
614
652
|
def get_blocks(self) -> list[Block]:
|
|
615
|
-
|
|
653
|
+
blocks: list[Block] = [SectionBlock(text=self.get_text())]
|
|
654
|
+
|
|
655
|
+
# Add documentation link if Jira post-mortem exists
|
|
656
|
+
if hasattr(self.incident, "jira_postmortem_for") and self.incident.jira_postmortem_for:
|
|
657
|
+
blocks.append(
|
|
658
|
+
SectionBlock(
|
|
659
|
+
text="Need guidance on how to fill Post-Mortems in Jira? See our documentation",
|
|
660
|
+
accessory=ButtonElement(
|
|
661
|
+
text="Open documentation",
|
|
662
|
+
url="https://manomano.atlassian.net/wiki/spaces/TC/pages/5639635000/How+to+fill+Post-Mortems+in+Jira",
|
|
663
|
+
value="jira_postmortem_documentation",
|
|
664
|
+
action_id="open_link",
|
|
665
|
+
),
|
|
666
|
+
)
|
|
667
|
+
)
|
|
668
|
+
|
|
669
|
+
return blocks
|
|
616
670
|
|
|
617
671
|
|
|
618
672
|
class SlackMessageIncidentPostMortemCreatedAnnouncement(SlackMessageSurface):
|
|
@@ -696,7 +750,7 @@ class SlackMessagePostMortemReminder5Days(SlackMessageSurface):
|
|
|
696
750
|
if hasattr(self.incident, "postmortem_for"):
|
|
697
751
|
pm_links.append(
|
|
698
752
|
ButtonElement(
|
|
699
|
-
text="Open
|
|
753
|
+
text="Open Post-Mortem (Confluence)",
|
|
700
754
|
url=self.incident.postmortem_for.page_edit_url,
|
|
701
755
|
action_id="open_link",
|
|
702
756
|
)
|
|
@@ -3,10 +3,15 @@ from __future__ import annotations
|
|
|
3
3
|
import logging
|
|
4
4
|
from typing import TYPE_CHECKING, Any
|
|
5
5
|
|
|
6
|
-
from
|
|
6
|
+
from django.core.exceptions import ObjectDoesNotExist
|
|
7
|
+
from slack_sdk.models.blocks.block_elements import ButtonElement
|
|
8
|
+
from slack_sdk.models.blocks.blocks import ActionsBlock, Block, SectionBlock
|
|
7
9
|
from slack_sdk.models.views import View
|
|
8
10
|
|
|
9
|
-
from firefighter.
|
|
11
|
+
from firefighter.confluence.models import PostMortemManager
|
|
12
|
+
from firefighter.incidents.models.incident import Incident
|
|
13
|
+
from firefighter.slack.utils import respond
|
|
14
|
+
from firefighter.slack.views.modals.base_modal.base import SlackModal, app
|
|
10
15
|
from firefighter.slack.views.modals.base_modal.mixins import (
|
|
11
16
|
IncidentSelectableModalMixin,
|
|
12
17
|
)
|
|
@@ -14,8 +19,6 @@ from firefighter.slack.views.modals.base_modal.mixins import (
|
|
|
14
19
|
if TYPE_CHECKING:
|
|
15
20
|
from slack_bolt.context.ack.ack import Ack
|
|
16
21
|
|
|
17
|
-
from firefighter.incidents.models.incident import Incident
|
|
18
|
-
|
|
19
22
|
|
|
20
23
|
logger = logging.getLogger(__name__)
|
|
21
24
|
|
|
@@ -32,8 +35,8 @@ class PostMortemModal(
|
|
|
32
35
|
blocks: list[Block] = []
|
|
33
36
|
|
|
34
37
|
# Check existing post-mortems
|
|
35
|
-
has_confluence =
|
|
36
|
-
has_jira =
|
|
38
|
+
has_confluence = _safe_has_relation(incident, "postmortem_for")
|
|
39
|
+
has_jira = _safe_has_relation(incident, "jira_postmortem_for")
|
|
37
40
|
|
|
38
41
|
if has_confluence or has_jira:
|
|
39
42
|
blocks.append(
|
|
@@ -53,12 +56,29 @@ class PostMortemModal(
|
|
|
53
56
|
text=f"• Jira: <{incident.jira_postmortem_for.issue_url}|{incident.jira_postmortem_for.jira_issue_key}>"
|
|
54
57
|
)
|
|
55
58
|
)
|
|
56
|
-
|
|
59
|
+
elif incident.needs_postmortem:
|
|
57
60
|
blocks.append(
|
|
58
61
|
SectionBlock(
|
|
59
62
|
text=f"Post-mortem for incident #{incident.id} will be automatically created when the incident reaches MITIGATED status."
|
|
60
63
|
)
|
|
61
64
|
)
|
|
65
|
+
else:
|
|
66
|
+
blocks.extend(
|
|
67
|
+
[
|
|
68
|
+
SectionBlock(
|
|
69
|
+
text="P3 incident post-mortem is not mandatory. You can still have one if you think is necessary by clicking on the button below."
|
|
70
|
+
),
|
|
71
|
+
ActionsBlock(
|
|
72
|
+
elements=[
|
|
73
|
+
ButtonElement(
|
|
74
|
+
text="Create post-mortem now",
|
|
75
|
+
action_id="incident_create_postmortem_now",
|
|
76
|
+
value=str(incident.id),
|
|
77
|
+
)
|
|
78
|
+
]
|
|
79
|
+
),
|
|
80
|
+
]
|
|
81
|
+
)
|
|
62
82
|
|
|
63
83
|
return View(
|
|
64
84
|
type="modal",
|
|
@@ -76,4 +96,52 @@ class PostMortemModal(
|
|
|
76
96
|
ack()
|
|
77
97
|
|
|
78
98
|
|
|
99
|
+
@app.action("incident_create_postmortem_now")
|
|
100
|
+
def handle_create_postmortem_action(ack: Ack, body: dict[str, Any]) -> None:
|
|
101
|
+
"""Create post-mortem(s) on demand from the modal (e.g. P3+ incidents)."""
|
|
102
|
+
ack()
|
|
103
|
+
|
|
104
|
+
incident_id = str(body.get("actions", [{}])[0].get("value", "")).strip()
|
|
105
|
+
try:
|
|
106
|
+
incident = Incident.objects.get(pk=incident_id)
|
|
107
|
+
except Incident.DoesNotExist:
|
|
108
|
+
respond(body, text=":x: Incident not found.")
|
|
109
|
+
return
|
|
110
|
+
|
|
111
|
+
try:
|
|
112
|
+
confluence_pm, jira_pm = PostMortemManager.create_postmortem_for_incident(
|
|
113
|
+
incident
|
|
114
|
+
)
|
|
115
|
+
except Exception:
|
|
116
|
+
logger.exception("Failed to create post-mortem for incident #%s", incident_id)
|
|
117
|
+
respond(body, text=":x: Failed to create post-mortem. Please try again.")
|
|
118
|
+
return
|
|
119
|
+
|
|
120
|
+
created_targets: list[str] = []
|
|
121
|
+
if confluence_pm:
|
|
122
|
+
created_targets.append("Confluence")
|
|
123
|
+
if jira_pm:
|
|
124
|
+
created_targets.append("Jira")
|
|
125
|
+
|
|
126
|
+
if created_targets:
|
|
127
|
+
targets = " and ".join(created_targets)
|
|
128
|
+
respond(body, text=f":white_check_mark: {targets} post-mortem created.")
|
|
129
|
+
else:
|
|
130
|
+
respond(body, text=":warning: No post-mortem was created.")
|
|
131
|
+
|
|
132
|
+
|
|
79
133
|
modal_postmortem = PostMortemModal()
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _safe_has_relation(instance: Incident, attr: str) -> bool:
|
|
137
|
+
"""Safely check if a reverse relation exists without triggering KeyError in cache.
|
|
138
|
+
|
|
139
|
+
Django's reverse OneToOne descriptor can raise KeyError when using hasattr
|
|
140
|
+
on unsaved or freshly created instances. We guard against that here.
|
|
141
|
+
"""
|
|
142
|
+
try:
|
|
143
|
+
getattr(instance, attr)
|
|
144
|
+
except (AttributeError, ObjectDoesNotExist, KeyError):
|
|
145
|
+
return False
|
|
146
|
+
else:
|
|
147
|
+
return True
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: firefighter-incident
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.29
|
|
4
4
|
Summary: Incident Management tool made for Slack using Django
|
|
5
5
|
Project-URL: Repository, https://github.com/ManoManoTech/firefighter-incident
|
|
6
6
|
Project-URL: Documentation, https://manomanotech.github.io/firefighter-incident/latest/
|
|
@@ -6,7 +6,7 @@ gunicorn.conf.py,sha256=vHsTGjaKOr8FDMp6fTKYTX4AtokmPgYvvt5Mr0Q6APc,273
|
|
|
6
6
|
main.py,sha256=CsbprHoOYhjCLpTJmq9Z_aRYFoFgWxoz2pDLuwm8Eqg,1558
|
|
7
7
|
manage.py,sha256=5ivHGD13C6nJ8QvltKsJ9T9akA5he8da70HLWaEP3k8,689
|
|
8
8
|
firefighter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
-
firefighter/_version.py,sha256=
|
|
9
|
+
firefighter/_version.py,sha256=p4kcB0BmpcnphBIseH7188jNCCOqFZSly7Pka9LrUDM,706
|
|
10
10
|
firefighter/api/__init__.py,sha256=JQW0Bv6xwGqy7ioxx3h6UGMzkkJ4DntDpbvV1Ncgi8k,136
|
|
11
11
|
firefighter/api/admin.py,sha256=x9Ysy-GiYjb0rynmFdS9g56e6n24fkN0ouGy5QD9Yrc,4629
|
|
12
12
|
firefighter/api/apps.py,sha256=P5uU1_gMrDfzurdMbfqw1Bnb2uNKKcMq17WBPg2sLhc,204
|
|
@@ -264,7 +264,7 @@ firefighter/incidents/views/date_filter.py,sha256=fUhTjkBulMokI5tAHuqNDVv1dyspjm
|
|
|
264
264
|
firefighter/incidents/views/date_utils.py,sha256=tiRTlh7PmRv4eAH0asiSX3Gn7ajsal9egm4S1d7s3_s,5759
|
|
265
265
|
firefighter/incidents/views/errors.py,sha256=yDuH0YOdGf-voVNEC51yR9Ie3OU-az7g2EqWs_uV1Kk,7855
|
|
266
266
|
firefighter/incidents/views/reports.py,sha256=1Iegx04w-oHw4cj7u9w2_s7T_e9FH5I6RRPTwDZwZhg,20973
|
|
267
|
-
firefighter/incidents/views/views.py,sha256=
|
|
267
|
+
firefighter/incidents/views/views.py,sha256=Gx1qAYQmN6B60-smnG_Lc_xvzXN2Lrfr6J11wEBCufQ,10987
|
|
268
268
|
firefighter/incidents/views/components/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
269
269
|
firefighter/incidents/views/components/details.py,sha256=gFEezmL1TcVYnM_ryLNNMaynuIdjYV31Qzx_GfzrQiA,1040
|
|
270
270
|
firefighter/incidents/views/components/list.py,sha256=u8HfXetmdL59h_4AZIhiHmKcmrPRZXgekPfnucB4Rek,2207
|
|
@@ -291,7 +291,7 @@ firefighter/jira_app/signals/incident_key_events_updated.py,sha256=uaV3MON1QzeOZ
|
|
|
291
291
|
firefighter/jira_app/signals/postmortem_created.py,sha256=S7sKbEgo5RroC5ji1OepAT3HMckwS120dpKeUteNaXA,8300
|
|
292
292
|
firefighter/jira_app/tasks/__init__.py,sha256=XLCPkolM6LwIUGv0MNbk_0lCuBHyzgRFHsE3vTRD5ds,86
|
|
293
293
|
firefighter/jira_app/tasks/sync_users_jira.py,sha256=sSSLsVCdzkPNRS6Gt8j0YwCTuoRqkJAJLxDBu7IElmM,1437
|
|
294
|
-
firefighter/jira_app/templates/jira/postmortem/impact.txt,sha256=
|
|
294
|
+
firefighter/jira_app/templates/jira/postmortem/impact.txt,sha256=WeF6uuwKjSER-CYqpLgH6p-wIkWgtVaeGKp8TsWSekQ,515
|
|
295
295
|
firefighter/jira_app/templates/jira/postmortem/incident_summary.txt,sha256=Bnias41O8TR2v0CAWpOoyRVVBAyx6vk2iICeQsLOuCs,392
|
|
296
296
|
firefighter/jira_app/templates/jira/postmortem/mitigation_actions.txt,sha256=7DWOcMhU0NiJugiUmEvSg1Z3ajW7IDZejprxt8urue0,242
|
|
297
297
|
firefighter/jira_app/templates/jira/postmortem/root_causes.txt,sha256=27DgQdrtHvHcju1llyYqU1jufegeHjJN_qvVkyECINM,291
|
|
@@ -364,7 +364,7 @@ firefighter/slack/management/commands/generate_manifest.py,sha256=zFWHAC7ioozcDd
|
|
|
364
364
|
firefighter/slack/management/commands/switch_test_users.py,sha256=2KTSvCBxsEvZa61J8p0r3huPNhwuytcj2J7IawwZWpQ,11064
|
|
365
365
|
firefighter/slack/messages/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
366
366
|
firefighter/slack/messages/base.py,sha256=biH-YEAaldJ-OLHEs5ZjW-gtUYUbjOqxrAEflqV2XS0,4593
|
|
367
|
-
firefighter/slack/messages/slack_messages.py,sha256=
|
|
367
|
+
firefighter/slack/messages/slack_messages.py,sha256=9-zbxBuzDweKQZiwc2DMdiQcsnd23CGCi4YWhGgJx60,43713
|
|
368
368
|
firefighter/slack/migrations/0001_initial_oss.py,sha256=XmTPgq7zCME2xDwzRFoVi4OegSIG9eSKoyTNoW05Qtg,12933
|
|
369
369
|
firefighter/slack/migrations/0002_usergroup_tag.py,sha256=098tmGA81mT-R2uhb6uQfZ7gKiRG9bFhEwQ8rrp4SKM,583
|
|
370
370
|
firefighter/slack/migrations/0003_alter_usergroup_tag.py,sha256=ncH3KUWEPZHlbdcAtOJ0KGt5H6EX-cKspTGU3osrAhE,591
|
|
@@ -423,7 +423,7 @@ firefighter/slack/views/modals/downgrade_workflow.py,sha256=cRWsm3DmKRRI1-Jpjprb
|
|
|
423
423
|
firefighter/slack/views/modals/edit.py,sha256=1N0OBSxsDuN6lJoH-djbEljy7f0LcDEpJF-U5YoEFXA,5895
|
|
424
424
|
firefighter/slack/views/modals/key_event_message.py,sha256=C6yhQLQ6jBuhIr-YAoAyt-qZKu0V6nJMGZ_t3DLtUbo,5943
|
|
425
425
|
firefighter/slack/views/modals/open.py,sha256=YIxpo8_C4cWCy_pQ3YRWl7NMyLmjqNjggTQINTBW6mo,29189
|
|
426
|
-
firefighter/slack/views/modals/postmortem.py,sha256=
|
|
426
|
+
firefighter/slack/views/modals/postmortem.py,sha256=Re4F0ZQEEOfdXljVCkswU1ESZksmpAqRy3es3_Wmeiw,5070
|
|
427
427
|
firefighter/slack/views/modals/select.py,sha256=Y-Ji_ALnzhYkXDBAyi497UL1Xn2vCGqXCtj8eog75Jk,3312
|
|
428
428
|
firefighter/slack/views/modals/send_sos.py,sha256=bP6HgYyDwPrIcTq7n_sQz6UQsxhYbvBDS4HjM0uRccA,4838
|
|
429
429
|
firefighter/slack/views/modals/status.py,sha256=C8-eJRtquSeaHe568SC7yCFef1k14m2_6lUqBezdSH8,3970
|
|
@@ -521,12 +521,13 @@ firefighter_tests/test_slack/views/modals/test_form_utils_multiple_choice.py,sha
|
|
|
521
521
|
firefighter_tests/test_slack/views/modals/test_key_event_message.py,sha256=BCg-c27ZLJqNgFuG4JDgXrSTp8_sT4FeBtpASzSq8NI,1107
|
|
522
522
|
firefighter_tests/test_slack/views/modals/test_open.py,sha256=IzgG9le5NN_CvltehAIqkj94ioTKCqdA6yoRp2NlNsE,10700
|
|
523
523
|
firefighter_tests/test_slack/views/modals/test_opening_unified.py,sha256=OejtLyc_mehav2TDaLzUnhilMNvhCzc6T4FodCqfQPk,17406
|
|
524
|
+
firefighter_tests/test_slack/views/modals/test_postmortem_modal.py,sha256=zNN40sIRSM5w_kyOcQ-AODkH5WpVxkSGVXkh9rMgmQ0,2378
|
|
524
525
|
firefighter_tests/test_slack/views/modals/test_send_sos.py,sha256=_rE6jD-gOzcGyhlY0R9GzlGtPx65oOOguJYdENgxtLc,1289
|
|
525
526
|
firefighter_tests/test_slack/views/modals/test_status.py,sha256=oQzPfwdg2tkbo9nfkO1GfS3WydxqSC6vy1AZjZDKT30,2226
|
|
526
527
|
firefighter_tests/test_slack/views/modals/test_update_status.py,sha256=vbHGx6dkM_0swE1vJ0HrkhI1oJzD_WHZuIQ-_arAxXo,55686
|
|
527
528
|
firefighter_tests/test_slack/views/modals/test_utils.py,sha256=DJd2n9q6fFu8UuCRdiq9U_Cn19MdnC5c-ydLLrk6rkc,5218
|
|
528
|
-
firefighter_incident-0.0.
|
|
529
|
-
firefighter_incident-0.0.
|
|
530
|
-
firefighter_incident-0.0.
|
|
531
|
-
firefighter_incident-0.0.
|
|
532
|
-
firefighter_incident-0.0.
|
|
529
|
+
firefighter_incident-0.0.29.dist-info/METADATA,sha256=vMdQRWVVxjc_sHtb6YnFH7Z_iZB0xAwsFPG0inDS6SQ,5570
|
|
530
|
+
firefighter_incident-0.0.29.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
531
|
+
firefighter_incident-0.0.29.dist-info/entry_points.txt,sha256=c13meJbv7YNmYz7MipMOQwzQ5IeFOPXUBYAJ44XMQsM,61
|
|
532
|
+
firefighter_incident-0.0.29.dist-info/licenses/LICENSE,sha256=krRiGp-a9-1nH1bWpBEdxyTKLhjLmn6DMVVoIb0zF90,1087
|
|
533
|
+
firefighter_incident-0.0.29.dist-info/RECORD,,
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from types import SimpleNamespace
|
|
4
|
+
|
|
5
|
+
from firefighter.slack.views.modals.postmortem import PostMortemModal
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TestPostMortemModal:
|
|
9
|
+
@staticmethod
|
|
10
|
+
def _get_text_blocks(view_dict: dict) -> list[str]:
|
|
11
|
+
return [
|
|
12
|
+
block.get("text", {}).get("text", "")
|
|
13
|
+
for block in view_dict.get("blocks", [])
|
|
14
|
+
if block.get("type") == "section"
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
@staticmethod
|
|
18
|
+
def _get_action_elements(view_dict: dict) -> list[dict]:
|
|
19
|
+
elements: list[dict] = []
|
|
20
|
+
for block in view_dict.get("blocks", []):
|
|
21
|
+
if block.get("type") == "actions":
|
|
22
|
+
elements.extend(block.get("elements", []))
|
|
23
|
+
return elements
|
|
24
|
+
|
|
25
|
+
@staticmethod
|
|
26
|
+
def test_p1_p2_shows_auto_creation_message(mocker):
|
|
27
|
+
incident = SimpleNamespace(id=123, needs_postmortem=True)
|
|
28
|
+
|
|
29
|
+
# No existing PMs
|
|
30
|
+
mocker.patch(
|
|
31
|
+
"firefighter.slack.views.modals.postmortem._safe_has_relation",
|
|
32
|
+
return_value=False,
|
|
33
|
+
create=True,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
view = PostMortemModal().build_modal_fn(incident)
|
|
37
|
+
view_dict = view.to_dict()
|
|
38
|
+
|
|
39
|
+
texts = TestPostMortemModal._get_text_blocks(view_dict)
|
|
40
|
+
assert any(
|
|
41
|
+
"automatically created when the incident reaches MITIGATED" in t
|
|
42
|
+
for t in texts
|
|
43
|
+
)
|
|
44
|
+
# No manual-create button for mandatory PMs
|
|
45
|
+
action_ids = [
|
|
46
|
+
el.get("action_id")
|
|
47
|
+
for el in TestPostMortemModal._get_action_elements(view_dict)
|
|
48
|
+
]
|
|
49
|
+
assert "incident_create_postmortem_now" not in action_ids
|
|
50
|
+
|
|
51
|
+
@staticmethod
|
|
52
|
+
def test_p3_shows_optional_message_and_button(mocker):
|
|
53
|
+
incident = SimpleNamespace(id=456, needs_postmortem=False)
|
|
54
|
+
|
|
55
|
+
# No existing PMs
|
|
56
|
+
mocker.patch(
|
|
57
|
+
"firefighter.slack.views.modals.postmortem._safe_has_relation",
|
|
58
|
+
return_value=False,
|
|
59
|
+
create=True,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
view = PostMortemModal().build_modal_fn(incident)
|
|
63
|
+
view_dict = view.to_dict()
|
|
64
|
+
|
|
65
|
+
texts = TestPostMortemModal._get_text_blocks(view_dict)
|
|
66
|
+
assert any("P3 incident post-mortem is not mandatory" in t for t in texts)
|
|
67
|
+
|
|
68
|
+
action_ids = [
|
|
69
|
+
el.get("action_id")
|
|
70
|
+
for el in TestPostMortemModal._get_action_elements(view_dict)
|
|
71
|
+
]
|
|
72
|
+
assert "incident_create_postmortem_now" in action_ids
|
|
File without changes
|
{firefighter_incident-0.0.27.dist-info → firefighter_incident-0.0.29.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{firefighter_incident-0.0.27.dist-info → firefighter_incident-0.0.29.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|