mirror of https://github.com/zulip/zulip.git
integrations: Support handling batch updates for Clubhouse.
As the user can select multiple stories and edit multiple properties at the same time, this can generate requests without a "primary_id" containing multiple actions, while each action contains multiple changes. Fixes: #18022
This commit is contained in:
parent
e1a37d2e0a
commit
9ac55a8cf6
|
@ -0,0 +1,233 @@
|
||||||
|
{
|
||||||
|
"id": "60723fdc-2c6d-4b31-b160-ef4d438dc5bc",
|
||||||
|
"changed_at": "2021-04-11T00:16:28.845Z",
|
||||||
|
"version": "v1",
|
||||||
|
"member_id": "6071752f-e16e-4f79-b41e-7c78b76aa4bd",
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"id": 17,
|
||||||
|
"entity_type": "story",
|
||||||
|
"action": "update",
|
||||||
|
"name": "asd4",
|
||||||
|
"story_type": "bug",
|
||||||
|
"app_url": "https://app.clubhouse.io/pig208/story/17",
|
||||||
|
"changes": {
|
||||||
|
"story_type": {
|
||||||
|
"new": "bug",
|
||||||
|
"old": "feature"
|
||||||
|
},
|
||||||
|
"epic_id": {
|
||||||
|
"new": 23,
|
||||||
|
"old": 29
|
||||||
|
},
|
||||||
|
"requested_by_id": {
|
||||||
|
"new": "60723f5f-28ca-4ec2-a3a2-37b2dc5606ae",
|
||||||
|
"old": "6071752f-e16e-4f79-b41e-7c78b76aa4bd"
|
||||||
|
},
|
||||||
|
"label_ids": {
|
||||||
|
"adds": [
|
||||||
|
8
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"group_id": {
|
||||||
|
"new": "6071adb0-641f-46c4-b5e1-4945dae399ea",
|
||||||
|
"old": "6071752f-ece0-4772-854c-05a9666c480f"
|
||||||
|
},
|
||||||
|
"workflow_state_id": {
|
||||||
|
"new": 500000010,
|
||||||
|
"old": 500000006
|
||||||
|
},
|
||||||
|
"follower_ids": {
|
||||||
|
"adds": [
|
||||||
|
"60723f5f-28ca-4ec2-a3a2-37b2dc5606ae"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"owner_ids": {
|
||||||
|
"adds": [
|
||||||
|
"60723f5f-28ca-4ec2-a3a2-37b2dc5606ae"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"position": {
|
||||||
|
"new": 42147811328,
|
||||||
|
"old": 32147483648
|
||||||
|
},
|
||||||
|
"project_id": {
|
||||||
|
"new": 28,
|
||||||
|
"old": 2
|
||||||
|
},
|
||||||
|
"deadline": {
|
||||||
|
"new": "2021-04-12T16:00:00Z",
|
||||||
|
"old": "2021-04-11T16:00:00Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 26,
|
||||||
|
"entity_type": "story",
|
||||||
|
"action": "update",
|
||||||
|
"name": "new1",
|
||||||
|
"story_type": "bug",
|
||||||
|
"app_url": "https://app.clubhouse.io/pig208/story/26",
|
||||||
|
"changes": {
|
||||||
|
"story_type": {
|
||||||
|
"new": "bug",
|
||||||
|
"old": "feature"
|
||||||
|
},
|
||||||
|
"epic_id": {
|
||||||
|
"new": 23,
|
||||||
|
"old": 29
|
||||||
|
},
|
||||||
|
"requested_by_id": {
|
||||||
|
"new": "60723f5f-28ca-4ec2-a3a2-37b2dc5606ae",
|
||||||
|
"old": "6071752f-e16e-4f79-b41e-7c78b76aa4bd"
|
||||||
|
},
|
||||||
|
"label_ids": {
|
||||||
|
"adds": [
|
||||||
|
8
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"group_id": {
|
||||||
|
"new": "6071adb0-641f-46c4-b5e1-4945dae399ea",
|
||||||
|
"old": "6071752f-ece0-4772-854c-05a9666c480f"
|
||||||
|
},
|
||||||
|
"workflow_state_id": {
|
||||||
|
"new": 500000010,
|
||||||
|
"old": 500000006
|
||||||
|
},
|
||||||
|
"follower_ids": {
|
||||||
|
"adds": [
|
||||||
|
"60723f5f-28ca-4ec2-a3a2-37b2dc5606ae"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"owner_ids": {
|
||||||
|
"adds": [
|
||||||
|
"60723f5f-28ca-4ec2-a3a2-37b2dc5606ae"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"position": {
|
||||||
|
"new": 42147942400,
|
||||||
|
"old": 32147680256
|
||||||
|
},
|
||||||
|
"project_id": {
|
||||||
|
"new": 28,
|
||||||
|
"old": 2
|
||||||
|
},
|
||||||
|
"deadline": {
|
||||||
|
"new": "2021-04-12T16:00:00Z",
|
||||||
|
"old": "2021-04-11T16:00:00Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 27,
|
||||||
|
"entity_type": "story",
|
||||||
|
"action": "update",
|
||||||
|
"name": "new2",
|
||||||
|
"story_type": "bug",
|
||||||
|
"app_url": "https://app.clubhouse.io/pig208/story/27",
|
||||||
|
"changes": {
|
||||||
|
"story_type": {
|
||||||
|
"new": "bug",
|
||||||
|
"old": "feature"
|
||||||
|
},
|
||||||
|
"epic_id": {
|
||||||
|
"new": 23,
|
||||||
|
"old": 29
|
||||||
|
},
|
||||||
|
"requested_by_id": {
|
||||||
|
"new": "60723f5f-28ca-4ec2-a3a2-37b2dc5606ae",
|
||||||
|
"old": "6071752f-e16e-4f79-b41e-7c78b76aa4bd"
|
||||||
|
},
|
||||||
|
"label_ids": {
|
||||||
|
"adds": [
|
||||||
|
8
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"group_id": {
|
||||||
|
"new": "6071adb0-641f-46c4-b5e1-4945dae399ea",
|
||||||
|
"old": "6071752f-ece0-4772-854c-05a9666c480f"
|
||||||
|
},
|
||||||
|
"workflow_state_id": {
|
||||||
|
"new": 500000010,
|
||||||
|
"old": 500000006
|
||||||
|
},
|
||||||
|
"follower_ids": {
|
||||||
|
"adds": [
|
||||||
|
"60723f5f-28ca-4ec2-a3a2-37b2dc5606ae"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"owner_ids": {
|
||||||
|
"adds": [
|
||||||
|
"60723f5f-28ca-4ec2-a3a2-37b2dc5606ae"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"position": {
|
||||||
|
"new": 42147876864,
|
||||||
|
"old": 32147614720
|
||||||
|
},
|
||||||
|
"project_id": {
|
||||||
|
"new": 28,
|
||||||
|
"old": 2
|
||||||
|
},
|
||||||
|
"deadline": {
|
||||||
|
"new": "2021-04-12T16:00:00Z",
|
||||||
|
"old": "2021-04-11T16:00:00Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"id": "6071752f-ece0-4772-854c-05a9666c480f",
|
||||||
|
"entity_type": "group",
|
||||||
|
"name": "Team 1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 500000010,
|
||||||
|
"entity_type": "workflow-state",
|
||||||
|
"name": "Ready for Review",
|
||||||
|
"type": "started"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 500000006,
|
||||||
|
"entity_type": "workflow-state",
|
||||||
|
"name": "In Development",
|
||||||
|
"type": "started"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 8,
|
||||||
|
"entity_type": "label",
|
||||||
|
"name": "low priority",
|
||||||
|
"app_url": "https://app.clubhouse.io/pig208/label/8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 23,
|
||||||
|
"entity_type": "epic",
|
||||||
|
"name": "testeipc",
|
||||||
|
"app_url": "https://app.clubhouse.io/pig208/epic/23"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"entity_type": "project",
|
||||||
|
"name": "Product Development",
|
||||||
|
"app_url": "https://app.clubhouse.io/pig208/project/2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "6071adb0-641f-46c4-b5e1-4945dae399ea",
|
||||||
|
"entity_type": "group",
|
||||||
|
"name": "team2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 29,
|
||||||
|
"entity_type": "epic",
|
||||||
|
"name": "epic",
|
||||||
|
"app_url": "https://app.clubhouse.io/pig208/epic/29"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 28,
|
||||||
|
"entity_type": "project",
|
||||||
|
"name": "test2",
|
||||||
|
"app_url": "https://app.clubhouse.io/pig208/project/28"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,133 @@
|
||||||
|
{
|
||||||
|
"id": "60723fdc-2c6d-4b31-b160-ef4d438dc5bc",
|
||||||
|
"changed_at": "2021-04-11T00:16:28.845Z",
|
||||||
|
"version": "v1",
|
||||||
|
"member_id": "6071752f-e16e-4f79-b41e-7c78b76aa4bd",
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"id": 17,
|
||||||
|
"entity_type": "story",
|
||||||
|
"action": "update",
|
||||||
|
"name": "asd4",
|
||||||
|
"story_type": "bug",
|
||||||
|
"app_url": "https://app.clubhouse.io/pig208/story/17",
|
||||||
|
"changes": {
|
||||||
|
"story_type": {
|
||||||
|
"new": "bug",
|
||||||
|
"old": "feature"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 26,
|
||||||
|
"entity_type": "story",
|
||||||
|
"action": "update",
|
||||||
|
"name": "new1",
|
||||||
|
"story_type": "bug",
|
||||||
|
"app_url": "https://app.clubhouse.io/pig208/story/26",
|
||||||
|
"changes": {
|
||||||
|
"epic_id": {
|
||||||
|
"new": 23,
|
||||||
|
"old": 29
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 27,
|
||||||
|
"entity_type": "story",
|
||||||
|
"action": "update",
|
||||||
|
"name": "new2",
|
||||||
|
"story_type": "bug",
|
||||||
|
"app_url": "https://app.clubhouse.io/pig208/story/27",
|
||||||
|
"changes": {
|
||||||
|
"label_ids": {
|
||||||
|
"adds": [
|
||||||
|
8
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 28,
|
||||||
|
"entity_type": "story",
|
||||||
|
"action": "update",
|
||||||
|
"name": "new3",
|
||||||
|
"story_type": "bug",
|
||||||
|
"app_url": "https://app.clubhouse.io/pig208/story/28",
|
||||||
|
"changes": {
|
||||||
|
"workflow_state_id": {
|
||||||
|
"new": 500000010,
|
||||||
|
"old": 500000006
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 29,
|
||||||
|
"entity_type": "story",
|
||||||
|
"action": "update",
|
||||||
|
"name": "new4",
|
||||||
|
"story_type": "bug",
|
||||||
|
"app_url": "https://app.clubhouse.io/pig208/story/29",
|
||||||
|
"changes": {
|
||||||
|
"project_id": {
|
||||||
|
"new": 28,
|
||||||
|
"old": 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"id": "6071752f-ece0-4772-854c-05a9666c480f",
|
||||||
|
"entity_type": "group",
|
||||||
|
"name": "Team 1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 500000010,
|
||||||
|
"entity_type": "workflow-state",
|
||||||
|
"name": "Ready for Review",
|
||||||
|
"type": "started"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 500000006,
|
||||||
|
"entity_type": "workflow-state",
|
||||||
|
"name": "In Development",
|
||||||
|
"type": "started"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 8,
|
||||||
|
"entity_type": "label",
|
||||||
|
"name": "low priority",
|
||||||
|
"app_url": "https://app.clubhouse.io/pig208/label/8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 23,
|
||||||
|
"entity_type": "epic",
|
||||||
|
"name": "testeipc",
|
||||||
|
"app_url": "https://app.clubhouse.io/pig208/epic/23"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"entity_type": "project",
|
||||||
|
"name": "Product Development",
|
||||||
|
"app_url": "https://app.clubhouse.io/pig208/project/2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "6071adb0-641f-46c4-b5e1-4945dae399ea",
|
||||||
|
"entity_type": "group",
|
||||||
|
"name": "team2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 29,
|
||||||
|
"entity_type": "epic",
|
||||||
|
"name": "epic",
|
||||||
|
"app_url": "https://app.clubhouse.io/pig208/epic/29"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 28,
|
||||||
|
"entity_type": "project",
|
||||||
|
"name": "test2",
|
||||||
|
"app_url": "https://app.clubhouse.io/pig208/project/28"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
{
|
||||||
|
"id": "60723fdc-2c6d-4b31-b160-ef4d438dc5bc",
|
||||||
|
"changed_at": "2021-04-11T00:16:28.845Z",
|
||||||
|
"version": "v1",
|
||||||
|
"member_id": "6071752f-e16e-4f79-b41e-7c78b76aa4bd",
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"id": 17,
|
||||||
|
"entity_type": "story",
|
||||||
|
"action": "update",
|
||||||
|
"name": "asd4",
|
||||||
|
"story_type": "bug",
|
||||||
|
"app_url": "https://app.clubhouse.io/pig208/story/17",
|
||||||
|
"changes": {
|
||||||
|
"deadline": {
|
||||||
|
"new": "2021-04-12T16:00:00Z",
|
||||||
|
"old": "2021-04-11T16:00:00Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 26,
|
||||||
|
"entity_type": "story",
|
||||||
|
"action": "update",
|
||||||
|
"name": "new1",
|
||||||
|
"story_type": "bug",
|
||||||
|
"app_url": "https://app.clubhouse.io/pig208/story/26",
|
||||||
|
"changes": {
|
||||||
|
"owner_ids": {
|
||||||
|
"adds": [
|
||||||
|
"60723f5f-28ca-4ec2-a3a2-37b2dc5606ae"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 27,
|
||||||
|
"entity_type": "story",
|
||||||
|
"action": "update",
|
||||||
|
"name": "new2",
|
||||||
|
"story_type": "bug",
|
||||||
|
"app_url": "https://app.clubhouse.io/pig208/story/27",
|
||||||
|
"changes": {
|
||||||
|
"follower_ids": {
|
||||||
|
"adds": [
|
||||||
|
"60723f5f-28ca-4ec2-a3a2-37b2dc5606ae"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -289,6 +289,95 @@ class ClubhouseWebhookTest(WebhookTestCase):
|
||||||
"story_update_add_github_branch", "Testing pull requests with Story", expected_message
|
"story_update_add_github_branch", "Testing pull requests with Story", expected_message
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@patch("zerver.webhooks.clubhouse.view.check_send_webhook_message")
|
||||||
|
def test_story_update_batch(self, check_send_webhook_message_mock: MagicMock) -> None:
|
||||||
|
payload = self.get_body("story_update_everything_at_once")
|
||||||
|
self.client_post(self.url, payload, content_type="application/json")
|
||||||
|
expected_message = "The story [{name}]({url}) was moved from Epic **epic** to **testeipc**, Project **Product Development** to **test2**, and changed from type **feature** to **bug**, and added with the new label **low priority** (In Development -> Ready for Review)."
|
||||||
|
request, user_profile = (
|
||||||
|
check_send_webhook_message_mock.call_args_list[0][0][0],
|
||||||
|
check_send_webhook_message_mock.call_args_list[0][0][1],
|
||||||
|
)
|
||||||
|
expected_list = [
|
||||||
|
call(
|
||||||
|
request,
|
||||||
|
user_profile,
|
||||||
|
"asd4",
|
||||||
|
expected_message.format(
|
||||||
|
name="asd4", url="https://app.clubhouse.io/pig208/story/17"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
call(
|
||||||
|
request,
|
||||||
|
user_profile,
|
||||||
|
"new1",
|
||||||
|
expected_message.format(
|
||||||
|
name="new1", url="https://app.clubhouse.io/pig208/story/26"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
call(
|
||||||
|
request,
|
||||||
|
user_profile,
|
||||||
|
"new2",
|
||||||
|
expected_message.format(
|
||||||
|
name="new2", url="https://app.clubhouse.io/pig208/story/27"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
self.assertEqual(check_send_webhook_message_mock.call_args_list, expected_list)
|
||||||
|
|
||||||
|
@patch("zerver.webhooks.clubhouse.view.check_send_webhook_message")
|
||||||
|
def test_story_update_batch_each_with_one_change(
|
||||||
|
self, check_send_webhook_message_mock: MagicMock
|
||||||
|
) -> None:
|
||||||
|
payload = self.get_body("story_update_multiple_at_once")
|
||||||
|
self.client_post(self.url, payload, content_type="application/json")
|
||||||
|
expected_messages = [
|
||||||
|
(
|
||||||
|
"asd4",
|
||||||
|
"The type of the story [asd4](https://app.clubhouse.io/pig208/story/17) was changed from **feature** to **bug**.",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"new1",
|
||||||
|
"The story [new1](https://app.clubhouse.io/pig208/story/26) was moved from **epic** to **testeipc**.",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"new2",
|
||||||
|
"The label **low priority** was added to the story [new2](https://app.clubhouse.io/pig208/story/27).",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"new3",
|
||||||
|
"State of the story [new3](https://app.clubhouse.io/pig208/story/28) was changed from **In Development** to **Ready for Review**.",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"new4",
|
||||||
|
"The story [new4](https://app.clubhouse.io/pig208/story/29) was moved from the **Product Development** project to **test2**.",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
request, user_profile = (
|
||||||
|
check_send_webhook_message_mock.call_args_list[0][0][0],
|
||||||
|
check_send_webhook_message_mock.call_args_list[0][0][1],
|
||||||
|
)
|
||||||
|
expected_list = [
|
||||||
|
call(
|
||||||
|
request,
|
||||||
|
user_profile,
|
||||||
|
expected_message[0],
|
||||||
|
expected_message[1],
|
||||||
|
)
|
||||||
|
for expected_message in expected_messages
|
||||||
|
]
|
||||||
|
self.assertEqual(check_send_webhook_message_mock.call_args_list, expected_list)
|
||||||
|
|
||||||
|
@patch("zerver.webhooks.clubhouse.view.check_send_webhook_message")
|
||||||
|
def test_story_update_batch_not_supported_ignore(
|
||||||
|
self, check_send_webhook_message_mock: MagicMock
|
||||||
|
) -> None:
|
||||||
|
payload = self.get_body("story_update_multiple_not_supported")
|
||||||
|
result = self.client_post(self.url, payload, content_type="application/json")
|
||||||
|
self.assertFalse(check_send_webhook_message_mock.called)
|
||||||
|
self.assert_json_success(result)
|
||||||
|
|
||||||
@patch("zerver.webhooks.clubhouse.view.check_send_webhook_message")
|
@patch("zerver.webhooks.clubhouse.view.check_send_webhook_message")
|
||||||
def test_empty_post_request_body_ignore(
|
def test_empty_post_request_body_ignore(
|
||||||
self, check_send_webhook_message_mock: MagicMock
|
self, check_send_webhook_message_mock: MagicMock
|
||||||
|
|
|
@ -60,6 +60,10 @@ STORY_GITHUB_PR_TEMPLATE = (
|
||||||
)
|
)
|
||||||
STORY_GITHUB_COMMENT_PR_TEMPLATE = "Existing GitHub PR [#{name}]({url}) associated with story {name_template}{workflow_state_template}."
|
STORY_GITHUB_COMMENT_PR_TEMPLATE = "Existing GitHub PR [#{name}]({url}) associated with story {name_template}{workflow_state_template}."
|
||||||
STORY_GITHUB_BRANCH_TEMPLATE = "New GitHub branch [{name}]({url}) associated with story {name_template}{workflow_state_template}."
|
STORY_GITHUB_BRANCH_TEMPLATE = "New GitHub branch [{name}]({url}) associated with story {name_template}{workflow_state_template}."
|
||||||
|
STORY_UPDATE_BATCH_TEMPLATE = "The story {name_template} {templates}{workflow_state_template}."
|
||||||
|
STORY_UPDATE_BATCH_CHANGED_TEMPLATE = "{operation} from {sub_templates}"
|
||||||
|
STORY_UPDATE_BATCH_CHANGED_SUB_TEMPLATE = "{entity_type} **{old}** to **{new}**"
|
||||||
|
STORY_UPDATE_BATCH_ADD_REMOVE_TEMPLATE = "{operation} with {entity}"
|
||||||
|
|
||||||
|
|
||||||
def get_action_with_primary_id(payload: Dict[str, Any]) -> Dict[str, Any]:
|
def get_action_with_primary_id(payload: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
@ -73,6 +77,10 @@ def get_action_with_primary_id(payload: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
def get_event(payload: Dict[str, Any], action: Dict[str, Any]) -> Optional[str]:
|
def get_event(payload: Dict[str, Any], action: Dict[str, Any]) -> Optional[str]:
|
||||||
event = "{}_{}".format(action["entity_type"], action["action"])
|
event = "{}_{}".format(action["entity_type"], action["action"])
|
||||||
|
|
||||||
|
# We only consider the change to be a batch update only if there are multiple stories (thus there is no primary_id)
|
||||||
|
if event == "story_update" and payload.get("primary_id") is None:
|
||||||
|
return "{}_{}".format(event, "batch")
|
||||||
|
|
||||||
if event in IGNORED_EVENTS:
|
if event in IGNORED_EVENTS:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -497,6 +505,101 @@ def get_story_update_owner_body(payload: Dict[str, Any], action: Dict[str, Any])
|
||||||
return STORY_UPDATE_OWNER_TEMPLATE.format(**kwargs)
|
return STORY_UPDATE_OWNER_TEMPLATE.format(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def get_story_update_batch_body(payload: Dict[str, Any], action: Dict[str, Any]) -> Optional[str]:
|
||||||
|
# When the user selects one or more stories with the checkbox, they can perform
|
||||||
|
# a batch update on multiple stories while changing multiple attribtues at the
|
||||||
|
# same time.
|
||||||
|
changes = action["changes"]
|
||||||
|
kwargs = {
|
||||||
|
"name_template": STORY_NAME_TEMPLATE.format(
|
||||||
|
name=action["name"],
|
||||||
|
app_url=action["app_url"],
|
||||||
|
),
|
||||||
|
"workflow_state_template": "",
|
||||||
|
}
|
||||||
|
|
||||||
|
templates = []
|
||||||
|
last_change = "other"
|
||||||
|
|
||||||
|
move_sub_templates = []
|
||||||
|
if "epic_id" in changes:
|
||||||
|
last_change = "epic"
|
||||||
|
move_sub_templates.append(
|
||||||
|
STORY_UPDATE_BATCH_CHANGED_SUB_TEMPLATE.format(
|
||||||
|
entity_type="Epic",
|
||||||
|
old=get_reference_by_id(payload, changes["epic_id"].get("old")).get("name"),
|
||||||
|
new=get_reference_by_id(payload, changes["epic_id"].get("new")).get("name"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if "project_id" in changes:
|
||||||
|
last_change = "project"
|
||||||
|
move_sub_templates.append(
|
||||||
|
STORY_UPDATE_BATCH_CHANGED_SUB_TEMPLATE.format(
|
||||||
|
entity_type="Project",
|
||||||
|
old=get_reference_by_id(payload, changes["project_id"].get("old")).get("name"),
|
||||||
|
new=get_reference_by_id(payload, changes["project_id"].get("new")).get("name"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if len(move_sub_templates) > 0:
|
||||||
|
templates.append(
|
||||||
|
STORY_UPDATE_BATCH_CHANGED_TEMPLATE.format(
|
||||||
|
operation="was moved",
|
||||||
|
sub_templates=", ".join(move_sub_templates),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if "story_type" in changes:
|
||||||
|
last_change = "type"
|
||||||
|
templates.append(
|
||||||
|
STORY_UPDATE_BATCH_CHANGED_TEMPLATE.format(
|
||||||
|
operation="{} changed".format("was" if len(templates) == 0 else "and"),
|
||||||
|
sub_templates=STORY_UPDATE_BATCH_CHANGED_SUB_TEMPLATE.format(
|
||||||
|
entity_type="type",
|
||||||
|
old=changes["story_type"].get("old"),
|
||||||
|
new=changes["story_type"].get("new"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if "label_ids" in changes:
|
||||||
|
last_change = "label"
|
||||||
|
labels = get_story_joined_label_list(payload, action, changes["label_ids"].get("adds"))
|
||||||
|
templates.append(
|
||||||
|
STORY_UPDATE_BATCH_ADD_REMOVE_TEMPLATE.format(
|
||||||
|
operation="{} added".format("was" if len(templates) == 0 else "and"),
|
||||||
|
entity="the new label{plural} {labels}".format(
|
||||||
|
plural="s" if len(changes["label_ids"]) > 1 else "", labels=labels
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if "workflow_state_id" in changes:
|
||||||
|
last_change = "state"
|
||||||
|
kwargs.update(
|
||||||
|
workflow_state_template=TRAILING_WORKFLOW_STATE_CHANGE_TEMPLATE.format(
|
||||||
|
old=get_reference_by_id(payload, changes["workflow_state_id"].get("old")).get(
|
||||||
|
"name"
|
||||||
|
),
|
||||||
|
new=get_reference_by_id(payload, changes["workflow_state_id"].get("new")).get(
|
||||||
|
"name"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Use the default template for state change if it is the only one change.
|
||||||
|
if len(templates) <= 1 or (len(templates) == 0 and last_change == "state"):
|
||||||
|
event: str = "{}_{}".format("story_update", last_change)
|
||||||
|
alternative_body_func = EVENT_BODY_FUNCTION_MAPPER.get(event)
|
||||||
|
# If last_change is not one of "epic", "project", "type", "label" and "state"
|
||||||
|
# we should ignore the action as there is no way for us to render the changes.
|
||||||
|
if alternative_body_func is None:
|
||||||
|
return None
|
||||||
|
return alternative_body_func(payload, action)
|
||||||
|
|
||||||
|
kwargs.update(templates=", ".join(templates))
|
||||||
|
return STORY_UPDATE_BATCH_TEMPLATE.format(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
def get_entity_name(
|
def get_entity_name(
|
||||||
payload: Dict[str, Any], action: Dict[str, Any], entity: Optional[str] = None
|
payload: Dict[str, Any], action: Dict[str, Any], entity: Optional[str] = None
|
||||||
) -> Optional[str]:
|
) -> Optional[str]:
|
||||||
|
@ -570,6 +673,7 @@ EVENT_BODY_FUNCTION_MAPPER: Dict[str, Callable[[Dict[str, Any], Dict[str, Any]],
|
||||||
"story_update_state": get_story_update_state_body,
|
"story_update_state": get_story_update_state_body,
|
||||||
"epic_update_name": partial(get_update_name_body, entity="epic"),
|
"epic_update_name": partial(get_update_name_body, entity="epic"),
|
||||||
"story_update_name": partial(get_update_name_body, entity="story"),
|
"story_update_name": partial(get_update_name_body, entity="story"),
|
||||||
|
"story_update_batch": get_story_update_batch_body,
|
||||||
}
|
}
|
||||||
|
|
||||||
EVENT_TOPIC_FUNCTION_MAPPER = {
|
EVENT_TOPIC_FUNCTION_MAPPER = {
|
||||||
|
|
Loading…
Reference in New Issue